BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / queue.c
index 8afff20..03b226a 100644 (file)
@@ -1,33 +1,53 @@
 /*
 /*
+ * Copyright (c) 1983 Eric P. Allman
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at Berkeley. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission. This software
- * is provided ``as is'' without express or implied warranty.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- *  Sendmail
- *  Copyright (c) 1983  Eric P. Allman
- *  Berkeley, California
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef QUEUE
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef QUEUE
-static char sccsid[] = "@(#)queue.c    5.23 (Berkeley) %G% (with queueing)";
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (with queueing)";
 #else
 #else
-static char sccsid[] = "@(#)queue.c    5.23 (Berkeley) %G% (without queueing)";
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (without queueing)";
 #endif
 #endif /* not lint */
 
 # include <sys/stat.h>
 # include <sys/dir.h>
 #endif
 #endif /* not lint */
 
 # include <sys/stat.h>
 # include <sys/dir.h>
+# include <sys/file.h>
 # include <signal.h>
 # include <errno.h>
 # include <signal.h>
 # include <errno.h>
+# include <pwd.h>
 
 # ifdef QUEUE
 
 
 # ifdef QUEUE
 
@@ -44,6 +64,7 @@ struct work
 };
 
 typedef struct work    WORK;
 };
 
 typedef struct work    WORK;
+extern int la;
 
 WORK   *WorkQ;                 /* queue of things to be done */
 \f/*
 
 WORK   *WorkQ;                 /* queue of things to be done */
 \f/*
@@ -56,42 +77,53 @@ WORK        *WorkQ;                 /* queue of things to be done */
 **             announce -- if TRUE, tell when you are queueing up.
 **
 **     Returns:
 **             announce -- if TRUE, tell when you are queueing up.
 **
 **     Returns:
-**             none.
+**             locked FILE* to q file
 **
 **     Side Effects:
 **             The current request are saved in a control file.
 */
 
 **
 **     Side Effects:
 **             The current request are saved in a control file.
 */
 
+FILE *
 queueup(e, queueall, announce)
        register ENVELOPE *e;
        bool queueall;
        bool announce;
 {
 queueup(e, queueall, announce)
        register ENVELOPE *e;
        bool queueall;
        bool announce;
 {
-       char *tf;
        char *qf;
        char *qf;
-       char buf[MAXLINE];
+       char buf[MAXLINE], tf[MAXLINE];
        register FILE *tfp;
        register HDR *h;
        register ADDRESS *q;
        MAILER nullmailer;
        register FILE *tfp;
        register HDR *h;
        register ADDRESS *q;
        MAILER nullmailer;
+       int fd, ret;
 
        /*
        **  Create control file.
        */
 
 
        /*
        **  Create control file.
        */
 
-       tf = newstr(queuename(e, 't'));
-       tfp = fopen(tf, "w");
-       if (tfp == NULL)
-       {
-               syserr("queueup: cannot create temp file %s", tf);
-               return;
-       }
-       (void) chmod(tf, FileMode);
+       do {
+               strcpy(tf, queuename(e, 't'));
+               fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
+               if (fd < 0) {
+                       if ( errno != EEXIST) {
+                               syserr("queueup: cannot create temp file %s",
+                                       tf);
+                               return NULL;
+                       }
+               } else {
+                       if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
+                               if (errno != EWOULDBLOCK)
+                                       syserr("cannot flock(%s)", tf);
+                               close(fd);
+                               fd = -1;
+                       }
+               }
+       } while (fd < 0);
+
+       tfp = fdopen(fd, "w");
 
 
-# ifdef DEBUG
        if (tTd(40, 1))
                printf("queueing %s\n", e->e_id);
        if (tTd(40, 1))
                printf("queueing %s\n", e->e_id);
-# endif DEBUG
 
        /*
        **  If there is no data file yet, create one.
 
        /*
        **  If there is no data file yet, create one.
@@ -103,14 +135,14 @@ queueup(e, queueall, announce)
                extern putbody();
 
                e->e_df = newstr(queuename(e, 'd'));
                extern putbody();
 
                e->e_df = newstr(queuename(e, 'd'));
-               dfp = fopen(e->e_df, "w");
-               if (dfp == NULL)
+               fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
+               if (fd < 0)
                {
                        syserr("queueup: cannot create %s", e->e_df);
                        (void) fclose(tfp);
                {
                        syserr("queueup: cannot create %s", e->e_df);
                        (void) fclose(tfp);
-                       return;
+                       return NULL;
                }
                }
-               (void) chmod(e->e_df, FileMode);
+               dfp = fdopen(fd, "w");
                (*e->e_putbody)(dfp, ProgMailer, e);
                (void) fclose(dfp);
                e->e_putbody = putbody;
                (*e->e_putbody)(dfp, ProgMailer, e);
                (void) fclose(dfp);
                e->e_putbody = putbody;
@@ -141,9 +173,13 @@ queueup(e, queueall, announce)
        /* output list of recipient addresses */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
        /* output list of recipient addresses */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (queueall ? !bitset(QDONTSEND, q->q_flags) :
+               if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) :
                               bitset(QQUEUEUP, q->q_flags))
                {
                               bitset(QQUEUEUP, q->q_flags))
                {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
                        fprintf(tfp, "R%s\n", q->q_paddr);
                        if (announce)
                        {
                        fprintf(tfp, "R%s\n", q->q_paddr);
                        if (announce)
                        {
@@ -153,13 +189,11 @@ queueup(e, queueall, announce)
                                        logdelivery("queued");
                                e->e_to = NULL;
                        }
                                        logdelivery("queued");
                                e->e_to = NULL;
                        }
-#ifdef DEBUG
                        if (tTd(40, 1))
                        {
                                printf("queueing ");
                                printaddr(q, FALSE);
                        }
                        if (tTd(40, 1))
                        {
                                printf("queueing ");
                                printaddr(q, FALSE);
                        }
-#endif DEBUG
                }
        }
 
                }
        }
 
