Skip to content

Commit dff2794

Browse files
Add ShellUtils to define shell related utils since they don't belong in BackgroundJob
1 parent 3e0f74a commit dff2794

File tree

2 files changed

+140
-123
lines changed

2 files changed

+140
-123
lines changed

app/src/main/java/com/termux/app/BackgroundJob.java

+4-123
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,18 @@
55
import android.content.Intent;
66
import android.os.Bundle;
77

8-
import com.termux.BuildConfig;
98
import com.termux.app.utils.Logger;
9+
import com.termux.app.utils.ShellUtils;
1010
import com.termux.models.ExecutionCommand;
1111
import com.termux.models.ExecutionCommand.ExecutionState;
1212

1313
import java.io.BufferedReader;
1414
import java.io.File;
15-
import java.io.FileInputStream;
1615
import java.io.IOException;
1716
import java.io.InputStream;
1817
import java.io.InputStreamReader;
19-
import java.lang.reflect.Field;
2018
import java.nio.charset.StandardCharsets;
21-
import java.util.ArrayList;
2219
import java.util.Arrays;
23-
import java.util.Collections;
24-
import java.util.List;
2520

2621
/**
2722
* A background job launched by Termux.
@@ -37,12 +32,12 @@ public BackgroundJob(String executable, final String[] arguments, String working
3732
}
3833

3934
public BackgroundJob(ExecutionCommand executionCommand, final TermuxService service) {
40-
String[] env = buildEnvironment(false, executionCommand.workingDirectory);
35+
String[] env = ShellUtils.buildEnvironment(false, executionCommand.workingDirectory);
4136

4237
if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty())
4338
executionCommand.workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
4439

45-
final String[] commandArray = setupProcessArgs(executionCommand.executable, executionCommand.arguments);
40+
final String[] commandArray = ShellUtils.setupProcessArgs(executionCommand.executable, executionCommand.arguments);
4641
final String commandDescription = Arrays.toString(commandArray);
4742

4843
if(!executionCommand.setState(ExecutionState.EXECUTING))
@@ -59,7 +54,7 @@ public BackgroundJob(ExecutionCommand executionCommand, final TermuxService serv
5954
}
6055

6156
mProcess = process;
62-
final int pid = getPid(mProcess);
57+
final int pid = ShellUtils.getPid(mProcess);
6358
final Bundle result = new Bundle();
6459
final StringBuilder outResult = new StringBuilder();
6560
final StringBuilder errResult = new StringBuilder();
@@ -138,118 +133,4 @@ public void run() {
138133
}.start();
139134
}
140135

141-
private static void addToEnvIfPresent(List<String> environment, String name) {
142-
String value = System.getenv(name);
143-
if (value != null) {
144-
environment.add(name + "=" + value);
145-
}
146-
}
147-
148-
static String[] buildEnvironment(boolean isFailSafe, String workingDirectory) {
149-
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
150-
151-
if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
152-
153-
List<String> environment = new ArrayList<>();
154-
155-
environment.add("TERMUX_VERSION=" + BuildConfig.VERSION_NAME);
156-
environment.add("TERM=xterm-256color");
157-
environment.add("COLORTERM=truecolor");
158-
environment.add("HOME=" + TermuxConstants.TERMUX_HOME_DIR_PATH);
159-
environment.add("PREFIX=" + TermuxConstants.TERMUX_PREFIX_DIR_PATH);
160-
environment.add("BOOTCLASSPATH=" + System.getenv("BOOTCLASSPATH"));
161-
environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT"));
162-
environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA"));
163-
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
164-
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
165-
environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE"));
166-
167-
// These variables are needed if running on Android 10 and higher.
168-
addToEnvIfPresent(environment, "ANDROID_ART_ROOT");
169-
addToEnvIfPresent(environment, "DEX2OATBOOTCLASSPATH");
170-
addToEnvIfPresent(environment, "ANDROID_I18N_ROOT");
171-
addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT");
172-
addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT");
173-
174-
if (isFailSafe) {
175-
// Keep the default path so that system binaries can be used in the failsafe session.
176-
environment.add("PATH= " + System.getenv("PATH"));
177-
} else {
178-
environment.add("LANG=en_US.UTF-8");
179-
environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH);
180-
environment.add("PWD=" + workingDirectory);
181-
environment.add("TMPDIR=" + TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
182-
}
183-
184-
return environment.toArray(new String[0]);
185-
}
186-
187-
public static int getPid(Process p) {
188-
try {
189-
Field f = p.getClass().getDeclaredField("pid");
190-
f.setAccessible(true);
191-
try {
192-
return f.getInt(p);
193-
} finally {
194-
f.setAccessible(false);
195-
}
196-
} catch (Throwable e) {
197-
return -1;
198-
}
199-
}
200-
201-
static String[] setupProcessArgs(String fileToExecute, String[] arguments) {
202-
// The file to execute may either be:
203-
// - An elf file, in which we execute it directly.
204-
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
205-
// system /system/bin/sh. The system shell may vary and may not work at all due to LD_LIBRARY_PATH.
206-
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
207-
String interpreter = null;
208-
try {
209-
File file = new File(fileToExecute);
210-
try (FileInputStream in = new FileInputStream(file)) {
211-
byte[] buffer = new byte[256];
212-
int bytesRead = in.read(buffer);
213-
if (bytesRead > 4) {
214-
if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') {
215-
// Elf file, do nothing.
216-
} else if (buffer[0] == '#' && buffer[1] == '!') {
217-
// Try to parse shebang.
218-
StringBuilder builder = new StringBuilder();
219-
for (int i = 2; i < bytesRead; i++) {
220-
char c = (char) buffer[i];
221-
if (c == ' ' || c == '\n') {
222-
if (builder.length() == 0) {
223-
// Skip whitespace after shebang.
224-
} else {
225-
// End of shebang.
226-
String executable = builder.toString();
227-
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
228-
String[] parts = executable.split("/");
229-
String binary = parts[parts.length - 1];
230-
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/" + binary;
231-
}
232-
break;
233-
}
234-
} else {
235-
builder.append(c);
236-
}
237-
}
238-
} else {
239-
// No shebang and no ELF, use standard shell.
240-
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/sh";
241-
}
242-
}
243-
}
244-
} catch (IOException e) {
245-
// Ignore.
246-
}
247-
248-
List<String> result = new ArrayList<>();
249-
if (interpreter != null) result.add(interpreter);
250-
result.add(fileToExecute);
251-
if (arguments != null) Collections.addAll(result, arguments);
252-
return result.toArray(new String[0]);
253-
}
254-
255136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.termux.app.utils;
2+
3+
import com.termux.BuildConfig;
4+
import com.termux.app.TermuxConstants;
5+
6+
import java.io.File;
7+
import java.io.FileInputStream;
8+
import java.io.IOException;
9+
import java.lang.reflect.Field;
10+
import java.util.ArrayList;
11+
import java.util.Collections;
12+
import java.util.List;
13+
14+
public class ShellUtils {
15+
16+
public static String[] buildEnvironment(boolean isFailSafe, String workingDirectory) {
17+
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
18+
19+
if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
20+
21+
List<String> environment = new ArrayList<>();
22+
23+
environment.add("TERMUX_VERSION=" + BuildConfig.VERSION_NAME);
24+
environment.add("TERM=xterm-256color");
25+
environment.add("COLORTERM=truecolor");
26+
environment.add("HOME=" + TermuxConstants.TERMUX_HOME_DIR_PATH);
27+
environment.add("PREFIX=" + TermuxConstants.TERMUX_PREFIX_DIR_PATH);
28+
environment.add("BOOTCLASSPATH=" + System.getenv("BOOTCLASSPATH"));
29+
environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT"));
30+
environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA"));
31+
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
32+
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
33+
environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE"));
34+
35+
// These variables are needed if running on Android 10 and higher.
36+
addToEnvIfPresent(environment, "ANDROID_ART_ROOT");
37+
addToEnvIfPresent(environment, "DEX2OATBOOTCLASSPATH");
38+
addToEnvIfPresent(environment, "ANDROID_I18N_ROOT");
39+
addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT");
40+
addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT");
41+
42+
if (isFailSafe) {
43+
// Keep the default path so that system binaries can be used in the failsafe session.
44+
environment.add("PATH= " + System.getenv("PATH"));
45+
} else {
46+
environment.add("LANG=en_US.UTF-8");
47+
environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH);
48+
environment.add("PWD=" + workingDirectory);
49+
environment.add("TMPDIR=" + TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
50+
}
51+
52+
return environment.toArray(new String[0]);
53+
}
54+
55+
public static void addToEnvIfPresent(List<String> environment, String name) {
56+
String value = System.getenv(name);
57+
if (value != null) {
58+
environment.add(name + "=" + value);
59+
}
60+
}
61+
62+
public static int getPid(Process p) {
63+
try {
64+
Field f = p.getClass().getDeclaredField("pid");
65+
f.setAccessible(true);
66+
try {
67+
return f.getInt(p);
68+
} finally {
69+
f.setAccessible(false);
70+
}
71+
} catch (Throwable e) {
72+
return -1;
73+
}
74+
}
75+
76+
public static String[] setupProcessArgs(String fileToExecute, String[] arguments) {
77+
// The file to execute may either be:
78+
// - An elf file, in which we execute it directly.
79+
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
80+
// system /system/bin/sh. The system shell may vary and may not work at all due to LD_LIBRARY_PATH.
81+
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
82+
String interpreter = null;
83+
try {
84+
File file = new File(fileToExecute);
85+
try (FileInputStream in = new FileInputStream(file)) {
86+
byte[] buffer = new byte[256];
87+
int bytesRead = in.read(buffer);
88+
if (bytesRead > 4) {
89+
if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') {
90+
// Elf file, do nothing.
91+
} else if (buffer[0] == '#' && buffer[1] == '!') {
92+
// Try to parse shebang.
93+
StringBuilder builder = new StringBuilder();
94+
for (int i = 2; i < bytesRead; i++) {
95+
char c = (char) buffer[i];
96+
if (c == ' ' || c == '\n') {
97+
if (builder.length() == 0) {
98+
// Skip whitespace after shebang.
99+
} else {
100+
// End of shebang.
101+
String executable = builder.toString();
102+
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
103+
String[] parts = executable.split("/");
104+
String binary = parts[parts.length - 1];
105+
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/" + binary;
106+
}
107+
break;
108+
}
109+
} else {
110+
builder.append(c);
111+
}
112+
}
113+
} else {
114+
// No shebang and no ELF, use standard shell.
115+
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/sh";
116+
}
117+
}
118+
}
119+
} catch (IOException e) {
120+
// Ignore.
121+
}
122+
123+
List<String> result = new ArrayList<>();
124+
if (interpreter != null) result.add(interpreter);
125+
result.add(fileToExecute);
126+
if (arguments != null) Collections.addAll(result, arguments);
127+
return result.toArray(new String[0]);
128+
}
129+
130+
public static String getExecutableBasename(String executable) {
131+
if(executable == null) return null;
132+
int lastSlash = executable.lastIndexOf('/');
133+
return (lastSlash == -1) ? executable : executable.substring(lastSlash + 1);
134+
}
135+
136+
}

0 commit comments

Comments
 (0)