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