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