poll: Abolish max_nfds
[secnet] / process.c
1 #define _GNU_SOURCE
2 #include "secnet.h"
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #include <sys/wait.h>
7 #include <string.h>
8 #include "process.h"
9
10 /* Process handling - subprocesses, signals, etc. */
11
12 static bool_t signal_handling=False;
13 static sigset_t emptyset, fullset;
14 static sigset_t registered,pending;
15
16 struct child {
17 pid_t pid;
18 cstring_t desc;
19 process_callback_fn *cb;
20 void *cst;
21 bool_t finished;
22 struct child *next;
23 };
24
25 static struct child *children=NULL;
26
27 struct signotify {
28 int signum;
29 signal_notify_fn *notify;
30 void *cst;
31 struct signotify *next;
32 };
33
34 static struct signotify *sigs=NULL;
35
36 static int spw,spr; /* file descriptors for signal notification pipe */
37
38 static 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. */
44 pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
45 void *est, void *cst, cstring_t desc)
46 {
47 struct child *c;
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) {
56 fatal("makesubproc called before signal handling started");
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;
72 return p;
73 }
74
75 static signal_notify_fn sigchld_handler;
76 static 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
128 int sys_cmd(const char *path, const char *arg, ...)
129 {
130 va_list ap;
131 int rv, rc;
132 pid_t c;
133
134 c=fork();
135 if (c) {
136 /* Parent -> wait for child */
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\n",
156 path, arg, rv);
157 }
158 } else if (c==0) {
159 char *args[100];
160 int i;
161 /* Child -> exec command */
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(). */
167 va_start(ap,arg);
168 args[0]=(char *)arg; /* program name */
169 i=1;
170 while ((args[i++]=va_arg(ap,char *)));
171 execvp(path,args);
172 fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
173 _exit(1);
174 } else {
175 /* Error */
176 fatal_perror("sys_cmd(%s,%s,...)", path, arg);
177 }
178
179 return rv;
180 }
181
182 static beforepoll_fn signal_beforepoll;
183 static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
184 int *timeout_io)
185 {
186 BEFOREPOLL_WANT_FDS(1);
187 fds[0].fd=spr;
188 fds[0].events=POLLIN;
189 return 0;
190 }
191
192 /* Bodge to work around Ubuntu's strict header files */
193 static void discard(int anything) {}
194
195 static afterpoll_fn signal_afterpoll;
196 static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
197 {
198 uint8_t buf[16];
199 struct signotify *n;
200 sigset_t todo,old;
201
202 if (nfds && (fds->revents & POLLIN)) {
203 discard(read(spr,buf,16));
204 /* 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
226 static 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
243 static void signal_handler(int signum)
244 {
245 int saved_errno;
246 uint8_t thing=0;
247 sigaddset(&pending,signum);
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;
256 discard(write(spw,&thing,1));
257 /* We don't care if this fails (i.e. the pipe
258 is full) because the service routine will
259 spot the pending signal anyway */
260 errno=saved_errno;
261 }
262
263 static void register_signal_handler(struct signotify *s)
264 {
265 struct sigaction sa;
266 int rv;
267
268 if (!signal_handling) return;
269
270 if (sigismember(&registered,s->signum)) return;
271 sigaddset(&registered,s->signum);
272
273 sa.sa_handler=signal_handler;
274 sa.sa_mask=fullset;
275 sa.sa_flags=0;
276 rv=sigaction(s->signum,&sa,NULL);
277 if (rv!=0) {
278 fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
279 }
280 }
281
282 void request_signal_notification(int signum, signal_notify_fn *notify,
283 void *cst)
284 {
285 struct signotify *s;
286 sigset_t old;
287
288 s=safe_malloc(sizeof(*s),"request_signal_notification");
289 s->signum=signum;
290 s->notify=notify;
291 s->cst=cst;
292 s->next=sigs;
293 sigprocmask(SIG_SETMASK,&fullset,&old);
294 sigs=s;
295 register_signal_handler(s);
296 sigprocmask(SIG_SETMASK,&old,NULL);
297 }
298
299 void start_signal_handling(void)
300 {
301 int p[2];
302 struct signotify *i;
303
304 sigemptyset(&emptyset);
305 sigfillset(&fullset);
306 sigemptyset(&registered);
307 sigemptyset(&pending);
308
309 pipe_cloexec(p);
310 spw=p[1];
311 spr=p[0];
312 if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {
313 fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)");
314 }
315
316 register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
317 signal_handling=True;
318
319 /* Register signal handlers for all the signals we're interested in */
320 for (i=sigs; i; i=i->next) {
321 register_signal_handler(i);
322 }
323
324 request_signal_notification(SIGCHLD,sigchld_handler,NULL);
325 }