merge clone variants
[trinity.git] / main.c
blob53a849c7eda4bc7d573193977886c8300344bbb6
1 #include <errno.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <sys/prctl.h>
7 #include <sys/ptrace.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
12 #include "child.h"
13 #include "files.h"
14 #include "locks.h"
15 #include "log.h"
16 #include "params.h"
17 #include "pids.h"
18 #include "random.h"
19 #include "shm.h"
20 #include "signals.h"
21 #include "syscall.h"
22 #include "trinity.h"
24 /* Generate children*/
25 static void fork_children(void)
27 while (shm->running_childs < max_children) {
28 int pidslot;
29 int pid = 0;
31 if (shm->spawn_no_more == TRUE)
32 return;
34 /* a new child means a new seed, or the new child
35 * will do the same syscalls as the one in the pidslot it's replacing.
36 * (special case startup, or we reseed unnecessarily)
38 if (shm->ready == TRUE)
39 reseed();
41 /* Find a space for it in the pid map */
42 pidslot = find_pid_slot(EMPTY_PIDSLOT);
43 if (pidslot == PIDSLOT_NOT_FOUND) {
44 outputerr("## Pid map was full!\n");
45 dump_pid_slots();
46 exit(EXIT_FAILURE);
49 if (logging == TRUE) {
50 int fd;
52 fd = fileno(shm->logfiles[pidslot]);
53 if (ftruncate(fd, 0) == 0)
54 lseek(fd, 0, SEEK_SET);
57 (void)alarm(0);
58 fflush(stdout);
59 pid = fork();
61 if (pid == 0) {
62 /* Child process. */
63 int ret = 0;
65 init_child(pidslot);
66 ret = child_process(pidslot);
67 output(1, "child %d exiting.\n", pidslot);
68 _exit(ret);
69 } else {
70 if (pid == -1) {
71 output(0, "couldn't create child! (%s)\n", strerror(errno));
72 shm->exit_reason = EXIT_FORK_FAILURE;
73 exit(EXIT_FAILURE);
77 shm->pids[pidslot] = pid;
78 shm->running_childs++;
80 debugf("Created child %d in pidslot %d [total:%d/%d]\n",
81 shm->pids[pidslot], pidslot,
82 shm->running_childs, max_children);
84 if (shm->exit_reason != STILL_RUNNING)
85 return;
88 shm->ready = TRUE;
90 debugf("created enough children\n");
93 void reap_child(pid_t childpid)
95 int i;
97 lock(&shm->reaper_lock);
99 if (childpid == shm->last_reaped) {
100 debugf("already reaped %d!\n", childpid);
101 goto out;
104 i = find_pid_slot(childpid);
105 if (i == PIDSLOT_NOT_FOUND)
106 goto out;
108 debugf("Removing pid %d from pidmap.\n", childpid);
109 shm->pids[i] = EMPTY_PIDSLOT;
110 shm->running_childs--;
111 shm->tv[i].tv_sec = 0;
112 shm->last_reaped = childpid;
114 out:
115 unlock(&shm->reaper_lock);
118 static void handle_child(pid_t childpid, int childstatus)
120 switch (childpid) {
121 case 0:
122 //debugf("Nothing changed. children:%d\n", shm->running_childs);
123 break;
125 case -1:
126 if (shm->exit_reason != STILL_RUNNING)
127 return;
129 if (errno == ECHILD) {
130 unsigned int i;
131 bool seen = FALSE;
133 debugf("All children exited!\n");
135 for_each_pidslot(i) {
136 if (shm->pids[i] != EMPTY_PIDSLOT) {
137 if (pid_alive(shm->pids[i]) == -1) {
138 debugf("Removing %d from pidmap\n", shm->pids[i]);
139 shm->pids[i] = EMPTY_PIDSLOT;
140 shm->running_childs--;
141 } else {
142 debugf("%d looks still alive! ignoring.\n", shm->pids[i]);
144 seen = TRUE;
147 if (seen == FALSE)
148 shm->running_childs = 0;
149 break;
151 output(0, "error! (%s)\n", strerror(errno));
152 break;
154 default:
155 debugf("Something happened to pid %d\n", childpid);
157 if (WIFEXITED(childstatus)) {
159 int slot;
161 slot = find_pid_slot(childpid);
162 if (slot == PIDSLOT_NOT_FOUND) {
163 /* If we reaped it, it wouldn't show up, so check that. */
164 if (shm->last_reaped != childpid) {
165 outputerr("## Couldn't find pid slot for %d\n", childpid);
166 shm->exit_reason = EXIT_LOST_PID_SLOT;
167 dump_pid_slots();
169 } else {
170 debugf("Child %d exited after %ld syscalls.\n", childpid, shm->child_syscall_count[slot]);
171 reap_child(childpid);
173 break;
175 } else if (WIFSIGNALED(childstatus)) {
177 switch (WTERMSIG(childstatus)) {
178 case SIGALRM:
179 debugf("got a alarm signal from pid %d\n", childpid);
180 break;
181 case SIGFPE:
182 case SIGSEGV:
183 case SIGKILL:
184 case SIGPIPE:
185 case SIGABRT:
186 debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus)));
187 reap_child(childpid);
188 break;
189 default:
190 debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus));
191 break;
193 break;
195 } else if (WIFSTOPPED(childstatus)) {
197 switch (WSTOPSIG(childstatus)) {
198 case SIGALRM:
199 debugf("got an alarm signal from pid %d\n", childpid);
200 break;
201 case SIGSTOP:
202 debugf("Sending PTRACE_DETACH (and then KILL)\n");
203 ptrace(PTRACE_DETACH, childpid, NULL, NULL);
204 kill(childpid, SIGKILL);
205 reap_child(childpid);
206 break;
207 case SIGFPE:
208 case SIGSEGV:
209 case SIGKILL:
210 case SIGPIPE:
211 case SIGABRT:
212 debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus)));
213 reap_child(childpid);
214 break;
215 default:
216 debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus)));
217 break;
219 break;
221 } else if (WIFCONTINUED(childstatus)) {
222 break;
223 } else {
224 output(0, "erk, wtf\n");
229 static void handle_children(void)
231 unsigned int i;
232 int childstatus;
233 pid_t pid;
235 if (shm->running_childs == 0)
236 return;
238 /* First, we wait for *any* child to wake us up. */
239 pid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED);
241 /* We were awoken, handle it. */
242 handle_child(pid, childstatus);
244 /* While we're awake, let's see if the other children need attention.
245 * We do this instead of just waitpid(-1) again so that there's no way
246 * for any one child to starve the others of attention.
248 for_each_pidslot(i) {
250 pid = shm->pids[i];
252 if (pid == EMPTY_PIDSLOT)
253 continue;
255 if (pid_is_valid(pid) == FALSE)
256 return;
258 pid = waitpid(pid, &childstatus, WUNTRACED | WCONTINUED | WNOHANG);
259 if (pid != 0)
260 handle_child(pid, childstatus);
264 static const char *reasons[NUM_EXIT_REASONS] = {
265 "Still running.",
266 "No more syscalls enabled.",
267 "Reached maximum syscall count.",
268 "No file descriptors open.",
269 "Lost track of a pid slot.",
270 "shm corruption - Found a pid out of range.",
271 "ctrl-c",
272 "kernel became tainted.",
273 "SHM was corrupted!",
274 "Child reparenting problem",
275 "No files in file list.",
276 "Main process disappeared.",
277 "UID changed.",
278 "Something happened during fd init.",
279 "fork() failure",
282 static const char * decode_exit(unsigned int reason)
284 return reasons[reason];
287 void main_loop(void)
289 while (shm->exit_reason == STILL_RUNNING) {
291 if (shm->spawn_no_more == FALSE) {
292 if (shm->running_childs < max_children)
293 fork_children();
295 /* Periodic regenation of fd's etc. */
296 if (shm->regenerate >= REGENERATION_POINT)
297 regenerate();
299 if (shm->need_reseed == TRUE)
300 reseed();
303 handle_children();
306 /* Wait until all children have exited. */
307 while (pidmap_empty() == FALSE)
308 handle_children();
310 outputerr("Bailing main loop. Exit reason: %s\n", decode_exit(shm->exit_reason));