chiark / gitweb /
Import release 0.1.10
[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 /* Process handling - subprocesses, signals, etc. */
9
10 static bool_t signal_handling=False;
11 static sigset_t emptyset, fullset;
12 static sigset_t registered,pending;
13
14 struct child {
15     pid_t pid;
16     string_t desc;
17     process_callback_fn *cb;
18     void *cst;
19     bool_t finished;
20     struct child *next;
21 };
22
23 static struct child *children=NULL;
24
25 struct signotify {
26     int signum;
27     signal_notify_fn *notify;
28     void *cst;
29     struct signotify *next;
30 };
31
32 static struct signotify *sigs=NULL;
33
34 static int spw,spr; /* file descriptors for signal notification pipe */
35
36 static void set_default_signals(void);
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, string_t desc)
44 {
45     struct child *c;
46     pid_t p;
47
48     c=safe_malloc(sizeof(*c),"makesubproc");
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\n");
55     }
56     p=fork();
57     if (p==0) {
58         /* Child process */
59         set_default_signals();
60         sigprocmask(SIG_SETMASK,&emptyset,NULL);
61         entry(est);
62         abort();
63     } else if (p==-1) {
64         fatal_perror("makesubproc (%s): fork",desc);
65     }
66     c->pid=p;
67     c->finished=False;
68     c->next=children;
69     children=c;
70     return p;
71 }
72
73 static signal_notify_fn sigchld_handler;
74 static void sigchld_handler(void *st, int signum)
75 {
76     struct child *i,*n,**p;
77     struct work {
78         pid_t pid;
79         process_callback_fn *cb;
80         void *cst;
81         int status;
82         struct work *next;
83     };
84     struct work *w=NULL, *nw;
85     pid_t rv;
86     int status;
87
88     for (i=children; i; i=i->next) {
89         rv=waitpid(i->pid,&status,WNOHANG);
90         if (rv==-1) {
91             fatal_perror("sigchld_handler: waitpid");
92         }
93         if (rv==i->pid) {
94             i->finished=True;
95             
96             nw=safe_malloc(sizeof(*nw),"sigchld_handler");
97             nw->pid=i->pid;
98             nw->cb=i->cb;
99             nw->cst=i->cst;
100             nw->status=status;
101             nw->next=w;
102             w=nw;
103         }
104     }
105
106     /* Remove all the finished tasks from the list of children */
107     for (i=children, p=&children; i; i=n) {
108         n=i->next;
109         if (i->finished) {
110             free(i);
111             *p=n;
112         } else {
113             p=&i->next;
114         }
115     }
116
117     /* Notify as appropriate, then free the list */
118     while (w) {
119         w->cb(w->cst,w->pid,w->status);
120         nw=w;
121         w=w->next;
122         free(nw);
123     }
124 }
125
126 int sys_cmd(const char *path, char *arg, ...)
127 {
128     va_list ap;
129     int rv;
130     pid_t c;
131
132     va_start(ap,arg);
133     c=fork();
134     if (c) {
135         /* Parent -> wait for child */
136         waitpid(c,&rv,0);
137     } else if (c==0) {
138         char *args[100];
139         int i;
140         /* Child -> exec command */
141         args[0]=arg;
142         i=1;
143         while ((args[i++]=va_arg(ap,char *)));
144         execvp(path,args);
145         exit(1);
146     } else {
147         /* Error */
148         fatal_perror("sys_cmd(%s,%s,...)");
149     }
150
151     va_end(ap);
152     return rv;
153 }
154
155 static beforepoll_fn signal_beforepoll;
156 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
157                              int *timeout_io, const struct timeval *tv_now,
158                              uint64_t *now)
159 {
160     if (*nfds_io<1) {
161         *nfds_io=1;
162         return ERANGE;
163     }
164     *nfds_io=1;
165     fds[0].fd=spr;
166     fds[0].events=POLLIN;
167     return 0;
168 }
169
170 static afterpoll_fn signal_afterpoll;
171 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds,
172                              const struct timeval *tv, uint64_t *now)
173 {
174     uint8_t buf[16];
175     struct signotify *n;
176     sigset_t todo,old;
177
178     if (nfds && (fds->revents & POLLIN)) {
179         read(spr,buf,16); /* We don't actually care what we read; as
180                              long as there was at least one byte
181                              (which there was) we'll pick up the
182                              signals in the pending set */
183         
184         /* We reset 'pending' before processing any of the signals
185            that were pending so that we don't miss any signals that
186            are delivered partway-through processing (all we assume
187            about signal notification routines is that they handle all
188            the work available at their _start_ and only optionally any
189            work that arrives part-way through their execution). */
190         sigprocmask(SIG_SETMASK,&fullset,&old);
191         todo=pending;
192         sigemptyset(&pending);
193         sigprocmask(SIG_SETMASK,&old,NULL);
194         
195         for (n=sigs; n; n=n->next)
196             if (sigismember(&todo,n->signum))
197                 n->notify(n->cst,n->signum);
198     }
199 }
200
201 static void set_default_signals(void)
202 {
203     struct signotify *n;
204     sigset_t done;
205     struct sigaction sa;
206
207     sigemptyset(&done);
208     for (n=sigs; n; n=n->next)
209         if (!sigismember(&done,n->signum)) {
210             sigaddset(&done,n->signum);
211             sa.sa_handler=SIG_DFL;
212             sa.sa_mask=emptyset;
213             sa.sa_flags=0;
214             sigaction(n->signum,&sa,NULL);
215         }
216 }
217
218 static void signal_handler(int signum)
219 {
220     uint8_t thing=0;
221     sigaddset(&pending,signum);
222     write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe
223                             is full) because the service routine will
224                             spot the pending signal anyway */
225 }
226
227 static void register_signal_handler(struct signotify *s)
228 {
229     struct sigaction sa;
230     int rv;
231
232     if (!signal_handling) return;
233
234     if (sigismember(&registered,s->signum)) return;
235     sigaddset(&registered,s->signum);
236
237     sa.sa_handler=signal_handler;
238     sa.sa_mask=fullset;
239     sa.sa_flags=0;
240     rv=sigaction(s->signum,&sa,NULL);
241     if (rv!=0) {
242         fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
243     }
244 }
245
246 void request_signal_notification(int signum, signal_notify_fn *notify,
247                                  void *cst)
248 {
249     struct signotify *s;
250     sigset_t old;
251
252     s=safe_malloc(sizeof(*s),"request_signal_notification");
253     s->signum=signum;
254     s->notify=notify;
255     s->cst=cst;
256     s->next=sigs;
257     sigprocmask(SIG_SETMASK,&fullset,&old);
258     sigs=s;
259     register_signal_handler(s);
260     sigprocmask(SIG_SETMASK,&old,NULL);
261 }
262
263 void start_signal_handling(void)
264 {
265     int p[2];
266     struct signotify *i;
267
268     sigemptyset(&emptyset);
269     sigfillset(&fullset);
270     sigemptyset(&registered);
271     sigemptyset(&pending);
272
273     if (pipe(p)!=0) {
274         fatal_perror("start_signal_handling: pipe");
275     }
276     spw=p[1];
277     spr=p[0];
278     if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
279         fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
280     }
281
282     register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal");
283     signal_handling=True;
284
285     /* Register signal handlers for all the signals we're interested in */
286     for (i=sigs; i; i=i->next) {
287         register_signal_handler(i);
288     }
289
290     request_signal_notification(SIGCHLD,sigchld_handler,NULL);
291 }