Back to index

courier  0.68.2
cgidaemon.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2003-2007 Double Precision, Inc.  See COPYING for
00003 ** distribution information.
00004 */
00005 
00006 
00007 /*
00008 */
00009 #include      "cgi.h"
00010 #include      <stdio.h>
00011 #include      <errno.h>
00012 #include      <stdlib.h>
00013 #if    HAVE_UNISTD_H
00014 #include      <unistd.h>
00015 #endif
00016 #include      <string.h>
00017 #include      <signal.h>
00018 #include      <sys/types.h>
00019 #include      <sys/stat.h>
00020 #include        <sys/socket.h>
00021 #include        <sys/un.h>
00022 #if    HAVE_FCNTL_H
00023 #include      <fcntl.h>
00024 #endif
00025 #include      <ctype.h>
00026 #if HAVE_SYS_WAIT_H
00027 #include      <sys/wait.h>
00028 #endif
00029 
00030 #if    HAVE_SYS_SELECT_H
00031 #include      <sys/select.h>
00032 #endif
00033 
00034 #if    HAVE_SYS_UIO_H
00035 #include      <sys/uio.h>
00036 #endif
00037 
00038 
00039 extern char **environ;
00040 
00041 static void err(const char *func, const char *msg)
00042 {
00043        cginocache();
00044 
00045        printf("Content-Type: text/html; charset='utf-8'\n\n"
00046               "<html><head><title>Internal error</title></head>\n"
00047               "<body><h1>Internal Error</h1>\n"
00048               "<p>The webmail system is temporarily unavailable.  An error"
00049               " occured in function %s: %s</p></body></html>\n", func,
00050               msg);
00051        fflush(stdout);
00052        exit(0);
00053 }
00054 
00055 static void connect_err(const char *func)
00056 {
00057        cginocache();
00058 
00059        printf("Content-Type: text/html; charset='us-ascii'\n\n"
00060               "<html><head><title>System unavailable</title></head>\n"
00061               "<body><h1>System unavailable</h1>\n"
00062               "<p>The web page you're trying to access is not available"
00063               " at this time. Please try again later.\n"
00064               "</p><p>"
00065               "(%s: %s)</p></body></html>\n", func, strerror(errno));
00066        fflush(stdout);
00067        exit(0);
00068 }
00069 
00070 static void sys_err(const char *func)
00071 {
00072        err(func, strerror(errno));
00073 }
00074 
00075 static const char *force_write(int s, const char *p, size_t l)
00076 {
00077        while (l)
00078        {
00079               int n;
00080 
00081               n=write(s, p, l);
00082               if (n <= 0)
00083                      return ("write");
00084               p += n;
00085               l -= n;
00086        }
00087        return NULL;
00088 }
00089 
00090 /* Pass along the CGI environment variables to sqwebmaild */
00091 
00092 static void send_environ(int fd, int passfd)
00093 {
00094        char buf[SOCKENVIRONLEN];
00095 
00096        char *p=buf+sizeof(size_t)+1;
00097        size_t l=sizeof(buf)-sizeof(size_t)-2;
00098        size_t i;
00099        const char *cp;
00100 
00101        buf[sizeof(l)]=passfd;
00102 
00103        for (i=0; environ[i]; i++)
00104        {
00105               size_t m;
00106 
00107               if (!VALIDCGIVAR(environ[i]))
00108                      continue;
00109 
00110               m=strlen(environ[i])+1;
00111 
00112               if (m > l)
00113                      err("CGI", "CGI environment exceeds allowed "
00114                          "maximum size.");
00115 
00116               memcpy(p, environ[i], m);
00117               p += m;
00118               l -= m;
00119        }
00120 
00121        l=p-(buf+sizeof(l)+1);
00122        memcpy(buf, &l, sizeof(l));
00123 
00124        cp=force_write(fd, buf, l + sizeof(l)+1);
00125        if (cp)
00126               sys_err(cp);
00127 
00128        /*
00129        ** If the platform supports it, pass the file descriptors
00130        ** to sqwebmaild.
00131        */
00132 
00133 #if    CGI_PASSFD
00134 
00135        if (passfd)
00136        {
00137               struct iovec iov;
00138               char dummy;
00139 
00140 #if CGI_PASSFD_MSGACCRIGHTS
00141 
00142               int fdbuf[2];
00143               struct msghdr msg;
00144 
00145               fdbuf[0]=0;
00146               fdbuf[1]=1;
00147               memset(&iov, 0, sizeof(iov));
00148               msg.msg_accrights=(caddr_t)fdbuf;
00149               msg.msg_accrightslen=sizeof(fdbuf);
00150 #endif
00151 
00152 #if CGI_PASSFD_MSGCONTROL
00153 
00154               int fdbuf[2];
00155               struct msghdr msg;
00156               struct cmsghdr *cmsg;
00157               char buf[CMSG_SPACE(sizeof(fdbuf))];
00158 
00159               fdbuf[0]=0;
00160               fdbuf[1]=1;
00161 
00162               memset(&msg, 0, sizeof(msg));
00163               msg.msg_control=buf;
00164               msg.msg_controllen=sizeof(buf);
00165               cmsg = CMSG_FIRSTHDR(&msg);
00166               cmsg->cmsg_level=SOL_SOCKET;
00167               cmsg->cmsg_type=SCM_RIGHTS;
00168               cmsg->cmsg_len=CMSG_LEN(sizeof(fdbuf));
00169               memcpy(CMSG_DATA(cmsg), fdbuf, sizeof(fdbuf));
00170 #endif
00171               msg.msg_iov=&iov;
00172               msg.msg_iovlen=1;
00173               iov.iov_base=&dummy;
00174               iov.iov_len=1;
00175 
00176               dummy=0;
00177               if (sendmsg(fd, &msg, 0) < 0)
00178                      sys_err("sendmsg(filedescriptor)");
00179        }
00180 #endif
00181 
00182 }
00183 
00184 static void passthrough(int s, int passed_fd)
00185 {
00186        char toclientbuf[8192];
00187        char tosqbuf[8192];
00188 
00189        char *toclientptr, *tosqptr;
00190        size_t toclientlen, tosqlen;
00191        int stdin_closed=0;
00192 
00193        toclientptr=NULL;
00194        tosqptr=NULL;
00195        toclientlen=0;
00196        tosqlen=0;
00197 
00198        if (passed_fd)
00199        {
00200               stdin_closed=1;  /* sqwebmaild will read on the fd itself */
00201               if (fcntl(s, F_SETFL, O_NDELAY) < 0)
00202                      sys_err("fcntl");
00203        }
00204 
00205        /* When the file descriptor is passed, we will not do any actual I/O,
00206        ** so there's no need to set stdin/stdout to nonblock mode
00207        */
00208        else if (fcntl(0, F_SETFL, O_NDELAY) < 0 ||
00209                fcntl(1, F_SETFL, O_NDELAY) < 0 ||
00210                fcntl(s, F_SETFL, O_NDELAY) < 0)
00211               sys_err("fcntl");
00212 
00213        for (;;)
00214        {
00215               fd_set fdr, fdw;
00216 
00217               FD_ZERO(&fdr);
00218               FD_ZERO(&fdw);
00219 
00220               if (tosqlen)
00221                      FD_SET(s, &fdw);
00222               else if (!stdin_closed)
00223                      FD_SET(0, &fdr);
00224 
00225               if (toclientlen)
00226                      FD_SET(1, &fdw);
00227               else
00228                      FD_SET(s, &fdr);
00229 
00230               if (select(s+1, &fdr, &fdw, 0, NULL) <= 0)
00231               {
00232                      fcntl(1, F_SETFL, 0);
00233                      sys_err("select");
00234               }
00235 
00236               if (tosqlen)
00237               {
00238                      if (FD_ISSET(s, &fdw))
00239                      {
00240                             int m=write(s, tosqptr, tosqlen);
00241 
00242                             if (m <= 0)
00243                             {
00244                                    fcntl(1, F_SETFL, 0);
00245                                    sys_err("write");
00246                             }
00247 
00248                             tosqptr += m;
00249                             tosqlen -= m;
00250                      }
00251               }
00252               else
00253               {
00254                      if (FD_ISSET(0, &fdr))
00255                      {
00256                             int m=read(0, tosqbuf, sizeof(tosqbuf));
00257 
00258                             if (m < 0) /* network error */
00259                                    return;
00260 
00261                             if (m == 0)
00262                                    stdin_closed=1;
00263 
00264                             tosqptr=tosqbuf;
00265                             tosqlen=m;
00266                      }
00267               }
00268 
00269               if (toclientlen)
00270               {
00271                      if (FD_ISSET(1, &fdw))
00272                      {
00273                             int m=write(1, toclientptr, toclientlen);
00274 
00275                             if (m <= 0)
00276                                    return; /* Client aborted, nocare */
00277 
00278                             toclientptr += m;
00279                             toclientlen -= m;
00280                      }
00281               }
00282               else
00283               {
00284                      if (FD_ISSET(s, &fdr))
00285                      {
00286                             int m=read(s, toclientbuf,
00287                                       sizeof(toclientbuf));
00288 
00289                             if (m <= 0)
00290                                    return;
00291 
00292                             toclientptr=toclientbuf;
00293                             toclientlen=m;
00294                      }
00295               }
00296        }
00297 }
00298 
00299 void cgi_connectdaemon(const char *sockfilename, int pass_fd)
00300 {
00301        int    s;
00302        struct  sockaddr_un ssun;
00303        int    triedagain=0;
00304        int    rc;
00305 
00306        /* Connect to sqwebmaild via a socket */
00307 
00308        signal(SIGPIPE, SIG_IGN);
00309        if ((s=socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
00310               sys_err("socket");
00311 
00312        if (fcntl(s, F_SETFL, O_NDELAY) < 0)
00313               sys_err("fcntl");
00314 
00315        ssun.sun_family=AF_UNIX;
00316        strcpy(ssun.sun_path, sockfilename);
00317 
00318        while ((rc=connect(s, (struct sockaddr *)&ssun, sizeof(ssun))) < 0
00319               && errno == EAGAIN)
00320        {
00321               if (++triedagain > 5)
00322                      break;
00323               sleep(1);
00324               ssun.sun_family=AF_UNIX;
00325               strcpy(ssun.sun_path, sockfilename);
00326        }
00327 
00328        if (rc < 0)
00329        {
00330               struct timeval       tv;
00331               fd_set fds;
00332 
00333               int    errcode;
00334               socklen_t errcode_l;
00335 
00336               if (errno != EINPROGRESS)
00337                      connect_err("connect");
00338 
00339               tv.tv_sec=30;
00340               tv.tv_usec=0;
00341               FD_ZERO(&fds);
00342               FD_SET(s, &fds);
00343               if (select(s+1, 0, &fds, 0, &tv) <= 0)
00344                      connect_err("select");
00345 
00346 
00347               errcode_l=sizeof(errcode);
00348 
00349               if (getsockopt(s, SOL_SOCKET, SO_ERROR, &errcode, &errcode_l)
00350                   < 0)
00351                      connect_err("setsockopt");
00352 
00353 
00354               if (errcode)
00355               {
00356                      errno=errcode;
00357                      connect_err("connect");
00358               }
00359        }
00360 
00361        if (triedagain)
00362        {
00363               fprintf(stderr,
00364                      "CRIT: Several attempts were necessary to connect to sqwebmaild\n");
00365               fprintf(stderr,
00366                      "CRIT: Consider increasing the number of pre-forked sqwebmaild processes\n");
00367        }
00368 
00369        if (fcntl(s, F_SETFL, 0) < 0)
00370               sys_err("fcntl");
00371 
00372        send_environ(s, pass_fd);
00373        passthrough(s, pass_fd);
00374        close(s);
00375        exit(0);
00376 }