@@ -167,7 +201,13 @@ queueup(e, queueall, announce)
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND, q->q_flags))
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND, q->q_flags))
+               {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
                        fprintf(tfp, "E%s\n", q->q_paddr);
                        fprintf(tfp, "E%s\n", q->q_paddr);
+               }
        }
 
        /*
        }
 
        /*
@@ -231,21 +271,18 @@ queueup(e, queueall, announce)
        **  Clean up.
        */
 
        **  Clean up.
        */
 
-       (void) fclose(tfp);
        qf = queuename(e, 'q');
        qf = queuename(e, 'q');
-       if (tf != NULL)
-       {
-               (void) unlink(qf);
-               if (rename(tf, qf) < 0)
-                       syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df);
-               errno = 0;
-       }
+       if (rename(tf, qf) < 0)
+               syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
+       errno = 0;
 
 # ifdef LOG
        /* save log info */
        if (LogLevel > 15)
                syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
 # endif LOG
 
 # ifdef LOG
        /* save log info */
        if (LogLevel > 15)
                syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
 # endif LOG
+       fflush(tfp);
+       return tfp;
 }
 \f/*
 **  RUNQUEUE -- run the jobs in the queue.
 }
 \f/*
 **  RUNQUEUE -- run the jobs in the queue.
@@ -275,6 +312,8 @@ runqueue(forkflag)
        **  the queue.
        */
 
        **  the queue.
        */
 
