OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #define _GNU_SOURCE | |
6 #include "init_process.h" | |
7 | |
8 #include <dirent.h> | |
9 #include <fcntl.h> | |
10 #include <signal.h> | |
11 #include <stdbool.h> | |
12 #include <stdio.h> | |
13 #include <stdlib.h> | |
14 #include <string.h> | |
15 #include <sys/types.h> | |
16 #include <sys/wait.h> | |
17 #include <unistd.h> | |
18 | |
19 | |
20 static void sigchld(int signo, siginfo_t *info, void *unused) { | |
21 return; | |
22 } | |
23 | |
24 static int getProcessStatus(int proc_fd, const char *process, | |
25 const char *field) { | |
26 int ret = -1; | |
27 | |
28 // Open "/proc/${process}/status" | |
29 char *buf = malloc(strlen(process) + 80); | |
30 sprintf(buf, "%s/status", process); | |
31 int fd = openat(proc_fd, buf, O_RDONLY); | |
32 if (fd >= 0) { | |
33 // Only bother to read the first 4kB. All of the fields that we | |
34 // are interested in will show up much earlier. | |
35 buf = realloc(buf, 4097); | |
36 size_t sz = read(fd, buf, 4096); | |
37 if (sz > 0) { | |
38 // Find a matching "field" | |
39 buf[sz] = '\000'; | |
40 char *f = malloc(strlen(field) + 4); | |
41 sprintf(f, "\n%s:\t", field); | |
42 char *ptr = strstr(buf, f); | |
43 if (ptr) { | |
44 // Extract the numerical value of the "field" | |
45 ret = atoi(ptr + strlen(f)); | |
46 } | |
47 free(f); | |
48 } | |
49 close(fd); | |
50 } | |
51 free(buf); | |
52 return ret; | |
53 } | |
54 | |
55 static bool hasChildren(int proc_fd, int pid) { | |
56 bool ret = false; | |
57 | |
58 // Open "/proc" | |
59 int fd = dup(proc_fd); | |
60 lseek(fd, SEEK_SET, 0); | |
61 DIR *dir = fd >= 0 ? fdopendir(fd) : NULL; | |
62 struct dirent de, *res; | |
63 while (dir && !readdir_r(dir, &de, &res) && res) { | |
64 // Find numerical entries. Those are processes. | |
65 if (res->d_name[0] <= '0' || res->d_name[0] > '9') { | |
66 continue; | |
67 } | |
68 | |
69 // For each process, check the parent's pid | |
70 int ppid = getProcessStatus(proc_fd, res->d_name, "PPid"); | |
71 | |
72 if (ppid == pid) { | |
73 // We found a child process. We can stop searching, now | |
74 ret = true; | |
75 break; | |
76 } | |
77 } | |
78 closedir(dir); | |
79 return ret; | |
80 } | |
81 | |
82 void SystemInitProcess(int init_fd, int child_pid, int proc_fd, int null_fd) { | |
83 int ret = 0; | |
84 | |
85 // CLONE_NEWPID doesn't adjust the contents of the "/proc" file system. | |
86 // This is very confusing. And it is even possible the kernel developers | |
87 // will consider this a bug and fix it at some point in the future. | |
88 // So, to be on the safe side, we explicitly retrieve our process id | |
89 // from the "/proc" file system. This should continue to work, even if | |
90 // the kernel eventually gets fixed so that "/proc" shows the view from | |
91 // inside of the new pid namespace. | |
92 pid_t init_pid = getProcessStatus(proc_fd, "self", "Pid"); | |
93 if (init_pid <= 0) { | |
94 fprintf(stderr, | |
95 "Failed to determine real process id of new \"init\" process\n"); | |
96 _exit(1); | |
97 } | |
98 | |
99 // Redirect stdio to /dev/null | |
100 if (null_fd < 0 || | |
101 dup2(null_fd, 0) != 0 || | |
102 dup2(null_fd, 1) != 1 || | |
103 dup2(null_fd, 2) != 2) { | |
104 fprintf(stderr, "Failed to point stdio to a safe place\n"); | |
105 _exit(1); | |
106 } | |
107 close(null_fd); | |
108 | |
109 // Close all file handles | |
110 int fds_fd = openat(proc_fd, "self/fd", O_RDONLY | O_DIRECTORY); | |
111 DIR *dir = fds_fd >= 0 ? fdopendir(fds_fd) : NULL; | |
112 if (dir == NULL) { | |
113 // If we don't know the list of our open file handles, just try closing | |
114 // all valid ones. | |
115 for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) { | |
116 if (fd != init_fd && fd != proc_fd) { | |
117 close(fd); | |
118 } | |
119 } | |
120 } else { | |
121 // If available, it is much more efficient to just close the file | |
122 // handles that show up in "/proc/self/fd/" | |
123 struct dirent de, *res; | |
124 while (!readdir_r(dir, &de, &res) && res) { | |
125 if (res->d_name[0] < '0') | |
126 continue; | |
127 int fd = atoi(res->d_name); | |
128 if (fd > 2 && fd != init_fd && fd != proc_fd && fd != dirfd(dir)) { | |
129 close(fd); | |
130 } | |
131 } | |
132 closedir(dir); | |
133 } | |
134 | |
135 // Set up signal handler to catch SIGCHLD, but mask the signal for now | |
136 sigset_t mask; | |
137 sigemptyset(&mask); | |
138 sigaddset(&mask, SIGCHLD); | |
139 sigprocmask(SIG_BLOCK, &mask, NULL); | |
140 struct sigaction sa; | |
141 memset(&sa, 0, sizeof(sa)); | |
142 sa.sa_sigaction = sigchld; | |
143 sa.sa_flags = SA_RESTART | SA_SIGINFO; | |
agl
2012/01/27 15:14:32
you setup a sigaction struct, but I don't see wher
Markus (顧孟勤)
2012/01/27 17:44:09
Good call. There was supposed to be call to sigact
| |
144 | |
145 // Notify other processes that we are done initializing | |
146 write(init_fd, " ", 1); | |
147 close(init_fd); | |
148 | |
149 // Handle dying processes that have been re-parented to the "init" process | |
150 for (;;) { | |
151 // Wait until we receive a SIGCHLD signal. Our signal handler doesn't | |
152 // actually need to do anything, though | |
153 sigwaitinfo(&mask, NULL); | |
154 | |
155 bool retry = false; | |
156 do { | |
157 for (;;) { | |
158 // Reap all exit codes of our child processes. This includes both | |
159 // processes that originally were our immediate children, and processes | |
160 // that have since been re-parented to be our children. | |
161 int status; | |
162 pid_t pid = waitpid(0, &status, __WALL | WNOHANG); | |
163 if (pid <= 0) { | |
164 break; | |
165 } else { | |
166 // We found some newly deceased child processes. Better schedule | |
167 // another very thorough inspection of our state. | |
168 retry = false; | |
agl
2012/01/27 15:14:32
If you're assuming that waitpid will already retur
Markus (顧孟勤)
2012/01/27 17:44:09
This code is subtle. If you can think of a better
| |
169 } | |
170 if (pid == child_pid) { | |
171 // If our first immediate child died, remember its exit code. That's | |
172 // the exit code that we should be reporting to our parent process | |
173 if (WIFEXITED(status)) { | |
174 ret = WEXITSTATUS(status); | |
175 } else if (WIFSIGNALED(status)) { | |
176 ret = -WTERMSIG(status); | |
177 } | |
178 } | |
179 } | |
180 if (hasChildren(proc_fd, init_pid)) { | |
181 // As long as we still have child processes, continue waiting for | |
182 // their ultimate demise. | |
183 retry = false; | |
184 } else { | |
185 if (retry) { | |
186 // No more child processes. We can exit now. | |
187 if (ret < 0) { | |
188 // Try to exit with the same signal that our child terminated with | |
189 signal(-ret, SIG_DFL); | |
190 kill(1, -ret); | |
191 ret = 1; | |
192 } | |
193 // Exit with the same exit code that our child exited with | |
194 _exit(ret); | |
195 } else { | |
196 // There is a little bit of a race condition between getting | |
197 // notifications and scanning the "/proc" file system. This is | |
198 // particularly true, because scanning "/proc" cannot possibly be | |
199 // an atomic operation. | |
200 // If we find that we no longer appear to have any children, we check | |
201 // one more time whether there are any children we can now reap. | |
202 // They might have died while we were scanning "/proc" and if so, | |
203 // they should now show up. | |
204 retry = true; | |
205 } | |
206 } | |
207 } while (retry); | |
208 } | |
209 } | |
OLD | NEW |