chiark / gitweb /
NEW etc.: Replace most calls to safe_realloc_ary
[secnet] / 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 /* 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. */
42 pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
43                  void *est, void *cst, cstring_t desc)
44 {
45     struct child *c;
46     pid_t p;
47
48     NEW(c);
49     c->desc=desc;
50     c->cb=cb;
51     c->cst=cst;
52
53     if (!signal_handling) {
54         fatal("makesubproc called before signal handling started");
55     }
56     p=fork();
57     if (p==0) {
58         /* Child process */
59         afterfork();
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;
69     return p;
70 }
71
72 static signal_notify_fn sigchld_handler;
73 static 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             
95             NEW(nw);
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
125 int sys_cmd(const char *path, const char *arg, ...)
126 {
127     va_list ap;
128     int rv, rc;
129     pid_t c;
130
131     c=fork();
132     if (c) {
133         /* Parent -> wait for child */
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) {
143             /* If the command failed report its exit status */
144             lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
145         }
146     } else if (c==0) {
147         char *args[100];
148         int i;
149         /* Child -> exec command */
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(). */
155         afterfork();
156         va_start(ap,arg);
157         args[0]=(char *)arg; /* program name */
158         i=1;
159         while ((args[i++]=va_arg(ap,char *)));
160         execvp(path,args);
161         fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
162         _exit(1);
163     } else {
164         /* Error */
165         fatal_perror("sys_cmd(%s,%s,...)", path, arg);
166     }
167
168     return rv;
169 }
170
171 static beforepoll_fn signal_beforepoll;
172 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
173                              int *timeout_io)
174 {
175     BEFOREPOLL_WANT_FDS(1);
176     fds[0].fd=spr;
177     fds[0].events=POLLIN;
178     return 0;
179 }
180
181 /* Bodge to work around Ubuntu's strict header files */
182 static void discard(int anything) {}
183
184 static afterpoll_fn signal_afterpoll;
185 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
186 {
187     uint8_t buf[16];
188     struct signotify *n;
189     sigset_t todo,old;
190
191     if (nfds && (fds->revents & POLLIN)) {
192         discard(read(spr,buf,16));
193                           /* We don't actually care what we read; as
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
215 void afterfork(void)
216 {
217     struct signotify *n;
218     sigset_t done;
219     struct sigaction sa;
220
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
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         }
234
235     sigemptyset(&emptyset);
236     sigprocmask(SIG_SETMASK,&emptyset,NULL);
237 }
238
239 void 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
249 static void signal_handler(int signum)
250 {
251     int saved_errno;
252     uint8_t thing=0;
253     sigaddset(&pending,signum);
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;
262     discard(write(spw,&thing,1));
263                          /* We don't care if this fails (i.e. the pipe
264                             is full) because the service routine will
265                             spot the pending signal anyway */
266     errno=saved_errno;
267 }
268
269 static 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
288 void request_signal_notification(int signum, signal_notify_fn *notify,
289                                  void *cst)
290 {
291     struct signotify *s;
292     sigset_t old;
293
294     NEW(s);
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
305 void 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
315     pipe_cloexec(p);
316     spw=p[1];
317     spr=p[0];
318     setnonblock(spw);
319     setnonblock(spr);
320
321     register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
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 }