+       la = getla();   /* get load average */
+
        if (shouldqueue(-100000000L))
        {
                if (Verbose)
        if (shouldqueue(-100000000L))
        {
                if (Verbose)
@@ -296,7 +335,7 @@ runqueue(forkflag)
                pid = dofork();
                if (pid != 0)
                {
                pid = dofork();
                if (pid != 0)
                {
-                       extern reapchild();
+                       extern void reapchild();
 
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
 
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
@@ -317,7 +356,7 @@ runqueue(forkflag)
 #endif SIGCHLD
        }
 
 #endif SIGCHLD
        }
 
-       setproctitle("running queue");
+       setproctitle("running queue: %s", QueueDir);
 
 # ifdef LOG
        if (LogLevel > 11)
 
 # ifdef LOG
        if (LogLevel > 11)
@@ -382,15 +421,6 @@ runqueue(forkflag)
 # define NEED_P                001
 # define NEED_T                002
 
 # define NEED_P                001
 # define NEED_T                002
 
-# ifndef DIR
-# define DIR           FILE
-# define direct                dir
-# define opendir(d)    fopen(d, "r")
-# define readdir(f)    ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0)
-static struct dir      dbuf;
-# define closedir(f)   fclose(f)
-# endif DIR
-
 orderq(doall)
        bool doall;
 {
 orderq(doall)
        bool doall;
 {
@@ -431,12 +461,6 @@ orderq(doall)
                char lbuf[MAXNAME];
 
                /* is this an interesting entry? */
                char lbuf[MAXNAME];
 
                /* is this an interesting entry? */
-               if (d->d_ino == 0)
-                       continue;
-# ifdef DEBUG
-               if (tTd(40, 10))
-                       printf("orderq: %12s\n", d->d_name);
-# endif DEBUG
                if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
                        continue;
 
                if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
                        continue;
 
@@ -448,11 +472,9 @@ orderq(doall)
                {
                        /* this may be some random person sending hir msgs */
                        /* syserr("orderq: cannot open %s", cbuf); */
                {
                        /* this may be some random person sending hir msgs */
                        /* syserr("orderq: cannot open %s", cbuf); */
-#ifdef DEBUG
                        if (tTd(41, 2))
                                printf("orderq: cannot open %s (%d)\n",
                                        d->d_name, errno);
                        if (tTd(41, 2))
                                printf("orderq: cannot open %s (%d)\n",
                                        d->d_name, errno);
-#endif DEBUG
                        errno = 0;
                        wn--;
                        continue;
                        errno = 0;
                        wn--;
                        continue;
@@ -516,13 +538,11 @@ orderq(doall)
                WorkQ = w;
        }
 
                WorkQ = w;
        }
 
-# ifdef DEBUG
        if (tTd(40, 1))
        {
                for (w = WorkQ; w != NULL; w = w->w_next)
                        printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
        }
        if (tTd(40, 1))
        {
                for (w = WorkQ; w != NULL; w = w->w_next)
                        printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
        }
-# endif DEBUG
 
        return (wn);
 }
 
        return (wn);
 }
@@ -575,10 +595,8 @@ dowork(w)
        register int i;
        extern bool shouldqueue();
 
        register int i;
        extern bool shouldqueue();
 
-# ifdef DEBUG
        if (tTd(40, 1))
                printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
        if (tTd(40, 1))
                printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
-# endif DEBUG
 
        /*
        **  Ignore jobs that are too expensive for the moment.
 
        /*
        **  Ignore jobs that are too expensive for the moment.
@@ -611,6 +629,7 @@ dowork(w)
 
        if (i == 0)
        {
 
        if (i == 0)
        {
+               FILE *qflock, *readqf();
                /*
                **  CHILD
                **      Lock the control file to avoid duplicate deliveries.
                /*
                **  CHILD
                **      Lock the control file to avoid duplicate deliveries.
@@ -634,25 +653,16 @@ dowork(w)
                /* don't use the headers from sendmail.cf... */
                CurEnv->e_header = NULL;
 
                /* don't use the headers from sendmail.cf... */
                CurEnv->e_header = NULL;
 
-               /* lock the control file during processing */
-               if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
+               /* read the queue control file */
+               /*  and lock the control file during processing */
+               if ((qflock=readqf(CurEnv, TRUE)) == NULL)
                {
                {
-                       /* being processed by another queuer */
-# ifdef LOG
-                       if (LogLevel > 4)
-                               syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
-# endif LOG
                        if (ForkQueueRuns)
                                exit(EX_OK);
                        else
                                return;
                }
 
                        if (ForkQueueRuns)
                                exit(EX_OK);
                        else
                                return;
                }
 
-               /* do basic system initialization */
-               initsys();
-
-               /* read the queue control file */
-               readqf(CurEnv, TRUE);
                CurEnv->e_flags |= EF_INQUEUE;
                eatheader(CurEnv);
 
                CurEnv->e_flags |= EF_INQUEUE;
                eatheader(CurEnv);
 
@@ -665,6 +675,7 @@ dowork(w)
                        finis();
                else
                        dropenvelope(CurEnv);
                        finis();
                else
                        dropenvelope(CurEnv);
+               fclose(qflock);
        }
        else
        {
        }
        else
        {
@@ -685,13 +696,15 @@ dowork(w)
 **                     read in info needed for a queue print.
 **
 **     Returns:
 **                     read in info needed for a queue print.
 **
 **     Returns:
-**             none.
+**             FILE * pointing to flock()ed fd so it can be closed
+**             after the mail is delivered
 **
 **     Side Effects:
 **             cf is read and created as the current job, as though
 **             we had been invoked by argument.
 */
 
 **
 **     Side Effects:
 **             cf is read and created as the current job, as though
 **             we had been invoked by argument.
 */
 
+FILE *
 readqf(e, full)
        register ENVELOPE *e;
        bool full;
 readqf(e, full)
        register ENVELOPE *e;
        bool full;
@@ -701,7 +714,8 @@ readqf(e, full)
        char buf[MAXFIELD];
        extern char *fgetfolded();
        extern long atol();
        char buf[MAXFIELD];
        extern char *fgetfolded();
        extern long atol();
-       extern ADDRESS *sendto();
+       int gotctluser = 0;
+       int fd;
 
        /*
        **  Read and process the file.
 
        /*
        **  Read and process the file.
@@ -711,23 +725,42 @@ readqf(e, full)
        qfp = fopen(qf, "r");
        if (qfp == NULL)
        {
        qfp = fopen(qf, "r");
        if (qfp == NULL)
        {
-               syserr("readqf: no control file %s", qf);
-               return;
+               if (errno != ENOENT)
+                       syserr("readqf: no control file %s", qf);
+               return NULL;
+       }
+
+       if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0)
+       {
+# ifdef LOG
+               /* being processed by another queuer */
+               if (Verbose)
+                       printf("%s: locked\n", CurEnv->e_id);
+# endif LOG
+               (void) fclose(qfp);
+               return NULL;
        }
        }
