1 /*
2 ** Copyright 2007 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 /*
7 */
8 #include "cgi.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #if HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16
17 #if TIME_WITH_SYS_TIME
18 #include <sys/time.h>
19 #include <time.h>
20 #else
21 #if HAVE_SYS_TIME_H
22 #include <sys/time.h>
23 #else
24 #include <time.h>
25 #endif
26 #endif
27 #if HAVE_SYS_WAIT_H
28 #include <sys/wait.h>
29 #endif
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 #include <sys/uio.h>
36 #include <sys/un.h>
37
38 static int read_environ(int);
39
40 static int start_daemon(const char *lockfilename);
41
42 static void run_daemon(int fd, int termfd, int connfd, void (*handler)(void *),
43 void *dummy);
44
45 static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
46 void *dummy);
47
cgi_daemon(int nprocs,const char * lockfile,void (* postinit)(void *),void (* handler)(void *),void * dummy)48 void cgi_daemon(int nprocs, const char *lockfile,
49 void (*postinit)(void *), void (*handler)(void *),
50 void *dummy)
51 {
52 int fd=start_daemon(lockfile);
53
54 if (postinit)
55 (*postinit)(dummy);
56
57 if (nprocs > 0)
58 run_prefork(fd, nprocs, handler, dummy);
59 else
60 run_daemon(fd, -1, -1, handler, dummy);
61 }
62
63 /* Start in daemon mode. Return listening socket file descriptor */
64
start_daemon(const char * lockfile)65 static int start_daemon(const char *lockfile)
66 {
67 int fd;
68 struct sockaddr_un skun;
69
70 unlink(lockfile);
71
72 fd=socket(PF_UNIX, SOCK_STREAM, 0);
73 if (fd < 0)
74 {
75 perror("socket");
76 return (-1);
77 }
78
79 skun.sun_family=AF_UNIX;
80 strcpy(skun.sun_path, lockfile);
81 if (bind(fd, (const struct sockaddr *)&skun, sizeof(skun)) ||
82 listen(fd, SOMAXCONN) ||
83 chmod(skun.sun_path, 0777) ||
84 fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
85 {
86 perror(lockfile);
87 close(fd);
88 return (-1);
89 }
90 return fd;
91 }
92
93 static int prefork(int listenfd, int *allpipes, size_t ndaemos,
94 int *termpipe, void (*handler)(void *), void *dummy);
95
run_prefork(int fd,size_t ndaemons,void (* handler)(void *),void * dummy)96 static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
97 void *dummy)
98 {
99 int *cpipes; /* Completion pipes from preforked processes */
100 int termpipe[2]; /* Termination pipe to preforked processes */
101 size_t i;
102
103 if ((cpipes=malloc(sizeof(int)*ndaemons)) == NULL)
104 {
105 fprintf(stderr,
106 "CRIT: malloc failed: %s\n",
107 strerror(errno));
108 exit(1);
109 }
110
111 if (pipe(termpipe) < 0)
112 {
113 fprintf(stderr,
114 "CRIT: pipe failed: %s\n",
115 strerror(errno));
116 exit(1);
117 }
118
119
120 /* Start the initial set of preforked daemons */
121
122 for (i=0; i<ndaemons; i++)
123 cpipes[i]= -1;
124
125 for (i=0; i<ndaemons; i++)
126 cpipes[i]=prefork(fd, cpipes, ndaemons, termpipe, handler,
127 dummy);
128
129
130 for (;;)
131 {
132 fd_set fdr;
133 int maxfd=0;
134
135 FD_ZERO(&fdr);
136
137 for (i=0; i<ndaemons; i++)
138 {
139 if (cpipes[i] >= maxfd)
140 maxfd=cpipes[i]+1;
141
142 FD_SET(cpipes[i], &fdr);
143 }
144
145 if (select(maxfd, &fdr, NULL, NULL, NULL) <= 0)
146 continue;
147
148 /*
149 ** When child process gets a connection, it closes its
150 ** completion pipe, which makes the pipe selectable for
151 ** read.
152 */
153
154 for (i=0; i<ndaemons; i++)
155 {
156 if (FD_ISSET(cpipes[i], &fdr))
157 {
158 close(cpipes[i]);
159 cpipes[i]= -1;
160 cpipes[i]=prefork(fd, cpipes,
161 ndaemons, termpipe, handler,
162 dummy);
163 }
164 }
165 }
166 }
167
168 /* Start a preforked process */
169
prefork(int listenfd,int * allpipes,size_t ndaemons,int * termpipe,void (* handler)(void *),void * dummy)170 static int prefork(int listenfd, int *allpipes, size_t ndaemons,
171 int *termpipe, void (*handler)(void *), void *dummy)
172 {
173 int newpipe[2];
174 pid_t p;
175 int waitstat;
176 size_t i;
177
178 if (pipe(newpipe) < 0)
179 {
180 fprintf(stderr,
181 "CRIT: pipe failed: %s\n", strerror(errno));
182 exit(1);
183 }
184
185 while ((p=fork()) < 0)
186 {
187 fprintf(stderr,
188 "CRIT: fork failed: %s\n", strerror(errno));
189 sleep(5);
190 }
191
192 if (p) /* parent */
193 {
194 close(newpipe[1]);
195
196 /* Wait for first child process to go away */
197
198 while (wait(&waitstat) != p)
199 ;
200
201 return (newpipe[0]);
202 }
203
204 close(newpipe[0]);
205 close(termpipe[1]);
206
207 /* New child doesn't need pipes from other children */
208
209 for (i=0; i<ndaemons; i++)
210 if (allpipes[i] >= 0)
211 close(allpipes[i]);
212
213 /* Fork once more, so that the parent process can continue */
214
215 if (fork())
216 exit(0);
217
218 run_daemon(listenfd, termpipe[0], newpipe[1], handler, dummy);
219 return (-1);
220 }
221
run_daemon(int fd,int termfd,int acceptedfd,void (* handler)(void *),void * dummy)222 static void run_daemon(int fd, int termfd, int acceptedfd,
223 void (*handler)(void *), void *dummy)
224 {
225 int cfd;
226
227 for (;;)
228 {
229 fd_set fdr;
230 pid_t p;
231 int maxfd;
232
233 FD_ZERO(&fdr);
234
235 FD_SET(fd, &fdr);
236
237 maxfd=fd;
238
239 if (termfd >= 0)
240 {
241 if (termfd > maxfd)
242 maxfd=termfd;
243
244 FD_SET(termfd, &fdr);
245 }
246
247 if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
248 continue;
249 if (termfd >= 0 &&
250 FD_ISSET(termfd, &fdr)) /* Terminate all child procs */
251 exit(0);
252
253 if (!FD_ISSET(fd, &fdr))
254 continue;
255
256 cfd=accept(fd, NULL, 0);
257
258 if (cfd < 0)
259 continue;
260
261 if (acceptedfd >= 0) /* preforked daemon */
262 {
263 if (termfd >= 0)
264 close(termfd);
265 close(acceptedfd);
266 break;
267 }
268
269 p=fork();
270
271 if (p < 0)
272 {
273 fprintf(stderr,
274 "CRIT: fork() failed: %s\n", strerror(errno));
275 continue;
276 }
277
278 if (p)
279 {
280 int dummy;
281
282 close(cfd);
283 while (wait(&dummy) != p)
284 continue;
285 continue;
286 }
287
288 /* Child forks once more, parent exits */
289
290 if (fork())
291 exit(0);
292
293 break;
294
295 }
296
297 /* child */
298
299 if (fcntl(cfd, F_SETFL, 0) < 0)
300 {
301 fprintf(stderr,
302 "CRIT: fcntl(): %s\n", strerror(errno));
303 exit(0);
304 }
305
306 close(fd);
307 if (read_environ(cfd))
308 {
309 close(0);
310 close(1);
311 if (dup(cfd) != 0 || dup(cfd) != 1)
312 {
313 fprintf(stderr,
314 "CRIT: dup() did not work as expected\n");
315 exit(0);
316 }
317 close(cfd);
318 }
319
320 if (fcntl(0, F_SETFL, 0) < 0 ||
321 fcntl(1, F_SETFL, 0) < 0)
322 {
323 fprintf(stderr,
324 "CRIT: fcntl() failed: %s\n", strerror(errno));
325 exit(0);
326 }
327
328 (*handler)(dummy);
329 exit(0);
330 }
331
332
333 /* Read environment from the sqwebmail stub */
334
force_read(int cfd,char * p,size_t l)335 static void force_read(int cfd, char *p, size_t l)
336 {
337 int m;
338
339 while (l)
340 {
341 m=read(cfd, p, l);
342 if (m <= 0)
343 {
344 fprintf(stderr,
345 "WARN: socket closed while reading"
346 " environment.\n");
347 exit(0);
348 }
349
350 p += m;
351 l -= m;
352 }
353 }
354
355 /* Receive CGI environment */
356
read_environ(int cfd)357 static int read_environ(int cfd)
358 {
359 static char buf[SOCKENVIRONLEN];
360 size_t l;
361 char *p;
362 int passfd;
363
364 force_read(cfd, buf, 1+sizeof(l));
365
366 memcpy(&l, buf, sizeof(l));
367 passfd=buf[sizeof(l)];
368
369 if (l >= sizeof(buf))
370 {
371 fprintf(stderr,
372 "WARN: invalid environment received via socket.\n");
373 exit(0);
374 }
375
376 alarm(10); /* Just in case - punt */
377 force_read(cfd, buf, l);
378 buf[l]=0;
379 alarm(0);
380
381 /* Vet environment strings for only known good strings */
382
383 p=buf;
384 while (p < buf+l)
385 {
386 if (strchr(p, '=') == NULL ||
387 !VALIDCGIVAR(p))
388 {
389 fprintf(stderr,
390 "WARN: invalid environment received"
391 " via socket: %s\n" , p);
392 exit(0);
393 }
394
395 putenv(p);
396
397 while (*p++)
398 ;
399 }
400
401 /* Receive file descriptors, if supported by the platform */
402
403 #if CGI_PASSFD
404
405 if (passfd)
406 {
407 struct iovec iov;
408 char dummy;
409
410 #if CGI_PASSFD_MSGACCRIGHTS
411
412 int fdbuf[2];
413 struct msghdr msg;
414 #endif
415
416 #if CGI_PASSFD_MSGCONTROL
417
418 int fdbuf[2];
419 struct msghdr msg;
420 struct cmsghdr *cmsg;
421 char buf[CMSG_SPACE(sizeof(fdbuf))];
422 #endif
423
424 #if CGI_PASSFD_MSGACCRIGHTS
425 memset(&iov, 0, sizeof(iov));
426 msg.msg_accrights=(caddr_t)fdbuf;
427 msg.msg_accrightslen=sizeof(fdbuf);
428 #endif
429
430
431 #if CGI_PASSFD_MSGCONTROL
432 memset(&msg, 0, sizeof(msg));
433 msg.msg_control=buf;
434 msg.msg_controllen=sizeof(buf);
435 #endif
436
437 msg.msg_iov=&iov;
438 msg.msg_iovlen=1;
439
440 iov.iov_base=&dummy;
441 iov.iov_len=1;
442
443 if (recvmsg(cfd, &msg, 0) <= 0)
444 {
445 perror("Internal error - recvmsg() failed");
446 exit(0);
447 }
448
449 #if CGI_PASSFD_MSGACCRIGHTS
450
451 if (msg.msg_accrightslen < sizeof(fdbuf))
452 {
453 perror("Internal error - malformed recvmsg()");
454 exit(0);
455 }
456
457 #endif
458
459 #if CGI_PASSFD_MSGCONTROL
460 if (msg.msg_controllen < sizeof(buf) ||
461 (cmsg = CMSG_FIRSTHDR(&msg))->cmsg_level != SOL_SOCKET ||
462 cmsg->cmsg_type != SCM_RIGHTS ||
463 cmsg->cmsg_len != CMSG_LEN(sizeof(fdbuf)))
464 {
465 perror("Internal error - malformed recvmsg()");
466 exit(0);
467 }
468
469 memcpy(fdbuf, CMSG_DATA(cmsg), sizeof(fdbuf));
470 #endif
471 close(0);
472 close(1);
473 if (dup(fdbuf[0]) != 0 || dup(fdbuf[1]) != 1)
474 fprintf(stderr,
475 "CRIT: dup() did not work as expected in"
476 " read_environ()\n");
477 close(fdbuf[0]);
478 close(fdbuf[1]);
479 return 0;
480 }
481 #endif
482
483 return 1;
484 }
485