ksgi
/
kfcgi.c
1135 строк · 25.0 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2015--2016 Kristaps Dzonsons <kristaps@bsd.lv>
4*
5* Permission to use, copy, modify, and distribute this software for any
6* purpose with or without fee is hereby granted, provided that the above
7* copyright notice and this permission notice appear in all copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17#include "config.h"
18
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/un.h>
22#include <sys/wait.h>
23
24#include <assert.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <getopt.h>
28#include <limits.h>
29#include <pwd.h>
30#include <poll.h>
31#include <signal.h>
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdint.h>
36#include <string.h>
37#include <syslog.h>
38#include <time.h>
39#include <unistd.h>
40
41/*
42* This is used by the "variable pool" implementation to keep track of
43* children, their input descriptor, and their active work.
44*/
45struct worker {
46int fd; /* active request fd or -1 */
47int ctrl; /* control socket */
48pid_t pid; /* process */
49time_t last; /* last deschedule or 0 if never */
50uint64_t cookie;
51};
52
53/*
54* Whether we're supposed to stop or whether we've had a child exit.
55*/
56static volatile sig_atomic_t stop = 0;
57static volatile sig_atomic_t chld = 0;
58static volatile sig_atomic_t hup = 0;
59
60static int verbose = 0;
61
62static void dbg(const char *fmt, ...)
63__attribute__((format(printf, 1, 2)));
64
65static void
66sighandlehup(int sig)
67{
68
69hup = 1;
70}
71
72static void
73sighandlestop(int sig)
74{
75
76stop = 1;
77}
78
79static void
80sighandlechld(int sig)
81{
82
83chld = 1;
84}
85
86static void
87dbg(const char *fmt, ...)
88{
89va_list ap;
90
91if (0 == verbose)
92return;
93va_start(ap, fmt);
94vsyslog(LOG_DEBUG, fmt, ap);
95va_end(ap);
96}
97
98/*
99* Fully write a file descriptor and the non-optional buffer.
100* Return 0 on failure, 1 on success.
101*/
102static int
103fullwritefd(int fd, int sendfd, void *b, size_t bsz)
104{
105struct msghdr msg;
106int rc;
107char buf[CMSG_SPACE(sizeof(fd))];
108struct iovec io;
109struct cmsghdr *cmsg;
110struct pollfd pfd;
111
112assert(bsz && bsz <= 256);
113
114memset(buf, 0, sizeof(buf));
115memset(&msg, 0, sizeof(struct msghdr));
116memset(&io, 0, sizeof(struct iovec));
117
118io.iov_base = b;
119io.iov_len = bsz;
120
121msg.msg_iov = &io;
122msg.msg_iovlen = 1;
123msg.msg_control = buf;
124msg.msg_controllen = sizeof(buf);
125
126cmsg = CMSG_FIRSTHDR(&msg);
127cmsg->cmsg_level = SOL_SOCKET;
128cmsg->cmsg_type = SCM_RIGHTS;
129cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
130
131*((int *)CMSG_DATA(cmsg)) = sendfd;
132
133msg.msg_controllen = cmsg->cmsg_len;
134
135pfd.fd = fd;
136pfd.events = POLLOUT;
137again:
138if ((rc = poll(&pfd, 1, -1)) < 0) {
139syslog(LOG_ERR, "poll: passing "
140"connection to worker: %m");
141return(0);
142} else if (0 == rc) {
143syslog(LOG_WARNING, "poll: passing "
144"connection to worker: timeout!?");
145goto again;
146} else if ( ! (POLLOUT & pfd.revents)) {
147syslog(LOG_ERR, "poll: passing "
148"connection to worker: disconnect");
149return(0);
150} else if (sendmsg(fd, &msg, 0) < 0) {
151syslog(LOG_ERR, "sendmsg: passing "
152"connection to worker: %m");
153return(0);
154}
155return(1);
156}
157
158/*
159* Fully read a buffer of size "bufsz".
160* Returns 0 on failure (soft and hard), 1 on success.
161*/
162static int
163fullread(int fd, void *buf, size_t bufsz)
164{
165ssize_t ssz;
166size_t sz;
167struct pollfd pfd;
168int rc;
169
170pfd.fd = fd;
171pfd.events = POLLIN;
172
173for (sz = 0; sz < bufsz; sz += (size_t)ssz) {
174if ((rc = poll(&pfd, 1, -1)) < 0) {
175syslog(LOG_ERR, "poll: receiving "
176"ack from worker: %m");
177return(0);
178} else if (0 == rc) {
179syslog(LOG_WARNING, "poll: receiving "
180"ack from worker: timeout");
181ssz = 0;
182continue;
183} else if ( ! (POLLIN & pfd.revents)) {
184syslog(LOG_ERR, "poll: receiving "
185"ack from worker: disconnect");
186return(0);
187} else if ((ssz = read(fd, buf + sz, bufsz - sz)) < 0) {
188syslog(LOG_ERR, "read: receiving "
189"ack from worker: %m");
190return(0);
191} else if (0 == ssz && sz > 0) {
192syslog(LOG_ERR, "read: receiving "
193"ack from worker: short read");
194return(0);
195} else if (0 == ssz && sz == 0) {
196syslog(LOG_ERR, "read: receiving "
197"ack from worker: EOF");
198return(0);
199} else if (sz > SIZE_MAX - (size_t)ssz) {
200syslog(LOG_ERR, "read: receiving "
201"ack from worker: overflow");
202return(0);
203}
204}
205
206return(1);
207}
208
209/*
210* Create a non-blocking socket pair.
211* Remove 0 on failure, 1 on success.
212*/
213static int
214xsocketpair(int *sock)
215{
216int rc, fl1, fl2;
217
218sock[0] = sock[1] = -1;
219rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
220if (-1 == rc)
221syslog(LOG_ERR, "socketpair: "
222"connection to worker: %m");
223else if (-1 == (fl1 = fcntl(sock[0], F_GETFL, 0)))
224syslog(LOG_ERR, "fcntl: F_GETFL: "
225"connection to worker: %m");
226else if (-1 == (fl2 = fcntl(sock[1], F_GETFL, 0)))
227syslog(LOG_ERR, "fcntl: F_GETFL: "
228"connection to worker: %m");
229else if (-1 == fcntl(sock[0], F_SETFL, fl1 | O_NONBLOCK))
230syslog(LOG_ERR, "fcntl: F_SETFL: "
231"connection to worker: %m");
232else if (-1 == fcntl(sock[1], F_SETFL, fl2 | O_NONBLOCK))
233syslog(LOG_ERR, "fcntl: F_SETFL: "
234"connection to worker: %m");
235else
236return(1);
237
238if (-1 != sock[0])
239close(sock[0]);
240if (-1 != sock[1])
241close(sock[1]);
242
243return(0);
244}
245
246/*
247* Start a worker for the variable pool.
248*/
249static int
250varpool_start(struct worker *w, const struct worker *ws,
251size_t wsz, int fd, char **nargv)
252{
253int pair[2];
254char buf[64];
255size_t i;
256
257w->ctrl = w->fd = -1;
258w->pid = -1;
259
260if ( ! xsocketpair(pair))
261return(0);
262w->ctrl = pair[0];
263
264if (-1 == (w->pid = fork())) {
265syslog(LOG_ERR, "fork: worker: %m");
266close(pair[0]);
267close(pair[1]);
268w->ctrl = -1;
269return(0);
270} else if (0 == w->pid) {
271/*
272* Close out all current descriptors.
273* Note that we're a "new-mode" FastCGI manager to the
274* child via our environment variable.
275*/
276for (i = 0; i < wsz; i++)
277if (-1 != ws[i].ctrl &&
278-1 == close(ws[i].ctrl))
279syslog(LOG_ERR, "close: "
280"worker cleanup: %m");
281
282close(fd);
283snprintf(buf, sizeof(buf), "%d", pair[1]);
284setenv("FCGI_LISTENSOCK_DESCRIPTORS", buf, 1);
285execv(nargv[0], nargv);
286syslog(LOG_ERR, "execv: %s: %m", nargv[0]);
287_exit(EXIT_FAILURE);
288}
289
290/* Close the child descriptor. */
291if (-1 == close(pair[1])) {
292syslog(LOG_ERR, "close: worker-%u pipe: %m", w->pid);
293return(0);
294}
295
296dbg("worker-%u: started", w->pid);
297return(1);
298}
299
300/*
301* A variable-sized pool of web application clients.
302* A minimum of "wsz" applications are always running, and will grow to
303* "maxwsz" at which point no new connections are accepted.
304* This is an EXPERIMENTAL extension.
305*/
306static int
307varpool(size_t wsz, size_t maxwsz, time_t waittime,
308int fd, const char *sockpath, char *argv[])
309{
310struct worker *ws;
311struct worker *slough;
312size_t pfdsz, opfdsz, pfdmaxsz, i, j, minwsz,
313sloughsz, sloughmaxsz;
314int rc, exitcode, afd, accepting;
315struct pollfd *pfd, *opfd;
316struct sockaddr_storage ss;
317socklen_t sslen;
318void *pp;
319time_t t;
320sigset_t set;
321uint64_t cookie;
322
323dbg("YOU ARE RUNNING VARIABLE MODE AT YOUR OWN RISK.");
324
325signal(SIGCHLD, sighandlechld);
326signal(SIGTERM, sighandlestop);
327signal(SIGHUP, sighandlehup);
328sigemptyset(&set);
329sigaddset(&set, SIGCHLD);
330sigaddset(&set, SIGTERM);
331sigaddset(&set, SIGHUP);
332sigprocmask(SIG_BLOCK, &set, NULL);
333
334again:
335stop = chld = hup = 0;
336
337/*
338* Allocate worker array, polling descriptor array, and slough
339* array (exiting workers).
340* Set our initial exit code and our acceptance status.
341*/
342minwsz = wsz;
343ws = calloc(wsz, sizeof(struct worker));
344sloughmaxsz = (maxwsz - minwsz) * 2;
345slough = calloc(sloughmaxsz, sizeof(struct worker));
346pfdmaxsz = wsz + 1;
347pfd = calloc(pfdmaxsz, sizeof(struct pollfd));
348exitcode = 0;
349
350if (NULL == ws || NULL == pfd || NULL == slough) {
351/* Serious problems. */
352syslog(LOG_ERR, "calloc: initialisation: %m");
353close(fd);
354unlink(sockpath);
355free(ws);
356free(slough);
357free(pfd);
358return(0);
359}
360
361/* Zeroth pfd is always the domain socket. */
362pfd[0].fd = fd;
363pfd[0].events = POLLIN;
364
365/* Guard against bad de-allocation. */
366for (i = 0; i < wsz; i++) {
367ws[i].ctrl = ws[i].fd = -1;
368ws[i].pid = -1;
369}
370
371sloughsz = 0;
372accepting = 1;
373pfdsz = 1;
374
375/*
376* Start up the [initial] worker processes.
377* We'll later spin up workers when we need them.
378* If these die immediately, we'll find out below when we
379* unblock our signals.
380*/
381for (i = 0; i < wsz; i++)
382if ( ! varpool_start(&ws[i], ws, wsz, fd, argv))
383goto out;
384pollagain:
385/*
386* Main part.
387* Poll on our control socket (pfd[0]) and the children that
388* have active connections.
389* If we're not accepting new connections, avoid polling on the
390* first entry by offsetting.
391* We have a timeout if we get a termination signal whilst
392* waiting.
393*/
394opfd = accepting ? pfd : pfd + 1;
395opfdsz = accepting ? pfdsz : pfdsz - 1;
396
397sigprocmask(SIG_UNBLOCK, &set, NULL);
398rc = poll(opfd, opfdsz, 1000);
399sigprocmask(SIG_BLOCK, &set, NULL);
400
401/* Did an error occur? */
402if (rc < 0 && EINTR != errno) {
403syslog(LOG_ERR, "poll: main event: %m");
404goto out;
405}
406
407if (stop) {
408/*
409* If we're being requested to stop, go to exit now.
410* We'll immediately kill off our children and wait.
411*/
412dbg("servicing exit request");
413exitcode = 1;
414goto out;
415} else if (chld) {
416/*
417* A child has exited.
418* This can mean one of two things: either a worker has
419* exited abnormally or one of the "sloughed" workers
420* has finished its exit.
421*/
422if (0 == sloughsz) {
423syslog(LOG_ERR, "worker unexpectedly exited");
424goto out;
425}
426
427/* Look at the running children. */
428for (i = 0; i < wsz; i++) {
429assert(-1 != ws[i].pid);
430rc = waitpid(ws[i].pid, NULL, WNOHANG);
431if (rc < 0) {
432syslog(LOG_ERR, "wait: worker-%u "
433"check: %m", ws[i].pid);
434goto out;
435} else if (0 == rc)
436continue;
437/*
438* We found a process that has already exited.
439* Close its fd as well so that we know that the
440* pid of -1 means it's completely finished.
441*/
442syslog(LOG_ERR, "worker-%u "
443"unexpectedly exited", ws[i].pid);
444if (-1 == close(ws[i].ctrl))
445syslog(LOG_ERR, "close: worker-%u "
446"control socket: %m",
447ws[wsz - 1].pid);
448ws[i].pid = -1;
449goto out;
450}
451
452/* Ok... an exiting child can be reaped. */
453chld = 0;
454for (i = 0; i < sloughsz; ) {
455rc = waitpid(slough[i].pid, NULL, WNOHANG);
456if (0 == rc) {
457i++;
458continue;
459} else if (rc < 0) {
460syslog(LOG_ERR, "wait: sloughed "
461"worker-%u check: %m",
462slough[i].pid);
463goto out;
464}
465dbg("slough: releasing worker-%u\n",
466slough[i].pid);
467if (i < sloughsz - 1)
468slough[i] = slough[sloughsz - 1];
469sloughsz--;
470}
471} else if (hup) {
472dbg("servicing restart request");
473goto out;
474}
475
476/*
477* See if we should reduce our pool size.
478* We only do this if we've grown beyond the minimum.
479*/
480if (wsz > minwsz) {
481t = time(NULL);
482/*
483* We only reduce if the last worker is (1) not running
484* and (2) was last descheduled a while ago.
485*/
486assert(-1 != ws[wsz - 1].fd || ws[wsz - 1].last);
487assert(wsz > 1);
488
489if (-1 == ws[wsz - 1].fd &&
490t - ws[wsz - 1].last > waittime) {
491assert(-1 != ws[wsz - 1].ctrl);
492assert(-1 != ws[wsz - 1].pid);
493assert(-1 == ws[wsz - 1].fd);
494assert(-1 == pfd[pfdmaxsz - 1].fd);
495
496if (sloughsz >= sloughmaxsz) {
497syslog(LOG_ERR, "slough pool "
498"maximum size reached");
499goto out;
500}
501
502/* Close down the worker in the usual way. */
503if (-1 == close(ws[wsz - 1].ctrl)) {
504syslog(LOG_ERR, "close: worker-%u "
505"control socket: %m",
506ws[wsz - 1].pid);
507goto out;
508}
509if (-1 == kill(ws[wsz - 1].pid, SIGTERM)) {
510syslog(LOG_ERR, "kill: worker-%u: %m",
511ws[wsz - 1].pid);
512goto out;
513}
514
515/*
516* Append the dying client to the slough array,
517* since workers may take time to die.
518*/
519dbg("slough: acquiring worker-%u\n",
520ws[wsz - 1].pid);
521slough[sloughsz++] = ws[wsz - 1];
522
523/* Reallocations. */
524pp = reallocarray(ws, wsz - 1,
525sizeof(struct worker));
526if (NULL == pp) {
527syslog(LOG_ERR, "reallocarray: "
528"worker array: %m");
529goto out;
530}
531ws = pp;
532wsz--;
533pp = reallocarray(pfd, pfdmaxsz - 1,
534sizeof(struct pollfd));
535if (NULL == pp) {
536syslog(LOG_ERR, "reallocarray: "
537"descriptor array: %m");
538goto out;
539}
540pfd = pp;
541pfdmaxsz--;
542}
543}
544
545if (0 == rc)
546goto pollagain;
547
548/*
549* Now we see which of the workers has exited.
550* We do this until we've processed all of them.
551*/
552assert(rc > 0);
553for (i = 1; i < pfdsz && rc > 0; ) {
554if (POLLHUP & pfd[i].revents ||
555POLLERR & pfd[i].revents) {
556syslog(LOG_ERR, "poll: worker disconnect");
557goto out;
558} else if ( ! (POLLIN & pfd[i].revents)) {
559i++;
560continue;
561}
562
563/*
564* Read the "identifier" that the child process gives
565* to us.
566*/
567if ( ! fullread(pfd[i].fd, &cookie, sizeof(uint64_t)))
568goto out;
569
570/*
571* First, look up the worker that's now free.
572* Mark its active descriptor as free.
573* TODO: use a queue of "working" processes to prevent
574* this from being so expensive.
575*/
576for (j = 0; j < wsz; j++)
577if (ws[j].cookie == cookie)
578break;
579
580if (j == wsz) {
581syslog(LOG_ERR, "poll: bad worker response");
582goto out;
583}
584
585dbg("worker-%u: release %d", ws[j].pid, ws[j].fd);
586
587/*
588* Close the descriptor (that we still hold) and mark
589* this worker as no longer working.
590*/
591rc--;
592close(ws[j].fd);
593if (0 == accepting) {
594accepting = 1;
595dbg("rate-limiting: disabled");
596}
597ws[j].fd = -1;
598ws[j].last = time(NULL);
599
600/*
601* Now, clear the active descriptor from the file
602* descriptor array.
603* Do so by flipping it into the last slot then
604* truncating the array size.
605* Obviously, we only do this if we're not the current
606* end of array...
607*/
608if (pfdsz - 1 != i)
609pfd[i] = pfd[pfdsz - 1];
610pfd[pfdsz - 1].fd = -1;
611pfdsz--;
612}
613
614if (0 == accepting)
615goto pollagain;
616
617if (POLLHUP & pfd[0].revents) {
618syslog(LOG_ERR, "poll: control hangup");
619goto out;
620} else if (POLLERR & pfd[0].revents) {
621syslog(LOG_ERR, "poll: control error?");
622goto out;
623} else if ( ! (POLLIN & pfd[0].revents))
624goto pollagain;
625
626/*
627* We have a new request.
628* First, see if we need to allocate more workers.
629*/
630if (pfdsz == pfdmaxsz) {
631if (wsz + 1 > maxwsz) {
632accepting = 0;
633dbg("rate-limiting: enabled");
634goto pollagain;
635}
636pp = reallocarray(pfd, pfdmaxsz + 1,
637sizeof(struct pollfd));
638if (NULL == pp) {
639syslog(LOG_ERR, "reallocarray: workers: %m");
640goto out;
641}
642pfd = pp;
643memset(&pfd[pfdmaxsz], 0, sizeof(struct pollfd));
644pfd[pfdmaxsz].fd = -1;
645
646pp = reallocarray(ws, wsz + 1,
647sizeof(struct worker));
648if (NULL == pp) {
649syslog(LOG_ERR, "reallocarray: descriptors: %m");
650goto out;
651}
652ws = pp;
653memset(&ws[wsz], 0, sizeof(struct worker));
654
655if ( ! varpool_start(&ws[wsz], ws, wsz + 1, fd, argv))
656goto out;
657pfdmaxsz++;
658wsz++;
659}
660
661/*
662* Actually accept the socket.
663* Don't do anything with it, however.
664*/
665sslen = sizeof(ss);
666afd = accept(fd, (struct sockaddr *)&ss, &sslen);
667if (afd < 0) {
668if (EAGAIN == errno ||
669EWOULDBLOCK == errno)
670goto pollagain;
671syslog(LOG_ERR, "accept: new connection: %m");
672goto out;
673}
674
675/*
676* Look up the next unavailable worker.
677*/
678for (i = 0; i < wsz; i++)
679if (-1 == ws[i].fd)
680break;
681
682assert(i < wsz);
683ws[i].fd = afd;
684#if HAVE_ARC4RANDOM
685ws[i].cookie = arc4random();
686#else
687ws[i].cookie = random();
688#endif
689dbg("worker-%u: acquire %d "
690"(pollers %zu/%zu: workers %zu/%zu)",
691ws[i].pid, afd, pfdsz, pfdmaxsz, wsz, maxwsz);
692pfd[pfdsz].events = POLLIN;
693pfd[pfdsz].fd = ws[i].ctrl;
694pfdsz++;
695
696if (fullwritefd(ws[i].ctrl, ws[i].fd, &ws[i].cookie, sizeof(uint64_t)))
697goto pollagain;
698
699out:
700if ( ! hup) {
701/*
702* If we're not going to restart, then close the FastCGI
703* file descriptor as soon as possible.
704*/
705dbg("closing control socket");
706if (-1 == close(fd))
707syslog(LOG_ERR, "close: control: %m");
708fd = -1;
709}
710
711/*
712* Close the application's control socket; then, if that doesn't
713* make it exit (kcgi(3) will, but other applications may not),
714* we also deliver a SIGTERM.
715*/
716for (i = 0; i < wsz; i++) {
717if (-1 == ws[i].pid)
718continue;
719dbg("worker-%u: terminating", ws[i].pid);
720if (-1 == close(ws[i].ctrl))
721syslog(LOG_ERR, "close: "
722"worker-%u control: %m", ws[i].pid);
723if (-1 == kill(ws[i].pid, SIGTERM))
724syslog(LOG_ERR, "kill: "
725"worker-%u: %m", ws[i].pid);
726}
727
728/*
729* Now wait for the children and pending children.
730*/
731for (i = 0; i < wsz; i++) {
732if (-1 == ws[i].pid)
733continue;
734dbg("worker-%u: reaping", ws[i].pid);
735if (-1 == waitpid(ws[i].pid, NULL, 0))
736syslog(LOG_ERR, "wait: "
737"worker-%u: %m", ws[i].pid);
738}
739
740for (i = 0; i < sloughsz; i++) {
741dbg("sloughed worker-%u: reaping", slough[i].pid);
742if (-1 == waitpid(slough[i].pid, NULL, 0))
743syslog(LOG_ERR, "wait: sloughed "
744"worker-%u: %m", slough[i].pid);
745}
746
747free(ws);
748free(slough);
749free(pfd);
750
751if (hup)
752goto again;
753
754/*
755* Now we're really exiting.
756* Do our final cleanup if we didn't already...
757*/
758if (-1 != fd) {
759dbg("closing control socket");
760if (-1 == close(fd))
761syslog(LOG_ERR, "close: control: %m");
762}
763return(exitcode);
764}
765
766static int
767fixedpool(size_t wsz, int fd, const char *sockpath, char *argv[])
768{
769pid_t *ws;
770size_t i;
771sigset_t set, oset;
772void (*sigfp)(int);
773
774/*
775* Dying children should notify us that something is horribly
776* wrong and we should exit.
777* Also handle SIGTERM in the same way.
778*/
779signal(SIGTERM, sighandlestop);
780signal(SIGCHLD, sighandlechld);
781signal(SIGHUP, sighandlehup);
782sigemptyset(&set);
783sigaddset(&set, SIGCHLD);
784sigaddset(&set, SIGTERM);
785sigaddset(&set, SIGHUP);
786sigprocmask(SIG_BLOCK, &set, &oset);
787
788again:
789hup = stop = chld = 0;
790
791/* Allocate worker array. */
792if (NULL == (ws = calloc(wsz, sizeof(pid_t)))) {
793syslog(LOG_ERR, "calloc: initialisation: %m");
794close(fd);
795unlink(sockpath);
796return(0);
797}
798
799/*
800* "Zero" the workers.
801* This is in case the initialisation fails.
802*/
803for (i = 0; i < wsz; i++)
804ws[i] = -1;
805
806for (i = 0; i < wsz; i++) {
807if (-1 == (ws[i] = fork())) {
808syslog(LOG_ERR, "fork: worker: %m");
809goto out;
810} else if (0 == ws[i]) {
811/*
812* Assign stdin to be the socket over which
813* we're going to transfer request descriptors
814* when we get them.
815*/
816if (-1 == dup2(fd, STDIN_FILENO)) {
817syslog(LOG_ERR, "dup2: worker: %m");
818_exit(EXIT_FAILURE);
819}
820close(fd);
821execv(argv[0], argv);
822syslog(LOG_ERR, "execve: %s: %m", argv[0]);
823_exit(EXIT_FAILURE);
824}
825}
826
827sigsuspend(&oset);
828
829if (stop)
830dbg("servicing exit request");
831else if (chld)
832syslog(LOG_ERR, "worker unexpectedly exited");
833else
834dbg("servicing restart request");
835out:
836if ( ! hup) {
837/*
838* If we're not going to restart, then close the FastCGI
839* file descriptor as soon as possible.
840*/
841if (-1 == close(fd))
842syslog(LOG_ERR, "close: control: %m");
843fd = -1;
844}
845
846/* Suppress child exit signals whilst we kill them. */
847sigfp = signal(SIGCHLD, SIG_DFL);
848
849/*
850* Now wait on the children.
851* This can take forever, but properly-written children will
852* exit when receiving SIGTERM.
853*/
854for (i = 0; i < wsz; i++)
855if (-1 != ws[i] && -1 == kill(ws[i], SIGTERM))
856syslog(LOG_ERR, "kill: worker-%u: %m", ws[i]);
857
858for (i = 0; i < wsz; i++)
859if (-1 != ws[i] && -1 == waitpid(ws[i], NULL, 0))
860syslog(LOG_ERR, "wait: worker-%u: %m", ws[i]);
861
862signal(SIGCHLD, sigfp);
863
864free(ws);
865
866if (hup)
867goto again;
868
869/*
870* Now we're really exiting.
871* Do our final cleanup if we didn't already...
872*/
873if (-1 != fd && -1 == close(fd))
874syslog(LOG_ERR, "close: control: %m");
875return(1);
876}
877
878int
879main(int argc, char *argv[])
880{
881int c, fd, varp, usemax, useq, nod, logop;
882struct passwd *pw;
883size_t i, wsz, sz, lsz, maxwsz;
884time_t waittime;
885const char *pname, *sockpath, *chpath,
886*sockuser, *procuser, *errstr;
887struct sockaddr_un un;
888mode_t old_umask;
889uid_t sockuid, procuid;
890gid_t sockgid, procgid;
891char **nargv;
892
893if ((pname = strrchr(argv[0], '/')) == NULL)
894pname = argv[0];
895else
896++pname;
897
898if (0 != geteuid()) {
899fprintf(stderr, "%s: need root privileges\n", pname);
900return(EXIT_FAILURE);
901}
902
903sockuid = procuid = sockgid = procgid = -1;
904wsz = 5;
905usemax = useq = 0;
906sockpath = "/var/www/run/httpd.sock";
907chpath = "/var/www";
908sockuser = procuser = NULL;
909varp = 0;
910nod = 0;
911maxwsz = lsz = 0;
912waittime = 60 * 5;
913
914while (-1 != (c = getopt(argc, argv, "l:p:n:N:s:u:U:rvdw:")))
915switch (c) {
916case ('l'):
917useq = 1;
918lsz = strtonum(optarg, 1, INT_MAX, &errstr);
919if (NULL == errstr)
920break;
921fprintf(stderr, "-l must be "
922"between 1 and %d\n", INT_MAX);
923return(EXIT_FAILURE);
924case ('n'):
925wsz = strtonum(optarg, 0, INT_MAX, &errstr);
926if (NULL == errstr)
927break;
928fprintf(stderr, "-n must be "
929"between 0 and %d\n", INT_MAX);
930return(EXIT_FAILURE);
931case ('N'):
932usemax = 1;
933maxwsz = strtonum(optarg, 0, INT_MAX, &errstr);
934if (NULL == errstr)
935break;
936fprintf(stderr, "-N must be "
937"between 0 and %d\n", INT_MAX);
938return(EXIT_FAILURE);
939case ('p'):
940chpath = optarg;
941break;
942case ('s'):
943sockpath = optarg;
944break;
945case ('u'):
946sockuser = optarg;
947break;
948case ('U'):
949procuser = optarg;
950break;
951case ('r'):
952varp = 1;
953break;
954case ('v'):
955verbose = 1;
956break;
957case ('d'):
958nod = 1;
959break;
960case ('w'):
961waittime = strtonum(optarg, 0, INT_MAX, &errstr);
962if (NULL == errstr)
963break;
964fprintf(stderr, "-w must be "
965"between 0 and %d\n", INT_MAX);
966return(EXIT_FAILURE);
967default:
968goto usage;
969}
970
971argc -= optind;
972argv += optind;
973
974if (0 == argc)
975goto usage;
976
977if (usemax && varp) {
978if (maxwsz == wsz)
979varp = 0;
980else if (maxwsz < wsz)
981goto usage;
982} else
983maxwsz = wsz * 2;
984
985if (0 == useq)
986lsz = (varp ? maxwsz : wsz) * 2;
987
988assert(lsz);
989
990pw = NULL;
991if (NULL != procuser && NULL == (pw = getpwnam(procuser))) {
992fprintf(stderr, "%s: no such user\n", procuser);
993return(EXIT_FAILURE);
994} else if (NULL != pw) {
995procuid = pw->pw_uid;
996procgid = pw->pw_gid;
997}
998
999pw = NULL;
1000if (NULL != sockuser && NULL == (pw = getpwnam(sockuser))) {
1001fprintf(stderr, "%s: no such user\n", sockuser);
1002return(EXIT_FAILURE);
1003} else if (NULL != pw) {
1004sockuid = pw->pw_uid;
1005sockgid = pw->pw_gid;
1006}
1007
1008/* Do the usual dance to set up UNIX sockets. */
1009memset(&un, 0, sizeof(un));
1010un.sun_family = AF_UNIX;
1011sz = strlcpy(un.sun_path, sockpath, sizeof(un.sun_path));
1012if (sz >= sizeof(un.sun_path)) {
1013fprintf(stderr, "socket path to long\n");
1014return(EXIT_FAILURE);
1015}
1016#if !defined(__linux__) && !defined(__sun)
1017un.sun_len = sz;
1018#endif
1019
1020/*
1021* Prepare the socket then unlink any dead existing ones.
1022* This is because we want to control the socket.
1023*/
1024if (-1 == (fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
1025perror("socket");
1026return(EXIT_FAILURE);
1027} else if (-1 == unlink(sockpath)) {
1028if (ENOENT != errno) {
1029perror(sockpath);
1030close(fd);
1031return(EXIT_FAILURE);
1032}
1033}
1034
1035old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
1036
1037/*
1038* Now actually bind to the FastCGI socket set up our
1039* listeners, and make sure that we're not blocking.
1040* If necessary, change the file's ownership.
1041* We buffer up to the number of available workers.
1042*/
1043if (-1 == bind(fd, (struct sockaddr *)&un, sizeof(un))) {
1044perror("bind");
1045close(fd);
1046return(EXIT_FAILURE);
1047}
1048umask(old_umask);
1049
1050if (NULL != sockuser)
1051if (chown(sockpath, sockuid, sockgid) == -1) {
1052perror(sockpath);
1053close(fd);
1054return(EXIT_FAILURE);
1055}
1056
1057if (-1 == listen(fd, lsz)) {
1058perror(sockpath);
1059close(fd);
1060return(EXIT_FAILURE);
1061}
1062
1063/*
1064* Jail our file-system.
1065*/
1066if (-1 == chroot(chpath)) {
1067perror("chroot");
1068close(fd);
1069return(EXIT_FAILURE);
1070} else if (-1 == chdir("/")) {
1071perror("chdir");
1072close(fd);
1073unlink(sockpath);
1074return(EXIT_FAILURE);
1075}
1076
1077if (NULL != procuser) {
1078if (0 != setgid(procgid)) {
1079perror(procuser);
1080close(fd);
1081return(EXIT_FAILURE);
1082} else if (0 != setuid(procuid)) {
1083perror(procuser);
1084close(fd);
1085return(EXIT_FAILURE);
1086} else if (-1 != setuid(0)) {
1087fprintf(stderr, "%s: managed to regain "
1088"root privileges: aborting\n", pname);
1089close(fd);
1090return(EXIT_FAILURE);
1091}
1092}
1093
1094nargv = calloc(argc + 1, sizeof(char *));
1095if (NULL == nargv) {
1096perror(NULL);
1097close(fd);
1098return(EXIT_FAILURE);
1099}
1100
1101for (i = 0; i < (size_t)argc; i++)
1102nargv[i] = argv[i];
1103
1104if ( ! nod && -1 == daemon(1, 0)) {
1105perror("daemon");
1106close(fd);
1107unlink(sockpath);
1108free(nargv);
1109return(EXIT_FAILURE);
1110}
1111
1112logop = LOG_PID;
1113#ifdef LOG_PERROR
1114if (nod)
1115logop |= LOG_PERROR;
1116#endif
1117openlog(pname, logop, LOG_DAEMON);
1118
1119c = varp ?
1120varpool(wsz, maxwsz, waittime, fd, sockpath, nargv) :
1121fixedpool(wsz, fd, sockpath, nargv);
1122
1123free(nargv);
1124return(c ? EXIT_SUCCESS : EXIT_FAILURE);
1125usage:
1126fprintf(stderr, "usage: %s "
1127"[-l backlog] "
1128"[-n workers] "
1129"[-p chroot] "
1130"[-s sockpath] "
1131"[-u sockuser] "
1132"[-U procuser] "
1133"-- prog [arg1...]\n", pname);
1134return(EXIT_FAILURE);
1135}
1136