dma: rewrite file management
authorSimon Schubert <corecode@dragonflybsd.org>
Mon, 20 Jul 2009 19:53:32 +0000 (21:53 +0200)
committerSimon Schubert <corecode@dragonflybsd.org>
Tue, 21 Jul 2009 22:13:15 +0000 (00:13 +0200)
Close files as early as possible, possibly re-open them later.  This is
so that we avoid filedesc sharing problems completely and that we won't
run out of fdesc in case of a large queue.

libexec/dma/dma.c
libexec/dma/dma.h
libexec/dma/spool.c

index 448ff37..d9b5d49 100644 (file)
@@ -66,12 +66,11 @@ struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
 struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
 struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
-static int daemonize = 1;
 struct config *config;
-static const char *username;
-static uid_t uid;
-static const char *logident_base;
+const char *username;
 
+static int daemonize = 1;
+static const char *logident_base;
 
 const char *
 hostname(void)
@@ -151,6 +150,7 @@ set_username(void)
 {
        struct passwd *pwd;
        char *u = NULL;
+       uid_t uid;
 
        uid = getuid();
        username = check_username(getlogin(), uid);
@@ -358,26 +358,24 @@ readmail(struct queue *queue, const char *sender, int nodot)
 {
        char line[1000];        /* by RFC2822 */
        size_t linelen;
-       int error;
+       size_t error;
        int had_headers = 0;
        int had_from = 0;
        int had_messagid = 0;
        int had_date = 0;
 
-       error = snprintf(line, sizeof(line),
+       error = fprintf(queue->mailf,
                "Received: from %s (uid %d)\n"
                "\t(envelope-from %s)\n"
                "\tid %s\n"
                "\tby %s (%s)\n"
                "\t%s\n",
-               username, uid,
+               username, getuid(),
                sender,
                queue->id,
                hostname(), VERSION,
                rfc822date());
-       if (error < 0 || (size_t)error >= sizeof(line))
-               return (-1);
-       if (write(queue->mailfd, line, error) != error)
+       if ((ssize_t)error < 0)
                return (-1);
 
        while (!feof(stdin)) {
@@ -411,21 +409,16 @@ readmail(struct queue *queue, const char *sender, int nodot)
                                        had_from = 1;
                                        snprintf(line, sizeof(line), "From: <%s>\n", sender);
                                }
-                               if ((size_t)write(queue->mailfd, line, strlen(line)) != strlen(line))
+                               if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
                                        return (-1);
                        }
                        strcpy(line, "\n");
                }
                if (!nodot && linelen == 2 && line[0] == '.')
                        break;
-               if ((size_t)write(queue->mailfd, line, linelen) != linelen)
+               if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
                        return (-1);
        }
-       if (fsync(queue->mailfd) != 0)
-               return (-1);
-
-       syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
-              username, uid, sender);
 
        return (0);
 }
@@ -450,10 +443,8 @@ go_background(struct queue *queue)
 
        LIST_FOREACH(it, &queue->queue, next) {
                /* No need to fork for the last dest */
-               if (LIST_NEXT(it, next) == NULL) {
-                       setlogident("%s", it->queueid);
-                       return (it);
-               }
+               if (LIST_NEXT(it, next) == NULL)
+                       goto retit;
 
                pid = fork();
                switch (pid) {
@@ -468,7 +459,16 @@ go_background(struct queue *queue)
                         *
                         * return and deliver mail
                         */
+retit:
+                       /*
+                        * If necessary, aquire the queue and * mail files.
+                        * If this fails, we probably were raced by another
+                        * process.
+                        */
                        setlogident("%s", it->queueid);
+                       if (aquirespool(it) < 0)
+                               exit(1);
+                       dropspool(queue, it);
                        return (it);
 
                default:
@@ -501,16 +501,13 @@ bounce(struct qitem *it, const char *reason)
        }
 
        LIST_INIT(&bounceq.queue);
-       if (add_recp(&bounceq, it->sender, "", 1) != 0)
-               goto fail;
        if (newspoolf(&bounceq, "") != 0)
                goto fail;
 
        syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
        setlogident("%s", bounceq.id);
 
-       bit = LIST_FIRST(&bounceq.queue);
-       error = fprintf(bit->mailf,
+       error = fprintf(bounceq.mailf,
                "Received: from MAILER-DAEMON\n"
                "\tid %s\n"
                "\tby %s (%s)\n"
@@ -545,14 +542,15 @@ bounce(struct qitem *it, const char *reason)
                    "Message headers follow.");
        if (error < 0)
                goto fail;
-       if (fflush(bit->mailf) != 0)
+
+       if (add_recp(&bounceq, it->sender, "", 1) != 0)
                goto fail;
 
        if (fseek(it->mailf, it->hdrlen, SEEK_SET) != 0)
                goto fail;
        if (config->features & FULLBOUNCE) {
                while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
-                       if ((size_t)write(bounceq.mailfd, line, pos) != pos)
+                       if (fwrite(line, 1, pos, bounceq.mailf) != pos)
                                goto fail;
                }
        } else {
@@ -561,13 +559,12 @@ bounce(struct qitem *it, const char *reason)
                                break;
                        if (line[0] == '\n')
                                break;
-                       if ((size_t)write(bounceq.mailfd, line, strlen(line)) != strlen(line))
+                       if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
                                goto fail;
                }
        }
