Back to index

courier  0.68.2
cgidaemond.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2007 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 /*
00007 */
00008 #include      "cgi.h"
00009 #include      <stdio.h>
00010 #include      <stdlib.h>
00011 #include      <string.h>
00012 
00013 #if    HAVE_UNISTD_H
00014 #include      <unistd.h>
00015 #endif
00016 
00017 #if    TIME_WITH_SYS_TIME
00018 #include      <sys/time.h>
00019 #include      <time.h>
00020 #else
00021 #if    HAVE_SYS_TIME_H
00022 #include      <sys/time.h>
00023 #else
00024 #include      <time.h>
00025 #endif
00026 #endif
00027 #if HAVE_SYS_WAIT_H
00028 #include      <sys/wait.h>
00029 #endif
00030 #include      <errno.h>
00031 #include      <fcntl.h>
00032 #include      <sys/types.h>
00033 #include      <sys/stat.h>
00034 #include        <sys/socket.h>
00035 #include      <sys/uio.h>
00036 #include        <sys/un.h>
00037 
00038 static int read_environ(int);
00039 
00040 static int start_daemon(const char *lockfilename);
00041 
00042 static void run_daemon(int fd, int termfd, int connfd, void (*handler)(void *),
00043                      void *dummy);
00044 
00045 static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
00046                      void *dummy);
00047 
00048 void cgi_daemon(int nprocs, const char *lockfile,
00049               void (*postinit)(void *), void (*handler)(void *),
00050               void *dummy)
00051 {
00052        int fd=start_daemon(lockfile);
00053 
00054        if (postinit)
00055               (*postinit)(dummy);
00056 
00057        if (nprocs > 0)
00058               run_prefork(fd, nprocs, handler, dummy);
00059        else
00060               run_daemon(fd, -1, -1, handler, dummy);
00061 }
00062 
00063 /* Start in daemon mode.  Return listening socket file descriptor */
00064 
00065 static int start_daemon(const char *lockfile)
00066 {
00067        int     fd;
00068        struct  sockaddr_un skun;
00069 
00070        unlink(lockfile);
00071 
00072        fd=socket(PF_UNIX, SOCK_STREAM, 0);
00073         if (fd < 0)
00074        {
00075               perror("socket");
00076               return (-1);
00077        }
00078 
00079         skun.sun_family=AF_UNIX;
00080         strcpy(skun.sun_path, lockfile);
00081         if (bind(fd, (const struct sockaddr *)&skun, sizeof(skun)) ||
00082                 listen(fd, SOMAXCONN) ||
00083                 chmod(skun.sun_path, 0777) ||
00084                 fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
00085         {
00086                 perror(lockfile);
00087                 close(fd);
00088                 return (-1);
00089         }
00090        return fd;
00091 }
00092 
00093 static int prefork(int listenfd, int *allpipes, size_t ndaemos,
00094                  int *termpipe, void (*handler)(void *), void *dummy);
00095 
00096 static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
00097                      void *dummy)
00098 {
00099        int *cpipes; /* Completion pipes from preforked processes */
00100        int termpipe[2]; /* Termination pipe to preforked processes */
00101        size_t i;
00102 
00103        if ((cpipes=malloc(sizeof(int)*ndaemons)) == NULL)
00104        {
00105               fprintf(stderr,
00106                      "CRIT: malloc failed: %s\n",
00107                      strerror(errno));
00108               exit(1);
00109        }
00110 
00111        if (pipe(termpipe) < 0)
00112        {
00113               fprintf(stderr,
00114                      "CRIT: pipe failed: %s\n",
00115                      strerror(errno));
00116               exit(1);
00117        }
00118 
00119 
00120        /* Start the initial set of preforked daemons */
00121 
00122        for (i=0; i<ndaemons; i++)
00123               cpipes[i]= -1;
00124 
00125        for (i=0; i<ndaemons; i++)
00126               cpipes[i]=prefork(fd, cpipes, ndaemons, termpipe, handler,
00127                               dummy);
00128 
00129 
00130        for (;;)
00131        {
00132               fd_set fdr;
00133               int    maxfd=0;
00134 
00135               FD_ZERO(&fdr);
00136 
00137               for (i=0; i<ndaemons; i++)
00138               {
00139                      if (cpipes[i] >= maxfd)
00140                             maxfd=cpipes[i]+1;
00141 
00142                      FD_SET(cpipes[i], &fdr);
00143               }
00144 
00145               if (select(maxfd, &fdr, NULL, NULL, NULL) <= 0)
00146                      continue;
00147 
00148               /*
00149               ** When child process gets a connection, it closes its
00150               ** completion pipe, which makes the pipe selectable for
00151               ** read.
00152               */
00153 
00154               for (i=0; i<ndaemons; i++)
00155               {
00156                      if (FD_ISSET(cpipes[i], &fdr))
00157                      {
00158                             close(cpipes[i]);
00159                             cpipes[i]= -1;
00160                             cpipes[i]=prefork(fd, cpipes,
00161                                             ndaemons, termpipe, handler,
00162                                             dummy);
00163                      }
00164               }
00165        }
00166 }
00167 
00168 /* Start a preforked process */
00169 
00170 static int prefork(int listenfd, int *allpipes, size_t ndaemons,
00171                  int *termpipe, void (*handler)(void *), void *dummy)
00172 {
00173        int newpipe[2];
00174        pid_t p;
00175        int waitstat;
00176        size_t i;
00177 
00178        if (pipe(newpipe) < 0)
00179        {
00180               fprintf(stderr,
00181                      "CRIT: pipe failed: %s\n", strerror(errno));
00182               exit(1);
00183        }
00184 
00185        while ((p=fork()) < 0)
00186        {
00187               fprintf(stderr,
00188                      "CRIT: fork failed: %s\n", strerror(errno));
00189               sleep(5);
00190        }
00191 
00192        if (p) /* parent */
00193        {
00194               close(newpipe[1]);
00195 
00196               /* Wait for first child process to go away */
00197 
00198               while (wait(&waitstat) != p)
00199                      ;
00200 
00201               return (newpipe[0]);
00202        }
00203 
00204        close(newpipe[0]);
00205        close(termpipe[1]);
00206 
00207        /* New child doesn't need pipes from other children */
00208 
00209        for (i=0; i<ndaemons; i++)
00210               if (allpipes[i] >= 0)
00211                      close(allpipes[i]);
00212 
00213        /* Fork once more, so that the parent process can continue */
00214 
00215        if (fork())
00216               exit(0);
00217 
00218        run_daemon(listenfd, termpipe[0], newpipe[1], handler, dummy);
00219        return (-1);
00220 }
00221 
00222 static void run_daemon(int fd, int termfd, int acceptedfd,
00223                      void (*handler)(void *), void *dummy)
00224 {
00225        int cfd;
00226 
00227        for (;;)
00228        {
00229               fd_set fdr;
00230               pid_t p;
00231               int maxfd;
00232 
00233               FD_ZERO(&fdr);
00234 
00235               FD_SET(fd, &fdr);
00236 
00237               maxfd=fd;
00238 
00239               if (termfd >= 0)
00240               {
00241                      if (termfd > maxfd)
00242                             maxfd=termfd;
00243 
00244                      FD_SET(termfd, &fdr);
00245               }
00246 
00247               if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
00248                      continue;
00249               if (termfd >= 0 &&
00250                   FD_ISSET(termfd, &fdr)) /* Terminate all child procs */
00251                      exit(0);
00252 
00253               if (!FD_ISSET(fd, &fdr))
00254                      continue;
00255 
00256               cfd=accept(fd, NULL, 0);
00257 
00258               if (cfd < 0)
00259                      continue;
00260 
00261               if (acceptedfd >= 0) /* preforked daemon */
00262               {
00263                      if (termfd >= 0)
00264                             close(termfd);
00265                      close(acceptedfd);
00266                      break;
00267               }
00268 
00269               p=fork();
00270 
00271               if (p < 0)
00272               {
00273                      fprintf(stderr,
00274                             "CRIT: fork() failed: %s\n", strerror(errno));
00275                      continue;
00276               }
00277 
00278               if (p)
00279               {
00280                      int dummy;
00281 
00282                      close(cfd);
00283                      while (wait(&dummy) != p)
00284                             continue;
00285                      continue;
00286               }
00287 
00288               /* Child forks once more, parent exits */
00289 
00290               if (fork())
00291                      exit(0);
00292 
00293               break;
00294 
00295        }
00296 
00297        /* child */
00298 
00299        if (fcntl(cfd, F_SETFL, 0) < 0)
00300        {
00301               fprintf(stderr,
00302                      "CRIT: fcntl(): %s\n", strerror(errno));
00303               exit(0);
00304        }
00305 
00306        close(fd);
00307        if (read_environ(cfd))
00308        {
00309               close(0);
00310               close(1);
00311               if (dup(cfd) != 0 || dup(cfd) != 1)
00312               {
00313                      fprintf(stderr,
00314                             "CRIT: dup() did not work as expected\n");
00315                      exit(0);
00316               }
00317               close(cfd);
00318        }
00319 
00320        if (fcntl(0, F_SETFL, 0) < 0 ||
00321            fcntl(1, F_SETFL, 0) < 0)
00322        {
00323               fprintf(stderr,
00324                      "CRIT: fcntl() failed: %s\n", strerror(errno));
00325               exit(0);
00326        }
00327 
00328        (*handler)(dummy);
00329        exit(0);
00330 }
00331 
00332 
00333 /* Read environment from the sqwebmail stub */
00334 
00335 static void force_read(int cfd, char *p, size_t l)
00336 {
00337        int m;
00338 
00339        while (l)
00340        {
00341               m=read(cfd, p, l);
00342               if (m <= 0)
00343               {
00344                      fprintf(stderr,
00345                             "WARN: socket closed while reading"
00346                             " environment.\n");
00347                      exit(0);
00348               }
00349 
00350               p += m;
00351               l -= m;
00352        }
00353 }
00354 
00355 /* Receive CGI environment */
00356 
00357 static int read_environ(int cfd)
00358 {
00359        static char buf[SOCKENVIRONLEN];
00360        size_t l;
00361        char *p;
00362        int passfd;
00363 
00364        force_read(cfd, buf, 1+sizeof(l));
00365 
00366        memcpy(&l, buf, sizeof(l));
00367        passfd=buf[sizeof(l)];
00368 
00369        if (l >= sizeof(buf))
00370        {
00371               fprintf(stderr,
00372                      "WARN: invalid environment received via socket.\n");
00373               exit(0);
00374        }
00375 
00376        alarm(10); /* Just in case - punt */
00377        force_read(cfd, buf, l);
00378        buf[l]=0;
00379        alarm(0);
00380 
00381        /* Vet environment strings for only known good strings */
00382 
00383        p=buf;
00384        while (p < buf+l)
00385        {
00386               if (strchr(p, '=') == NULL ||
00387                   !VALIDCGIVAR(p))
00388               {
00389                      fprintf(stderr,
00390                             "WARN: invalid environment received"
00391                             " via socket: %s\n" , p);
00392                      exit(0);
00393               }
00394 
00395               putenv(p);
00396 
00397               while (*p++)
00398                      ;
00399        }
00400 
00401        /* Receive file descriptors, if supported by the platform */
00402 
00403 #if    CGI_PASSFD
00404 
00405        if (passfd)
00406        {
00407               struct iovec iov;
00408               char dummy;
00409 
00410 #if CGI_PASSFD_MSGACCRIGHTS
00411 
00412               int fdbuf[2];
00413               struct msghdr msg;
00414 #endif
00415 
00416 #if CGI_PASSFD_MSGCONTROL
00417 
00418               int fdbuf[2];
00419               struct msghdr msg;
00420               struct cmsghdr *cmsg;
00421               char buf[CMSG_SPACE(sizeof(fdbuf))];
00422 #endif
00423 
00424 #if CGI_PASSFD_MSGACCRIGHTS
00425               memset(&iov, 0, sizeof(iov));
00426               msg.msg_accrights=(caddr_t)fdbuf;
00427               msg.msg_accrightslen=sizeof(fdbuf);
00428 #endif
00429 
00430 
00431 #if CGI_PASSFD_MSGCONTROL
00432               memset(&msg, 0, sizeof(msg));
00433               msg.msg_control=buf;
00434               msg.msg_controllen=sizeof(buf);
00435 #endif
00436 
00437               msg.msg_iov=&iov;
00438               msg.msg_iovlen=1;
00439 
00440               iov.iov_base=&dummy;
00441               iov.iov_len=1;
00442 
00443               if (recvmsg(cfd, &msg, 0) <= 0)
00444               {
00445                      perror("Internal error - recvmsg() failed");
00446                      exit(0);
00447               }
00448 
00449 #if CGI_PASSFD_MSGACCRIGHTS
00450 
00451               if (msg.msg_accrightslen < sizeof(fdbuf))
00452               {
00453                      perror("Internal error - malformed recvmsg()");
00454                      exit(0);
00455               }
00456 
00457 #endif
00458 
00459 #if CGI_PASSFD_MSGCONTROL
00460               if (msg.msg_controllen < sizeof(buf) ||
00461                   (cmsg = CMSG_FIRSTHDR(&msg))->cmsg_level != SOL_SOCKET ||
00462                   cmsg->cmsg_type != SCM_RIGHTS ||
00463                   cmsg->cmsg_len != CMSG_LEN(sizeof(fdbuf)))
00464               {
00465                      perror("Internal error - malformed recvmsg()");
00466                      exit(0);
00467               }
00468 
00469               memcpy(fdbuf, CMSG_DATA(cmsg), sizeof(fdbuf));
00470 #endif
00471               close(0);
00472               close(1);
00473               if (dup(fdbuf[0]) != 0 || dup(fdbuf[1]) != 1)
00474                      fprintf(stderr,
00475                             "CRIT: dup() did not work as expected in"
00476                             " read_environ()\n");
00477               close(fdbuf[0]);
00478               close(fdbuf[1]);
00479               return 0;
00480        }
00481 #endif
00482 
00483        return 1;
00484 }