Import release 0.1.7
[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 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 }