chiark / gitweb /
Import release 0.1.6
[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 extern void makesubproc(process_entry_fn *entry, process_callback_fn *cb,
43                         void *est, void *cst, string_t desc)
44 {
45     struct child *c;
46     sigset_t sigchld;
47     pid_t p;
48
49     c=safe_malloc(sizeof(*c),"makesubproc");
50     c->desc=desc;
51     c->cb=cb;
52     c->cst=cst;
53
54     if (!signal_handling) {
55         sigemptyset(&sigchld);
56         sigaddset(&sigchld,SIGCHLD);
57         sigprocmask(SIG_BLOCK,&sigchld,NULL);
58     }
59     p=fork();
60     if (p==0) {
61         /* Child process */
62         set_default_signals();
63         sigprocmask(SIG_SETMASK,&emptyset,NULL);
64         entry(est);
65         abort();
66     } else if (p==-1) {
67         fatal_perror("makesubproc (%s): fork",desc);
68     }
69     c->pid=p;
70     c->finished=False;
71     c->next=children;
72     children=c;
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, char *arg, ...)
129 {
130     va_list ap;
131     int rv;
132     pid_t c;
133
134     va_start(ap,arg);
135     c=fork();
136     if (c) {
137         /* Parent -> wait for child */
138         waitpid(c,&rv,0);
139     } else if (c==0) {
140         char *args[100];
141         int i;
142         /* Child -> exec command */
143         args[0]=arg;
144         i=1;
145         while ((args[i++]=va_arg(ap,char *)));
146         execvp(path,args);
147         exit(1);
148     } else {
149         /* Error */
150         fatal_perror("sys_cmd(%s,%s,...)");
151     }
152
153     va_end(ap);
154     return rv;
155 }
156
157 static beforepoll_fn signal_beforepoll;
158 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
159                              int *timeout_io, const struct timeval *tv_now,
160                              uint64_t *now)
161 {
162     if (*nfds_io<1) {
163         *nfds_io=1;
164         return ERANGE;
165     }
166     *nfds_io=1;
167     fds[0].fd=spr;
168     fds[0].events=POLLIN;
169     return 0;
170 }
171
172 static afterpoll_fn signal_afterpoll;
173 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds,
174                              const struct timeval *tv, uint64_t *now)
175 {
176     uint8_t buf[16];
177     struct signotify *n;
178     sigset_t todo,old;
179
180     if (nfds && (fds->revents & POLLIN)) {
181         read(spr,buf,16); /* We don't actually care what we read; as
182                              long as there was at least one byte
183                              (which there was) we'll pick up the
184                              signals in the pending set */
185         
186         /* We reset 'pending' before processing any of the signals
187            that were pending so that we don't miss any signals that
188            are delivered partway-through processing (all we assume
189            about signal notification routines is that they handle all
190            the work available at their _start_ and only optionally any
191            work that arrives part-way through their execution). */
192         sigprocmask(SIG_SETMASK,&fullset,&old);
193         todo=pending;
194         sigemptyset(&pending);
195         sigprocmask(SIG_SETMASK,&old,NULL);
196         
197         for (n=sigs; n; n=n->next)
198             if (sigismember(&todo,n->signum))
199                 n->notify(n->cst,n->signum);
200     }
201 }
202
203 static void set_default_signals(void)
204 {
205     struct signotify *n;
206     sigset_t done;
207     struct sigaction sa;
208
209     sigemptyset(&done);
210     for (n=sigs; n; n=n->next)
211         if (!sigismember(&done,n->signum)) {
212             sigaddset(&done,n->signum);
213             sa.sa_handler=SIG_DFL;
214             sa.sa_mask=emptyset;
215             sa.sa_flags=0;
216             sigaction(n->signum,&sa,NULL);
217         }
218 }
219
220 static void signal_handler(int signum)
221 {
222     uint8_t thing=0;
223     sigaddset(&pending,signum);
224     write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe
225                             is full) because the service routine will
226                             spot the pending signal anyway */
227 }
228
229 static void register_signal_handler(struct signotify *s)
230 {
231     struct sigaction sa;
232     int rv;
233
234     if (!signal_handling) return;
235
236     if (sigismember(&registered,s->signum)) return;
237     sigaddset(&registered,s->signum);
238
239     sa.sa_handler=signal_handler;
240     sa.sa_mask=fullset;
241     sa.sa_flags=0;
242     rv=sigaction(s->signum,&sa,NULL);
243     if (rv!=0) {
244         fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
245     }
246 }
247
248 void request_signal_notification(int signum, signal_notify_fn *notify,
249                                  void *cst)
250 {
251     struct signotify *s;
252     sigset_t old;
253
254     s=safe_malloc(sizeof(*s),"request_signal_notification");
255     s->signum=signum;
256     s->notify=notify;
257     s->cst=cst;
258     s->next=sigs;
259     sigprocmask(SIG_SETMASK,&fullset,&old);
260     sigs=s;
261     register_signal_handler(s);
262     sigprocmask(SIG_SETMASK,&old,NULL);
263 }
264
265 void start_signal_handling(void)
266 {
267     int p[2];
268     struct signotify *i;
269
270     sigemptyset(&emptyset);
271     sigfillset(&fullset);
272     sigemptyset(&registered);
273     sigemptyset(&pending);
274
275     if (pipe(p)!=0) {
276         fatal_perror("start_signal_handling: pipe");
277     }
278     spw=p[1];
279     spr=p[0];
280     if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
281         fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
282     }
283
284     register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal");
285     signal_handling=True;
286
287     /* Register signal handlers for all the signals we're interested in */
288     for (i=sigs; i; i=i->next) {
289         register_signal_handler(i);
290     }
291
292     request_signal_notification(SIGCHLD,sigchld_handler,NULL);
293 }