+
+       /* do basic system initialization */
+       initsys();
+
        FileName = qf;
        LineNumber = 0;
        if (Verbose && full)
                printf("\nRunning %s\n", e->e_id);
        while (fgetfolded(buf, sizeof buf, qfp) != NULL)
        {
        FileName = qf;
        LineNumber = 0;
        if (Verbose && full)
                printf("\nRunning %s\n", e->e_id);
        while (fgetfolded(buf, sizeof buf, qfp) != NULL)
        {
-# ifdef DEBUG
                if (tTd(40, 4))
                        printf("+++++ %s\n", buf);
                if (tTd(40, 4))
                        printf("+++++ %s\n", buf);
-# endif DEBUG
                switch (buf[0])
                {
                switch (buf[0])
                {
+                 case 'C':             /* specify controlling user */
+                       setctluser(&buf[1]);
+                       gotctluser = 1;
+                       break;
+
                  case 'R':             /* specify recipient */
                  case 'R':             /* specify recipient */
-                       (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0);
+                       sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
                        break;
 
                  case 'E':             /* specify error recipient */
                        break;
 
                  case 'E':             /* specify error recipient */
@@ -772,9 +805,25 @@ readqf(e, full)
                                LineNumber, buf);
                        break;
                }
                                LineNumber, buf);
                        break;
                }
+               /*
+               **  The `C' queue file command operates on the next line,
+               **  so we use "gotctluser" to maintain state as follows:
+               **      0 - no controlling user,
+               **      1 - controlling user has been set but not used,
+               **      2 - controlling user must be used on next iteration.
+               */
+               if (gotctluser == 1)
+                       gotctluser++;
+               else if (gotctluser == 2)
+               {
+                       clrctluser();
+                       gotctluser = 0;
+               }
        }
 
        }
 
-       (void) fclose(qfp);
+       /* clear controlling user in case we break out prematurely */
+       clrctluser();
+
        FileName = NULL;
 
        /*
        FileName = NULL;
 
        /*
@@ -787,6 +836,7 @@ readqf(e, full)
                errno = 0;
                e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
        }
                errno = 0;
                e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
        }
+       return qfp;
 }
 \f/*
 **  PRINTQUEUE -- print out a representation of the mail queue
 }
 \f/*
 **  PRINTQUEUE -- print out a representation of the mail queue
@@ -807,6 +857,7 @@ printqueue()
        FILE *f;
        int nrequests;
        char buf[MAXLINE];
        FILE *f;
        int nrequests;
        char buf[MAXLINE];
+       char cbuf[MAXLINE];
 
        /*
        **  Read and order the queue.
 
        /*
        **  Read and order the queue.
@@ -825,6 +876,8 @@ printqueue()
                return;
        }
 
                return;
        }
 
+       la = getla();   /* get load average */
+
        printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
        if (nrequests > QUEUESIZE)
                printf(", only %d printed", QUEUESIZE);
        printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
        if (nrequests > QUEUESIZE)
                printf(", only %d printed", QUEUESIZE);
