use usleep for <1s sleeps
[trinity.git] / main.c
blobd973f4e5816b6aea1a269cc1f99b2e9c5c76ece0
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 int check_tainted(void)
26 int fd;
27 unsigned int ret;
28 char buffer[11];
30 buffer[10] = 0; //make sure that we can fit the whole int.
32 fd = open("/proc/sys/kernel/tainted", O_RDONLY);
33 if (fd < 0)
34 return -1;
35 ret = read(fd, buffer, 10);
36 close(fd);
38 if (ret > 0)
39 ret = atoi(buffer);
40 else {
41 /* We should never fail, but if we do, assume untainted. */
42 ret = 0;
45 return ret;
48 static void oom_score_adj(int adj)
50 FILE *fp;
52 fp = fopen("/proc/self/oom_score_adj", "w");
53 if (!fp)
54 return;
56 fprintf(fp, "%d", adj);
57 fclose(fp);
60 /* Generate children*/
61 static void fork_children(void)
63 while (shm->running_childs < max_children) {
64 int pidslot;
65 int pid = 0;
67 if (shm->spawn_no_more == TRUE)
68 return;
70 /* a new child means a new seed, or the new child
71 * will do the same syscalls as the one in the pidslot it's replacing.
72 * (special case startup, or we reseed unnecessarily)
74 if (shm->ready == TRUE)
75 reseed();
77 /* Find a space for it in the pid map */
78 pidslot = find_pid_slot(EMPTY_PIDSLOT);
79 if (pidslot == PIDSLOT_NOT_FOUND) {
80 outputerr("## Pid map was full!\n");
81 dump_pid_slots();
82 exit(EXIT_FAILURE);
85 if (logging == TRUE) {
86 int fd;
88 fd = fileno(shm->logfiles[pidslot]);
89 if (ftruncate(fd, 0) == 0)
90 lseek(fd, 0, SEEK_SET);
93 (void)alarm(0);
94 fflush(stdout);
95 pid = fork();
96 if (pid != 0) {
97 if (pid == -1) {
98 output(0, "couldn't create child! (%s)\n", strerror(errno));
99 shm->exit_reason = EXIT_FORK_FAILURE;
100 exit(EXIT_FAILURE);
101 } else
102 shm->pids[pidslot] = pid;
103 } else {
104 /* Child process. */
105 char childname[17];
106 int ret = 0;
108 mask_signals_child();
110 memset(childname, 0, sizeof(childname));
111 sprintf(childname, "trinity-c%d", pidslot);
112 prctl(PR_SET_NAME, (unsigned long) &childname);
114 oom_score_adj(500);
116 /* Wait for parent to set our pidslot */
117 while (shm->pids[pidslot] != getpid()) {
118 /* Make sure parent is actually alive to wait for us. */
119 ret = pid_alive(shm->mainpid);
120 if (ret != 0) {
121 shm->exit_reason = EXIT_SHM_CORRUPTION;
122 outputerr(BUGTXT "parent (%d) went away!\n", shm->mainpid);
123 sleep(20000);
127 /* Wait for all the children to start up. */
128 while (shm->ready == FALSE)
129 sleep(1);
131 init_child(pidslot);
133 ret = child_process(pidslot);
135 output(1, "child exiting.\n");
137 _exit(ret);
139 shm->running_childs++;
140 debugf("Created child %d in pidslot %d [total:%d/%d]\n",
141 shm->pids[pidslot], pidslot,
142 shm->running_childs, max_children);
144 if (shm->exit_reason != STILL_RUNNING)
145 return;
148 shm->ready = TRUE;
150 debugf("created enough children\n");
153 void reap_child(pid_t childpid)
155 int i;
157 acquire(&shm->reaper_lock);
159 if (childpid == shm->last_reaped) {
160 debugf("already reaped %d!\n", childpid);
161 goto out;
164 i = find_pid_slot(childpid);
165 if (i == PIDSLOT_NOT_FOUND)
166 goto out;
168 debugf("Removing pid %d from pidmap.\n", childpid);
169 shm->pids[i] = EMPTY_PIDSLOT;
170 shm->running_childs--;
171 shm->tv[i].tv_sec = 0;
172 shm->last_reaped = childpid;
174 out:
175 release(&shm->reaper_lock);
178 static void handle_child(pid_t childpid, int childstatus)
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 unsigned int i;
191 bool seen = FALSE;
193 debugf("All children exited!\n");
195 for_each_pidslot(i) {
196 if (shm->pids[i] != EMPTY_PIDSLOT) {
197 if (pid_alive(shm->pids[i]) == -1) {
198 debugf("Removing %d from pidmap\n", shm->pids[i]);
199 shm->pids[i] = EMPTY_PIDSLOT;
200 shm->running_childs--;
201 } else {
202 debugf("%d looks still alive! ignoring.\n", shm->pids[i]);
204 seen = TRUE;
207 if (seen == FALSE)
208 shm->running_childs = 0;
209 break;
211 output(0, "error! (%s)\n", strerror(errno));
212 break;
214 default:
215 debugf("Something happened to pid %d\n", childpid);
217 if (WIFEXITED(childstatus)) {
219 int slot;
221 slot = find_pid_slot(childpid);
222 if (slot == PIDSLOT_NOT_FOUND) {
223 /* If we reaped it, it wouldn't show up, so check that. */
224 if (shm->last_reaped != childpid) {
225 outputerr("## Couldn't find pid slot for %d\n", childpid);
226 shm->exit_reason = EXIT_LOST_PID_SLOT;
227 dump_pid_slots();
229 } else {
230 debugf("Child %d exited after %ld syscalls.\n", childpid, shm->child_syscall_count[slot]);
231 reap_child(childpid);
233 break;
235 } else if (WIFSIGNALED(childstatus)) {
237 switch (WTERMSIG(childstatus)) {
238 case SIGALRM:
239 debugf("got a alarm signal from pid %d\n", childpid);
240 break;
241 case SIGFPE:
242 case SIGSEGV:
243 case SIGKILL:
244 case SIGPIPE:
245 case SIGABRT:
246 debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus)));
247 reap_child(childpid);
248 break;
249 default:
250 debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus));
251 break;
253 break;
255 } else if (WIFSTOPPED(childstatus)) {
257 switch (WSTOPSIG(childstatus)) {
258 case SIGALRM:
259 debugf("got an alarm signal from pid %d\n", childpid);
260 break;
261 case SIGSTOP:
262 debugf("Sending PTRACE_DETACH (and then KILL)\n");
263 ptrace(PTRACE_DETACH, childpid, NULL, NULL);
264 kill(childpid, SIGKILL);
265 reap_child(childpid);
266 break;
267 case SIGFPE:
268 case SIGSEGV:
269 case SIGKILL:
270 case SIGPIPE:
271 case SIGABRT:
272 debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus)));
273 reap_child(childpid);
274 break;
275 default:
276 debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus)));
277 break;
279 break;
281 } else if (WIFCONTINUED(childstatus)) {
282 break;
283 } else {
284 output(0, "erk, wtf\n");
289 static void handle_children(void)
291 unsigned int i;
292 int childstatus;
293 pid_t pid;
295 if (shm->running_childs == 0)
296 return;
298 /* First, we wait for *any* child to wake us up. */
299 pid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED);
301 /* We were awoken, handle it. */
302 handle_child(pid, childstatus);
304 /* While we're awake, let's see if the other children need attention.
305 * We do this instead of just waitpid(-1) again so that there's no way
306 * for any one child to starve the others of attention.
308 for_each_pidslot(i) {
310 pid = shm->pids[i];
312 if (pid == EMPTY_PIDSLOT)
313 continue;
315 if (pid_is_valid(pid) == FALSE)
316 return;
318 pid = waitpid(pid, &childstatus, WUNTRACED | WCONTINUED | WNOHANG);
319 if (pid != 0)
320 handle_child(pid, childstatus);
324 static const char *reasons[NUM_EXIT_REASONS] = {
325 "Still running.",
326 "No more syscalls enabled.",
327 "Reached maximum syscall count.",
328 "No file descriptors open.",
329 "Lost track of a pid slot.",
330 "shm corruption - Found a pid out of range.",
331 "ctrl-c",
332 "kernel became tainted.",
333 "SHM was corrupted!",
334 "Child reparenting problem",
335 "No files in file list.",
336 "Main process disappeared.",
337 "UID changed.",
338 "Something happened during fd init.",
339 "fork() failure",
342 static const char * decode_exit(unsigned int reason)
344 return reasons[reason];
347 void main_loop(void)
349 while (shm->exit_reason == STILL_RUNNING) {
351 if (shm->spawn_no_more == FALSE) {
352 if (shm->running_childs < max_children)
353 fork_children();
355 /* Periodic regenation of fd's etc. */
356 if (shm->regenerate >= REGENERATION_POINT)
357 regenerate();
359 if (shm->need_reseed == TRUE)
360 reseed();
363 handle_children();
366 /* Wait until all children have exited. */
367 while (pidmap_empty() == FALSE)
368 handle_children();
370 outputerr("Bailing main loop. Exit reason: %s\n", decode_exit(shm->exit_reason));