-       if (fsync(bounceq.mailfd) != 0)
-               goto fail;
-       if (linkspool(&bounceq) != 0)
+
+       if (linkspool(&bounceq, "MAILER-DAEMON") != 0)
                goto fail;
        /* bounce is safe */
 
@@ -651,6 +648,7 @@ static void
 show_queue(struct queue *queue)
 {
        struct qitem *it;
+       int locked = 0; /* XXX */
 
        if (LIST_EMPTY(&queue->queue)) {
                printf("Mail queue is empty\n");
@@ -663,7 +661,7 @@ show_queue(struct queue *queue)
                       "To\t: %s\n"
                       "--\n",
                       it->queueid,
-                      it->locked ? "*" : "",
+                      locked ? "*" : "",
                       it->sender, it->addr);
        }
 }
@@ -794,13 +792,13 @@ skipopts:
                err(1, "can not read SMTP authentication file");
 
        if (showq) {
-               load_queue(&lqueue, 1);
+               load_queue(&lqueue);
                show_queue(&lqueue);
                return (0);
        }
 
        if (doqueue) {
-               load_queue(&lqueue, 0);
+               load_queue(&lqueue);
                run_queue(&lqueue);
                return (0);
        }
@@ -827,7 +825,7 @@ skipopts:
        if (readmail(&queue, sender, nodot) != 0)
                err(1, "can not read mail");
 
-       if (linkspool(&queue) != 0)
+       if (linkspool(&queue, sender) != 0)
                err(1, "can not create spools");
 
        /* From here on the mail is safe. */
index bbb6e50..75ce1cc 100644 (file)
@@ -94,18 +94,17 @@ struct qitem {
        char *queuefn;
        char *mailfn;
        char *queueid;
+       FILE *queuef;
        FILE *mailf;
-       int queuefd;
        off_t hdrlen;
        int remote;
-       int locked;
 };
 LIST_HEAD(queueh, qitem);
 
 struct queue {
        struct queueh queue;
        char *id;
-       int mailfd;
+       FILE *mailf;
        char *tmpf;
 };
 
@@ -146,6 +145,7 @@ extern struct config *config;
 extern struct strlist tmpfs;
 extern struct virtusers virtusers;
 extern struct authusers authusers;
+extern const char *username;
 
 extern char neterr[BUF_SIZE];
 
@@ -181,9 +181,11 @@ const char *hostname(void);
 
 /* spool.c */
 int newspoolf(struct queue *, const char *);
-int linkspool(struct queue *);
-void load_queue(struct queue *, int);
+int linkspool(struct queue *, const char *);
+void load_queue(struct queue *);
 void delqueue(struct qitem *);
+int aquirespool(struct qitem *);
+void dropspool(struct queue *, struct qitem *);
 
 /* local.c */
 int deliver_local(struct qitem *, const char **errmsg);
