From 9afa363f46406be3ba45faebf414b649ecfa02b8 Mon Sep 17 00:00:00 2001 From: Simon Schubert Date: Mon, 20 Jul 2009 21:53:32 +0200 Subject: [PATCH] dma: rewrite file management 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 | 68 ++++++++++---------- libexec/dma/dma.h | 12 ++-- libexec/dma/spool.c | 147 ++++++++++++++++++++++++++------------------ 3 files changed, 128 insertions(+), 99 deletions(-) diff --git a/libexec/dma/dma.c b/libexec/dma/dma.c index 448ff3730d..d9b5d49a07 100644 --- a/libexec/dma/dma.c +++ b/libexec/dma/dma.c @@ -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. */ diff --git a/libexec/dma/dma.h b/libexec/dma/dma.h index bbb6e50ec2..75ce1cccaf 100644 --- a/libexec/dma/dma.h +++ b/libexec/dma/dma.h @@ -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); diff --git a/libexec/dma/spool.c b/libexec/dma/spool.c index ae3c9879a3..0013a72b14 100644 --- a/libexec/dma/spool.c +++ b/libexec/dma/spool.c @@ -66,54 +66,47 @@ 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); + } +} -- 2.41.0