Import release 0.1.6
[secnet] / 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 }