make a bunch of sparse missing prototype warnings go away
[trinity.git] / main.c
bloba4d9c665ddb85edea5378caa34512ed6bd7c1695
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 "trinity.h"
13 #include "child.h"
14 #include "signals.h"
15 #include "shm.h"
16 #include "files.h"
17 #include "random.h"
18 #include "syscall.h"
19 #include "pids.h"
20 #include "log.h"
21 #include "params.h"
23 int check_tainted(void)
25 int fd;
26 unsigned int ret;
27 char buffer[11];
29 buffer[10] = 0; //make sure that we can fit the whole int.
31 fd = open("/proc/sys/kernel/tainted", O_RDONLY);
32 if (fd < 0)
33 return -1;
34 ret = read(fd, buffer, 10);
35 close(fd);
37 if (ret > 0)
38 ret = atoi(buffer);
39 else {
40 /* We should never fail, but if we do, assume untainted. */
41 ret = 0;
44 return ret;
47 static void oom_score_adj(int adj)
49 FILE *fp;
51 fp = fopen("/proc/self/oom_score_adj", "w");
52 if (!fp)
53 return;
55 fprintf(fp, "%d", adj);
56 fclose(fp);
59 /* Generate children*/
60 static void fork_children(void)
62 while (shm->running_childs < max_children) {
63 int pidslot;
64 int pid = 0;
66 if (shm->spawn_no_more == TRUE)
67 return;
69 /* a new child means a new seed, or the new child
70 * will do the same syscalls as the one in the pidslot it's replacing.
71 * (special case startup, or we reseed unnecessarily)
73 if (shm->ready == TRUE)
74 reseed();
76 /* Find a space for it in the pid map */
77 pidslot = find_pid_slot(EMPTY_PIDSLOT);
78 if (pidslot == PIDSLOT_NOT_FOUND) {
79 outputerr("## Pid map was full!\n");
80 dump_pid_slots();
81 exit(EXIT_FAILURE);
84 if (logging == TRUE) {
85 int fd;
87 fd = fileno(shm->logfiles[pidslot]);
88 if (ftruncate(fd, 0) == 0)
89 lseek(fd, 0, SEEK_SET);
92 (void)alarm(0);
93 fflush(stdout);
94 pid = fork();
95 if (pid != 0) {
96 if (pid == -1) {
97 output(0, "couldn't create child! (%s)\n", strerror(errno));
98 shm->exit_reason = EXIT_FORK_FAILURE;
99 exit(EXIT_FAILURE);
100 } else
101 shm->pids[pidslot] = pid;
102 } else {
103 /* Child process. */
104 char childname[17];
105 int ret = 0;
107 mask_signals_child();
109 memset(childname, 0, sizeof(childname));
110 sprintf(childname, "trinity-c%d", pidslot);
111 prctl(PR_SET_NAME, (unsigned long) &childname);
113 oom_score_adj(500);
115 /* Wait for parent to set our pidslot */
116 while (shm->pids[pidslot] != getpid()) {
117 /* Make sure parent is actually alive to wait for us. */
118 ret = pid_alive(shm->mainpid);
119 if (ret != 0) {
120 shm->exit_reason = EXIT_SHM_CORRUPTION;
121 outputerr(BUGTXT "parent (%d) went away!\n", shm->mainpid);
122 sleep(20000);
126 /* Wait for all the children to start up. */
127 while (shm->ready == FALSE)
128 sleep(1);
130 init_child(pidslot);
132 ret = child_process(pidslot);
134 output(1, "child exiting.\n");
136 _exit(ret);
138 shm->running_childs++;
139 debugf("Created child %d in pidslot %d [total:%d/%d]\n",
140 shm->pids[pidslot], pidslot,
141 shm->running_childs, max_children);
143 if (shm->exit_reason != STILL_RUNNING)
144 return;
147 shm->ready = TRUE;
149 debugf("created enough children\n");
152 void reap_child(pid_t childpid)
154 int i;
156 while (shm->reaper_lock == LOCKED);
158 shm->reaper_lock = LOCKED;
160 if (childpid == shm->last_reaped) {
161 debugf("already reaped %d!\n", childpid);
162 goto out;
165 i = find_pid_slot(childpid);
166 if (i == PIDSLOT_NOT_FOUND)
167 goto out;
169 debugf("Removing pid %d from pidmap.\n", childpid);
170 shm->pids[i] = EMPTY_PIDSLOT;
171 shm->running_childs--;
172 shm->tv[i].tv_sec = 0;
173 shm->last_reaped = childpid;
175 out:
176 shm->reaper_lock = UNLOCKED;
179 static void handle_child(pid_t childpid, int childstatus)
181 switch (childpid) {
182 case 0:
183 //debugf("Nothing changed. children:%d\n", shm->running_childs);
184 break;
186 case -1:
187 if (shm->exit_reason != STILL_RUNNING)
188 return;
190 if (errno == ECHILD) {
191 unsigned int i;
192 bool seen = FALSE;
194 debugf("All children exited!\n");
196 for_each_pidslot(i) {
197 if (shm->pids[i] != EMPTY_PIDSLOT) {
198 if (pid_alive(shm->pids[i]) == -1) {
199 debugf("Removing %d from pidmap\n", shm->pids[i]);
200 shm->pids[i] = EMPTY_PIDSLOT;
201 shm->running_childs--;
202 } else {
203 debugf("%d looks still alive! ignoring.\n", shm->pids[i]);
205 seen = TRUE;
208 if (seen == FALSE)
209 shm->running_childs = 0;
210 break;
212 output(0, "error! (%s)\n", strerror(errno));
213 break;
215 default:
216 debugf("Something happened to pid %d\n", childpid);
218 if (WIFEXITED(childstatus)) {
220 int slot;
222 slot = find_pid_slot(childpid);
223 if (slot == PIDSLOT_NOT_FOUND) {
224 /* If we reaped it, it wouldn't show up, so check that. */
225 if (shm->last_reaped != childpid) {
226 outputerr("## Couldn't find pid slot for %d\n", childpid);
227 shm->exit_reason = EXIT_LOST_PID_SLOT;
228 dump_pid_slots();
230 } else {
231 debugf("Child %d exited after %ld syscalls.\n", childpid, shm->child_syscall_count[slot]);
232 reap_child(childpid);
234 break;
236 } else if (WIFSIGNALED(childstatus)) {
238 switch (WTERMSIG(childstatus)) {
239 case SIGALRM:
240 debugf("got a alarm signal from pid %d\n", childpid);
241 break;
242 case SIGFPE:
243 case SIGSEGV:
244 case SIGKILL:
245 case SIGPIPE:
246 case SIGABRT:
247 debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus)));
248 reap_child(childpid);
249 break;
250 default:
251 debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus));
252 break;
254 break;
256 } else if (WIFSTOPPED(childstatus)) {
258 switch (WSTOPSIG(childstatus)) {
259 case SIGALRM:
260 debugf("got an alarm signal from pid %d\n", childpid);
261 break;
262 case SIGSTOP:
263 debugf("Sending PTRACE_DETACH (and then KILL)\n");
264 ptrace(PTRACE_DETACH, childpid, NULL, NULL);
265 kill(childpid, SIGKILL);
266 reap_child(childpid);
267 break;
268 case SIGFPE:
269 case SIGSEGV:
270 case SIGKILL:
271 case SIGPIPE:
272 case SIGABRT:
273 debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus)));
274 reap_child(childpid);
275 break;
276 default:
277 debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus)));
278 break;
280 break;
282 } else if (WIFCONTINUED(childstatus)) {
283 break;
284 } else {
285 output(0, "erk, wtf\n");
290 static void handle_children(void)
292 unsigned int i;
293 int childstatus;
294 pid_t pid;
296 if (shm->running_childs == 0)
297 return;
299 /* First, we wait for *any* child to wake us up. */
300 pid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED);
302 /* We were awoken, handle it. */
303 handle_child(pid, childstatus);
305 /* While we're awake, let's see if the other children need attention.
306 * We do this instead of just waitpid(-1) again so that there's no way
307 * for any one child to starve the others of attention.
309 for_each_pidslot(i) {
311 pid = shm->pids[i];
313 if (pid == EMPTY_PIDSLOT)
314 continue;
316 if (pid_is_valid(pid) == FALSE)
317 return;
319 pid = waitpid(pid, &childstatus, WUNTRACED | WCONTINUED | WNOHANG);
320 if (pid != 0)
321 handle_child(pid, childstatus);
325 static const char *reasons[NUM_EXIT_REASONS] = {
326 "Still running.",
327 "No more syscalls enabled.",
328 "Reached maximum syscall count.",
329 "No file descriptors open.",
330 "Lost track of a pid slot.",
331 "shm corruption - Found a pid out of range.",
332 "ctrl-c",
333 "kernel became tainted.",
334 "SHM was corrupted!",
335 "Child reparenting problem",
336 "No files in file list.",
337 "Main process disappeared.",
338 "UID changed.",
339 "Something happened during fd init.",
340 "fork() failure",
343 static const char * decode_exit(unsigned int reason)
345 return reasons[reason];
348 void main_loop(void)
350 while (shm->exit_reason == STILL_RUNNING) {
352 if (shm->spawn_no_more == FALSE) {
353 if (shm->running_childs < max_children)
354 fork_children();
356 /* Periodic regenation of fd's etc. */
357 if (shm->regenerate >= REGENERATION_POINT)
358 regenerate();
360 if (shm->need_reseed == TRUE)
361 reseed();
364 handle_children();
367 /* Wait until all children have exited. */
368 while (pidmap_empty() == FALSE)
369 handle_children();
371 outputerr("Bailing main loop. Exit reason: %s\n", decode_exit(shm->exit_reason));