chiark / gitweb /
0c6d44388dbad630b0db367585a449be41fa20b4
[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     string_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, string_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\n");
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, 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         args[0]=arg;
159         i=1;
160         while ((args[i++]=va_arg(ap,char *)));
161         execvp(path,args);
162         exit(1);
163     } else {
164         /* Error */
165         fatal_perror("sys_cmd(%s,%s,...)");
166     }
167
168     va_end(ap);
169     return rv;
170 }
171
172 static beforepoll_fn signal_beforepoll;
173 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
174                              int *timeout_io, const struct timeval *tv_now,
175                              uint64_t *now)
176 {
177     if (*nfds_io<1) {
178         *nfds_io=1;
179         return ERANGE;
180     }
181     *nfds_io=1;
182     fds[0].fd=spr;
183     fds[0].events=POLLIN;
184     return 0;
185 }
186
187 static afterpoll_fn signal_afterpoll;
188 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds,
189                              const struct timeval *tv, uint64_t *now)
190 {
191     uint8_t buf[16];
192     struct signotify *n;
193     sigset_t todo,old;
194
195     if (nfds && (fds->revents & POLLIN)) {
196         read(spr,buf,16); /* We don't actually care what we read; as
197                              long as there was at least one byte
198                              (which there was) we'll pick up the
199                              signals in the pending set */
200         
201         /* We reset 'pending' before processing any of the signals
202            that were pending so that we don't miss any signals that
203            are delivered partway-through processing (all we assume
204            about signal notification routines is that they handle all
205            the work available at their _start_ and only optionally any
206            work that arrives part-way through their execution). */
207         sigprocmask(SIG_SETMASK,&fullset,&old);
208         todo=pending;
209         sigemptyset(&pending);
210         sigprocmask(SIG_SETMASK,&old,NULL);
211         
212         for (n=sigs; n; n=n->next)
213             if (sigismember(&todo,n->signum))
214                 n->notify(n->cst,n->signum);
215     }
216 }
217
218 static void set_default_signals(void)
219 {
220     struct signotify *n;
221     sigset_t done;
222     struct sigaction sa;
223
224     sigemptyset(&done);
225     for (n=sigs; n; n=n->next)
226         if (!sigismember(&done,n->signum)) {
227             sigaddset(&done,n->signum);
228             sa.sa_handler=SIG_DFL;
229             sa.sa_mask=emptyset;
230             sa.sa_flags=0;
231             sigaction(n->signum,&sa,NULL);
232         }
233 }
234
235 static void signal_handler(int signum)
236 {
237     int saved_errno;
238     uint8_t thing=0;
239     sigaddset(&pending,signum);
240     /* XXX the write() may set errno, which can make the main program fail.
241        However, signal handlers aren't allowed to modify anything which
242        is not of type sig_atomic_t.  The world is broken. */
243     /* I have decided to save and restore errno anyway; on most
244        architectures on which secnet can run modifications to errno
245        will be atomic, and it seems to be the lesser of the two
246        evils. */
247     saved_errno=errno;
248     write(spw,&thing,1); /* 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     if (pipe(p)!=0) {
301         fatal_perror("start_signal_handling: pipe");
302     }
303     spw=p[1];
304     spr=p[0];
305     if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
306         fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
307     }
308
309     register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal");
310     signal_handling=True;
311
312     /* Register signal handlers for all the signals we're interested in */
313     for (i=sigs; i; i=i->next) {
314         register_signal_handler(i);
315     }
316
317     request_signal_notification(SIGCHLD,sigchld_handler,NULL);
318 }