@@ -837,7 +890,6 @@ printqueue()
                struct stat st;
                auto time_t submittime = 0;
                long dfsize = -1;
                struct stat st;
                auto time_t submittime = 0;
                long dfsize = -1;
-               char lf[20];
                char message[MAXLINE];
                extern bool shouldqueue();
 
                char message[MAXLINE];
                extern bool shouldqueue();
 
@@ -848,9 +900,7 @@ printqueue()
                        continue;
                }
                printf("%7s", w->w_name + 2);
                        continue;
                }
                printf("%7s", w->w_name + 2);
-               (void) strcpy(lf, w->w_name);
-               lf[0] = 'l';
-               if (stat(lf, &st) >= 0)
+               if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0)
                        printf("*");
                else if (shouldqueue(w->w_pri))
                        printf("X");
                        printf("*");
                else if (shouldqueue(w->w_pri))
                        printf("X");
@@ -859,6 +909,7 @@ printqueue()
                errno = 0;
 
                message[0] = '\0';
                errno = 0;
 
                message[0] = '\0';
+               cbuf[0] = '\0';
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        fixcrlf(buf, TRUE);
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        fixcrlf(buf, TRUE);
@@ -879,8 +930,23 @@ printqueue()
                                if (message[0] != '\0')
                                        printf("\n\t\t (%.60s)", message);
                                break;
                                if (message[0] != '\0')
                                        printf("\n\t\t (%.60s)", message);
                                break;
