polypath: Make message class an argument to bad() callbacks
[secnet] / process.c
CommitLineData
baab3a63 1#define _GNU_SOURCE
7138d0c5
SE
2#include "secnet.h"
3#include <unistd.h>
4#include <fcntl.h>
5#include <errno.h>
6#include <sys/wait.h>
baab3a63 7#include <string.h>
7138d0c5
SE
8#include "process.h"
9
10/* Process handling - subprocesses, signals, etc. */
11
12static bool_t signal_handling=False;
13static sigset_t emptyset, fullset;
14static sigset_t registered,pending;
15
16struct child {
17 pid_t pid;
fe5e9cc4 18 cstring_t desc;
7138d0c5
SE
19 process_callback_fn *cb;
20 void *cst;
21 bool_t finished;
22 struct child *next;
23};
24
25static struct child *children=NULL;
26
27struct signotify {
28 int signum;
29 signal_notify_fn *notify;
30 void *cst;
31 struct signotify *next;
32};
33
34static struct signotify *sigs=NULL;
35
36static int spw,spr; /* file descriptors for signal notification pipe */
37
7138d0c5
SE
38/* Long-lived subprocesses can only be started once we've started
39 signal processing so that we can catch SIGCHLD for them and report
40 their exit status using the callback function. We block SIGCHLD
41 until signal processing has begun. */
042a8da9 42pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
fe5e9cc4 43 void *est, void *cst, cstring_t desc)
7138d0c5
SE
44{
45 struct child *c;
7138d0c5
SE
46 pid_t p;
47
b7886fd4 48 NEW(c);
7138d0c5
SE
49 c->desc=desc;
50 c->cb=cb;
51 c->cst=cst;
52
53 if (!signal_handling) {
4f5e39ec 54 fatal("makesubproc called before signal handling started");
7138d0c5
SE
55 }
56 p=fork();
57 if (p==0) {
58 /* Child process */
5e7a5e2d 59 afterfork();
7138d0c5
SE
60 entry(est);
61 abort();
62 } else if (p==-1) {
63 fatal_perror("makesubproc (%s): fork",desc);
64 }
65 c->pid=p;
66 c->finished=False;
67 c->next=children;
68 children=c;
042a8da9 69 return p;
7138d0c5
SE
70}
71
72static signal_notify_fn sigchld_handler;
73static void sigchld_handler(void *st, int signum)
74{
75 struct child *i,*n,**p;
76 struct work {
77 pid_t pid;
78 process_callback_fn *cb;
79 void *cst;
80 int status;
81 struct work *next;
82 };
83 struct work *w=NULL, *nw;
84 pid_t rv;
85 int status;
86
87 for (i=children; i; i=i->next) {
88 rv=waitpid(i->pid,&status,WNOHANG);
89 if (rv==-1) {
90 fatal_perror("sigchld_handler: waitpid");
91 }
92 if (rv==i->pid) {
93 i->finished=True;
94
b7886fd4 95 NEW(nw);
7138d0c5
SE
96 nw->pid=i->pid;
97 nw->cb=i->cb;
98 nw->cst=i->cst;
99 nw->status=status;
100 nw->next=w;
101 w=nw;
102 }
103 }
104
105 /* Remove all the finished tasks from the list of children */
106 for (i=children, p=&children; i; i=n) {
107 n=i->next;
108 if (i->finished) {
109 free(i);
110 *p=n;
111 } else {
112 p=&i->next;
113 }
114 }
115
116 /* Notify as appropriate, then free the list */
117 while (w) {
118 w->cb(w->cst,w->pid,w->status);
119 nw=w;
120 w=w->next;
121 free(nw);
122 }
123}
124
fe5e9cc4 125int sys_cmd(const char *path, const char *arg, ...)
7138d0c5
SE
126{
127 va_list ap;
baab3a63 128 int rv, rc;
7138d0c5
SE
129 pid_t c;
130
7138d0c5
SE
131 c=fork();
132 if (c) {
133 /* Parent -> wait for child */
baab3a63
RK
134 do {
135 rc = waitpid(c,&rv,0);
136 } while(rc < 0 && errno == EINTR);
137 if (rc < 0)
138 fatal_perror("sys_cmd: waitpid for %s", path);
139 if (rc != c) /* OS has gone mad */
140 fatal("sys_cmd: waitpid for %s returned wrong process ID!",
141 path);
142 if (rv) {
4ac7fd3f
IJ
143 /* If the command failed report its exit status */
144 lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
baab3a63 145 }
7138d0c5
SE
146 } else if (c==0) {
147 char *args[100];
148 int i;
149 /* Child -> exec command */
fe5e9cc4
SE
150 /* Really we ought to strcpy() the arguments into the args array,
151 since the arguments are const char *. Since we'll exit anyway
152 if the execvp() fails this seems somewhat pointless, and
153 increases the chance of the child process failing before it
154 gets to exec(). */
5e7a5e2d 155 afterfork();
baab3a63
RK
156 va_start(ap,arg);
157 args[0]=(char *)arg; /* program name */
7138d0c5
SE
158 i=1;
159 while ((args[i++]=va_arg(ap,char *)));
160 execvp(path,args);
baab3a63
RK
161 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
162 _exit(1);
7138d0c5
SE
163 } else {
164 /* Error */
baab3a63 165 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
7138d0c5
SE
166 }
167
7138d0c5
SE
168 return rv;
169}
170
171static beforepoll_fn signal_beforepoll;
172static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
90a39563 173 int *timeout_io)
7138d0c5 174{
ee697dd9 175 BEFOREPOLL_WANT_FDS(1);
7138d0c5
SE
176 fds[0].fd=spr;
177 fds[0].events=POLLIN;
178 return 0;
179}
180
5a8a7053
RK
181/* Bodge to work around Ubuntu's strict header files */
182static void discard(int anything) {}
183
7138d0c5 184static afterpoll_fn signal_afterpoll;
90a39563 185static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
7138d0c5
SE
186{
187 uint8_t buf[16];
188 struct signotify *n;
189 sigset_t todo,old;
190
191 if (nfds && (fds->revents & POLLIN)) {
5a8a7053
RK
192 discard(read(spr,buf,16));
193 /* We don't actually care what we read; as
7138d0c5
SE
194 long as there was at least one byte
195 (which there was) we'll pick up the
196 signals in the pending set */
197
198 /* We reset 'pending' before processing any of the signals
199 that were pending so that we don't miss any signals that
200 are delivered partway-through processing (all we assume
201 about signal notification routines is that they handle all
202 the work available at their _start_ and only optionally any
203 work that arrives part-way through their execution). */
204 sigprocmask(SIG_SETMASK,&fullset,&old);
205 todo=pending;
206 sigemptyset(&pending);
207 sigprocmask(SIG_SETMASK,&old,NULL);
208
209 for (n=sigs; n; n=n->next)
210 if (sigismember(&todo,n->signum))
211 n->notify(n->cst,n->signum);
212 }
213}
214
5e7a5e2d 215void afterfork(void)
7138d0c5
SE
216{
217 struct signotify *n;
218 sigset_t done;
219 struct sigaction sa;
220
c72aa743
IJ
221 clear_phase_hooks(PHASE_SHUTDOWN);
222 /* Prevents calls to fatal() etc. in the child from running off
223 and doing a lot of unhelpful things */
224
7138d0c5
SE
225 sigemptyset(&done);
226 for (n=sigs; n; n=n->next)
227 if (!sigismember(&done,n->signum)) {
228 sigaddset(&done,n->signum);
229 sa.sa_handler=SIG_DFL;
230 sa.sa_mask=emptyset;
231 sa.sa_flags=0;
232 sigaction(n->signum,&sa,NULL);
233 }
5e7a5e2d
IJ
234
235 sigemptyset(&emptyset);
236 sigprocmask(SIG_SETMASK,&emptyset,NULL);
7138d0c5
SE
237}
238
32654a31
IJ
239void childpersist_closefd_hook(void *fd_vp, uint32_t newphase)
240{
241 int *fd_p=fd_vp;
242 int fd=*fd_p;
243 if (fd<0) return;
244 *fd_p=-1;
245 setnonblock(fd); /* in case close() might block */
246 close(fd); /* discard errors - we don't care, in the child */
247}
248
7138d0c5
SE
249static void signal_handler(int signum)
250{
ff05a229 251 int saved_errno;
7138d0c5
SE
252 uint8_t thing=0;
253 sigaddset(&pending,signum);
ff05a229
SE
254 /* XXX the write() may set errno, which can make the main program fail.
255 However, signal handlers aren't allowed to modify anything which
256 is not of type sig_atomic_t. The world is broken. */
257 /* I have decided to save and restore errno anyway; on most
258 architectures on which secnet can run modifications to errno
259 will be atomic, and it seems to be the lesser of the two
260 evils. */
261 saved_errno=errno;
5a8a7053
RK
262 discard(write(spw,&thing,1));
263 /* We don't care if this fails (i.e. the pipe
7138d0c5
SE
264 is full) because the service routine will
265 spot the pending signal anyway */
ff05a229 266 errno=saved_errno;
7138d0c5
SE
267}
268
269static void register_signal_handler(struct signotify *s)
270{
271 struct sigaction sa;
272 int rv;
273
274 if (!signal_handling) return;
275
276 if (sigismember(&registered,s->signum)) return;
277 sigaddset(&registered,s->signum);
278
279 sa.sa_handler=signal_handler;
280 sa.sa_mask=fullset;
281 sa.sa_flags=0;
282 rv=sigaction(s->signum,&sa,NULL);
283 if (rv!=0) {
284 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
285 }
286}
287
288void request_signal_notification(int signum, signal_notify_fn *notify,
289 void *cst)
290{
291 struct signotify *s;
292 sigset_t old;
293
b7886fd4 294 NEW(s);
7138d0c5
SE
295 s->signum=signum;
296 s->notify=notify;
297 s->cst=cst;
298 s->next=sigs;
299 sigprocmask(SIG_SETMASK,&fullset,&old);
300 sigs=s;
301 register_signal_handler(s);
302 sigprocmask(SIG_SETMASK,&old,NULL);
303}
304
305void start_signal_handling(void)
306{
307 int p[2];
308 struct signotify *i;
309
310 sigemptyset(&emptyset);
311 sigfillset(&fullset);
312 sigemptyset(&registered);
313 sigemptyset(&pending);
314
6a06198c 315 pipe_cloexec(p);
7138d0c5
SE
316 spw=p[1];
317 spr=p[0];
f54d5ada 318 setnonblock(spw);
ba703386 319 setnonblock(spr);
7138d0c5 320
32fc582f 321 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
7138d0c5
SE
322 signal_handling=True;
323
324 /* Register signal handlers for all the signals we're interested in */
325 for (i=sigs; i; i=i->next) {
326 register_signal_handler(i);
327 }
328
329 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
330}