Skip to content

Commit 07c7199

Browse files
committed
Convert run_server into a native executable
Also, relocate am.jar to AppManager directory as a fallback. Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
1 parent 04ed88d commit 07c7199

15 files changed

Lines changed: 351 additions & 303 deletions

File tree

app/src/main/assets/run_server.sh

Lines changed: 0 additions & 51 deletions
This file was deleted.

app/src/main/cpp/CMakeLists.txt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections -fno-exceptions
66
set(LINKER_FLAGS "-Wl,--hash-style=both -Wl,--build-id=none")
77

88
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
9-
message("Builing Release...")
9+
message("Building Release...")
1010

1111
set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden")
1212
set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections")
1313
else()
14-
message("Builing Debug...")
14+
message("Building Debug...")
1515

1616
add_definitions(-DDEBUG)
1717
endif ()
@@ -24,6 +24,7 @@ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
2424

2525
find_library(log-lib log)
2626

27+
# BEGIN libam.so
2728
add_library(am SHARED
2829
AhoCorasick.cpp
2930
io_github_muntashirakon_algo_AhoCorasick.cpp
@@ -35,4 +36,24 @@ target_link_libraries(am ${log-lib})
3536
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
3637
add_custom_command(TARGET am POST_BUILD
3738
COMMAND ${CMAKE_STRIP} --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libam.so")
38-
endif ()
39+
endif ()
40+
41+
# END libam.so
42+
43+
# BEGIN librun_server.so
44+
add_executable(run_server run_server.c)
45+
46+
# Force the output to be packaged as a .so file
47+
set_target_properties(run_server PROPERTIES
48+
OUTPUT_NAME "run_server"
49+
PREFIX "lib"
50+
SUFFIX ".so"
51+
COMPILE_FLAGS "-fPIE -fPIC"
52+
LINK_FLAGS "-pie"
53+
)
54+
55+
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
56+
add_custom_command(TARGET run_server POST_BUILD
57+
COMMAND ${CMAKE_STRIP} --remove-section=.comment "$<TARGET_FILE:run_server>")
58+
endif ()
59+
# END librun_server.so

app/src/main/cpp/run_server.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <unistd.h>
7+
#include <sys/stat.h>
8+
#include <sys/types.h>
9+
#include <fcntl.h>
10+
#include <errno.h>
11+
12+
#define SERVER_NAME "am_local_server"
13+
#define JAR_MAIN_CLASS "io.github.muntashirakon.AppManager.server.ServerRunner"
14+
#define TMP_PATH "/data/local/tmp"
15+
16+
int is_safe_string(const char *str) {
17+
if (!str || str[0] == '\0') {
18+
return 0;
19+
}
20+
21+
// Prevent exactly "." or ".."
22+
if (strcmp(str, ".") == 0 || strcmp(str, "..") == 0) {
23+
return 0;
24+
}
25+
26+
// Only allow alphanumeric, dot, underscore, and dash
27+
for (int i = 0; str[i] != '\0'; i++) {
28+
char c = str[i];
29+
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
30+
(c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-')) {
31+
// Invalid character found
32+
return 0;
33+
}
34+
}
35+
// All characters are valid
36+
return 1;
37+
}
38+
39+
int copy_file(const char *src, const char *dst) {
40+
int fd_src = open(src, O_RDONLY | O_CLOEXEC);
41+
if (fd_src < 0) {
42+
return -1;
43+
}
44+
45+
// Remove any existing file at destination to prevent symlink following
46+
unlink(dst);
47+
48+
int fd_dst = open(dst, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0755);
49+
if (fd_dst < 0) {
50+
close(fd_src);
51+
return -1;
52+
}
53+
54+
char buf[BUFSIZ];
55+
ssize_t n;
56+
while ((n = read(fd_src, buf, sizeof(buf))) > 0) {
57+
if (write(fd_dst, buf, n) != n) {
58+
// Write failed
59+
close(fd_src);
60+
close(fd_dst);
61+
unlink(dst);
62+
return -1;
63+
}
64+
}
65+
66+
close(fd_src);
67+
close(fd_dst);
68+
69+
return (n < 0) ? -1 : 0;
70+
}
71+
72+
int main(int argc, char *argv[]) {
73+
if (argc < 8) {
74+
fprintf(stderr,
75+
"USAGE: %s <port> <token> <jar_name> <AppID> <user_id> <bgrun(1|0)> <debug(1|0)> [extra_args...]\n",
76+
argv[0]);
77+
return 1;
78+
}
79+
80+
const char *port = argv[1];
81+
const char *token = argv[2];
82+
const char *jar_name = argv[3];
83+
const char *app_id = argv[4];
84+
const char *user_id = argv[5];
85+
const char *bgrun = argv[6];
86+
const char *debug = argv[7];
87+
88+
// Validate Paths
89+
if (!is_safe_string(jar_name) || !is_safe_string(app_id) || !is_safe_string(user_id)) {
90+
fprintf(stderr, "Error! Invalid characters in arguments.\n");
91+
return 1;
92+
}
93+
94+
// Validate debug and bgrun
95+
if ((strcmp(bgrun, "0") != 0 && strcmp(bgrun, "1") != 0) ||
96+
(strcmp(debug, "0") != 0 && strcmp(debug, "1") != 0)) {
97+
fprintf(stderr, "Error! bgrun and debug must be either 0 or 1.\n");
98+
return 1;
99+
}
100+
101+
// /data/local/tmp/am.jar
102+
char exec_jar_path[512];
103+
if (snprintf(exec_jar_path, sizeof(exec_jar_path), "%s/%s", TMP_PATH, jar_name) >=
104+
sizeof(exec_jar_path)) {
105+
fprintf(stderr, "Error! Buffer overflow on exec_jar_path.\n");
106+
return 1;
107+
}
108+
109+
// Prioritized list of fallback source paths
110+
char fallbacks[4][512];
111+
snprintf(fallbacks[0], sizeof(fallbacks[0]), "/sdcard/Android/data/%s/cache/%s", app_id,
112+
jar_name);
113+
snprintf(fallbacks[1], sizeof(fallbacks[1]), "/storage/emulated/%s/Android/data/%s/cache/%s",
114+
user_id, app_id, jar_name);
115+
snprintf(fallbacks[2], sizeof(fallbacks[2]), "/sdcard/AppManager/%s", jar_name);
116+
snprintf(fallbacks[3], sizeof(fallbacks[3]), "/storage/emulated/%s/AppManager/%s", user_id,
117+
jar_name);
118+
119+
uid_t uid = getuid();
120+
gid_t gid = getgid();
121+
printf("Starting %s as %d:%d...\n", SERVER_NAME, uid, gid);
122+
123+
// Copy am.jar as /data/local/tmp/am.jar
124+
const char *resolved_jar_path = NULL;
125+
for (int i = 0; i < 4; i++) {
126+
if (copy_file(fallbacks[i], exec_jar_path) == 0) {
127+
resolved_jar_path = fallbacks[i];
128+
break;
129+
}
130+
}
131+
132+
if (resolved_jar_path == NULL) {
133+
fprintf(stderr, "Error! %s could not be found or copied.\n", jar_name);
134+
return 1;
135+
}
136+
137+
// Fix ownership
138+
if (chown(exec_jar_path, uid, gid) != 0) {
139+
fprintf(stderr, "Warning: chown failed: %s\n", strerror(errno));
140+
// Although it failed, still proceed
141+
}
142+
143+
// Build argument for am.jar
144+
char args_buf[2048];
145+
if (snprintf(args_buf, sizeof(args_buf), "path:%s,token:%s,app:%s,bgrun:%s,debug:%s",
146+
port, token, app_id, bgrun, debug) >= sizeof(args_buf)) {
147+
fprintf(stderr, "Error! Buffer overflow on args_buf.\n");
148+
unlink(exec_jar_path);
149+
return 1;
150+
}
151+
152+
printf("Resolved Jar path: %s\n", resolved_jar_path);
153+
printf("Args: %s\n", args_buf);
154+
155+
// Execute app_process
156+
if (setenv("CLASSPATH", exec_jar_path, 1) != 0) {
157+
fprintf(stderr, "Error setting CLASSPATH\n");
158+
unlink(exec_jar_path);
159+
return 1;
160+
}
161+
162+
int extra_args_count = argc - 8;
163+
int exec_argc = 6 + extra_args_count;
164+
char **exec_argv = malloc(sizeof(char *) * exec_argc);
165+
if (!exec_argv) {
166+
fprintf(stderr, "Out of memory\n");
167+
unlink(exec_jar_path);
168+
return 1;
169+
}
170+
171+
exec_argv[0] = "app_process";
172+
exec_argv[1] = "/system/bin";
173+
174+
char nice_name_buf[128];
175+
snprintf(nice_name_buf, sizeof(nice_name_buf), "--nice-name=%s", SERVER_NAME);
176+
exec_argv[2] = nice_name_buf;
177+
178+
exec_argv[3] = JAR_MAIN_CLASS;
179+
exec_argv[4] = args_buf;
180+
181+
for (int i = 0; i < extra_args_count; i++) {
182+
exec_argv[5 + i] = argv[8 + i];
183+
}
184+
exec_argv[exec_argc - 1] = NULL;
185+
186+
printf("Local server has started.\n");
187+
fflush(stdout);
188+
189+
// Start in the background
190+
pid_t pid = fork();
191+
192+
if (pid < 0) {
193+
fprintf(stderr, "Error! Fork failed: %s\n", strerror(errno));
194+
unlink(exec_jar_path);
195+
free(exec_argv);
196+
return 1;
197+
}
198+
199+
if (pid > 0) {
200+
// Parent process exits successfully
201+
printf("Local server started successfully (PID: %d).\n", pid);
202+
free(exec_argv);
203+
return 0;
204+
}
205+
206+
// Detach the background process
207+
if (setsid() < 0) {
208+
unlink(exec_jar_path);
209+
exit(1);
210+
}
211+
212+
// Execute local server
213+
execv("/system/bin/app_process", exec_argv);
214+
215+
// If execv returns, an error occurred
216+
fprintf(stderr, "Error! Could not start local server: %s\n", strerror(errno));
217+
218+
// Cleanup
219+
unlink(exec_jar_path);
220+
free(exec_argv);
221+
exit(1);
222+
}

app/src/main/java/io/github/muntashirakon/AppManager/self/Migrations.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import io.github.muntashirakon.AppManager.db.AppsDb;
1212
import io.github.muntashirakon.AppManager.logs.Log;
13-
import io.github.muntashirakon.AppManager.servermanager.ServerConfig;
1413
import io.github.muntashirakon.AppManager.settings.FeatureController;
1514
import io.github.muntashirakon.AppManager.utils.ContextUtils;
1615
import io.github.muntashirakon.AppManager.utils.FileUtils;
@@ -27,7 +26,7 @@ public void run() {
2726
// Delete am database, am.jar
2827
File internalFilesDir = ContextUtils.getDeContext(context).getFilesDir().getParentFile();
2928
File[] paths = new File[]{
30-
ServerConfig.getDestJarFile(),
29+
new File(internalFilesDir, "am.jar"),
3130
new File(internalFilesDir, "main.jar"),
3231
new File(internalFilesDir, "run_server.sh"),
3332
context.getDatabasePath("am"),

0 commit comments

Comments
 (0)