Remove wheel submodule - will re-add later with new URL
[dmon:dmon.git] / task.c
1 /*
2  * task.c
3  * Copyright (C) 2010 Adrian Perez <aperez@igalia.com>
4  *
5  * Distributed under terms of the MIT license.
6  */
7
8 #include "task.h"
9 #include "wheel.h"
10 #include <sys/types.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <signal.h>
15 #include <time.h>
16 #include <grp.h> /* setgroups() */
17
18
19 void
20 task_start (task_t *task)
21 {
22     time_t now = time (NULL);
23     unsigned sleep_time = (difftime (now, task->started) > 1) ? 0 : 1;
24
25     w_assert (task != NULL);
26
27     w_debug (("Last start $Is ago, will wait for $Is\n",
28              (unsigned) difftime (now, task->started), sleep_time));
29     memcpy (&task->started, &now, sizeof (time_t));
30
31     if ((task->pid = fork ()) < 0)
32         w_die ("fork failed: $E\n");
33
34     task->action = A_NONE;
35
36     /* We got a valid PID, return */
37     if (task->pid > 0) {
38         w_debug (("child pid = $I\n", (unsigned) task->pid));
39         return;
40     }
41
42     /*
43      * Sleep before exec'ing the child: this is needed to avoid performing
44      * the classical "continued fork-exec without child reaping" DoS attack.
45      * We do this here, as soon as the child has been forked.
46      */
47     safe_sleep (sleep_time);
48
49     /* Execute child */
50     if (task->write_fd >= 0) {
51         w_debug (("redirecting write_fd = $i -> $i\n", task->write_fd, STDOUT_FILENO));
52         if (dup2 (task->write_fd, STDOUT_FILENO) < 0) {
53             w_debug (("redirection failed: $E\n"));
54             _exit (111);
55         }
56     }
57     if (task->read_fd >= 0) {
58         w_debug (("redirecting read_fd = $i -> $i\n", task->read_fd, STDIN_FILENO));
59         if (dup2 (task->read_fd, STDIN_FILENO) < 0) {
60             w_debug (("redirection failed: $E\n"));
61             _exit (111);
62         }
63     }
64
65     if (task->redir_errfd) {
66         w_debug (("redirecting stderr -> stdout\n"));
67         if (dup2 (STDOUT_FILENO, STDERR_FILENO) < 0) {
68             w_debug (("could not redirect stderr: $E\n"));
69             exit (111);
70         }
71     }
72
73     /* Groups must be changed first, whilw we have privileges */
74     if (task->user.gid > 0) {
75         w_debug (("set group id $I\n", (unsigned) task->pid));
76         if (setgid (task->user.gid))
77             w_die ("could not set groud id: $E\n");
78     }
79
80     if (task->user.ngid > 0) {
81         w_debug (("calling setgroups ($I groups)\n", task->user.ngid));
82         if (setgroups (task->user.ngid, task->user.gids))
83             w_die ("could not set additional groups: $E\n");
84     }
85
86     /* Now drop privileges */
87     if (task->user.uid > 0) {
88         w_debug (("set user id $I\n", (unsigned) task->user.uid));
89         if (setuid (task->user.uid))
90             w_die ("could not set user id: $E\n");
91     }
92
93     execvp (task->argv[0], task->argv);
94     _exit (111);
95 }
96
97
98 void
99 task_signal_dispatch (task_t *task)
100 {
101     w_assert (task != NULL);
102
103     if (task->signal == NO_SIGNAL) /* Invalid signal, nothing to do */
104         return;
105
106     w_debug (("dispatch signal $i to process $I\n",
107              task->signal, (unsigned) task->pid));
108
109     if (kill (task->pid, task->signal) < 0) {
110         w_die ("cannot send signal $i to process $L: $E\n",
111              task->signal, (unsigned long) task->pid);
112     }
113     task->signal = NO_SIGNAL;
114 }
115
116
117 void
118 task_signal (task_t *task, int signum)
119 {
120     w_assert (task != NULL);
121
122     /* Dispatch pending signal first if needed */
123     task_signal_dispatch (task);
124
125     /* Then send our own */
126     task->signal = signum;
127     task_signal_dispatch (task);
128 }
129
130
131 void
132 task_action_dispatch (task_t *task)
133 {
134     w_assert (task != NULL);
135
136     switch (task->action) {
137         case A_NONE: /* Nothing to do */
138             return;
139         case A_START:
140             task_start (task);
141             break;
142         case A_STOP:
143             task->action = A_NONE;
144             if (task->pid != NO_PID) {
145                 task_signal (task, SIGTERM);
146                 task_signal (task, SIGCONT);
147             }
148             break;
149         case A_SIGNAL:
150             task->action = A_NONE;
151             task_signal_dispatch (task);
152             break;
153     }
154 }
155
156
157 void
158 task_action (task_t *task, action_t action)
159 {
160     w_assert (task != NULL);
161
162     /* Dispatch pending action. */
163     task_action_dispatch (task);
164
165     /* Send our own action. */
166     task->action = action;
167     task_action_dispatch (task);
168 }
169
170
171 /* vim: expandtab tabstop=4 shiftwidth=4
172  */