X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=secnet.git;a=blobdiff_plain;f=process.c;h=f83625815456648ced0ad0a1781f2610e04a07d4;hp=b40801b17a593fc5ce5875687c64220af6eab0b9;hb=94ca562bb14422940ff1986ce8dfca87c222cb59;hpb=7138d0c54cd2212439434d27cb2d6ea775c3039b diff --git a/process.c b/process.c index b40801b..f836258 100644 --- a/process.c +++ b/process.c @@ -1,8 +1,10 @@ +#define _GNU_SOURCE #include "secnet.h" #include #include #include #include +#include #include "process.h" /* Process handling - subprocesses, signals, etc. */ @@ -13,7 +15,7 @@ static sigset_t registered,pending; struct child { pid_t pid; - string_t desc; + cstring_t desc; process_callback_fn *cb; void *cst; bool_t finished; @@ -33,34 +35,28 @@ static struct signotify *sigs=NULL; static int spw,spr; /* file descriptors for signal notification pipe */ -static void set_default_signals(void); - /* Long-lived subprocesses can only be started once we've started signal processing so that we can catch SIGCHLD for them and report their exit status using the callback function. We block SIGCHLD until signal processing has begun. */ -extern void makesubproc(process_entry_fn *entry, process_callback_fn *cb, - void *est, void *cst, string_t desc) +pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb, + void *est, void *cst, cstring_t desc) { struct child *c; - sigset_t sigchld; pid_t p; - c=safe_malloc(sizeof(*c),"makesubproc"); + NEW(c); c->desc=desc; c->cb=cb; c->cst=cst; if (!signal_handling) { - sigemptyset(&sigchld); - sigaddset(&sigchld,SIGCHLD); - sigprocmask(SIG_BLOCK,&sigchld,NULL); + fatal("makesubproc called before signal handling started"); } p=fork(); if (p==0) { /* Child process */ - set_default_signals(); - sigprocmask(SIG_SETMASK,&emptyset,NULL); + afterfork(); entry(est); abort(); } else if (p==-1) { @@ -70,6 +66,7 @@ extern void makesubproc(process_entry_fn *entry, process_callback_fn *cb, c->finished=False; c->next=children; children=c; + return p; } static signal_notify_fn sigchld_handler; @@ -95,7 +92,7 @@ static void sigchld_handler(void *st, int signum) if (rv==i->pid) { i->finished=True; - nw=safe_malloc(sizeof(*nw),"sigchld_handler"); + NEW(nw); nw->pid=i->pid; nw->cb=i->cb; nw->cst=i->cst; @@ -125,60 +122,75 @@ static void sigchld_handler(void *st, int signum) } } -int sys_cmd(const char *path, char *arg, ...) +int sys_cmd(const char *path, const char *arg, ...) { va_list ap; - int rv; + int rv, rc; pid_t c; - va_start(ap,arg); c=fork(); if (c) { /* Parent -> wait for child */ - waitpid(c,&rv,0); + do { + rc = waitpid(c,&rv,0); + } while(rc < 0 && errno == EINTR); + if (rc < 0) + fatal_perror("sys_cmd: waitpid for %s", path); + if (rc != c) /* OS has gone mad */ + fatal("sys_cmd: waitpid for %s returned wrong process ID!", + path); + if (rv) { + /* If the command failed report its exit status */ + lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path); + } } else if (c==0) { char *args[100]; int i; /* Child -> exec command */ - args[0]=arg; + /* Really we ought to strcpy() the arguments into the args array, + since the arguments are const char *. Since we'll exit anyway + if the execvp() fails this seems somewhat pointless, and + increases the chance of the child process failing before it + gets to exec(). */ + afterfork(); + va_start(ap,arg); + args[0]=(char *)arg; /* program name */ i=1; while ((args[i++]=va_arg(ap,char *))); execvp(path,args); - exit(1); + fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno)); + _exit(1); } else { /* Error */ - fatal_perror("sys_cmd(%s,%s,...)"); + fatal_perror("sys_cmd(%s,%s,...)", path, arg); } - va_end(ap); return rv; } static beforepoll_fn signal_beforepoll; static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io, - int *timeout_io, const struct timeval *tv_now, - uint64_t *now) + int *timeout_io) { - if (*nfds_io<1) { - *nfds_io=1; - return ERANGE; - } - *nfds_io=1; + BEFOREPOLL_WANT_FDS(1); fds[0].fd=spr; fds[0].events=POLLIN; return 0; } +/* Bodge to work around Ubuntu's strict header files */ +static void discard(int anything) {} + static afterpoll_fn signal_afterpoll; -static void signal_afterpoll(void *st, struct pollfd *fds, int nfds, - const struct timeval *tv, uint64_t *now) +static void signal_afterpoll(void *st, struct pollfd *fds, int nfds) { uint8_t buf[16]; struct signotify *n; sigset_t todo,old; if (nfds && (fds->revents & POLLIN)) { - read(spr,buf,16); /* We don't actually care what we read; as + discard(read(spr,buf,16)); + /* We don't actually care what we read; as long as there was at least one byte (which there was) we'll pick up the signals in the pending set */ @@ -200,12 +212,16 @@ static void signal_afterpoll(void *st, struct pollfd *fds, int nfds, } } -static void set_default_signals(void) +void afterfork(void) { struct signotify *n; sigset_t done; struct sigaction sa; + clear_phase_hooks(PHASE_SHUTDOWN); + /* Prevents calls to fatal() etc. in the child from running off + and doing a lot of unhelpful things */ + sigemptyset(&done); for (n=sigs; n; n=n->next) if (!sigismember(&done,n->signum)) { @@ -215,15 +231,39 @@ static void set_default_signals(void) sa.sa_flags=0; sigaction(n->signum,&sa,NULL); } + + sigemptyset(&emptyset); + sigprocmask(SIG_SETMASK,&emptyset,NULL); +} + +void childpersist_closefd_hook(void *fd_vp, uint32_t newphase) +{ + int *fd_p=fd_vp; + int fd=*fd_p; + if (fd<0) return; + *fd_p=-1; + setnonblock(fd); /* in case close() might block */ + close(fd); /* discard errors - we don't care, in the child */ } static void signal_handler(int signum) { + int saved_errno; uint8_t thing=0; sigaddset(&pending,signum); - write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe + /* XXX the write() may set errno, which can make the main program fail. + However, signal handlers aren't allowed to modify anything which + is not of type sig_atomic_t. The world is broken. */ + /* I have decided to save and restore errno anyway; on most + architectures on which secnet can run modifications to errno + will be atomic, and it seems to be the lesser of the two + evils. */ + saved_errno=errno; + discard(write(spw,&thing,1)); + /* We don't care if this fails (i.e. the pipe is full) because the service routine will spot the pending signal anyway */ + errno=saved_errno; } static void register_signal_handler(struct signotify *s) @@ -251,7 +291,7 @@ void request_signal_notification(int signum, signal_notify_fn *notify, struct signotify *s; sigset_t old; - s=safe_malloc(sizeof(*s),"request_signal_notification"); + NEW(s); s->signum=signum; s->notify=notify; s->cst=cst; @@ -272,16 +312,13 @@ void start_signal_handling(void) sigemptyset(®istered); sigemptyset(&pending); - if (pipe(p)!=0) { - fatal_perror("start_signal_handling: pipe"); - } + pipe_cloexec(p); spw=p[1]; spr=p[0]; - if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) { - fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)"); - } + setnonblock(spw); + setnonblock(spr); - register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal"); + register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal"); signal_handling=True; /* Register signal handlers for all the signals we're interested in */