21
21
#include < sys/wait.h>
22
22
#include < fcntl.h>
23
23
#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
+ }
24
45
25
46
/* Proper daemonization includes forking, closing the current STDIN/STDOUT/STDERR, creating a new
26
47
* session, and forking again, making sure the twice-forked process becomes a child of init (1) */
27
48
static int fork_daemon (int returnParent) {
28
49
pid_t child = fork ();
29
- if (child == 0 ) {
50
+ if (child == 0 ) { // 1st child
30
51
close (STDIN_FILENO);
31
52
close (STDOUT_FILENO);
32
53
close (STDERR_FILENO);
@@ -39,21 +60,46 @@ static int fork_daemon(int returnParent) {
39
60
40
61
setsid ();
41
62
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
+ }
44
84
}
45
- if (child < 0 ) return -1 ; // error on 1st fork
46
- int status;
47
- waitpid (child, &status, 0 );
48
85
if (!returnParent) exit (EXIT_SUCCESS);
49
86
return 1 ; // success parent
50
87
}
51
88
52
89
extern " C" {
53
90
54
91
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);
57
103
}
58
104
}
59
105
0 commit comments