chiark / gitweb /
Makefile.in: turn of -Wsign-compare for bison output
[secnet] / process.c
CommitLineData
c215a4bc
IJ
1/*
2 * This file is part of secnet.
3 * See README for full list of copyright holders.
4 *
5 * secnet is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version d of the License, or
8 * (at your option) any later version.
9 *
10 * secnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * version 3 along with secnet; if not, see
17 * https://www.gnu.org/licenses/gpl.html.
18 */
19
baab3a63 20#define _GNU_SOURCE
7138d0c5
SE
21#include "secnet.h"
22#include <unistd.h>
23#include <fcntl.h>
24#include <errno.h>
25#include <sys/wait.h>
baab3a63 26#include <string.h>
7138d0c5
SE
27#include "process.h"
28
29/* Process handling - subprocesses, signals, etc. */
30
31static bool_t signal_handling=False;
32static sigset_t emptyset, fullset;
33static sigset_t registered,pending;
34
35struct child {
36 pid_t pid;
fe5e9cc4 37 cstring_t desc;
7138d0c5
SE
38 process_callback_fn *cb;
39 void *cst;
40 bool_t finished;
41 struct child *next;
42};
43
44static struct child *children=NULL;
45
46struct signotify {
47 int signum;
48 signal_notify_fn *notify;
49 void *cst;
50 struct signotify *next;
51};
52
53static struct signotify *sigs=NULL;
54
55static int spw,spr; /* file descriptors for signal notification pipe */
56
7138d0c5
SE
57/* Long-lived subprocesses can only be started once we've started
58 signal processing so that we can catch SIGCHLD for them and report
59 their exit status using the callback function. We block SIGCHLD
60 until signal processing has begun. */
042a8da9 61pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
fe5e9cc4 62 void *est, void *cst, cstring_t desc)
7138d0c5
SE
63{
64 struct child *c;
7138d0c5
SE
65 pid_t p;
66
b7886fd4 67 NEW(c);
7138d0c5
SE
68 c->desc=desc;
69 c->cb=cb;
70 c->cst=cst;
71
72 if (!signal_handling) {
4f5e39ec 73 fatal("makesubproc called before signal handling started");
7138d0c5
SE
74 }
75 p=fork();
76 if (p==0) {
77 /* Child process */
5e7a5e2d 78 afterfork();
7138d0c5
SE
79 entry(est);
80 abort();
81 } else if (p==-1) {
82 fatal_perror("makesubproc (%s): fork",desc);
83 }
84 c->pid=p;
85 c->finished=False;
86 c->next=children;
87 children=c;
042a8da9 88 return p;
7138d0c5
SE
89}
90
91static signal_notify_fn sigchld_handler;
92static void sigchld_handler(void *st, int signum)
93{
94 struct child *i,*n,**p;
95 struct work {
96 pid_t pid;
97 process_callback_fn *cb;
98 void *cst;
99 int status;
100 struct work *next;
101 };
102 struct work *w=NULL, *nw;
103 pid_t rv;
104 int status;
105
106 for (i=children; i; i=i->next) {
107 rv=waitpid(i->pid,&status,WNOHANG);
108 if (rv==-1) {
109 fatal_perror("sigchld_handler: waitpid");
110 }
111 if (rv==i->pid) {
112 i->finished=True;
113
b7886fd4 114 NEW(nw);
7138d0c5
SE
115 nw->pid=i->pid;
116 nw->cb=i->cb;
117 nw->cst=i->cst;
118 nw->status=status;
119 nw->next=w;
120 w=nw;
121 }
122 }
123
124 /* Remove all the finished tasks from the list of children */
125 for (i=children, p=&children; i; i=n) {
126 n=i->next;
127 if (i->finished) {
128 free(i);
129 *p=n;
130 } else {
131 p=&i->next;
132 }
133 }
134
135 /* Notify as appropriate, then free the list */
136 while (w) {
137 w->cb(w->cst,w->pid,w->status);
138 nw=w;
139 w=w->next;
140 free(nw);
141 }
142}
143
fe5e9cc4 144int sys_cmd(const char *path, const char *arg, ...)
7138d0c5
SE
145{
146 va_list ap;
baab3a63 147 int rv, rc;
7138d0c5
SE
148 pid_t c;
149
7138d0c5
SE
150 c=fork();
151 if (c) {
152 /* Parent -> wait for child */
baab3a63
RK
153 do {
154 rc = waitpid(c,&rv,0);
155 } while(rc < 0 && errno == EINTR);
156 if (rc < 0)
157 fatal_perror("sys_cmd: waitpid for %s", path);
158 if (rc != c) /* OS has gone mad */
159 fatal("sys_cmd: waitpid for %s returned wrong process ID!",
160 path);
161 if (rv) {
4ac7fd3f
IJ
162 /* If the command failed report its exit status */
163 lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
baab3a63 164 }
7138d0c5
SE
165 } else if (c==0) {
166 char *args[100];
167 int i;
168 /* Child -> exec command */
fe5e9cc4
SE
169 /* Really we ought to strcpy() the arguments into the args array,
170 since the arguments are const char *. Since we'll exit anyway
171 if the execvp() fails this seems somewhat pointless, and
172 increases the chance of the child process failing before it
173 gets to exec(). */
5e7a5e2d 174 afterfork();
baab3a63
RK
175 va_start(ap,arg);
176 args[0]=(char *)arg; /* program name */
7138d0c5
SE
177 i=1;
178 while ((args[i++]=va_arg(ap,char *)));
179 execvp(path,args);
baab3a63
RK
180 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
181 _exit(1);
7138d0c5
SE
182 } else {
183 /* Error */
baab3a63 184 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
7138d0c5
SE
185 }
186
7138d0c5
SE
187 return rv;
188}
189
190static beforepoll_fn signal_beforepoll;
191static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
90a39563 192 int *timeout_io)
7138d0c5 193{
ee697dd9 194 BEFOREPOLL_WANT_FDS(1);
7138d0c5
SE
195 fds[0].fd=spr;
196 fds[0].events=POLLIN;
197 return 0;
198}
199
5a8a7053
RK
200/* Bodge to work around Ubuntu's strict header files */
201static void discard(int anything) {}
202
7138d0c5 203static afterpoll_fn signal_afterpoll;
90a39563 204static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
7138d0c5
SE
205{
206 uint8_t buf[16];
207 struct signotify *n;
208 sigset_t todo,old;
209
210 if (nfds && (fds->revents & POLLIN)) {
5a8a7053
RK
211 discard(read(spr,buf,16));
212 /* We don't actually care what we read; as
7138d0c5
SE
213 long as there was at least one byte
214 (which there was) we'll pick up the
215 signals in the pending set */
216
217 /* We reset 'pending' before processing any of the signals
218 that were pending so that we don't miss any signals that
219 are delivered partway-through processing (all we assume
220 about signal notification routines is that they handle all
221 the work available at their _start_ and only optionally any
222 work that arrives part-way through their execution). */
223 sigprocmask(SIG_SETMASK,&fullset,&old);
224 todo=pending;
225 sigemptyset(&pending);
226 sigprocmask(SIG_SETMASK,&old,NULL);
227
228 for (n=sigs; n; n=n->next)
229 if (sigismember(&todo,n->signum))
230 n->notify(n->cst,n->signum);
231 }
232}
233
5e7a5e2d 234void afterfork(void)
7138d0c5
SE
235{
236 struct signotify *n;
237 sigset_t done;
238 struct sigaction sa;
239
c72aa743
IJ
240 clear_phase_hooks(PHASE_SHUTDOWN);
241 /* Prevents calls to fatal() etc. in the child from running off
242 and doing a lot of unhelpful things */
243
7138d0c5
SE
244 sigemptyset(&done);
245 for (n=sigs; n; n=n->next)
246 if (!sigismember(&done,n->signum)) {
247 sigaddset(&done,n->signum);
248 sa.sa_handler=SIG_DFL;
249 sa.sa_mask=emptyset;
250 sa.sa_flags=0;
251 sigaction(n->signum,&sa,NULL);
252 }
5e7a5e2d
IJ
253
254 sigemptyset(&emptyset);
255 sigprocmask(SIG_SETMASK,&emptyset,NULL);
7138d0c5
SE
256}
257
32654a31
IJ
258void childpersist_closefd_hook(void *fd_vp, uint32_t newphase)
259{
260 int *fd_p=fd_vp;
261 int fd=*fd_p;
262 if (fd<0) return;
263 *fd_p=-1;
264 setnonblock(fd); /* in case close() might block */
265 close(fd); /* discard errors - we don't care, in the child */
266}
267
7138d0c5
SE
268static void signal_handler(int signum)
269{
ff05a229 270 int saved_errno;
7138d0c5
SE
271 uint8_t thing=0;
272 sigaddset(&pending,signum);
ff05a229
SE
273 /* XXX the write() may set errno, which can make the main program fail.
274 However, signal handlers aren't allowed to modify anything which
275 is not of type sig_atomic_t. The world is broken. */
276 /* I have decided to save and restore errno anyway; on most
277 architectures on which secnet can run modifications to errno
278 will be atomic, and it seems to be the lesser of the two
279 evils. */
280 saved_errno=errno;
5a8a7053
RK
281 discard(write(spw,&thing,1));
282 /* We don't care if this fails (i.e. the pipe
7138d0c5
SE
283 is full) because the service routine will
284 spot the pending signal anyway */
ff05a229 285 errno=saved_errno;
7138d0c5
SE
286}
287
288static void register_signal_handler(struct signotify *s)
289{
290 struct sigaction sa;
291 int rv;
292
293 if (!signal_handling) return;
294
295 if (sigismember(&registered,s->signum)) return;
296 sigaddset(&registered,s->signum);
297
298 sa.sa_handler=signal_handler;
299 sa.sa_mask=fullset;
300 sa.sa_flags=0;
301 rv=sigaction(s->signum,&sa,NULL);
302 if (rv!=0) {
303 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
304 }
305}
306
307void request_signal_notification(int signum, signal_notify_fn *notify,
308 void *cst)
309{
310 struct signotify *s;
311 sigset_t old;
312
b7886fd4 313 NEW(s);
7138d0c5
SE
314 s->signum=signum;
315 s->notify=notify;
316 s->cst=cst;
317 s->next=sigs;
318 sigprocmask(SIG_SETMASK,&fullset,&old);
319 sigs=s;
320 register_signal_handler(s);
321 sigprocmask(SIG_SETMASK,&old,NULL);
322}
323
324void start_signal_handling(void)
325{
326 int p[2];
327 struct signotify *i;
328
329 sigemptyset(&emptyset);
330 sigfillset(&fullset);
331 sigemptyset(&registered);
332 sigemptyset(&pending);
333
6a06198c 334 pipe_cloexec(p);
7138d0c5
SE
335 spw=p[1];
336 spr=p[0];
f54d5ada 337 setnonblock(spw);
ba703386 338 setnonblock(spr);
7138d0c5 339
32fc582f 340 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
7138d0c5
SE
341 signal_handling=True;
342
343 /* Register signal handlers for all the signals we're interested in */
344 for (i=sigs; i; i=i->next) {
345 register_signal_handler(i);
346 }
347
348 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
349}