logging: Use lg_exitstatus
[secnet] / process.c
CommitLineData
baab3a63 1#define _GNU_SOURCE
7138d0c5
SE
2#include "secnet.h"
3#include <unistd.h>
4#include <fcntl.h>
5#include <errno.h>
6#include <sys/wait.h>
baab3a63 7#include <string.h>
7138d0c5
SE
8#include "process.h"
9
10/* Process handling - subprocesses, signals, etc. */
11
12static bool_t signal_handling=False;
13static sigset_t emptyset, fullset;
14static sigset_t registered,pending;
15
16struct child {
17 pid_t pid;
fe5e9cc4 18 cstring_t desc;
7138d0c5
SE
19 process_callback_fn *cb;
20 void *cst;
21 bool_t finished;
22 struct child *next;
23};
24
25static struct child *children=NULL;
26
27struct signotify {
28 int signum;
29 signal_notify_fn *notify;
30 void *cst;
31 struct signotify *next;
32};
33
34static struct signotify *sigs=NULL;
35
36static int spw,spr; /* file descriptors for signal notification pipe */
37
38static void set_default_signals(void);
39
40/* Long-lived subprocesses can only be started once we've started
41 signal processing so that we can catch SIGCHLD for them and report
42 their exit status using the callback function. We block SIGCHLD
43 until signal processing has begun. */
042a8da9 44pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
fe5e9cc4 45 void *est, void *cst, cstring_t desc)
7138d0c5
SE
46{
47 struct child *c;
7138d0c5
SE
48 pid_t p;
49
50 c=safe_malloc(sizeof(*c),"makesubproc");
51 c->desc=desc;
52 c->cb=cb;
53 c->cst=cst;
54
55 if (!signal_handling) {
4f5e39ec 56 fatal("makesubproc called before signal handling started");
7138d0c5
SE
57 }
58 p=fork();
59 if (p==0) {
60 /* Child process */
61 set_default_signals();
62 sigprocmask(SIG_SETMASK,&emptyset,NULL);
63 entry(est);
64 abort();
65 } else if (p==-1) {
66 fatal_perror("makesubproc (%s): fork",desc);
67 }
68 c->pid=p;
69 c->finished=False;
70 c->next=children;
71 children=c;
042a8da9 72 return p;
7138d0c5
SE
73}
74
75static signal_notify_fn sigchld_handler;
76static 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
fe5e9cc4 128int sys_cmd(const char *path, const char *arg, ...)
7138d0c5
SE
129{
130 va_list ap;
baab3a63 131 int rv, rc;
7138d0c5
SE
132 pid_t c;
133
7138d0c5
SE
134 c=fork();
135 if (c) {
136 /* Parent -> wait for child */
baab3a63
RK
137 do {
138 rc = waitpid(c,&rv,0);
139 } while(rc < 0 && errno == EINTR);
140 if (rc < 0)
141 fatal_perror("sys_cmd: waitpid for %s", path);
142 if (rc != c) /* OS has gone mad */
143 fatal("sys_cmd: waitpid for %s returned wrong process ID!",
144 path);
145 if (rv) {
4ac7fd3f
IJ
146 /* If the command failed report its exit status */
147 lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
baab3a63 148 }
7138d0c5
SE
149 } else if (c==0) {
150 char *args[100];
151 int i;
152 /* Child -> exec command */
fe5e9cc4
SE
153 /* Really we ought to strcpy() the arguments into the args array,
154 since the arguments are const char *. Since we'll exit anyway
155 if the execvp() fails this seems somewhat pointless, and
156 increases the chance of the child process failing before it
157 gets to exec(). */
baab3a63
RK
158 va_start(ap,arg);
159 args[0]=(char *)arg; /* program name */
7138d0c5
SE
160 i=1;
161 while ((args[i++]=va_arg(ap,char *)));
162 execvp(path,args);
baab3a63
RK
163 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
164 _exit(1);
7138d0c5
SE
165 } else {
166 /* Error */
baab3a63 167 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
7138d0c5
SE
168 }
169
7138d0c5
SE
170 return rv;
171}
172
173static beforepoll_fn signal_beforepoll;
174static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
90a39563 175 int *timeout_io)
7138d0c5 176{
ee697dd9 177 BEFOREPOLL_WANT_FDS(1);
7138d0c5
SE
178 fds[0].fd=spr;
179 fds[0].events=POLLIN;
180 return 0;
181}
182
5a8a7053
RK
183/* Bodge to work around Ubuntu's strict header files */
184static void discard(int anything) {}
185
7138d0c5 186static afterpoll_fn signal_afterpoll;
90a39563 187static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
7138d0c5
SE
188{
189 uint8_t buf[16];
190 struct signotify *n;
191 sigset_t todo,old;
192
193 if (nfds && (fds->revents & POLLIN)) {
5a8a7053
RK
194 discard(read(spr,buf,16));
195 /* We don't actually care what we read; as
7138d0c5
SE
196 long as there was at least one byte
197 (which there was) we'll pick up the
198 signals in the pending set */
199
200 /* We reset 'pending' before processing any of the signals
201 that were pending so that we don't miss any signals that
202 are delivered partway-through processing (all we assume
203 about signal notification routines is that they handle all
204 the work available at their _start_ and only optionally any
205 work that arrives part-way through their execution). */
206 sigprocmask(SIG_SETMASK,&fullset,&old);
207 todo=pending;
208 sigemptyset(&pending);
209 sigprocmask(SIG_SETMASK,&old,NULL);
210
211 for (n=sigs; n; n=n->next)
212 if (sigismember(&todo,n->signum))
213 n->notify(n->cst,n->signum);
214 }
215}
216
217static void set_default_signals(void)
218{
219 struct signotify *n;
220 sigset_t done;
221 struct sigaction sa;
222
223 sigemptyset(&done);
224 for (n=sigs; n; n=n->next)
225 if (!sigismember(&done,n->signum)) {
226 sigaddset(&done,n->signum);
227 sa.sa_handler=SIG_DFL;
228 sa.sa_mask=emptyset;
229 sa.sa_flags=0;
230 sigaction(n->signum,&sa,NULL);
231 }
232}
233
234static void signal_handler(int signum)
235{
ff05a229 236 int saved_errno;
7138d0c5
SE
237 uint8_t thing=0;
238 sigaddset(&pending,signum);
ff05a229
SE
239 /* XXX the write() may set errno, which can make the main program fail.
240 However, signal handlers aren't allowed to modify anything which
241 is not of type sig_atomic_t. The world is broken. */
242 /* I have decided to save and restore errno anyway; on most
243 architectures on which secnet can run modifications to errno
244 will be atomic, and it seems to be the lesser of the two
245 evils. */
246 saved_errno=errno;
5a8a7053
RK
247 discard(write(spw,&thing,1));
248 /* We don't care if this fails (i.e. the pipe
7138d0c5
SE
249 is full) because the service routine will
250 spot the pending signal anyway */
ff05a229 251 errno=saved_errno;
7138d0c5
SE
252}
253
254static void register_signal_handler(struct signotify *s)
255{
256 struct sigaction sa;
257 int rv;
258
259 if (!signal_handling) return;
260
261 if (sigismember(&registered,s->signum)) return;
262 sigaddset(&registered,s->signum);
263
264 sa.sa_handler=signal_handler;
265 sa.sa_mask=fullset;
266 sa.sa_flags=0;
267 rv=sigaction(s->signum,&sa,NULL);
268 if (rv!=0) {
269 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
270 }
271}
272
273void request_signal_notification(int signum, signal_notify_fn *notify,
274 void *cst)
275{
276 struct signotify *s;
277 sigset_t old;
278
279 s=safe_malloc(sizeof(*s),"request_signal_notification");
280 s->signum=signum;
281 s->notify=notify;
282 s->cst=cst;
283 s->next=sigs;
284 sigprocmask(SIG_SETMASK,&fullset,&old);
285 sigs=s;
286 register_signal_handler(s);
287 sigprocmask(SIG_SETMASK,&old,NULL);
288}
289
290void start_signal_handling(void)
291{
292 int p[2];
293 struct signotify *i;
294
295 sigemptyset(&emptyset);
296 sigfillset(&fullset);
297 sigemptyset(&registered);
298 sigemptyset(&pending);
299
6a06198c 300 pipe_cloexec(p);
7138d0c5
SE
301 spw=p[1];
302 spr=p[0];
303 if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
304 fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
305 }
306
32fc582f 307 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
7138d0c5
SE
308 signal_handling=True;
309
310 /* Register signal handlers for all the signals we're interested in */
311 for (i=sigs; i; i=i->next) {
312 register_signal_handler(i);
313 }
314
315 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
316}