home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / sendmail / src / queue.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-20  |  25.3 KB  |  1,236 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. All advertising materials mentioning features or use of this software
  15.  *    must display the following acknowledgement:
  16.  *    This product includes software developed by the University of
  17.  *    California, Berkeley and its contributors.
  18.  * 4. Neither the name of the University nor the names of its contributors
  19.  *    may be used to endorse or promote products derived from this software
  20.  *    without specific prior written permission.
  21.  *
  22.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32.  * SUCH DAMAGE.
  33.  */
  34.  
  35. # include "sendmail.h"
  36.  
  37. #ifndef lint
  38. #ifdef QUEUE
  39. static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (with queueing)";
  40. #else
  41. static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (without queueing)";
  42. #endif
  43. #endif /* not lint */
  44.  
  45. # include <sys/stat.h>
  46. # include <sys/dir.h>
  47. # include <sys/file.h>
  48. # include <signal.h>
  49. # include <errno.h>
  50. # include <pwd.h>
  51.  
  52. # ifdef QUEUE
  53.  
  54. /*
  55. **  Work queue.
  56. */
  57.  
  58. struct work
  59. {
  60.     char        *w_name;    /* name of control file */
  61.     long        w_pri;        /* priority of message, see below */
  62.     time_t        w_ctime;    /* creation time of message */
  63.     struct work    *w_next;    /* next in queue */
  64. };
  65.  
  66. typedef struct work    WORK;
  67. extern int la;
  68.  
  69. WORK    *WorkQ;            /* queue of things to be done */
  70. /*
  71. **  QUEUEUP -- queue a message up for future transmission.
  72. **
  73. **    Parameters:
  74. **        e -- the envelope to queue up.
  75. **        queueall -- if TRUE, queue all addresses, rather than
  76. **            just those with the QQUEUEUP flag set.
  77. **        announce -- if TRUE, tell when you are queueing up.
  78. **
  79. **    Returns:
  80. **        locked FILE* to q file
  81. **
  82. **    Side Effects:
  83. **        The current request are saved in a control file.
  84. */
  85.  
  86. FILE *
  87. queueup(e, queueall, announce)
  88.     register ENVELOPE *e;
  89.     bool queueall;
  90.     bool announce;
  91. {
  92.     char *qf;
  93.     char buf[MAXLINE], tf[MAXLINE];
  94.     register FILE *tfp;
  95.     register HDR *h;
  96.     register ADDRESS *q;
  97.     MAILER nullmailer;
  98.     int fd, ret;
  99.  
  100.     /*
  101.     **  Create control file.
  102.     */
  103.  
  104.     do {
  105.         strcpy(tf, queuename(e, 't'));
  106.         fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
  107.         if (fd < 0) {
  108.             if ( errno != EEXIST) {
  109.                 syserr("queueup: cannot create temp file %s",
  110.                     tf);
  111.                 return NULL;
  112.             }
  113.         } else {
  114.             if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
  115.                 if (errno != EWOULDBLOCK)
  116.                     syserr("cannot flock(%s)", tf);
  117.                 close(fd);
  118.                 fd = -1;
  119.             }
  120.         }
  121.     } while (fd < 0);
  122.  
  123.     tfp = fdopen(fd, "w");
  124.  
  125.     if (tTd(40, 1))
  126.         printf("queueing %s\n", e->e_id);
  127.  
  128.     /*
  129.     **  If there is no data file yet, create one.
  130.     */
  131.  
  132.     if (e->e_df == NULL)
  133.     {
  134.         register FILE *dfp;
  135.         extern putbody();
  136.  
  137.         e->e_df = newstr(queuename(e, 'd'));
  138.         fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
  139.         if (fd < 0)
  140.         {
  141.             syserr("queueup: cannot create %s", e->e_df);
  142.             (void) fclose(tfp);
  143.             return NULL;
  144.         }
  145.         dfp = fdopen(fd, "w");
  146.         (*e->e_putbody)(dfp, ProgMailer, e);
  147.         (void) fclose(dfp);
  148.         e->e_putbody = putbody;
  149.     }
  150.  
  151.     /*
  152.     **  Output future work requests.
  153.     **    Priority and creation time should be first, since
  154.     **    they are required by orderq.
  155.     */
  156.  
  157.     /* output message priority */
  158.     fprintf(tfp, "P%ld\n", e->e_msgpriority);
  159.  
  160.     /* output creation time */
  161.     fprintf(tfp, "T%ld\n", e->e_ctime);
  162.  
  163.     /* output name of data file */
  164.     fprintf(tfp, "D%s\n", e->e_df);
  165.  
  166.     /* message from envelope, if it exists */
  167.     if (e->e_message != NULL)
  168.         fprintf(tfp, "M%s\n", e->e_message);
  169.  
  170.     /* output name of sender */
  171.     fprintf(tfp, "S%s\n", e->e_from.q_paddr);
  172.  
  173.     /* output list of recipient addresses */
  174.     for (q = e->e_sendqueue; q != NULL; q = q->q_next)
  175.     {
  176.         if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) :
  177.                    bitset(QQUEUEUP, q->q_flags))
  178.         {
  179.             char *ctluser, *getctluser();
  180.  
  181.             if ((ctluser = getctluser(q)) != NULL)
  182.                 fprintf(tfp, "C%s\n", ctluser);
  183.             fprintf(tfp, "R%s\n", q->q_paddr);
  184.             if (announce)
  185.             {
  186.                 e->e_to = q->q_paddr;
  187.                 message(Arpa_Info, "queued");
  188.                 if (LogLevel > 4)
  189.                     logdelivery("queued");
  190.                 e->e_to = NULL;
  191.             }
  192.             if (tTd(40, 1))
  193.             {
  194.                 printf("queueing ");
  195.                 printaddr(q, FALSE);
  196.             }
  197.         }
  198.     }
  199.  
  200.     /* output list of error recipients */
  201.     for (q = e->e_errorqueue; q != NULL; q = q->q_next)
  202.     {
  203.         if (!bitset(QDONTSEND, q->q_flags))
  204.         {
  205.             char *ctluser, *getctluser();
  206.  
  207.             if ((ctluser = getctluser(q)) != NULL)
  208.                 fprintf(tfp, "C%s\n", ctluser);
  209.             fprintf(tfp, "E%s\n", q->q_paddr);
  210.         }
  211.     }
  212.  
  213.     /*
  214.     **  Output headers for this message.
  215.     **    Expand macros completely here.  Queue run will deal with
  216.     **    everything as absolute headers.
  217.     **        All headers that must be relative to the recipient
  218.     **        can be cracked later.
  219.     **    We set up a "null mailer" -- i.e., a mailer that will have
  220.     **    no effect on the addresses as they are output.
  221.     */
  222.  
  223.     bzero((char *) &nullmailer, sizeof nullmailer);
  224.     nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
  225.     nullmailer.m_eol = "\n";
  226.  
  227.     define('g', "\001f", e);
  228.     for (h = e->e_header; h != NULL; h = h->h_link)
  229.     {
  230.         extern bool bitzerop();
  231.  
  232.         /* don't output null headers */
  233.         if (h->h_value == NULL || h->h_value[0] == '\0')
  234.             continue;
  235.  
  236.         /* don't output resent headers on non-resent messages */
  237.         if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
  238.             continue;
  239.  
  240.         /* output this header */
  241.         fprintf(tfp, "H");
  242.  
  243.         /* if conditional, output the set of conditions */
  244.         if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
  245.         {
  246.             int j;
  247.  
  248.             (void) putc('?', tfp);
  249.             for (j = '\0'; j <= '\177'; j++)
  250.                 if (bitnset(j, h->h_mflags))
  251.                     (void) putc(j, tfp);
  252.             (void) putc('?', tfp);
  253.         }
  254.  
  255.         /* output the header: expand macros, convert addresses */
  256.         if (bitset(H_DEFAULT, h->h_flags))
  257.         {
  258.             (void) expand(h->h_value, buf, &buf[sizeof buf], e);
  259.             fprintf(tfp, "%s: %s\n", h->h_field, buf);
  260.         }
  261.         else if (bitset(H_FROM|H_RCPT, h->h_flags))
  262.         {
  263.             commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
  264.                  &nullmailer);
  265.         }
  266.         else
  267.             fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
  268.     }
  269.  
  270.     /*
  271.     **  Clean up.
  272.     */
  273.  
  274.     qf = queuename(e, 'q');
  275.     if (rename(tf, qf) < 0)
  276.         syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
  277.     errno = 0;
  278.  
  279. # ifdef LOG
  280.     /* save log info */
  281.     if (LogLevel > 15)
  282.         syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
  283. # endif LOG
  284.     fflush(tfp);
  285.     return tfp;
  286. }
  287. /*
  288. **  RUNQUEUE -- run the jobs in the queue.
  289. **
  290. **    Gets the stuff out of the queue in some presumably logical
  291. **    order and processes them.
  292. **
  293. **    Parameters:
  294. **        forkflag -- TRUE if the queue scanning should be done in
  295. **            a child process.  We double-fork so it is not our
  296. **            child and we don't have to clean up after it.
  297. **
  298. **    Returns:
  299. **        none.
  300. **
  301. **    Side Effects:
  302. **        runs things in the mail queue.
  303. */
  304.  
  305. runqueue(forkflag)
  306.     bool forkflag;
  307. {
  308.     extern bool shouldqueue();
  309.  
  310.     /*
  311.     **  If no work will ever be selected, don't even bother reading
  312.     **  the queue.
  313.     */
  314.  
  315.     la = getla();    /* get load average */
  316.  
  317.     if (shouldqueue(-100000000L))
  318.     {
  319.         if (Verbose)
  320.             printf("Skipping queue run -- load average too high\n");
  321.  
  322.         if (forkflag)
  323.             return;
  324.         finis();
  325.     }
  326.  
  327.     /*
  328.     **  See if we want to go off and do other useful work.
  329.     */
  330.  
  331.     if (forkflag)
  332.     {
  333.         int pid;
  334.  
  335.         pid = dofork();
  336.         if (pid != 0)
  337.         {
  338.             extern void reapchild();
  339.  
  340.             /* parent -- pick up intermediate zombie */
  341. #ifndef SIGCHLD
  342.             (void) waitfor(pid);
  343. #else SIGCHLD
  344.             (void) signal(SIGCHLD, reapchild);
  345. #endif SIGCHLD
  346.             if (QueueIntvl != 0)
  347.                 (void) setevent(QueueIntvl, runqueue, TRUE);
  348.             return;
  349.         }
  350.         /* child -- double fork */
  351. #ifndef SIGCHLD
  352.         if (fork() != 0)
  353.             exit(EX_OK);
  354. #else SIGCHLD
  355.         (void) signal(SIGCHLD, SIG_DFL);
  356. #endif SIGCHLD
  357.     }
  358.  
  359.     setproctitle("running queue: %s", QueueDir);
  360.  
  361. # ifdef LOG
  362.     if (LogLevel > 11)
  363.         syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
  364. # endif LOG
  365.  
  366.     /*
  367.     **  Release any resources used by the daemon code.
  368.     */
  369.  
  370. # ifdef DAEMON
  371.     clrdaemon();
  372. # endif DAEMON
  373.  
  374.     /*
  375.     **  Make sure the alias database is open.
  376.     */
  377.  
  378.     initaliases(AliasFile, FALSE);
  379.  
  380.     /*
  381.     **  Start making passes through the queue.
  382.     **    First, read and sort the entire queue.
  383.     **    Then, process the work in that order.
  384.     **        But if you take too long, start over.
  385.     */
  386.  
  387.     /* order the existing work requests */
  388.     (void) orderq(FALSE);
  389.  
  390.     /* process them once at a time */
  391.     while (WorkQ != NULL)
  392.     {
  393.         WORK *w = WorkQ;
  394.  
  395.         WorkQ = WorkQ->w_next;
  396.         dowork(w);
  397.         free(w->w_name);
  398.         free((char *) w);
  399.     }
  400.  
  401.     /* exit without the usual cleanup */
  402.     exit(ExitStat);
  403. }
  404. /*
  405. **  ORDERQ -- order the work queue.
  406. **
  407. **    Parameters:
  408. **        doall -- if set, include everything in the queue (even
  409. **            the jobs that cannot be run because the load
  410. **            average is too high).  Otherwise, exclude those
  411. **            jobs.
  412. **
  413. **    Returns:
  414. **        The number of request in the queue (not necessarily
  415. **        the number of requests in WorkQ however).
  416. **
  417. **    Side Effects:
  418. **        Sets WorkQ to the queue of available work, in order.
  419. */
  420.  
  421. # define NEED_P        001
  422. # define NEED_T        002
  423.  
  424. orderq(doall)
  425.     bool doall;
  426. {
  427.     register struct direct *d;
  428.     register WORK *w;
  429.     DIR *f;
  430.     register int i;
  431.     WORK wlist[QUEUESIZE+1];
  432.     int wn = -1;
  433.     extern workcmpf();
  434.  
  435.     /* clear out old WorkQ */
  436.     for (w = WorkQ; w != NULL; )
  437.     {
  438.         register WORK *nw = w->w_next;
  439.  
  440.         WorkQ = nw;
  441.         free(w->w_name);
  442.         free((char *) w);
  443.         w = nw;
  444.     }
  445.  
  446.     /* open the queue directory */
  447.     f = opendir(".");
  448.     if (f == NULL)
  449.     {
  450.         syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
  451.         return (0);
  452.     }
  453.  
  454.     /*
  455.     **  Read the work directory.
  456.     */
  457.  
  458.     while ((d = readdir(f)) != NULL)
  459.     {
  460.         FILE *cf;
  461.         char lbuf[MAXNAME];
  462.  
  463.         /* is this an interesting entry? */
  464.         if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
  465.             continue;
  466.  
  467.         /* yes -- open control file (if not too many files) */
  468.         if (++wn >= QUEUESIZE)
  469.             continue;
  470.         cf = fopen(d->d_name, "r");
  471.         if (cf == NULL)
  472.         {
  473.             /* this may be some random person sending hir msgs */
  474.             /* syserr("orderq: cannot open %s", cbuf); */
  475.             if (tTd(41, 2))
  476.                 printf("orderq: cannot open %s (%d)\n",
  477.                     d->d_name, errno);
  478.             errno = 0;
  479.             wn--;
  480.             continue;
  481.         }
  482.         w = &wlist[wn];
  483.         w->w_name = newstr(d->d_name);
  484.  
  485.         /* make sure jobs in creation don't clog queue */
  486.         w->w_pri = 0x7fffffff;
  487.         w->w_ctime = 0;
  488.  
  489.         /* extract useful information */
  490.         i = NEED_P | NEED_T;
  491.         while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
  492.         {
  493.             extern long atol();
  494.  
  495.             switch (lbuf[0])
  496.             {
  497.               case 'P':
  498.                 w->w_pri = atol(&lbuf[1]);
  499.                 i &= ~NEED_P;
  500.                 break;
  501.  
  502.               case 'T':
  503.                 w->w_ctime = atol(&lbuf[1]);
  504.                 i &= ~NEED_T;
  505.                 break;
  506.             }
  507.         }
  508.         (void) fclose(cf);
  509.  
  510.         if (!doall && shouldqueue(w->w_pri))
  511.         {
  512.             /* don't even bother sorting this job in */
  513.             wn--;
  514.         }
  515.     }
  516.     (void) closedir(f);
  517.     wn++;
  518.  
  519.     /*
  520.     **  Sort the work directory.
  521.     */
  522.  
  523.     qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
  524.  
  525.     /*
  526.     **  Convert the work list into canonical form.
  527.     **    Should be turning it into a list of envelopes here perhaps.
  528.     */
  529.  
  530.     WorkQ = NULL;
  531.     for (i = min(wn, QUEUESIZE); --i >= 0; )
  532.     {
  533.         w = (WORK *) xalloc(sizeof *w);
  534.         w->w_name = wlist[i].w_name;
  535.         w->w_pri = wlist[i].w_pri;
  536.         w->w_ctime = wlist[i].w_ctime;
  537.         w->w_next = WorkQ;
  538.         WorkQ = w;
  539.     }
  540.  
  541.     if (tTd(40, 1))
  542.     {
  543.         for (w = WorkQ; w != NULL; w = w->w_next)
  544.             printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
  545.     }
  546.  
  547.     return (wn);
  548. }
  549. /*
  550. **  WORKCMPF -- compare function for ordering work.
  551. **
  552. **    Parameters:
  553. **        a -- the first argument.
  554. **        b -- the second argument.
  555. **
  556. **    Returns:
  557. **        -1 if a < b
  558. **         0 if a == b
  559. **        +1 if a > b
  560. **
  561. **    Side Effects:
  562. **        none.
  563. */
  564.  
  565. workcmpf(a, b)
  566.     register WORK *a;
  567.     register WORK *b;
  568. {
  569.     long pa = a->w_pri + a->w_ctime;
  570.     long pb = b->w_pri + b->w_ctime;
  571.  
  572.     if (pa == pb)
  573.         return (0);
  574.     else if (pa > pb)
  575.         return (1);
  576.     else
  577.         return (-1);
  578. }
  579. /*
  580. **  DOWORK -- do a work request.
  581. **
  582. **    Parameters:
  583. **        w -- the work request to be satisfied.
  584. **
  585. **    Returns:
  586. **        none.
  587. **
  588. **    Side Effects:
  589. **        The work request is satisfied if possible.
  590. */
  591.  
  592. dowork(w)
  593.     register WORK *w;
  594. {
  595.     register int i;
  596.     extern bool shouldqueue();
  597.  
  598.     if (tTd(40, 1))
  599.         printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
  600.  
  601.     /*
  602.     **  Ignore jobs that are too expensive for the moment.
  603.     */
  604.  
  605.     if (shouldqueue(w->w_pri))
  606.     {
  607.         if (Verbose)
  608.             printf("\nSkipping %s\n", w->w_name + 2);
  609.         return;
  610.     }
  611.  
  612.     /*
  613.     **  Fork for work.
  614.     */
  615.  
  616.     if (ForkQueueRuns)
  617.     {
  618.         i = fork();
  619.         if (i < 0)
  620.         {
  621.             syserr("dowork: cannot fork");
  622.             return;
  623.         }
  624.     }
  625.     else
  626.     {
  627.         i = 0;
  628.     }
  629.  
  630.     if (i == 0)
  631.     {
  632.         FILE *qflock, *readqf();
  633.         /*
  634.         **  CHILD
  635.         **    Lock the control file to avoid duplicate deliveries.
  636.         **        Then run the file as though we had just read it.
  637.         **    We save an idea of the temporary name so we
  638.         **        can recover on interrupt.
  639.         */
  640.  
  641.         /* set basic modes, etc. */
  642.         (void) alarm(0);
  643.         clearenvelope(CurEnv, FALSE);
  644.         QueueRun = TRUE;
  645.         ErrorMode = EM_MAIL;
  646.         CurEnv->e_id = &w->w_name[2];
  647. # ifdef LOG
  648.         if (LogLevel > 11)
  649.             syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
  650.                    getpid());
  651. # endif LOG
  652.  
  653.         /* don't use the headers from sendmail.cf... */
  654.         CurEnv->e_header = NULL;
  655.  
  656.         /* read the queue control file */
  657.         /*  and lock the control file during processing */
  658.         if ((qflock=readqf(CurEnv, TRUE)) == NULL)
  659.         {
  660.             if (ForkQueueRuns)
  661.                 exit(EX_OK);
  662.             else
  663.                 return;
  664.         }
  665.  
  666.         CurEnv->e_flags |= EF_INQUEUE;
  667.         eatheader(CurEnv);
  668.  
  669.         /* do the delivery */
  670.         if (!bitset(EF_FATALERRS, CurEnv->e_flags))
  671.             sendall(CurEnv, SM_DELIVER);
  672.  
  673.         /* finish up and exit */
  674.         if (ForkQueueRuns)
  675.             finis();
  676.         else
  677.             dropenvelope(CurEnv);
  678.         fclose(qflock);
  679.     }
  680.     else
  681.     {
  682.         /*
  683.         **  Parent -- pick up results.
  684.         */
  685.  
  686.         errno = 0;
  687.         (void) waitfor(i);
  688.     }
  689. }
  690. /*
  691. **  READQF -- read queue file and set up environment.
  692. **
  693. **    Parameters:
  694. **        e -- the envelope of the job to run.
  695. **        full -- if set, read in all information.  Otherwise just
  696. **            read in info needed for a queue print.
  697. **
  698. **    Returns:
  699. **        FILE * pointing to flock()ed fd so it can be closed
  700. **        after the mail is delivered
  701. **
  702. **    Side Effects:
  703. **        cf is read and created as the current job, as though
  704. **        we had been invoked by argument.
  705. */
  706.  
  707. FILE *
  708. readqf(e, full)
  709.     register ENVELOPE *e;
  710.     bool full;
  711. {
  712.     char *qf;
  713.     register FILE *qfp;
  714.     char buf[MAXFIELD];
  715.     extern char *fgetfolded();
  716.     extern long atol();
  717.     int gotctluser = 0;
  718.     int fd;
  719.  
  720.     /*
  721.     **  Read and process the file.
  722.     */
  723.  
  724.     qf = queuename(e, 'q');
  725.     qfp = fopen(qf, "r");
  726.     if (qfp == NULL)
  727.     {
  728.         if (errno != ENOENT)
  729.             syserr("readqf: no control file %s", qf);
  730.         return NULL;
  731.     }
  732.  
  733.     if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0)
  734.     {
  735. # ifdef LOG
  736.         /* being processed by another queuer */
  737.         if (Verbose)
  738.             printf("%s: locked\n", CurEnv->e_id);
  739. # endif LOG
  740.         (void) fclose(qfp);
  741.         return NULL;
  742.     }
  743.  
  744.     /* do basic system initialization */
  745.     initsys();
  746.  
  747.     FileName = qf;
  748.     LineNumber = 0;
  749.     if (Verbose && full)
  750.         printf("\nRunning %s\n", e->e_id);
  751.     while (fgetfolded(buf, sizeof buf, qfp) != NULL)
  752.     {
  753.         if (tTd(40, 4))
  754.             printf("+++++ %s\n", buf);
  755.         switch (buf[0])
  756.         {
  757.           case 'C':        /* specify controlling user */
  758.             setctluser(&buf[1]);
  759.             gotctluser = 1;
  760.             break;
  761.  
  762.           case 'R':        /* specify recipient */
  763.             sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
  764.             break;
  765.  
  766.           case 'E':        /* specify error recipient */
  767.             sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue);
  768.             break;
  769.  
  770.           case 'H':        /* header */
  771.             if (full)
  772.                 (void) chompheader(&buf[1], FALSE);
  773.             break;
  774.  
  775.           case 'M':        /* message */
  776.             e->e_message = newstr(&buf[1]);
  777.             break;
  778.  
  779.           case 'S':        /* sender */
  780.             setsender(newstr(&buf[1]));
  781.             break;
  782.  
  783.           case 'D':        /* data file name */
  784.             if (!full)
  785.                 break;
  786.             e->e_df = newstr(&buf[1]);
  787.             e->e_dfp = fopen(e->e_df, "r");
  788.             if (e->e_dfp == NULL)
  789.                 syserr("readqf: cannot open %s", e->e_df);
  790.             break;
  791.  
  792.           case 'T':        /* init time */
  793.             e->e_ctime = atol(&buf[1]);
  794.             break;
  795.  
  796.           case 'P':        /* message priority */
  797.             e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
  798.             break;
  799.  
  800.           case '\0':        /* blank line; ignore */
  801.             break;
  802.  
  803.           default:
  804.             syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
  805.                 LineNumber, buf);
  806.             break;
  807.         }
  808.         /*
  809.         **  The `C' queue file command operates on the next line,
  810.         **  so we use "gotctluser" to maintain state as follows:
  811.         **      0 - no controlling user,
  812.         **      1 - controlling user has been set but not used,
  813.         **      2 - controlling user must be used on next iteration.
  814.         */
  815.         if (gotctluser == 1)
  816.             gotctluser++;
  817.         else if (gotctluser == 2)
  818.         {
  819.             clrctluser();
  820.             gotctluser = 0;
  821.         }
  822.     }
  823.  
  824.     /* clear controlling user in case we break out prematurely */
  825.     clrctluser();
  826.  
  827.     FileName = NULL;
  828.  
  829.     /*
  830.     **  If we haven't read any lines, this queue file is empty.
  831.     **  Arrange to remove it without referencing any null pointers.
  832.     */
  833.  
  834.     if (LineNumber == 0)
  835.     {
  836.         errno = 0;
  837.         e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
  838.     }
  839.     return qfp;
  840. }
  841. /*
  842. **  PRINTQUEUE -- print out a representation of the mail queue
  843. **
  844. **    Parameters:
  845. **        none.
  846. **
  847. **    Returns:
  848. **        none.
  849. **
  850. **    Side Effects:
  851. **        Prints a listing of the mail queue on the standard output.
  852. */
  853.  
  854. printqueue()
  855. {
  856.     register WORK *w;
  857.     FILE *f;
  858.     int nrequests;
  859.     char buf[MAXLINE];
  860.     char cbuf[MAXLINE];
  861.  
  862.     /*
  863.     **  Read and order the queue.
  864.     */
  865.  
  866.     nrequests = orderq(TRUE);
  867.  
  868.     /*
  869.     **  Print the work list that we have read.
  870.     */
  871.  
  872.     /* first see if there is anything */
  873.     if (nrequests <= 0)
  874.     {
  875.         printf("Mail queue is empty\n");
  876.         return;
  877.     }
  878.  
  879.     la = getla();    /* get load average */
  880.  
  881.     printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
  882.     if (nrequests > QUEUESIZE)
  883.         printf(", only %d printed", QUEUESIZE);
  884.     if (Verbose)
  885.         printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
  886.     else
  887.         printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
  888.     for (w = WorkQ; w != NULL; w = w->w_next)
  889.     {
  890.         struct stat st;
  891.         auto time_t submittime = 0;
  892.         long dfsize = -1;
  893.         char message[MAXLINE];
  894.         extern bool shouldqueue();
  895.  
  896.         f = fopen(w->w_name, "r");
  897.         if (f == NULL)
  898.         {
  899.             errno = 0;
  900.             continue;
  901.         }
  902.         printf("%7s", w->w_name + 2);
  903.         if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0)
  904.             printf("*");
  905.         else if (shouldqueue(w->w_pri))
  906.             printf("X");
  907.         else
  908.             printf(" ");
  909.         errno = 0;
  910.  
  911.         message[0] = '\0';
  912.         cbuf[0] = '\0';
  913.         while (fgets(buf, sizeof buf, f) != NULL)
  914.         {
  915.             fixcrlf(buf, TRUE);
  916.             switch (buf[0])
  917.             {
  918.               case 'M':    /* error message */
  919.                 (void) strcpy(message, &buf[1]);
  920.                 break;
  921.  
  922.               case 'S':    /* sender name */
  923.                 if (Verbose)
  924.                     printf("%8ld %10ld %.12s %.38s", dfsize,
  925.                         w->w_pri, ctime(&submittime) + 4,
  926.                         &buf[1]);
  927.                 else
  928.                     printf("%8ld %.16s %.45s", dfsize,
  929.                         ctime(&submittime), &buf[1]);
  930.                 if (message[0] != '\0')
  931.                     printf("\n\t\t (%.60s)", message);
  932.                 break;
  933.               case 'C':    /* controlling user */
  934.                 if (strlen(buf) < MAXLINE-3)    /* sanity */
  935.                     (void) strcat(buf, ") ");
  936.                 cbuf[0] = cbuf[1] = '(';
  937.                 (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
  938.                 cbuf[MAXLINE-1] = '\0';
  939.                 break;
  940.  
  941.               case 'R':    /* recipient name */
  942.                 if (cbuf[0] != '\0') {
  943.                     /* prepend controlling user to `buf' */
  944.                     (void) strncat(cbuf, &buf[1],
  945.                                   MAXLINE-strlen(cbuf));
  946.                     cbuf[MAXLINE-1] = '\0';
  947.                     (void) strcpy(buf, cbuf);
  948.                     cbuf[0] = '\0';
  949.                 }
  950.                 if (Verbose)
  951.                     printf("\n\t\t\t\t\t %.38s", &buf[1]);
  952.                 else
  953.                     printf("\n\t\t\t\t  %.45s", &buf[1]);
  954.                 break;
  955.  
  956.               case 'T':    /* creation time */
  957.                 submittime = atol(&buf[1]);
  958.                 break;
  959.  
  960.               case 'D':    /* data file name */
  961.                 if (stat(&buf[1], &st) >= 0)
  962.                     dfsize = st.st_size;
  963.                 break;
  964.             }
  965.         }
  966.         if (submittime == (time_t) 0)
  967.             printf(" (no control file)");
  968.         printf("\n");
  969.         (void) fclose(f);
  970.     }
  971. }
  972.  
  973. # endif QUEUE
  974. /*
  975. **  QUEUENAME -- build a file name in the queue directory for this envelope.
  976. **
  977. **    Assigns an id code if one does not already exist.
  978. **    This code is very careful to avoid trashing existing files
  979. **    under any circumstances.
  980. **
  981. **    Parameters:
  982. **        e -- envelope to build it in/from.
  983. **        type -- the file type, used as the first character
  984. **            of the file name.
  985. **
  986. **    Returns:
  987. **        a pointer to the new file name (in a static buffer).
  988. **
  989. **    Side Effects:
  990. **        Will create the qf file if no id code is
  991. **        already assigned.  This will cause the envelope
  992. **        to be modified.
  993. */
  994.  
  995. char *
  996. queuename(e, type)
  997.     register ENVELOPE *e;
  998.     char type;
  999. {
  1000.     static char buf[MAXNAME];
  1001.     static int pid = -1;
  1002.     char c1 = 'A';
  1003.     char c2 = 'A';
  1004.  
  1005.     if (e->e_id == NULL)
  1006.     {
  1007.         char qf[20];
  1008.  
  1009.         /* find a unique id */
  1010.         if (pid != getpid())
  1011.         {
  1012.             /* new process -- start back at "AA" */
  1013.             pid = getpid();
  1014.             c1 = 'A';
  1015.             c2 = 'A' - 1;
  1016.         }
  1017.         (void) sprintf(qf, "qfAA%05d", pid);
  1018.  
  1019.         while (c1 < '~' || c2 < 'Z')
  1020.         {
  1021.             int i;
  1022.  
  1023.             if (c2 >= 'Z')
  1024.             {
  1025.                 c1++;
  1026.                 c2 = 'A' - 1;
  1027.             }
  1028.             qf[2] = c1;
  1029.             qf[3] = ++c2;
  1030.             if (tTd(7, 20))
  1031.                 printf("queuename: trying \"%s\"\n", qf);
  1032.  
  1033.             i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
  1034.             if (i < 0) {
  1035.                 if (errno != EEXIST) {
  1036.                     syserr("queuename: Cannot create \"%s\" in \"%s\"",
  1037.                         qf, QueueDir);
  1038.                     exit(EX_UNAVAILABLE);
  1039.                 }
  1040.             } else {
  1041.                 (void) close(i);
  1042.                 break;
  1043.             }
  1044.         }
  1045.         if (c1 >= '~' && c2 >= 'Z')
  1046.         {
  1047.             syserr("queuename: Cannot create \"%s\" in \"%s\"",
  1048.                 qf, QueueDir);
  1049.             exit(EX_OSERR);
  1050.         }
  1051.         e->e_id = newstr(&qf[2]);
  1052.         define('i', e->e_id, e);
  1053.         if (tTd(7, 1))
  1054.             printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
  1055. # ifdef LOG
  1056.         if (LogLevel > 16)
  1057.             syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
  1058. # endif LOG
  1059.     }
  1060.  
  1061.     if (type == '\0')
  1062.         return (NULL);
  1063.     (void) sprintf(buf, "%cf%s", type, e->e_id);
  1064.     if (tTd(7, 2))
  1065.         printf("queuename: %s\n", buf);
  1066.     return (buf);
  1067. }
  1068. /*
  1069. **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
  1070. **
  1071. **    Parameters:
  1072. **        e -- the envelope to unlock.
  1073. **
  1074. **    Returns:
  1075. **        none
  1076. **
  1077. **    Side Effects:
  1078. **        unlocks the queue for `e'.
  1079. */
  1080.  
  1081. unlockqueue(e)
  1082.     ENVELOPE *e;
  1083. {
  1084.     /* remove the transcript */
  1085. # ifdef LOG
  1086.     if (LogLevel > 19)
  1087.         syslog(LOG_DEBUG, "%s: unlock", e->e_id);
  1088. # endif LOG
  1089.     if (!tTd(51, 4))
  1090.         xunlink(queuename(e, 'x'));
  1091.  
  1092. }
  1093. /*
  1094. **  GETCTLUSER -- return controlling user if mailing to prog or file
  1095. **
  1096. **    Check for a "|" or "/" at the beginning of the address.  If
  1097. **    found, return a controlling username.
  1098. **
  1099. **    Parameters:
  1100. **        a - the address to check out
  1101. **
  1102. **    Returns:
  1103. **        Either NULL, if we werent mailing to a program or file,
  1104. **        or a controlling user name (possibly in getpwuid's
  1105. **        static buffer).
  1106. **
  1107. **    Side Effects:
  1108. **        none.
  1109. */
  1110.  
  1111. char *
  1112. getctluser(a)
  1113.     ADDRESS *a;
  1114. {
  1115.     extern ADDRESS *getctladdr();
  1116.     struct passwd *pw;
  1117.     char *retstr;
  1118.  
  1119.     /*
  1120.     **  Get unquoted user for file, program or user.name check.
  1121.     **  N.B. remove this code block to always emit controlling
  1122.     **  addresses (at the expense of backward compatibility).
  1123.     */
  1124.  
  1125.     {
  1126.         char buf[MAXNAME];
  1127.         (void) strncpy(buf, a->q_paddr, MAXNAME);
  1128.         buf[MAXNAME-1] = '\0';
  1129.         stripquotes(buf, TRUE);
  1130.  
  1131.         if (buf[0] != '|' && buf[0] != '/')
  1132.             return((char *)NULL);
  1133.     }
  1134.  
  1135.     a = getctladdr(a);        /* find controlling address */
  1136.  
  1137.     if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
  1138.         retstr = pw->pw_name;
  1139.     else                /* use default user */
  1140.         retstr = DefUser;
  1141.  
  1142.     if (tTd(40, 5))
  1143.         printf("Set controlling user for `%s' to `%s'\n",
  1144.                (a == NULL)? "<null>": a->q_paddr, retstr);
  1145.  
  1146.     return(retstr);
  1147. }
  1148. /*
  1149. **  SETCTLUSER - sets `CtlUser' to controlling user
  1150. **  CLRCTLUSER - clears controlling user (no params, nothing returned)
  1151. **
  1152. **    These routines manipulate `CtlUser'.
  1153. **
  1154. **    Parameters:
  1155. **        str  - controlling user as passed to setctluser()
  1156. **
  1157. **    Returns:
  1158. **        None.
  1159. **
  1160. **    Side Effects:
  1161. **        `CtlUser' is changed.
  1162. */
  1163.  
  1164. static char CtlUser[MAXNAME];
  1165.  
  1166. setctluser(str)
  1167. register char *str;
  1168. {
  1169.     (void) strncpy(CtlUser, str, MAXNAME);
  1170.     CtlUser[MAXNAME-1] = '\0';
  1171. }
  1172.  
  1173. clrctluser()
  1174. {
  1175.     CtlUser[0] = '\0';
  1176. }
  1177.  
  1178. /*
  1179. **  SETCTLADDR -- create a controlling address
  1180. **
  1181. **    If global variable `CtlUser' is set and we are given a valid
  1182. **    address, make that address a controlling address; change the
  1183. **    `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
  1184. **
  1185. **    Parameters:
  1186. **        a - address for which control uid/gid info may apply
  1187. **
  1188. **    Returns:
  1189. **        None.    
  1190. **
  1191. **    Side Effects:
  1192. **        Fills in uid/gid fields in address and sets QGOODUID
  1193. **        flag if appropriate.
  1194. */
  1195.  
  1196. setctladdr(a)
  1197.     ADDRESS *a;
  1198. {
  1199.     struct passwd *pw;
  1200.  
  1201.     /*
  1202.     **  If there is no current controlling user, or we were passed a
  1203.     **  NULL addr ptr or we already have a controlling user, return.
  1204.     */
  1205.  
  1206.     if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
  1207.         return;
  1208.  
  1209.     /*
  1210.     **  Set up addr fields for controlling user.  If `CtlUser' is no
  1211.     **  longer valid, use the default user/group.
  1212.     */
  1213.  
  1214.     if ((pw = getpwnam(CtlUser)) != NULL)
  1215.     {
  1216.         if (a->q_home)
  1217.             free(a->q_home);
  1218.         a->q_home = newstr(pw->pw_dir);
  1219.         a->q_uid = pw->pw_uid;
  1220.         a->q_gid = pw->pw_gid;
  1221.         a->q_ruser = newstr(CtlUser);
  1222.     }
  1223.     else
  1224.     {
  1225.         a->q_uid = DefUid;
  1226.         a->q_gid = DefGid;
  1227.         a->q_ruser = newstr(DefUser);
  1228.     }
  1229.  
  1230.     a->q_flags |= QGOODUID;        /* flag as a "ctladdr"  */
  1231.  
  1232.     if (tTd(40, 5))
  1233.         printf("Restored controlling user for `%s' to `%s'\n",
  1234.                a->q_paddr, a->q_ruser);
  1235. }
  1236.