Skip to content

Commit 89360b4

Browse files
committed
libdaemonize: retry launch several times
On some devices in the early boot stages, execv will fail with EACCESS, cause unknown. Retrying a couple of times seems to work. Most-seen required attempts is three. That retrying works implies some sort of race-condition, possibly SELinux related.
1 parent 7ac3338 commit 89360b4

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

librootjavadaemon/CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
cmake_minimum_required(VERSION 3.4.1)
22

3+
34
add_executable( libdaemonize.so src/main/jni/daemonize.cpp )
5+
6+
target_link_libraries( libdaemonize.so log )
7+
8+
#target_compile_definitions( libdaemonize.so PRIVATE DEBUG )

librootjavadaemon/src/main/jni/daemonize.cpp

+54-8
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,33 @@
2121
#include <sys/wait.h>
2222
#include <fcntl.h>
2323
#include <stdio.h>
24+
#include <errno.h>
25+
#include <time.h>
26+
27+
#ifdef DEBUG
28+
#include <android/log.h>
29+
#define LOG(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libdaemonize", __VA_ARGS__))
30+
#else
31+
#define LOG(...) ((void)0)
32+
#endif
33+
34+
int sleep_ms(int ms) {
35+
struct timespec ts;
36+
ts.tv_sec = ms / 1000;
37+
ts.tv_nsec = (ms % 1000) * 1000000;
38+
if ((nanosleep(&ts,&ts) == -1) && (errno == EINTR)) {
39+
int ret = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
40+
if (ret < 1) ret = 1;
41+
return ret;
42+
}
43+
return 0;
44+
}
2445

2546
/* Proper daemonization includes forking, closing the current STDIN/STDOUT/STDERR, creating a new
2647
* session, and forking again, making sure the twice-forked process becomes a child of init (1) */
2748
static int fork_daemon(int returnParent) {
2849
pid_t child = fork();
29-
if (child == 0) {
50+
if (child == 0) { // 1st child
3051
close(STDIN_FILENO);
3152
close(STDOUT_FILENO);
3253
close(STDERR_FILENO);
@@ -39,21 +60,46 @@ static int fork_daemon(int returnParent) {
3960

4061
setsid();
4162
pid_t child2 = fork();
42-
if (child2 <= 0) return 0; // success child or error on 2nd fork
43-
exit(EXIT_SUCCESS);
63+
if (child2 == 0) { // 2nd child
64+
return 0; // return execution to caller
65+
} else if (child2 > 0) { // 1st child, fork ok
66+
exit(EXIT_SUCCESS);
67+
} else if (child2 < 0) { // 1st child, fork fail
68+
LOG("2nd fork failed (%d)", errno);
69+
exit(EXIT_FAILURE);
70+
}
71+
}
72+
73+
// parent
74+
if (child < 0) {
75+
LOG("1st fork failed (%d)", errno);
76+
return -1; // error on 1st fork
77+
}
78+
while (true) {
79+
int status;
80+
pid_t waited = waitpid(child, &status, 0);
81+
if ((waited == child) && WIFEXITED(status)) {
82+
break;
83+
}
4484
}
45-
if (child < 0) return -1; // error on 1st fork
46-
int status;
47-
waitpid(child, &status, 0);
4885
if (!returnParent) exit(EXIT_SUCCESS);
4986
return 1; // success parent
5087
}
5188

5289
extern "C" {
5390

5491
int main(int argc, char *argv[], char** envp) {
55-
if (fork_daemon(0) == 0) {
56-
execv(argv[1], &argv[1]);
92+
if (fork_daemon(0) == 0) { // daemonized child
93+
// On some devices in the early boot stages, execv will fail with EACCESS, cause unknown.
94+
// Retrying a couple of times seems to work. Most-seen required attempts is three.
95+
// That retrying works implies some sort of race-condition, possibly SELinux related.
96+
for (int i = 0; i < 16; i++) {
97+
execv(argv[1], &argv[1]); // never returns if successful
98+
LOG("[%d] execv(%s, ...)-->%d", i, argv[1], errno);
99+
sleep_ms(16);
100+
}
101+
LOG("too many failures, aborting");
102+
exit(EXIT_FAILURE);
57103
}
58104
}
59105

0 commit comments

Comments
 (0)