Add a man page.
[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) {
146 /* If the command failed reporting its exit status */
147 if (WIFEXITED(rv))
148 Message(M_ERR, "sys_cmd(%s,%s,...) exited with status %d\n",
149 path, arg, WEXITSTATUS(rv));
150 else if(WIFSIGNALED(rv))
151 Message(M_ERR, "sys_cmd(%s,%s,...) exited with signal %d (%s)%s\n",
152 path, arg, WTERMSIG(rv), strsignal(WTERMSIG(rv)),
153 WCOREDUMP(rv) ? " - core dumped" : "");
154 else
155 Message(M_ERR, "sys_cmd(%s,%s,...) exited with wstat %#x",
156 path, arg, rv);
157 }
7138d0c5
SE
158 } else if (c==0) {
159 char *args[100];
160 int i;
161 /* Child -> exec command */
fe5e9cc4
SE
162 /* Really we ought to strcpy() the arguments into the args array,
163 since the arguments are const char *. Since we'll exit anyway
164 if the execvp() fails this seems somewhat pointless, and
165 increases the chance of the child process failing before it
166 gets to exec(). */
baab3a63
RK
167 va_start(ap,arg);
168 args[0]=(char *)arg; /* program name */
7138d0c5
SE
169 i=1;
170 while ((args[i++]=va_arg(ap,char *)));
171 execvp(path,args);
baab3a63
RK
172 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
173 _exit(1);
7138d0c5
SE
174 } else {
175 /* Error */
baab3a63 176 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
7138d0c5
SE
177 }
178
7138d0c5
SE
179 return rv;
180}
181
182static beforepoll_fn signal_beforepoll;
183static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
90a39563 184 int *timeout_io)
7138d0c5
SE
185{
186 if (*nfds_io<1) {
187 *nfds_io=1;
188 return ERANGE;
189 }
190 *nfds_io=1;
191 fds[0].fd=spr;
192 fds[0].events=POLLIN;
193 return 0;
194}
195
196static afterpoll_fn signal_afterpoll;
90a39563 197static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
7138d0c5
SE
198{
199 uint8_t buf[16];
200 struct signotify *n;
201 sigset_t todo,old;
202
203 if (nfds && (fds->revents & POLLIN)) {
204 read(spr,buf,16); /* We don't actually care what we read; as
205 long as there was at least one byte
206 (which there was) we'll pick up the
207 signals in the pending set */
208
209 /* We reset 'pending' before processing any of the signals
210 that were pending so that we don't miss any signals that
211 are delivered partway-through processing (all we assume
212 about signal notification routines is that they handle all
213 the work available at their _start_ and only optionally any
214 work that arrives part-way through their execution). */
215 sigprocmask(SIG_SETMASK,&fullset,&old);
216 todo=pending;
217 sigemptyset(&pending);
218 sigprocmask(SIG_SETMASK,&old,NULL);
219
220 for (n=sigs; n; n=n->next)
221 if (sigismember(&todo,n->signum))
222 n->notify(n->cst,n->signum);
223 }
224}
225
226static void set_default_signals(void)
227{
228 struct signotify *n;
229 sigset_t done;
230 struct sigaction sa;
231
232 sigemptyset(&done);
233 for (n=sigs; n; n=n->next)
234 if (!sigismember(&done,n->signum)) {
235 sigaddset(&done,n->signum);
236 sa.sa_handler=SIG_DFL;
237 sa.sa_mask=emptyset;
238 sa.sa_flags=0;
239 sigaction(n->signum,&sa,NULL);
240 }
241}
242
243static void signal_handler(int signum)
244{
ff05a229 245 int saved_errno;
7138d0c5
SE
246 uint8_t thing=0;
247 sigaddset(&pending,signum);
ff05a229
SE
248 /* XXX the write() may set errno, which can make the main program fail.
249 However, signal handlers aren't allowed to modify anything which
250 is not of type sig_atomic_t. The world is broken. */
251 /* I have decided to save and restore errno anyway; on most
252 architectures on which secnet can run modifications to errno
253 will be atomic, and it seems to be the lesser of the two
254 evils. */
255 saved_errno=errno;
7138d0c5
SE
256 write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe
257 is full) because the service routine will
258 spot the pending signal anyway */
ff05a229 259 errno=saved_errno;
7138d0c5
SE
260}
261
262static void register_signal_handler(struct signotify *s)
263{
264 struct sigaction sa;
265 int rv;
266
267 if (!signal_handling) return;
268
269 if (sigismember(&registered,s->signum)) return;
270 sigaddset(&registered,s->signum);
271
272 sa.sa_handler=signal_handler;
273 sa.sa_mask=fullset;
274 sa.sa_flags=0;
275 rv=sigaction(s->signum,&sa,NULL);
276 if (rv!=0) {
277 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
278 }
279}
280
281void request_signal_notification(int signum, signal_notify_fn *notify,
282 void *cst)
283{
284 struct signotify *s;
285 sigset_t old;
286
287 s=safe_malloc(sizeof(*s),"request_signal_notification");
288 s->signum=signum;
289 s->notify=notify;
290 s->cst=cst;
291 s->next=sigs;
292 sigprocmask(SIG_SETMASK,&fullset,&old);
293 sigs=s;
294 register_signal_handler(s);
295 sigprocmask(SIG_SETMASK,&old,NULL);
296}
297
298void start_signal_handling(void)
299{
300 int p[2];
301 struct signotify *i;
302
303 sigemptyset(&emptyset);
304 sigfillset(&fullset);
305 sigemptyset(&registered);
306 sigemptyset(&pending);
307
308 if (pipe(p)!=0) {
309 fatal_perror("start_signal_handling: pipe");
310 }
311 spw=p[1];
312 spr=p[0];
313 if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
314 fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
315 }
316
317 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal");
318 signal_handling=True;
319
320 /* Register signal handlers for all the signals we're interested in */
321 for (i=sigs; i; i=i->next) {
322 register_signal_handler(i);
323 }
324
325 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
326}