+                         case 'C':     /* controlling user */
+                               if (strlen(buf) < MAXLINE-3)    /* sanity */
+                                       (void) strcat(buf, ") ");
+                               cbuf[0] = cbuf[1] = '(';
+                               (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
+                               cbuf[MAXLINE-1] = '\0';
+                               break;
 
                          case 'R':     /* recipient name */
 
                          case 'R':     /* recipient name */
+                               if (cbuf[0] != '\0') {
+                                       /* prepend controlling user to `buf' */
+                                       (void) strncat(cbuf, &buf[1],
+                                                     MAXLINE-strlen(cbuf));
+                                       cbuf[MAXLINE-1] = '\0';
+                                       (void) strcpy(buf, cbuf);
+                                       cbuf[0] = '\0';
+                               }
                                if (Verbose)
                                        printf("\n\t\t\t\t\t %.38s", &buf[1]);
                                else
                                if (Verbose)
                                        printf("\n\t\t\t\t\t %.38s", &buf[1]);
                                else
@@ -911,9 +977,6 @@ printqueue()
 **     Assigns an id code if one does not already exist.
 **     This code is very careful to avoid trashing existing files
 **     under any circumstances.
 **     Assigns an id code if one does not already exist.
 **     This code is very careful to avoid trashing existing files
 **     under any circumstances.
-**             We first create an nf file that is only used when
-**             assigning an id.  This file is always empty, so that
-**             we can never accidently truncate an lf file.
 **
 **     Parameters:
 **             e -- envelope to build it in/from.
 **
 **     Parameters:
 **             e -- envelope to build it in/from.
@@ -924,7 +987,7 @@ printqueue()
 **             a pointer to the new file name (in a static buffer).
 **
 **     Side Effects:
 **             a pointer to the new file name (in a static buffer).
 **
 **     Side Effects:
-**             Will create the lf and qf files if no id code is
+**             Will create the qf file if no id code is
 **             already assigned.  This will cause the envelope
 **             to be modified.
 */
 **             already assigned.  This will cause the envelope
 **             to be modified.
 */
@@ -942,8 +1005,6 @@ queuename(e, type)
        if (e->e_id == NULL)
        {
                char qf[20];
        if (e->e_id == NULL)
        {
                char qf[20];
-               char nf[20];
-               char lf[20];
 
                /* find a unique id */
                if (pid != getpid())
 
                /* find a unique id */
                if (pid != getpid())
@@ -954,10 +1015,6 @@ queuename(e, type)
                        c2 = 'A' - 1;
                }
                (void) sprintf(qf, "qfAA%05d", pid);
                        c2 = 'A' - 1;
                }
                (void) sprintf(qf, "qfAA%05d", pid);
-               (void) strcpy(lf, qf);
-               lf[0] = 'l';
-               (void) strcpy(nf, qf);
-               nf[0] = 'n';
 
                while (c1 < '~' || c2 < 'Z')
                {
 
                while (c1 < '~' || c2 < 'Z')
                {
@@ -968,35 +1025,22 @@ queuename(e, type)
                                c1++;
                                c2 = 'A' - 1;
                        }
                                c1++;
                                c2 = 'A' - 1;
                        }
-                       lf[2] = nf[2] = qf[2] = c1;
-                       lf[3] = nf[3] = qf[3] = ++c2;
-# ifdef DEBUG
+                       qf[2] = c1;
+                       qf[3] = ++c2;
                        if (tTd(7, 20))
                        if (tTd(7, 20))
-                               printf("queuename: trying \"%s\"\n", nf);
-# endif DEBUG
-
-# ifdef QUEUE
-                       if (access(lf, 0) >= 0 || access(qf, 0) >= 0)
-                               continue;
-                       errno = 0;
-                       i = creat(nf, FileMode);
-                       if (i < 0)
-                       {
-                               (void) unlink(nf);      /* kernel bug */
-                               continue;
-                       }
-                       (void) close(i);
-                       i = link(nf, lf);
-                       (void) unlink(nf);
-                       if (i < 0)
-                               continue;
-                       if (link(lf, qf) >= 0)
+                               printf("queuename: trying \"%s\"\n", qf);
+
+                       i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
+                       if (i < 0) {
+                               if (errno != EEXIST) {
+                                       syserr("queuename: Cannot create \"%s\" in \"%s\"",
+                                               qf, QueueDir);
+                                       exit(EX_UNAVAILABLE);
+                               }
+                       } else {
+                               (void) close(i);
                                break;
                                break;
-                       (void) unlink(lf);
-# else QUEUE
-                       if (close(creat(qf, FileMode)) >= 0)
-                               break;
-# endif QUEUE
+                       }
                }
                if (c1 >= '~' && c2 >= 'Z')
                {
                }
                if (c1 >= '~' && c2 >= 'Z')
                {
@@ -1006,23 +1050,19 @@ queuename(e, type)
                }
                e->e_id = newstr(&qf[2]);
                define('i', e->e_id, e);
                }
                e->e_id = newstr(&qf[2]);
                define('i', e->e_id, e);
-# ifdef DEBUG
                if (tTd(7, 1))
                        printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
 # ifdef LOG
                if (LogLevel > 16)
                        syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
 # endif LOG
                if (tTd(7, 1))
                        printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
 # ifdef LOG
                if (LogLevel > 16)
                        syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
 # endif LOG
-# endif DEBUG
        }
 
        if (type == '\0')
                return (NULL);
        (void) sprintf(buf, "%cf%s", type, e->e_id);
        }
 
        if (type == '\0')
                return (NULL);
        (void) sprintf(buf, "%cf%s", type, e->e_id);
-# ifdef DEBUG
        if (tTd(7, 2))
                printf("queuename: %s\n", buf);
        if (tTd(7, 2))
                printf("queuename: %s\n", buf);
