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