add a show_backtrace function for debugging.
[trinity.git] / main.c
blob246043cefd3216f40d8761b00b9e38069152ea42
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/types.h>
8 #include <sys/stat.h>
9 #include <sys/wait.h>
10 #include <sys/ptrace.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 static void fork_children(void)
61 int pidslot;
62 static char childname[17];
64 /* Generate children*/
66 while (shm->running_childs < shm->max_children) {
67 int pid = 0;
68 int fd;
70 if (shm->spawn_no_more == TRUE)
71 return;
73 /* a new child means a new seed, or the new child
74 * will do the same syscalls as the one in the pidslot it's replacing.
75 * (special case startup, or we reseed unnecessarily)
77 if (shm->ready == TRUE)
78 reseed();
80 /* Find a space for it in the pid map */
81 pidslot = find_pid_slot(EMPTY_PIDSLOT);
82 if (pidslot == PIDSLOT_NOT_FOUND) {
83 outputerr("## Pid map was full!\n");
84 dump_pid_slots();
85 exit(EXIT_FAILURE);
88 if (logging == TRUE) {
89 fd = fileno(shm->logfiles[pidslot]);
90 if (ftruncate(fd, 0) == 0)
91 lseek(fd, 0, SEEK_SET);
94 (void)alarm(0);
95 fflush(stdout);
96 pid = fork();
97 if (pid != 0)
98 shm->pids[pidslot] = pid;
99 else {
100 /* Child process. */
101 int ret = 0;
103 mask_signals_child();
105 memset(childname, 0, sizeof(childname));
106 sprintf(childname, "trinity-child%d", pidslot);
107 prctl(PR_SET_NAME, (unsigned long) &childname);
109 oom_score_adj(500);
111 /* Wait for parent to set our pidslot */
112 while (shm->pids[pidslot] != getpid()) {
113 /* Make sure parent is actually alive to wait for us. */
114 ret = pid_alive(shm->mainpid);
115 if (ret != 0) {
116 shm->exit_reason = EXIT_SHM_CORRUPTION;
117 outputerr(BUGTXT "parent (%d) went away!\n", shm->mainpid);
118 sleep(20000);
122 /* Wait for all the children to start up. */
123 while (shm->ready == FALSE)
124 sleep(1);
126 init_child(pidslot);
128 ret = child_process(pidslot);
130 output(1, "child exiting.\n");
132 _exit(ret);
134 shm->running_childs++;
135 debugf("Created child %d in pidslot %d [total:%d/%d]\n",
136 shm->pids[pidslot], pidslot,
137 shm->running_childs, shm->max_children);
139 if (shm->exit_reason != STILL_RUNNING)
140 return;
143 shm->ready = TRUE;
145 debugf("created enough children\n");
148 void reap_child(pid_t childpid)
150 int i;
152 while (shm->reaper_lock == LOCKED);
154 shm->reaper_lock = LOCKED;
156 if (childpid == shm->last_reaped) {
157 debugf("already reaped %d!\n", childpid);
158 goto out;
161 i = find_pid_slot(childpid);
162 if (i == PIDSLOT_NOT_FOUND)
163 goto out;
165 debugf("Removing pid %d from pidmap.\n", childpid);
166 shm->pids[i] = EMPTY_PIDSLOT;
167 shm->running_childs--;
168 shm->tv[i].tv_sec = 0;
169 shm->last_reaped = childpid;
171 out:
172 shm->reaper_lock = UNLOCKED;
175 static void handle_child(pid_t childpid, int childstatus)
177 unsigned int i;
178 int slot;
180 switch (childpid) {
181 case 0:
182 //debugf("Nothing changed. children:%d\n", shm->running_childs);
183 break;
185 case -1:
186 if (shm->exit_reason != STILL_RUNNING)
187 return;
189 if (errno == ECHILD) {
190 debugf("All children exited!\n");
191 for_each_pidslot(i) {
192 if (shm->pids[i] != EMPTY_PIDSLOT) {
193 if (pid_alive(shm->pids[i]) == -1) {
194 debugf("Removing %d from pidmap\n", shm->pids[i]);
195 shm->pids[i] = EMPTY_PIDSLOT;
196 shm->running_childs--;
197 } else {
198 debugf("%d looks still alive! ignoring.\n", shm->pids[i]);
202 break;
204 output(0, "error! (%s)\n", strerror(errno));
205 break;
207 default:
208 debugf("Something happened to pid %d\n", childpid);
210 if (WIFEXITED(childstatus)) {
212 slot = find_pid_slot(childpid);
213 if (slot == PIDSLOT_NOT_FOUND) {
214 /* If we reaped it, it wouldn't show up, so check that. */
215 if (shm->last_reaped != childpid) {
216 outputerr("## Couldn't find pid slot for %d\n", childpid);
217 shm->exit_reason = EXIT_LOST_PID_SLOT;
218 dump_pid_slots();
220 } else {
221 debugf("Child %d exited after %ld syscalls.\n", childpid, shm->child_syscall_count[slot]);
222 reap_child(childpid);
224 break;
226 } else if (WIFSIGNALED(childstatus)) {
228 switch (WTERMSIG(childstatus)) {
229 case SIGALRM:
230 debugf("got a alarm signal from pid %d\n", childpid);
231 break;
232 case SIGFPE:
233 case SIGSEGV:
234 case SIGKILL:
235 case SIGPIPE:
236 case SIGABRT:
237 debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus)));
238 reap_child(childpid);
239 break;
240 default:
241 debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus));
242 break;
244 break;
246 } else if (WIFSTOPPED(childstatus)) {
248 switch (WSTOPSIG(childstatus)) {
249 case SIGALRM:
250 debugf("got an alarm signal from pid %d\n", childpid);
251 break;
252 case SIGSTOP:
253 debugf("Sending PTRACE_DETACH (and then KILL)\n");
254 ptrace(PTRACE_DETACH, childpid, NULL, NULL);
255 kill(childpid, SIGKILL);
256 reap_child(childpid);
257 break;
258 case SIGFPE:
259 case SIGSEGV:
260 case SIGKILL:
261 case SIGPIPE:
262 case SIGABRT:
263 debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus)));
264 reap_child(childpid);
265 break;
266 default:
267 debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus)));
268 break;
270 break;
272 } else if (WIFCONTINUED(childstatus)) {
273 break;
274 } else {
275 output(0, "erk, wtf\n");
280 static void handle_children(void)
282 unsigned int i;
283 int childstatus;
284 pid_t pid;
286 if (shm->running_childs == 0)
287 return;
289 /* First, we wait for *any* child to wake us up. */
290 pid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED);
292 /* We were awoken, handle it. */
293 handle_child(pid, childstatus);
295 /* While we're awake, let's see if the other children need attention.
296 * We do this instead of just waitpid(-1) again so that there's no way
297 * for any one child to starve the others of attention.
299 for_each_pidslot(i) {
301 pid = shm->pids[i];
303 if (pid == EMPTY_PIDSLOT)
304 continue;
306 if (pid_is_valid(pid) == FALSE)
307 return;
309 pid = waitpid(pid, &childstatus, WUNTRACED | WCONTINUED | WNOHANG);
310 if (pid != 0)
311 handle_child(pid, childstatus);
315 static const char *reasons[] = {
316 "Still running.",
317 "No more syscalls enabled.",
318 "Reached maximum syscall count.",
319 "No file descriptors open.",
320 "Lost track of a pid slot.",
321 "shm corruption - Found a pid out of range.",
322 "ctrl-c",
323 "kernel became tainted.",
324 "SHM was corrupted!",
325 "Child reparenting problem",
326 "No files in file list.",
327 "Main process disappeared.",
328 "UID changed.",
331 static const char * decode_exit(unsigned int reason)
333 return reasons[reason];
336 static void main_loop(void)
338 while (shm->exit_reason == STILL_RUNNING) {
340 if (shm->spawn_no_more == FALSE) {
341 if (shm->running_childs < shm->max_children)
342 fork_children();
344 /* Periodic regenation of fd's etc. */
345 if (shm->regenerate >= REGENERATION_POINT)
346 regenerate();
348 if (shm->need_reseed == TRUE)
349 reseed();
352 handle_children();
357 void do_main_loop(void)
359 const char taskname[13]="trinity-main";
360 int childstatus;
361 pid_t pid;
363 /* do an extra fork so that the watchdog and the children don't share a common parent */
364 fflush(stdout);
365 pid = fork();
366 if (pid == 0) {
367 setup_main_signals();
369 shm->mainpid = getpid();
370 output(0, "Main thread is alive.\n");
371 prctl(PR_SET_NAME, (unsigned long) &taskname);
372 set_seed(0);
374 setup_fds();
375 if (no_files == FALSE) {
376 if (files_in_index == 0) {
377 shm->exit_reason = EXIT_NO_FILES;
378 _exit(EXIT_FAILURE);
382 main_loop();
384 /* Wait until all children have exited. */
385 while (pidmap_empty() == FALSE)
386 handle_children();
388 outputerr("Bailing main loop. Exit reason: %s\n", decode_exit(shm->exit_reason));
389 _exit(EXIT_SUCCESS);
392 /* wait for main loop process to exit. */
393 pid = waitpid(pid, &childstatus, 0);
394 shm->mainpid = 0;