Back to index

courier  0.68.2
comsubmitclient.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2012 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include      "courier_lib_config.h"
00007 #include      "courier.h"
00008 #include      "libexecdir.h"
00009 #include      "waitlib/waitlib.h"
00010 #include      "comsubmitclient.h"
00011 
00012 #include      <stdio.h>
00013 #if    HAVE_UNISTD_H
00014 #include      <unistd.h>
00015 #endif
00016 #if    HAVE_FCNTL_H
00017 #include      <fcntl.h>
00018 #endif
00019 #include      <stdlib.h>
00020 #include      <errno.h>
00021 #include      <ctype.h>
00022 #include      <string.h>
00023 #include      <signal.h>
00024 
00025 static void (*printfunc)(const char *);
00026 static void (*teergrubefunc)(void)=NULL;
00027 
00028 static pid_t submit_pid;
00029 FILE *submit_to;
00030 FILE *fromsubmit;
00031 static int submit_error;
00032 static void (*log_error_prefix_func)(void);
00033 
00034 #define       PRINT_RESPONSE       1
00035 #define       PRINT_CRLF    2
00036 #define       PRINT_ERRORS  4
00037 
00038 static RETSIGTYPE sighandler(int signum)
00039 {
00040        submit_cancel_async();
00041        signal(signum, SIG_DFL);
00042        kill(getpid(), signum);
00043        _exit(0);
00044 #if    RETSIGTYPE != void
00045        return (0);
00046 #endif
00047 }
00048 
00049 void submit_set_teergrube( void (*funcarg)(void))
00050 {
00051        teergrubefunc=funcarg;
00052 }
00053 
00054 /*
00055 ** Wait for the submit process to terminate.  NOTE: submit_to must be
00056 ** already closed.  Returns 0 if submit terminated normally, non-zero
00057 ** in all other situations.
00058 */
00059 
00060 static int submit_wait_noreset()
00061 {
00062 int    wait_stat;
00063 pid_t  wait_pid;
00064 
00065        while ((wait_pid=wait(&wait_stat)) != submit_pid)
00066        {
00067               if (wait_pid == -1 && errno == ECHILD)    return (-1);
00068        }
00069        if (WIFEXITED(wait_stat) && WEXITSTATUS(wait_stat) == 0)
00070               return (0);
00071        return (-1);
00072 }
00073 
00074 int submit_wait()
00075 {
00076 int    rc=submit_wait_noreset();
00077 
00078        signal(SIGINT, SIG_DFL);
00079        signal(SIGHUP, SIG_DFL);
00080        signal(SIGTERM, SIG_DFL);
00081        return (rc);
00082 }
00083 
00084 
00085 /****************************************************************************
00086 ** Fork and run the submit process, creating pipes to/from its standard I/O.
00087 ** A pipe to the submit process is created in submit_to, and we maintain
00088 ** the pipe from the submit process internally.
00089 **
00090 ** args - arguments to the submit process.
00091 ** env - submit process's environment.
00092 **
00093 ** Returns 0 if the submit process was forked succesfully, non-zero in all
00094 ** other situations.
00095 **
00096 ** Fork and run the submit process, creating pipes to/from its standard I/O.
00097 */
00098 
00099 int submit_fork_virtual(char **args, char **env,
00100                      void (*func)(const char *))
00101 {
00102 int    pipe0[2], pipe1[2];
00103 
00104        printfunc=func;
00105 
00106        submit_error=1;
00107        log_error_prefix_func=0;
00108        if (pipe(pipe0))
00109        {
00110               clog_msg_start_err();
00111               clog_msg_str(strerror(errno));
00112               clog_msg_send();
00113               return (-1);
00114        }
00115        if (pipe(pipe1))
00116        {
00117               close(pipe0[0]);
00118               close(pipe0[1]);
00119               clog_msg_start_err();
00120               clog_msg_str(strerror(errno));
00121               clog_msg_send();
00122               return (-1);
00123        }
00124 
00125        submit_pid=fork();
00126        if ( submit_pid == 0)
00127        {
00128        static char execfailed[]="400 Unable to submit message - service temporarily unavailable.\n";
00129 
00130               close(pipe0[1]);
00131               close(pipe1[0]);
00132               dup2(pipe0[0], 0);
00133               dup2(pipe1[1], 1);
00134               close(pipe0[0]);
00135               close(pipe1[1]);
00136 
00137               if (chdir(courierdir()))
00138                      exit(1);
00139 
00140               execve( LIBEXECDIR "/courier/submit", args, env );
00141 
00142               clog_msg_start_err();
00143               clog_msg_str(strerror(errno));
00144               clog_msg_send();
00145               if (write(1, execfailed, sizeof(execfailed)-1) != 1)
00146                      ; /* ignore */
00147               exit(1);
00148        }
00149        close(pipe0[0]);
00150        close(pipe1[1]);
00151 
00152        if (submit_pid == -1)
00153        {
00154               clog_msg_start_err();
00155               clog_msg_str(strerror(errno));
00156               clog_msg_send();
00157               close(pipe0[1]);
00158               close(pipe1[0]);
00159               return (-1);
00160        }
00161        if (fcntl(pipe0[1], F_SETFD, FD_CLOEXEC) ||
00162            (submit_to=fdopen(pipe0[1], "w")) == NULL)
00163        {
00164               clog_msg_start_err();
00165               clog_msg_str(strerror(errno));
00166               clog_msg_send();
00167               close(pipe0[1]);
00168               close(pipe1[0]);
00169               (void)submit_wait_noreset();
00170               return (-1);
00171        }
00172 
00173        if (fcntl(pipe1[0], F_SETFD, FD_CLOEXEC) ||
00174            (fromsubmit=fdopen(pipe1[0], "r")) == NULL)
00175        {
00176               clog_msg_start_err();
00177               clog_msg_str(strerror(errno));
00178               clog_msg_send();
00179               fclose(submit_to);
00180               close(pipe1[0]);
00181               (void)submit_wait_noreset();
00182               return (-1);
00183        }
00184        submit_error=0;
00185        signal(SIGINT, sighandler);
00186        signal(SIGTERM, sighandler);
00187        signal(SIGHUP, sighandler);
00188        return (0);
00189 }
00190 
00191 void submit_log_error_prefix(void (*func)(void))
00192 {
00193        log_error_prefix_func=func;
00194 }
00195 
00196 int submit_fork(char **args, char **env,
00197               void (*func)(const char *))
00198 {
00199        size_t i, j;
00200        const char *p=config_get_local_vhost();
00201        char **newargs;
00202        int rc;
00203        char *q=0;
00204 
00205        if (!p || !*p)
00206               return submit_fork_virtual(args, env, func);
00207 
00208        for (i=0; args[i]; ++i)
00209               ;
00210 
00211        newargs=malloc((i+2)*sizeof(char *));
00212 
00213        for (i=j=0; (newargs[j]=args[i]) != 0; ++i, ++j)
00214        {
00215               if (i == 0)
00216               {
00217                      ++j;
00218                      strcat(strcpy((q=newargs[j]=
00219                                    malloc(strlen(p)+10)),
00220                                   "-vhost="), p);
00221               }
00222        }
00223        rc=submit_fork_virtual(newargs, env, func);
00224 
00225        free(newargs);
00226        if (q) free(q);
00227        return rc;
00228 }
00229 
00230 /*
00231 ** Read one line of response from the submit process, echoing it if so
00232 ** specified by doprint.
00233 **
00234 ** If EOF indication is received, or if a response that does not follow
00235 ** RFC822 reply format is received, submit_error is set.
00236 **
00237 ** errcode should point to a four character buffer, the first character
00238 ** of which must be initialized to 0 before the first call.  errcode
00239 ** will be set to the RFC822 reply numeric code received (or initialized
00240 ** to 450 if an error condition occurs on the first call).
00241 **
00242 ** read_resp_oneline returns 0 if the reply line received from submit
00243 ** indicates the subsequent lines will follow, and will return non-0 if
00244 ** either the final response line has been received, or if an error
00245 ** condition occured.  In all cases, if doprint is set, an appropriate
00246 ** RFC822-conforming message is printed to standard out.
00247 */
00248 
00249 static int read_resp_oneline(char *errcode, int doprint)
00250 {
00251        static char linebuf[BUFSIZ];
00252        char   *c;
00253        int    rc;
00254        char   prev_errcode=errcode[0];
00255 
00256        if (submit_error ||
00257               fgets(linebuf, sizeof(linebuf), fromsubmit) == NULL)
00258        {
00259        const char *errmsg="Service temporarily unavailable.";
00260 
00261               if (!errcode[0])
00262                      strcpy(errcode, "432");
00263 
00264               if (!submit_error)
00265               {
00266                      submit_error=1;
00267                      clog_msg_start_err();
00268                      clog_msg_str("submitclient: EOF from submit.");
00269 #if 0
00270                      {
00271                             const char *p=getenv("TCPREMOTEIP");
00272 
00273                             if (p)
00274                             {
00275                                    clog_msg_str(" (ip: ");
00276                                    clog_msg_str(p);
00277                                    clog_msg_str(")");
00278                             }
00279                      }
00280 #endif
00281                      clog_msg_send();
00282               }
00283               if (doprint)
00284               {
00285                      (*printfunc)(errcode);
00286                      (*printfunc)(" ");
00287                      (*printfunc)(errmsg);
00288                      (*printfunc)(doprint & PRINT_CRLF ? "\r\n":"\n");
00289                      (*printfunc)(0);
00290               }
00291               return(-1);
00292        }
00293 
00294        if (!isdigit((int)(unsigned char)linebuf[0]) ||
00295               !isdigit((int)(unsigned char)linebuf[1]) ||
00296               !isdigit((int)(unsigned char)linebuf[2]) ||
00297               (errcode[0] && strncmp(errcode, linebuf, 3)) ||
00298               (linebuf[3] != ' ' && linebuf[3] != '-'))
00299        {
00300        char   *p;
00301 
00302               clog_msg_start_err();
00303               clog_msg_str("submitclient: bad response from submit: ");
00304               if ((p=strchr(linebuf, '\n')) != 0)       *p=0;
00305               clog_msg_str(linebuf);
00306               if (p) *p='\n';
00307               clog_msg_send();
00308 
00309               if (!errcode[0])
00310                      strcpy(errcode, "432");
00311               if (doprint)
00312               {
00313                      (*printfunc)(errcode);
00314                      (*printfunc)(" ");
00315               }
00316               submit_error=1;
00317               rc=1;
00318        }
00319        else
00320        {
00321               if (!errcode[0])
00322               {
00323                      memcpy(errcode, linebuf, 3);
00324                      errcode[3]=0;
00325               }
00326               rc=0;
00327               if (linebuf[3] == ' ')      rc=1;
00328        }
00329 
00330        if ((c=strchr(linebuf, '\n')) != NULL)    *c=0;
00331 
00332        switch (errcode[0])  {
00333        case '1':
00334        case '2':
00335        case '3':
00336               if (doprint & PRINT_ERRORS)
00337                      doprint=0;
00338               break;
00339        default:
00340               if (prev_errcode == 0 && errcode[0] == '5' &&
00341                   teergrubefunc)
00342               {
00343                      (*teergrubefunc)();
00344               }
00345 
00346               if (log_error_prefix_func)
00347               {
00348                      clog_msg_start_err();
00349                      (*log_error_prefix_func)();
00350                      clog_msg_str(linebuf);
00351                      clog_msg_send();
00352               }
00353        }
00354 
00355        if (doprint)
00356               (*printfunc)(linebuf);
00357 
00358        if (!c)
00359        {
00360        int    ch;
00361        int    n=0;
00362        char   buf[256];
00363 
00364               while ((ch=getc(fromsubmit)) != '\n')
00365               {
00366                      if (ch < 0)
00367                      {
00368                             submit_error=1;
00369                             clog_msg_start_err();
00370                             clog_msg_str("submitclient: EOF from submit.");
00371 #if 0
00372                             {
00373                                    const char *p=getenv("TCPREMOTEIP");
00374 
00375                                    if (p)
00376                                    {
00377                                           clog_msg_str(" (ip: ");
00378                                           clog_msg_str(p);
00379                                           clog_msg_str(")");
00380                                    }
00381                             }
00382 #endif
00383                             clog_msg_send();
00384                             break;
00385                      }
00386                      if (doprint)
00387                      {
00388                             buf[n++]=ch;
00389                             if (n == sizeof(buf)-1)
00390                             {
00391                                    buf[n]=0;
00392                                    (*printfunc)(buf);
00393                                    n=0;
00394                             }
00395                      }
00396               }
00397               if (doprint)
00398               {
00399                      buf[n]=0;
00400                      (*printfunc)(buf);
00401               }
00402        }
00403 
00404        if (doprint)
00405        {
00406               (*printfunc)(doprint & PRINT_CRLF ? "\r\n":"\n");
00407               (*printfunc)(0);
00408        }
00409        return (rc);
00410 }
00411 
00412 /*
00413 ** Repeatedly call read_resp_online until the entire RFC822 response is
00414 ** received from submit.  Echo on standard output if so selected.
00415 ** Returns zero if a succesfull response is received, non-0 in all other
00416 ** situations.
00417 */
00418 
00419 static int readresponse(int doprint)
00420 {
00421 char   errbuf[4];
00422 int    rc;
00423 
00424        errbuf[0]=0;
00425        do
00426        {
00427               rc=read_resp_oneline(errbuf, doprint);
00428        } while (rc == 0);
00429        switch (errbuf[0])   {
00430        case '4':
00431               return (-4);
00432        case '5':
00433               return (-5);
00434        }
00435        return (0);
00436 }
00437 
00438 int submit_readrc()
00439 {
00440        return (readresponse(0));
00441 }
00442 
00443 int submit_readrcprint()
00444 {
00445        return (readresponse(PRINT_RESPONSE));
00446 }
00447 
00448 int submit_readrcprinterr()
00449 {
00450        return (readresponse(PRINT_ERRORS));
00451 }
00452 
00453 int submit_readrcprintcrlf()
00454 {
00455        return (readresponse(PRINT_RESPONSE|PRINT_CRLF));
00456 }
00457 
00458 void submit_write_message(const char *msg)
00459 {
00460        if (submit_error)    return;
00461        for ( ; *msg; msg++)
00462               if (*msg != '\r' && *msg != '\n')
00463                      putc(*msg, submit_to);
00464        putc('\n', submit_to);
00465        fflush(submit_to);
00466        if (ferror(submit_to))      submit_error=1;
00467 }
00468 
00469 static int alarmflag;
00470 
00471 static RETSIGTYPE sigtrap(int signum)
00472 {
00473        alarmflag=1;
00474        signal(SIGALRM, sigtrap);
00475        alarm(1);
00476 #if RETSIGTYPE != void
00477        return (0);
00478 #endif
00479 }
00480 
00481 void submit_cancel_async()
00482 {
00483 char   buf[256];
00484 
00485        kill(submit_pid, SIGTERM);
00486        alarmflag=0;
00487        signal(SIGALRM, sigtrap);
00488        alarm(10);
00489        while (read(fileno(fromsubmit), buf, sizeof(buf)) > 0)
00490               ;
00491        alarm(0);
00492        signal(SIGALRM, SIG_DFL);
00493        if (alarmflag)       kill(submit_pid, SIGKILL);
00494        (void)submit_wait_noreset();
00495 }
00496 
00497 void submit_cancel()
00498 {
00499        submit_cancel_async();
00500        fclose(submit_to);
00501        fclose(fromsubmit);
00502 }