2 * This file is part of secnet.
3 * See README for full list of copyright holders.
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 3 of the License, or
8 * (at your option) any later version.
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.
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.
29 /* Process handling - subprocesses, signals, etc. */
31 static bool_t signal_handling=False;
32 static sigset_t emptyset, fullset;
33 static sigset_t registered,pending;
38 process_callback_fn *cb;
44 static struct child *children=NULL;
48 signal_notify_fn *notify;
50 struct signotify *next;
53 static struct signotify *sigs=NULL;
55 static int spw,spr; /* file descriptors for signal notification pipe */
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. */
61 pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
62 void *est, void *cst, cstring_t desc)
72 if (!signal_handling) {
73 fatal("makesubproc called before signal handling started");
82 fatal_perror("makesubproc (%s): fork",desc);
91 static signal_notify_fn sigchld_handler;
92 static void sigchld_handler(void *st, int signum)
94 struct child *i,*n,**p;
97 process_callback_fn *cb;
102 struct work *w=NULL, *nw;
106 for (i=children; i; i=i->next) {
107 rv=waitpid(i->pid,&status,WNOHANG);
109 fatal_perror("sigchld_handler: waitpid");
124 /* Remove all the finished tasks from the list of children */
125 for (i=children, p=&children; i; i=n) {
135 /* Notify as appropriate, then free the list */
137 w->cb(w->cst,w->pid,w->status);
144 int sys_cmd(const char *path, const char *arg, ...)
152 /* Parent -> wait for child */
154 rc = waitpid(c,&rv,0);
155 } while(rc < 0 && errno == EINTR);
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!",
162 /* If the command failed report its exit status */
163 lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
168 /* Child -> exec command */
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
176 args[0]=(char *)arg; /* program name */
178 while ((args[i++]=va_arg(ap,char *)));
180 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
184 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
190 static beforepoll_fn signal_beforepoll;
191 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
194 BEFOREPOLL_WANT_FDS(1);
196 fds[0].events=POLLIN;
200 /* Bodge to work around Ubuntu's strict header files */
201 static void discard(int anything) {}
203 static afterpoll_fn signal_afterpoll;
204 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
210 if (nfds && (fds->revents & POLLIN)) {
211 discard(read(spr,buf,16));
212 /* We don't actually care what we read; as
213 long as there was at least one byte
214 (which there was) we'll pick up the
215 signals in the pending set */
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);
225 sigemptyset(&pending);
226 sigprocmask(SIG_SETMASK,&old,NULL);
228 for (n=sigs; n; n=n->next)
229 if (sigismember(&todo,n->signum))
230 n->notify(n->cst,n->signum);
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 */
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;
251 sigaction(n->signum,&sa,NULL);
254 sigemptyset(&emptyset);
255 sigprocmask(SIG_SETMASK,&emptyset,NULL);
258 void childpersist_closefd_hook(void *fd_vp, uint32_t newphase)
264 setnonblock(fd); /* in case close() might block */
265 close(fd); /* discard errors - we don't care, in the child */
268 static void signal_handler(int signum)
272 sigaddset(&pending,signum);
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
281 discard(write(spw,&thing,1));
282 /* We don't care if this fails (i.e. the pipe
283 is full) because the service routine will
284 spot the pending signal anyway */
288 static void register_signal_handler(struct signotify *s)
293 if (!signal_handling) return;
295 if (sigismember(®istered,s->signum)) return;
296 sigaddset(®istered,s->signum);
298 sa.sa_handler=signal_handler;
301 rv=sigaction(s->signum,&sa,NULL);
303 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
307 void request_signal_notification(int signum, signal_notify_fn *notify,
318 sigprocmask(SIG_SETMASK,&fullset,&old);
320 register_signal_handler(s);
321 sigprocmask(SIG_SETMASK,&old,NULL);
324 void start_signal_handling(void)
329 sigemptyset(&emptyset);
330 sigfillset(&fullset);
331 sigemptyset(®istered);
332 sigemptyset(&pending);
340 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
341 signal_handling=True;
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);
348 request_signal_notification(SIGCHLD,sigchld_handler,NULL);