Make sure resources are closed

This commit is contained in:
topjohnwu 2021-01-31 04:49:45 -08:00
parent 1559d3eb9f
commit c5ddd26bf4
6 changed files with 108 additions and 96 deletions

View File

@ -1,50 +0,0 @@
/*
* Copyright 2021 John "topjohnwu" Wu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.topjohnwu.superuser.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static com.topjohnwu.superuser.internal.Utils.UTF_8;
interface InputHandler {
String TAG = "SHELL_IN";
void handleInput(OutputStream in) throws IOException;
static InputHandler newInstance(String... cmd) {
return in -> {
for (String command : cmd) {
in.write(command.getBytes(UTF_8));
in.write('\n');
Utils.log(TAG, command);
}
};
}
static InputHandler newInstance(InputStream is) {
return in -> {
Utils.log(TAG, "<InputStream>");
Utils.pump(is, in);
is.close();
// Make sure it ends properly
in.write('\n');
};
}
}

View File

@ -21,21 +21,22 @@ import androidx.annotation.Nullable;
import com.topjohnwu.superuser.Shell;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
class JobImpl extends Shell.Job {
class JobImpl extends Shell.Job implements Closeable {
protected List<String> out, err;
private final List<InputHandler> handlers;
private final List<ShellInputSource> sources;
protected ShellImpl shell;
private boolean stderrSet = false;
JobImpl() {
handlers = new ArrayList<>();
sources = new ArrayList<>();
}
JobImpl(ShellImpl s) {
@ -48,7 +49,7 @@ class JobImpl extends Shell.Job {
boolean redirect = !stderrSet && shell.redirect;
result.out = out;
result.err = redirect ? out : err;
Shell.Task task = shell.newTask(handlers, result);
Shell.Task task = shell.newTask(sources, result);
try {
shell.execTask(task);
} catch (IOException e) {
@ -58,6 +59,8 @@ class JobImpl extends Shell.Job {
Utils.err(e);
return ResultImpl.INSTANCE;
}
} finally {
close();
}
if (redirect)
result.err = null;
@ -97,7 +100,7 @@ class JobImpl extends Shell.Job {
@Override
public Shell.Job add(@NonNull InputStream in) {
if (in != null)
handlers.add(InputHandler.newInstance(in));
sources.add(new InputStreamSource(in));
return this;
}
@ -105,8 +108,13 @@ class JobImpl extends Shell.Job {
@Override
public Shell.Job add(@NonNull String... cmds) {
if (cmds != null && cmds.length > 0)
handlers.add(InputHandler.newInstance(cmds));
sources.add(new CommandSource(cmds));
return this;
}
@Override
public void close() {
for (ShellInputSource src : sources)
src.close();
}
}

View File

@ -28,11 +28,11 @@ import java.util.concurrent.Executor;
class PendingJob extends JobImpl {
private final boolean isSU;
private boolean retried;
private boolean retry;
PendingJob(boolean su) {
isSU = su;
retried = false;
retry = true;
to(NOPList.getInstance());
}
@ -42,16 +42,19 @@ class PendingJob extends JobImpl {
try {
shell = MainShell.get();
} catch (NoShellException e) {
close();
return ResultImpl.INSTANCE;
}
if (isSU && !shell.isRoot())
if (isSU && !shell.isRoot()) {
close();
return ResultImpl.INSTANCE;
}
if (out instanceof NOPList)
out = new ArrayList<>();
Shell.Result res = super.exec();
if (!retried && res == ResultImpl.SHELL_ERR) {
if (retry && res == ResultImpl.SHELL_ERR) {
// The cached shell is terminated, try to re-run this task
retried = true;
retry = false;
return exec();
}
return res;
@ -61,6 +64,7 @@ class PendingJob extends JobImpl {
public void submit(@Nullable Executor executor, @Nullable Shell.ResultCallback cb) {
MainShell.get(null, s -> {
if (isSU && !s.isRoot()) {
close();
ResultImpl.INSTANCE.callback(executor, cb);
return;
}
@ -68,9 +72,9 @@ class PendingJob extends JobImpl {
out = (cb == null) ? null : new ArrayList<>();
shell = (ShellImpl) s;
super.submit(executor, res -> {
if (!retried && res == ResultImpl.SHELL_ERR) {
if (retry && res == ResultImpl.SHELL_ERR) {
// The cached shell is terminated, try to re-schedule this task
retried = true;
retry = false;
submit(executor, cb);
} else if (cb != null) {
cb.onResult(res);

View File

@ -43,6 +43,13 @@ import java.util.concurrent.TimeoutException;
import static com.topjohnwu.superuser.internal.Utils.UTF_8;
class ShellTerminatedException extends IOException {
ShellTerminatedException() {
super("Shell terminated unexpectedly");
}
}
class ShellImpl extends Shell {
private static final String TAG = "SHELLIMPL";
@ -241,17 +248,17 @@ class ShellImpl extends Shell {
return new JobImpl(this);
}
Task newTask(List<InputHandler> handlers, ResultImpl res) {
return new DefaultTask(handlers, res);
Task newTask(List<ShellInputSource> sources, ResultImpl res) {
return new DefaultTask(sources, res);
}
private class DefaultTask implements Task {
private final ResultImpl res;
private final List<InputHandler> handlers;
private final List<ShellInputSource> sources;
DefaultTask(List<InputHandler> h, ResultImpl r) {
handlers = h;
DefaultTask(List<ShellInputSource> s, ResultImpl r) {
sources = s;
res = r;
}
@ -271,8 +278,8 @@ class ShellImpl extends Shell {
err = EXECUTOR.submit(errGobbler.set(stderr, res.err));
}
for (InputHandler handler : handlers)
handler.handleInput(stdin);
for (ShellInputSource src : sources)
src.serve(stdin);
stdin.write(endCmd);
stdin.flush();
try {

View File

@ -0,0 +1,69 @@
/*
* Copyright 2021 John "topjohnwu" Wu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.topjohnwu.superuser.internal;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static com.topjohnwu.superuser.internal.Utils.UTF_8;
interface ShellInputSource extends Closeable {
String TAG = "SHELL_IN";
void serve(OutputStream out) throws IOException;
@Override
default void close() {}
}
class InputStreamSource implements ShellInputSource {
private final InputStream in;
InputStreamSource(InputStream in) { this.in = in; }
@Override
public void serve(OutputStream out) throws IOException {
Utils.pump(in, out);
in.close();
out.write('\n');
Utils.log(TAG, "<InputStream>");
}
@Override
public void close() {
try {
in.close();
} catch (IOException ignored) {}
}
}
class CommandSource implements ShellInputSource {
private final String[] cmd;
CommandSource(String[] cmd) { this.cmd = cmd; }
@Override
public void serve(OutputStream out) throws IOException {
for (String command : cmd) {
out.write(command.getBytes(UTF_8));
out.write('\n');
Utils.log(TAG, command);
}
}
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2021 John "topjohnwu" Wu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.topjohnwu.superuser.internal;
import java.io.IOException;
class ShellTerminatedException extends IOException {
ShellTerminatedException() {
super("Shell terminated unexpectedly");
}
}