index ae3c987..0013a72 100644 (file)
 int
 newspoolf(struct queue *queue, const char *sender)
 {
-       char line[1000];        /* by RFC2822 */
        char fn[PATH_MAX+1];
        struct stat st;
        struct stritem *t;
        struct qitem *it;
-       FILE *mailf;
        off_t hdrlen;
-       int error;
+       int fd;
 
        if (snprintf(fn, sizeof(fn), "%s/%s", config->spooldir, "tmp_XXXXXXXXXX") <= 0)
                return (-1);
 
-       queue->mailfd = mkstemp(fn);
-       if (queue->mailfd < 0)
-               return (-1);
-       if (flock(queue->mailfd, LOCK_EX) == -1)
+       fd = mkstemp(fn);
+       if (fd < 0)
                return (-1);
-
+       if (flock(fd, LOCK_EX) == -1)
+               goto fail;
        queue->tmpf = strdup(fn);
        if (queue->tmpf == NULL)
                goto fail;
 
-       error = snprintf(line, sizeof(line), "%s\n", sender);
-       if (error < 0 || (size_t)error >= sizeof(line)) {
-               errno = E2BIG;
+       /*
+        * Assign queue id
+        */
+       if (fstat(fd, &st) != 0)
                goto fail;
-       }
-       if (write(queue->mailfd, line, error) != error)
+       if (asprintf(&queue->id, "%"PRIxMAX, st.st_ino) < 0)
                goto fail;
 
-       hdrlen = lseek(queue->mailfd, 0, SEEK_CUR);
+       queue->mailf = fdopen(fd, "r+");
+       if (queue->mailf == NULL)
+               goto fail;
 
-       mailf = fdopen(queue->mailfd, "r+");
-       if (mailf == NULL)
+       if (fprintf(queue->mailf, "%s\n", sender) < 0)
                goto fail;
+
+       hdrlen = ftello(queue->mailf);
+
        LIST_FOREACH(it, &queue->queue, next) {
-               it->mailf = mailf;
+               it->mailf = queue->mailf;
                it->hdrlen = hdrlen;
        }
 
-       /*
-        * Assign queue id
-        */
-       if (fstat(queue->mailfd, &st) != 0)
-               return (-1);
-       if (asprintf(&queue->id, "%"PRIxMAX, st.st_ino) < 0)
-               return (-1);
-
        t = malloc(sizeof(*t));
        if (t != NULL) {
                t->str = queue->tmpf;
@@ -122,20 +115,28 @@ newspoolf(struct queue *queue, const char *sender)
        return (0);
 
 fail:
-       close(queue->mailfd);
+       if (queue->mailf != NULL)
+               fclose(queue->mailf);
+       close(fd);
        unlink(fn);
        return (-1);
 }
 
 int
-linkspool(struct queue *queue)
+linkspool(struct queue *queue, const char *sender)
 {
        char line[1000];        /* by RFC2822 */
        struct stat st;
-       int error;
+       size_t error;
        int queuefd;
        struct qitem *it;
 
+       if (fflush(queue->mailf) != 0 || fsync(fileno(queue->mailf)) != 0)
+               goto delfiles;
+
+       syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
+              username, getuid(), sender);
+
        LIST_FOREACH(it, &queue->queue, next) {
                if (asprintf(&it->queueid, "%s.%"PRIxPTR, queue->id, (uintptr_t)it) <= 0)
                        goto delfiles;
@@ -149,16 +150,20 @@ linkspool(struct queue *queue)
                        goto delfiles;
 
                error = snprintf(line, sizeof(line), "%s %s\n", it->queueid, it->addr);
-               if (error < 0 || (size_t)error >= sizeof(line))
+               if ((ssize_t)error < 0 || error >= sizeof(line))
                        goto delfiles;
+
                queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0600);
                if (queuefd == -1)
                        goto delfiles;
-               if (write(queuefd, line, error) != error) {
-                       close(queuefd);
+               it->queuef = fdopen(queuefd, "w+");
+               if (it->queuef == NULL)
+                       goto delfiles;
+
+               if (fwrite(line, strlen(line), 1, it->queuef) != 1)
+                       goto delfiles;
+               if (fflush(it->queuef) != 0 || fsync(fileno(it->queuef)) != 0)
                        goto delfiles;
-               }
-               it->queuefd = queuefd;
 
                if (link(queue->tmpf, it->mailfn) != 0)
                        goto delfiles;
@@ -181,7 +186,7 @@ delfiles:
 }
 
 void