-# endif DEBUG
        return (buf);
 }
 \f/*
        return (buf);
 }
 \f/*
@@ -1042,17 +1082,154 @@ unlockqueue(e)
        ENVELOPE *e;
 {
        /* remove the transcript */
        ENVELOPE *e;
 {
        /* remove the transcript */
-#ifdef DEBUG
 # ifdef LOG
        if (LogLevel > 19)
                syslog(LOG_DEBUG, "%s: unlock", e->e_id);
 # endif LOG
        if (!tTd(51, 4))
 # ifdef LOG
        if (LogLevel > 19)
                syslog(LOG_DEBUG, "%s: unlock", e->e_id);
 # endif LOG
        if (!tTd(51, 4))
-#endif DEBUG
                xunlink(queuename(e, 'x'));
 
                xunlink(queuename(e, 'x'));
 
-# ifdef QUEUE
-       /* last but not least, remove the lock */
-       xunlink(queuename(e, 'l'));
-# endif QUEUE
+}
+\f/*
+**  GETCTLUSER -- return controlling user if mailing to prog or file
+**
+**     Check for a "|" or "/" at the beginning of the address.  If
+**     found, return a controlling username.
+**
+**     Parameters:
+**             a - the address to check out
+**
+**     Returns:
+**             Either NULL, if we werent mailing to a program or file,
+**             or a controlling user name (possibly in getpwuid's
+**             static buffer).
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+getctluser(a)
+       ADDRESS *a;
+{
+       extern ADDRESS *getctladdr();
+       struct passwd *pw;
+       char *retstr;
+
+       /*
+       **  Get unquoted user for file, program or user.name check.
+       **  N.B. remove this code block to always emit controlling
+       **  addresses (at the expense of backward compatibility).
+       */
+
+       {
+               char buf[MAXNAME];
+               (void) strncpy(buf, a->q_paddr, MAXNAME);
+               buf[MAXNAME-1] = '\0';
+               stripquotes(buf, TRUE);
+
+               if (buf[0] != '|' && buf[0] != '/')
+                       return((char *)NULL);
+       }
+
+       a = getctladdr(a);              /* find controlling address */
+
+       if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
+               retstr = pw->pw_name;
+       else                            /* use default user */
+               retstr = DefUser;
+
+       if (tTd(40, 5))
+               printf("Set controlling user for `%s' to `%s'\n",
+                      (a == NULL)? "<null>": a->q_paddr, retstr);
+
+       return(retstr);
+}
+\f/*
+**  SETCTLUSER - sets `CtlUser' to controlling user
+**  CLRCTLUSER - clears controlling user (no params, nothing returned)
+**
+**     These routines manipulate `CtlUser'.
+**
+**     Parameters:
+**             str  - controlling user as passed to setctluser()
+**
+**     Returns:
+**             None.
+**
+**     Side Effects:
+**             `CtlUser' is changed.
+*/
+
+static char CtlUser[MAXNAME];
+
+setctluser(str)
+register char *str;
+{
+       (void) strncpy(CtlUser, str, MAXNAME);
+       CtlUser[MAXNAME-1] = '\0';
+}
+
+clrctluser()
+{
+       CtlUser[0] = '\0';
+}
+
+\f/*
+**  SETCTLADDR -- create a controlling address
+**
+**     If global variable `CtlUser' is set and we are given a valid
+**     address, make that address a controlling address; change the
+**     `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
+**
+**     Parameters:
+**             a - address for which control uid/gid info may apply
+**
+**     Returns:
+**             None.   
+**
+**     Side Effects:
+**             Fills in uid/gid fields in address and sets QGOODUID
+**             flag if appropriate.
+*/
+
+setctladdr(a)
+       ADDRESS *a;
+{
+       struct passwd *pw;
+
+       /*
+       **  If there is no current controlling user, or we were passed a
+       **  NULL addr ptr or we already have a controlling user, return.
+       */
+
+       if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
+               return;
+
+       /*
+       **  Set up addr fields for controlling user.  If `CtlUser' is no
+       **  longer valid, use the default user/group.
+       */
+
+       if ((pw = getpwnam(CtlUser)) != NULL)
+       {
+               if (a->q_home)
+                       free(a->q_home);
+               a->q_home = newstr(pw->pw_dir);
+               a->q_uid = pw->pw_uid;
+               a->q_gid = pw->pw_gid;
+               a->q_ruser = newstr(CtlUser);
+       }
+       else
+       {
+               a->q_uid = DefUid;
+               a->q_gid = DefGid;
+               a->q_ruser = newstr(DefUser);
+       }
+
+       a->q_flags |= QGOODUID;         /* flag as a "ctladdr"  */
+
+       if (tTd(40, 5))
+               printf("Restored controlling user for `%s' to `%s'\n",
+                      a->q_paddr, a->q_ruser);
 }
 }