split up constants.h some
[trinity.git] / main.c
blobb65acf3e5371b1c4cf77886f9ac36d087eb27f16
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 init_child(pidslot);
64 child_process(pidslot);
65 output(1, "child %d exiting.\n", pidslot);
66 _exit(EXIT_SUCCESS);
67 } else {
68 if (pid == -1) {
69 output(0, "couldn't create child! (%s)\n", strerror(errno));
70 shm->exit_reason = EXIT_FORK_FAILURE;
71 exit(EXIT_FAILURE);
75 shm->pids[pidslot] = pid;
76 shm->running_childs++;
78 debugf("Created child %d in pidslot %d [total:%d/%d]\n",
79 shm->pids[pidslot], pidslot,
80 shm->running_childs, max_children);
82 if (shm->exit_reason != STILL_RUNNING)
83 return;
86 shm->ready = TRUE;
88 debugf("created enough children\n");
91 void reap_child(pid_t childpid)
93 int i;
95 lock(&shm->reaper_lock);
97 if (childpid == shm->last_reaped) {
98 debugf("already reaped %d!\n", childpid);
99 goto out;
102 i = find_pid_slot(childpid);
103 if (i == PIDSLOT_NOT_FOUND)
104 goto out;
106 debugf("Removing pid %d from pidmap.\n", childpid);
107 shm->pids[i] = EMPTY_PIDSLOT;
108 shm->running_childs--;
109 shm->tv[i].tv_sec = 0;
110 shm->last_reaped = childpid;
112 out:
113 unlock(&shm->reaper_lock);
116 static void handle_child(pid_t childpid, int childstatus)
118 switch (childpid) {
119 case 0:
120 //debugf("Nothing changed. children:%d\n", shm->running_childs);
121 break;
123 case -1:
124 if (shm->exit_reason != STILL_RUNNING)
125 return;
127 if (errno == ECHILD) {
128 unsigned int i;
129 bool seen = FALSE;
131 debugf("All children exited!\n");
133 for_each_pidslot(i) {
134 if (shm->pids[i] != EMPTY_PIDSLOT) {
135 if (pid_alive(shm->pids[i]) == -1) {
136 debugf("Removing %d from pidmap\n", shm->pids[i]);
137 shm->pids[i] = EMPTY_PIDSLOT;
138 shm->running_childs--;
139 } else {
140 debugf("%d looks still alive! ignoring.\n", shm->pids[i]);
142 seen = TRUE;
145 if (seen == FALSE)
146 shm->running_childs = 0;
147 break;
149 output(0, "error! (%s)\n", strerror(errno));
150 break;
152 default:
153 debugf("Something happened to pid %d\n", childpid);
155 if (WIFEXITED(childstatus)) {
157 int slot;
159 slot = find_pid_slot(childpid);
160 if (slot == PIDSLOT_NOT_FOUND) {
161 /* If we reaped it, it wouldn't show up, so check that. */
162 if (shm->last_reaped != childpid) {
163 outputerr("## Couldn't find pid slot for %d\n", childpid);
164 shm->exit_reason = EXIT_LOST_PID_SLOT;
165 dump_pid_slots();
167 } else {
168 debugf("Child %d exited after %ld operations.\n", childpid, shm->child_op_count[slot]);
169 reap_child(childpid);
171 break;
173 } else if (WIFSIGNALED(childstatus)) {
175 switch (WTERMSIG(childstatus)) {
176 case SIGALRM:
177 debugf("got a alarm signal from pid %d\n", childpid);
178 break;
179 case SIGFPE:
180 case SIGSEGV:
181 case SIGKILL:
182 case SIGPIPE:
183 case SIGABRT:
184 debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus)));
185 reap_child(childpid);
186 break;
187 default:
188 debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus));
189 break;
191 break;
193 } else if (WIFSTOPPED(childstatus)) {
195 switch (WSTOPSIG(childstatus)) {
196 case SIGALRM:
197 debugf("got an alarm signal from pid %d\n", childpid);
198 break;
199 case SIGSTOP:
200 debugf("Sending PTRACE_DETACH (and then KILL)\n");
201 ptrace(PTRACE_DETACH, childpid, NULL, NULL);
202 kill(childpid, SIGKILL);
203 reap_child(childpid);
204 break;
205 case SIGFPE:
206 case SIGSEGV:
207 case SIGKILL:
208 case SIGPIPE:
209 case SIGABRT:
210 debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus)));
211 reap_child(childpid);
212 break;
213 default:
214 debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus)));
215 break;
217 break;
219 } else if (WIFCONTINUED(childstatus)) {
220 break;
221 } else {
222 output(0, "erk, wtf\n");
227 static void handle_children(void)
229 unsigned int i;
230 int childstatus;
231 pid_t pid;
233 if (shm->running_childs == 0)
234 return;
236 /* First, we wait for *any* child to wake us up. */
237 pid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED);
239 /* We were awoken, handle it. */
240 handle_child(pid, childstatus);
242 /* While we're awake, let's see if the other children need attention.
243 * We do this instead of just waitpid(-1) again so that there's no way
244 * for any one child to starve the others of attention.
246 for_each_pidslot(i) {
248 pid = shm->pids[i];
250 if (pid == EMPTY_PIDSLOT)
251 continue;
253 if (pid_is_valid(pid) == FALSE)
254 return;
256 pid = waitpid(pid, &childstatus, WUNTRACED | WCONTINUED | WNOHANG);
257 if (pid != 0)
258 handle_child(pid, childstatus);
262 static const char *reasons[NUM_EXIT_REASONS] = {
263 "Still running.",
264 "No more syscalls enabled.",
265 "Completed maximum number of operations.",
266 "No file descriptors open.",
267 "Lost track of a pid slot.",
268 "shm corruption - Found a pid out of range.",
269 "ctrl-c",
270 "kernel became tainted.",
271 "SHM was corrupted!",
272 "Child reparenting problem",
273 "No files in file list.",
274 "Main process disappeared.",
275 "UID changed.",
276 "Something happened during fd init.",
277 "fork() failure",
280 static const char * decode_exit(unsigned int reason)
282 return reasons[reason];
285 void main_loop(void)
287 while (shm->exit_reason == STILL_RUNNING) {
289 if (shm->spawn_no_more == FALSE) {
290 if (shm->running_childs < max_children)
291 fork_children();
293 /* Periodic regenation of fd's etc. */
294 if (shm->regenerate >= REGENERATION_POINT)
295 regenerate();
298 handle_children();
301 /* Wait until all children have exited. */
302 while (pidmap_empty() == FALSE)
303 handle_children();
305 outputerr("Bailing main loop. Exit reason: %s\n", decode_exit(shm->exit_reason));