static void qmgr_active_corrupt(const char *queue_id) { const char *myname = "qmgr_active_corrupt"; if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) { if (errno != ENOENT) msg_fatal("%s: save corrupt file queue %s id %s: %m", myname, MAIL_QUEUE_ACTIVE, queue_id); } else { msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"", queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT); } }
void qmgr_move(const char *src_queue, const char *dst_queue, time_t time_stamp) { const char *myname = "qmgr_move"; SCAN_DIR *queue_dir; char *queue_id; struct utimbuf tbuf; const char *path; if (strcmp(src_queue, dst_queue) == 0) msg_panic("%s: source queue %s is destination", myname, src_queue); if (msg_verbose) msg_info("start move queue %s -> %s", src_queue, dst_queue); queue_dir = scan_dir_open(src_queue); while ((queue_id = mail_scan_dir_next(queue_dir)) != 0) { if (mail_queue_id_ok(queue_id)) { if (time_stamp > 0) { tbuf.actime = tbuf.modtime = time_stamp; path = mail_queue_path((VSTRING *) 0, src_queue, queue_id); if (utime(path, &tbuf) < 0) { if (errno != ENOENT) msg_fatal("%s: update %s time stamps: %m", myname, path); msg_warn("%s: update %s time stamps: %m", myname, path); continue; } } if (mail_queue_rename(queue_id, src_queue, dst_queue)) { if (errno != ENOENT) msg_fatal("%s: rename %s from %s to %s: %m", myname, queue_id, src_queue, dst_queue); msg_warn("%s: rename %s from %s to %s: %m", myname, queue_id, src_queue, dst_queue); continue; } if (msg_verbose) msg_info("%s: moved %s from %s to %s", myname, queue_id, src_queue, dst_queue); } else { msg_warn("%s: ignored: queue %s id %s", myname, src_queue, queue_id); } } scan_dir_close(queue_dir); if (msg_verbose) msg_info("end move queue %s -> %s", src_queue, dst_queue); }
static void qmgr_active_defer(const char *queue_name, const char *queue_id, const char *dest_queue, int delay) { const char *myname = "qmgr_active_defer"; const char *path; struct utimbuf tbuf; if (msg_verbose) msg_info("wakeup %s after %ld secs", queue_id, (long) delay); tbuf.actime = tbuf.modtime = event_time() + delay; path = mail_queue_path((VSTRING *) 0, queue_name, queue_id); if (utime(path, &tbuf) < 0 && errno != ENOENT) msg_fatal("%s: update %s time stamps: %m", myname, path); if (mail_queue_rename(queue_id, queue_name, dest_queue)) { if (errno != ENOENT) msg_fatal("%s: rename %s from %s to %s: %m", myname, queue_id, queue_name, dest_queue); msg_warn("%s: rename %s from %s to %s: %m", myname, queue_id, queue_name, dest_queue); } else if (msg_verbose) { msg_info("%s: defer %s", myname, queue_id); } }
int qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id) { const char *myname = "qmgr_active_feed"; QMGR_MESSAGE *message; struct stat st; const char *path; if (strcmp(scan_info->queue, MAIL_QUEUE_ACTIVE) == 0) msg_panic("%s: bad queue %s", myname, scan_info->queue); if (msg_verbose) msg_info("%s: queue %s", myname, scan_info->queue); /* * Make sure this is something we are willing to open. */ if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO) return (0); if (msg_verbose) msg_info("%s: %s", myname, path); /* * Skip files that have time stamps into the future. They need to cool * down. Incoming and deferred files can have future time stamps. */ if ((scan_info->flags & QMGR_SCAN_ALL) == 0 && st.st_mtime > time((time_t *) 0) + 1) { if (msg_verbose) msg_info("%s: skip %s (%ld seconds)", myname, queue_id, (long) (st.st_mtime - event_time())); return (0); } /* * Move the message to the active queue. File access errors are fatal. */ if (mail_queue_rename(queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE)) { if (errno != ENOENT) msg_fatal("%s: %s: rename from %s to %s: %m", myname, queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE); msg_warn("%s: %s: rename from %s to %s: %m", myname, queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE); return (0); } /* * Extract envelope information: sender and recipients. At this point, * mail addresses have been processed by the cleanup service so they * should be in canonical form. Generate requests to deliver this * message. * * Throwing away queue files seems bad, especially when they made it this * far into the mail system. Therefore we save bad files to a separate * directory for further inspection. * * After queue manager restart it is possible that a queue file is still * being delivered. In that case (the file is locked), defer delivery by * a minimal amount of time. */ #define QMGR_FLUSH_AFTER (QMGR_FLUSH_EACH | QMGR_FLUSH_DFXP) if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id, (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ? scan_info->flags | QMGR_FLUSH_AFTER : scan_info->flags, (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ? st.st_mode & ~MAIL_QUEUE_STAT_UNTHROTTLE : 0)) == 0) { qmgr_active_corrupt(queue_id); return (0); } else if (message == QMGR_MESSAGE_LOCKED) { qmgr_active_defer(MAIL_QUEUE_ACTIVE, queue_id, MAIL_QUEUE_INCOMING, 60); return (0); } else { /* * Special case if all recipients were already delivered. Send any * bounces and clean up. */ if (message->refcount == 0) qmgr_active_done(message); return (1); } }
static int flush_one_file(const char *queue_id, VSTRING *queue_file, struct utimbuf * tbuf, int how) { const char *myname = "flush_one_file"; const char *queue_name; const char *path; /* * Some other instance of this program may flush some logfile and may * just have moved this queue file to the incoming queue. */ for (queue_name = MAIL_QUEUE_DEFERRED; /* see below */ ; queue_name = MAIL_QUEUE_INCOMING) { path = mail_queue_path(queue_file, queue_name, queue_id); if (utime(path, tbuf) == 0) break; if (errno != ENOENT) msg_warn("%s: update %s time stamps: %m", myname, path); if (STREQ(queue_name, MAIL_QUEUE_INCOMING)) return (0); } /* * With the UNTHROTTLE_AFTER strategy, we leave it up to the queue * manager to unthrottle transports and queues as it reads recipients * from a queue file. We request this unthrottle operation by setting the * group read permission bit. * * Note: we must avoid using chmod(). It is not only slower than fchmod() * but it is also less secure. With chmod(), an attacker could repeatedly * send requests to the flush server and trick it into changing * permissions of non-queue files, by exploiting a race condition. * * We use safe_open() because we don't validate the file content before * modifying the file status. */ if (how & UNTHROTTLE_AFTER) { VSTRING *why; struct stat st; VSTREAM *fp; for (why = vstring_alloc(1); /* see below */ ; queue_name = MAIL_QUEUE_INCOMING, path = mail_queue_path(queue_file, queue_name, queue_id)) { if ((fp = safe_open(path, O_RDWR, 0, &st, -1, -1, why)) != 0) break; if (errno != ENOENT) msg_warn("%s: open %s: %s", myname, path, STR(why)); if (errno != ENOENT || STREQ(queue_name, MAIL_QUEUE_INCOMING)) { vstring_free(why); return (0); } } vstring_free(why); if ((st.st_mode & MAIL_QUEUE_STAT_READY) != MAIL_QUEUE_STAT_READY) { (void) vstream_fclose(fp); return (0); } if (fchmod(vstream_fileno(fp), st.st_mode | MAIL_QUEUE_STAT_UNTHROTTLE) < 0) msg_warn("%s: fchmod %s: %m", myname, path); (void) vstream_fclose(fp); } /* * Move the file to the incoming queue, if it isn't already there. */ if (STREQ(queue_name, MAIL_QUEUE_INCOMING) == 0 && mail_queue_rename(queue_id, queue_name, MAIL_QUEUE_INCOMING) < 0 && errno != ENOENT) msg_warn("%s: rename from %s to %s: %m", path, queue_name, MAIL_QUEUE_INCOMING); /* * If we got here, we achieved something, so let's claim succes. */ return (1); }