cleanup: build on Ubuntu Lucid
[secnet] / process.c
CommitLineData
7138d0c5
SE
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
794f2398
SE
8/* Advice about children from Peter:
9Better 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
7138d0c5
SE
25/* Process handling - subprocesses, signals, etc. */
26
27static bool_t signal_handling=False;
28static sigset_t emptyset, fullset;
29static sigset_t registered,pending;
30
31struct child {
32 pid_t pid;
fe5e9cc4 33 cstring_t desc;
7138d0c5
SE
34 process_callback_fn *cb;
35 void *cst;
36 bool_t finished;
37 struct child *next;
38};
39
40static struct child *children=NULL;
41
42struct signotify {
43 int signum;
44 signal_notify_fn *notify;
45 void *cst;
46 struct signotify *next;
47};
48
49static struct signotify *sigs=NULL;
50
51static int spw,spr; /* file descriptors for signal notification pipe */
52
53static 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. */
042a8da9 59pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
fe5e9cc4 60 void *est, void *cst, cstring_t desc)
7138d0c5
SE
61{
62 struct child *c;
7138d0c5
SE
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) {
4f5e39ec 71 fatal("makesubproc called before signal handling started");
7138d0c5
SE
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;
042a8da9 87 return p;
7138d0c5
SE
88}
89
90static signal_notify_fn sigchld_handler;
91static 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
fe5e9cc4 143int sys_cmd(const char *path, const char *arg, ...)
7138d0c5
SE
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 */
fe5e9cc4
SE
158 /* Really we ought to strcpy() the arguments into the args array,
159 since the arguments are const char *. Since we'll exit anyway
160 if the execvp() fails this seems somewhat pointless, and
161 increases the chance of the child process failing before it
162 gets to exec(). */
163 args[0]=(char *)arg;
7138d0c5
SE
164 i=1;
165 while ((args[i++]=va_arg(ap,char *)));
166 execvp(path,args);
167 exit(1);
168 } else {
169 /* Error */
170 fatal_perror("sys_cmd(%s,%s,...)");
171 }
172
173 va_end(ap);
174 return rv;
175}
176
177static beforepoll_fn signal_beforepoll;
178static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
179 int *timeout_io, const struct timeval *tv_now,
180 uint64_t *now)
181{
182 if (*nfds_io<1) {
183 *nfds_io=1;
184 return ERANGE;
185 }
186 *nfds_io=1;
187 fds[0].fd=spr;
188 fds[0].events=POLLIN;
189 return 0;
190}
191
2a5d3006
RK
192/* Bodge to work around Ubuntu's strict header files */
193static void discard(int anything) {}
194
7138d0c5
SE
195static afterpoll_fn signal_afterpoll;
196static void signal_afterpoll(void *st, struct pollfd *fds, int nfds,
197 const struct timeval *tv, uint64_t *now)
198{
199 uint8_t buf[16];
200 struct signotify *n;
201 sigset_t todo,old;
202
203 if (nfds && (fds->revents & POLLIN)) {
2a5d3006
RK
204 discard(read(spr,buf,16));
205 /* We don't actually care what we read; as
7138d0c5
SE
206 long as there was at least one byte
207 (which there was) we'll pick up the
208 signals in the pending set */
209
210 /* We reset 'pending' before processing any of the signals
211 that were pending so that we don't miss any signals that
212 are delivered partway-through processing (all we assume
213 about signal notification routines is that they handle all
214 the work available at their _start_ and only optionally any
215 work that arrives part-way through their execution). */
216 sigprocmask(SIG_SETMASK,&fullset,&old);
217 todo=pending;
218 sigemptyset(&pending);
219 sigprocmask(SIG_SETMASK,&old,NULL);
220
221 for (n=sigs; n; n=n->next)
222 if (sigismember(&todo,n->signum))
223 n->notify(n->cst,n->signum);
224 }
225}
226
227static void set_default_signals(void)
228{
229 struct signotify *n;
230 sigset_t done;
231 struct sigaction sa;
232
233 sigemptyset(&done);
234 for (n=sigs; n; n=n->next)
235 if (!sigismember(&done,n->signum)) {
236 sigaddset(&done,n->signum);
237 sa.sa_handler=SIG_DFL;
238 sa.sa_mask=emptyset;
239 sa.sa_flags=0;
240 sigaction(n->signum,&sa,NULL);
241 }
242}
243
244static void signal_handler(int signum)
245{
ff05a229 246 int saved_errno;
7138d0c5
SE
247 uint8_t thing=0;
248 sigaddset(&pending,signum);
ff05a229
SE
249 /* XXX the write() may set errno, which can make the main program fail.
250 However, signal handlers aren't allowed to modify anything which
251 is not of type sig_atomic_t. The world is broken. */
252 /* I have decided to save and restore errno anyway; on most
253 architectures on which secnet can run modifications to errno
254 will be atomic, and it seems to be the lesser of the two
255 evils. */
256 saved_errno=errno;
2a5d3006
RK
257 discard(write(spw,&thing,1));
258 /* We don't care if this fails (i.e. the pipe
7138d0c5
SE
259 is full) because the service routine will
260 spot the pending signal anyway */
ff05a229 261 errno=saved_errno;
7138d0c5
SE
262}
263
264static void register_signal_handler(struct signotify *s)
265{
266 struct sigaction sa;
267 int rv;
268
269 if (!signal_handling) return;
270
271 if (sigismember(&registered,s->signum)) return;
272 sigaddset(&registered,s->signum);
273
274 sa.sa_handler=signal_handler;
275 sa.sa_mask=fullset;
276 sa.sa_flags=0;
277 rv=sigaction(s->signum,&sa,NULL);
278 if (rv!=0) {
279 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
280 }
281}
282
283void request_signal_notification(int signum, signal_notify_fn *notify,
284 void *cst)
285{
286 struct signotify *s;
287 sigset_t old;
288
289 s=safe_malloc(sizeof(*s),"request_signal_notification");
290 s->signum=signum;
291 s->notify=notify;
292 s->cst=cst;
293 s->next=sigs;
294 sigprocmask(SIG_SETMASK,&fullset,&old);
295 sigs=s;
296 register_signal_handler(s);
297 sigprocmask(SIG_SETMASK,&old,NULL);
298}
299
300void start_signal_handling(void)
301{
302 int p[2];
303 struct signotify *i;
304
305 sigemptyset(&emptyset);
306 sigfillset(&fullset);
307 sigemptyset(&registered);
308 sigemptyset(&pending);
309
310 if (pipe(p)!=0) {
311 fatal_perror("start_signal_handling: pipe");
312 }
313 spw=p[1];
314 spr=p[0];
315 if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
316 fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
317 }
318
319 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal");
320 signal_handling=True;
321
322 /* Register signal handlers for all the signals we're interested in */
323 for (i=sigs; i; i=i->next) {
324 register_signal_handler(i);
325 }
326
327 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
328}