-load_queue(struct queue *queue, int ignorelock)
+load_queue(struct queue *queue)
 {
        struct qitem *it;
        //struct queue queue, itmqueue;
@@ -197,8 +202,6 @@ load_queue(struct queue *queue, int ignorelock)
        char *queuefn;
        char *mailfn;
        off_t hdrlen;
-       int fd;
-       int locked;
 
        LIST_INIT(&queue->queue);
 
@@ -207,13 +210,11 @@ load_queue(struct queue *queue, int ignorelock)
                err(1, "reading queue");
 
        while ((de = readdir(spooldir)) != NULL) {
-               fd = -1;
                sender = NULL;
                queuef = NULL;
                mailf = NULL;
                queueid = NULL;
                queuefn = NULL;
-               locked = 1;
                LIST_INIT(&itmqueue.queue);
 
                /* ignore temp files */
@@ -226,20 +227,6 @@ load_queue(struct queue *queue, int ignorelock)
                if (asprintf(&mailfn, "%s/M%s", config->spooldir, de->d_name + 1) < 0)
                        goto fail;
 
-               fd = open_locked(queuefn, O_RDONLY|O_NONBLOCK);
-               if (ignorelock) {
-                       if (fd < 0)
-                               fd = open(queuefn, O_RDONLY);
-                       else
-                               locked = 0;
-               }
-               if (fd < 0) {
-                       /* Ignore locked files */
-                       if (errno == EWOULDBLOCK)
-                               continue;
-                       goto skip_item;
-               }
-
                mailf = fopen(mailfn, "r");
                if (mailf == NULL)
                        goto skip_item;
@@ -252,7 +239,7 @@ load_queue(struct queue *queue, int ignorelock)
 
                hdrlen = ftell(mailf);
 
-               queuef = fdopen(fd, "r");
+               queuef = fopen(queuefn, "r");
                if (queuef == NULL)
                        goto skip_item;
 
@@ -269,16 +256,18 @@ load_queue(struct queue *queue, int ignorelock)
 
                if (add_recp(&itmqueue, addr, sender, 0) != 0)
                        goto skip_item;
+
                it = LIST_FIRST(&itmqueue.queue);
-               it->queuefd = fd;
-               it->mailf = mailf;
                it->queueid = queueid;
                it->queuefn = queuefn;
                it->mailfn = mailfn;
                it->hdrlen = hdrlen;
-               it->locked = locked;
                LIST_INSERT_HEAD(&queue->queue, it, next);
 
+               if (queuef != NULL)
+                       fclose(queuef);
+               if (mailf != NULL)
+                       fclose(mailf);
                continue;
 
 skip_item:
@@ -295,7 +284,6 @@ skip_item:
                        fclose(queuef);
                if (mailf != NULL)
                        fclose(mailf);
-               close(fd);
        }
        closedir(spooldir);
        return;
@@ -308,8 +296,49 @@ void
 delqueue(struct qitem *it)
 {
        unlink(it->queuefn);
-       close(it->queuefd);
        unlink(it->mailfn);
-       fclose(it->mailf);
+       if (it->queuef != NULL)
+               fclose(it->queuef);
+       if (it->mailf != NULL)
+               fclose(it->mailf);
        free(it);
 }
+
+int
+aquirespool(struct qitem *it)
+{
+       int queuefd;
+
+       if (it->queuef == NULL) {
+               queuefd = open_locked(it->queuefn, O_RDWR);
+               if (queuefd < 0)
+                       return (-1);
+               it->queuef = fdopen(queuefd, "r+");
+               if (it->queuef == NULL)
+                       return (-1);
+       }
+
+       if (it->mailf == NULL) {
+               it->mailf = fopen(it->mailfn, "r");
+               if (it->mailf == NULL)
+                       return (-1);
+       }
+
+       return (0);
+}
+
+void
+dropspool(struct queue *queue, struct qitem *keep)
+{
+       struct qitem *it;
+
+       LIST_FOREACH(it, &queue->queue, next) {
+               if (it == keep)
+                       continue;
+
+               if (it->queuef != NULL)
+                       fclose(it->queuef);
+               if (it->mailf != NULL)
+                       fclose(it->mailf);
+       }
+}