Back to index

courier  0.68.2
Functions
cgidaemond.c File Reference
#include "cgi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>

Go to the source code of this file.

Functions

static int read_environ (int)
static int start_daemon (const char *lockfilename)
static void run_daemon (int fd, int termfd, int connfd, void(*handler)(void *), void *dummy)
static void run_prefork (int fd, size_t ndaemons, void(*handler)(void *), void *dummy)
void cgi_daemon (int nprocs, const char *lockfile, void(*postinit)(void *), void(*handler)(void *), void *dummy)
static int prefork (int listenfd, int *allpipes, size_t ndaemos, int *termpipe, void(*handler)(void *), void *dummy)
static void force_read (int cfd, char *p, size_t l)

Function Documentation

void cgi_daemon ( int  nprocs,
const char *  lockfile,
void(*)(void *)  postinit,
void(*)(void *)  handler,
void *  dummy 
)

Definition at line 48 of file cgidaemond.c.

{
       int fd=start_daemon(lockfile);

       if (postinit)
              (*postinit)(dummy);

       if (nprocs > 0)
              run_prefork(fd, nprocs, handler, dummy);
       else
              run_daemon(fd, -1, -1, handler, dummy);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void force_read ( int  cfd,
char *  p,
size_t  l 
) [static]

Definition at line 335 of file cgidaemond.c.

{
       int m;

       while (l)
       {
              m=read(cfd, p, l);
              if (m <= 0)
              {
                     fprintf(stderr,
                            "WARN: socket closed while reading"
                            " environment.\n");
                     exit(0);
              }

              p += m;
              l -= m;
       }
}

Here is the caller graph for this function:

static int prefork ( int  listenfd,
int *  allpipes,
size_t  ndaemos,
int *  termpipe,
void(*)(void *)  handler,
void *  dummy 
) [static]

Definition at line 170 of file cgidaemond.c.

{
       int newpipe[2];
       pid_t p;
       int waitstat;
       size_t i;

       if (pipe(newpipe) < 0)
       {
              fprintf(stderr,
                     "CRIT: pipe failed: %s\n", strerror(errno));
              exit(1);
       }

       while ((p=fork()) < 0)
       {
              fprintf(stderr,
                     "CRIT: fork failed: %s\n", strerror(errno));
              sleep(5);
       }

       if (p) /* parent */
       {
              close(newpipe[1]);

              /* Wait for first child process to go away */

              while (wait(&waitstat) != p)
                     ;

              return (newpipe[0]);
       }

       close(newpipe[0]);
       close(termpipe[1]);

       /* New child doesn't need pipes from other children */

       for (i=0; i<ndaemons; i++)
              if (allpipes[i] >= 0)
                     close(allpipes[i]);

       /* Fork once more, so that the parent process can continue */

       if (fork())
              exit(0);

       run_daemon(listenfd, termpipe[0], newpipe[1], handler, dummy);
       return (-1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int read_environ ( int  cfd) [static]

Definition at line 357 of file cgidaemond.c.

{
       static char buf[SOCKENVIRONLEN];
       size_t l;
       char *p;
       int passfd;

       force_read(cfd, buf, 1+sizeof(l));

       memcpy(&l, buf, sizeof(l));
       passfd=buf[sizeof(l)];

       if (l >= sizeof(buf))
       {
              fprintf(stderr,
                     "WARN: invalid environment received via socket.\n");
              exit(0);
       }

       alarm(10); /* Just in case - punt */
       force_read(cfd, buf, l);
       buf[l]=0;
       alarm(0);

       /* Vet environment strings for only known good strings */

       p=buf;
       while (p < buf+l)
       {
              if (strchr(p, '=') == NULL ||
                  !VALIDCGIVAR(p))
              {
                     fprintf(stderr,
                            "WARN: invalid environment received"
                            " via socket: %s\n" , p);
                     exit(0);
              }

              putenv(p);

              while (*p++)
                     ;
       }

       /* Receive file descriptors, if supported by the platform */

#if    CGI_PASSFD

       if (passfd)
       {
              struct iovec iov;
              char dummy;

#if CGI_PASSFD_MSGACCRIGHTS

              int fdbuf[2];
              struct msghdr msg;
#endif

#if CGI_PASSFD_MSGCONTROL

              int fdbuf[2];
              struct msghdr msg;
              struct cmsghdr *cmsg;
              char buf[CMSG_SPACE(sizeof(fdbuf))];
#endif

#if CGI_PASSFD_MSGACCRIGHTS
              memset(&iov, 0, sizeof(iov));
              msg.msg_accrights=(caddr_t)fdbuf;
              msg.msg_accrightslen=sizeof(fdbuf);
#endif


#if CGI_PASSFD_MSGCONTROL
              memset(&msg, 0, sizeof(msg));
              msg.msg_control=buf;
              msg.msg_controllen=sizeof(buf);
#endif

              msg.msg_iov=&iov;
              msg.msg_iovlen=1;

              iov.iov_base=&dummy;
              iov.iov_len=1;

              if (recvmsg(cfd, &msg, 0) <= 0)
              {
                     perror("Internal error - recvmsg() failed");
                     exit(0);
              }

#if CGI_PASSFD_MSGACCRIGHTS

              if (msg.msg_accrightslen < sizeof(fdbuf))
              {
                     perror("Internal error - malformed recvmsg()");
                     exit(0);
              }

#endif

#if CGI_PASSFD_MSGCONTROL
              if (msg.msg_controllen < sizeof(buf) ||
                  (cmsg = CMSG_FIRSTHDR(&msg))->cmsg_level != SOL_SOCKET ||
                  cmsg->cmsg_type != SCM_RIGHTS ||
                  cmsg->cmsg_len != CMSG_LEN(sizeof(fdbuf)))
              {
                     perror("Internal error - malformed recvmsg()");
                     exit(0);
              }

              memcpy(fdbuf, CMSG_DATA(cmsg), sizeof(fdbuf));
#endif
              close(0);
              close(1);
              if (dup(fdbuf[0]) != 0 || dup(fdbuf[1]) != 1)
                     fprintf(stderr,
                            "CRIT: dup() did not work as expected in"
                            " read_environ()\n");
              close(fdbuf[0]);
              close(fdbuf[1]);
              return 0;
       }
#endif

       return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void run_daemon ( int  fd,
int  termfd,
int  connfd,
void(*)(void *)  handler,
void *  dummy 
) [static]

Definition at line 222 of file cgidaemond.c.

{
       int cfd;

       for (;;)
       {
              fd_set fdr;
              pid_t p;
              int maxfd;

              FD_ZERO(&fdr);

              FD_SET(fd, &fdr);

              maxfd=fd;

              if (termfd >= 0)
              {
                     if (termfd > maxfd)
                            maxfd=termfd;

                     FD_SET(termfd, &fdr);
              }

              if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
                     continue;
              if (termfd >= 0 &&
                  FD_ISSET(termfd, &fdr)) /* Terminate all child procs */
                     exit(0);

              if (!FD_ISSET(fd, &fdr))
                     continue;

              cfd=accept(fd, NULL, 0);

              if (cfd < 0)
                     continue;

              if (acceptedfd >= 0) /* preforked daemon */
              {
                     if (termfd >= 0)
                            close(termfd);
                     close(acceptedfd);
                     break;
              }

              p=fork();

              if (p < 0)
              {
                     fprintf(stderr,
                            "CRIT: fork() failed: %s\n", strerror(errno));
                     continue;
              }

              if (p)
              {
                     int dummy;

                     close(cfd);
                     while (wait(&dummy) != p)
                            continue;
                     continue;
              }

              /* Child forks once more, parent exits */

              if (fork())
                     exit(0);

              break;

       }

       /* child */

       if (fcntl(cfd, F_SETFL, 0) < 0)
       {
              fprintf(stderr,
                     "CRIT: fcntl(): %s\n", strerror(errno));
              exit(0);
       }

       close(fd);
       if (read_environ(cfd))
       {
              close(0);
              close(1);
              if (dup(cfd) != 0 || dup(cfd) != 1)
              {
                     fprintf(stderr,
                            "CRIT: dup() did not work as expected\n");
                     exit(0);
              }
              close(cfd);
       }

       if (fcntl(0, F_SETFL, 0) < 0 ||
           fcntl(1, F_SETFL, 0) < 0)
       {
              fprintf(stderr,
                     "CRIT: fcntl() failed: %s\n", strerror(errno));
              exit(0);
       }

       (*handler)(dummy);
       exit(0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void run_prefork ( int  fd,
size_t  ndaemons,
void(*)(void *)  handler,
void *  dummy 
) [static]

Definition at line 96 of file cgidaemond.c.

{
       int *cpipes; /* Completion pipes from preforked processes */
       int termpipe[2]; /* Termination pipe to preforked processes */
       size_t i;

       if ((cpipes=malloc(sizeof(int)*ndaemons)) == NULL)
       {
              fprintf(stderr,
                     "CRIT: malloc failed: %s\n",
                     strerror(errno));
              exit(1);
       }

       if (pipe(termpipe) < 0)
       {
              fprintf(stderr,
                     "CRIT: pipe failed: %s\n",
                     strerror(errno));
              exit(1);
       }


       /* Start the initial set of preforked daemons */

       for (i=0; i<ndaemons; i++)
              cpipes[i]= -1;

       for (i=0; i<ndaemons; i++)
              cpipes[i]=prefork(fd, cpipes, ndaemons, termpipe, handler,
                              dummy);


       for (;;)
       {
              fd_set fdr;
              int    maxfd=0;

              FD_ZERO(&fdr);

              for (i=0; i<ndaemons; i++)
              {
                     if (cpipes[i] >= maxfd)
                            maxfd=cpipes[i]+1;

                     FD_SET(cpipes[i], &fdr);
              }

              if (select(maxfd, &fdr, NULL, NULL, NULL) <= 0)
                     continue;

              /*
              ** When child process gets a connection, it closes its
              ** completion pipe, which makes the pipe selectable for
              ** read.
              */

              for (i=0; i<ndaemons; i++)
              {
                     if (FD_ISSET(cpipes[i], &fdr))
                     {
                            close(cpipes[i]);
                            cpipes[i]= -1;
                            cpipes[i]=prefork(fd, cpipes,
                                            ndaemons, termpipe, handler,
                                            dummy);
                     }
              }
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int start_daemon ( const char *  lockfilename) [static]

Definition at line 65 of file cgidaemond.c.

{
       int     fd;
       struct  sockaddr_un skun;

       unlink(lockfile);

       fd=socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0)
       {
              perror("socket");
              return (-1);
       }

        skun.sun_family=AF_UNIX;
        strcpy(skun.sun_path, lockfile);
        if (bind(fd, (const struct sockaddr *)&skun, sizeof(skun)) ||
                listen(fd, SOMAXCONN) ||
                chmod(skun.sun_path, 0777) ||
                fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
        {
                perror(lockfile);
                close(fd);
                return (-1);
        }
       return fd;
}

Here is the caller graph for this function: