// mail-enqueue - queue a mail message for delivery #include "max_align.h" #include "libutil.h" #include "shutil.h" #include #include #include #include #include #include #include #include #include using std::string; class spool_writer { string spooldir_; int notifyfd_; struct sockaddr_un notify_addr_; socklen_t notify_len_; public: spool_writer(const string &spooldir) : spooldir_(spooldir), notify_addr_{} { notifyfd_ = socket(AF_UNIX, SOCK_DGRAM, 0); if (notifyfd_ < 0) edie("socket failed"); notify_addr_.sun_family = AF_UNIX; snprintf(notify_addr_.sun_path, sizeof notify_addr_.sun_path, "%s/notify", spooldir.c_str()); notify_len_ = SUN_LEN(¬ify_addr_); } void queue(int msgfd, const char *recipient, size_t limit = (size_t)-1) { // Create temporary message char tmpname[256]; snprintf(tmpname, sizeof tmpname, "%s/pid/%d", spooldir_.c_str(), (int)getpid()); int tmpfd = open(tmpname, O_CREAT|O_EXCL|O_WRONLY, 0600); if (tmpfd < 0) edie("open %s failed", tmpname); if (copy_fd_n(tmpfd, msgfd, limit) < 0) edie("copy_fd message failed"); struct stat st; if (fstatx(tmpfd, &st, STAT_OMIT_NLINK) < 0) edie("fstat failed"); fsync(tmpfd); close(tmpfd); // Create envelope char envname[256]; snprintf(envname, sizeof envname, "%s/todo/%lu", spooldir_.c_str(), (unsigned long)st.st_ino); int envfd = open(envname, O_CREAT|O_EXCL|O_WRONLY, 0600); if (envfd < 0) edie("open %s failed", envname); xwrite(envfd, recipient, strlen(recipient)); fsync(envfd); close(envfd); // Move message into spool char msgname[256]; snprintf(msgname, sizeof msgname, "%s/mess/%lu", spooldir_.c_str(), (unsigned long)st.st_ino); if (rename(tmpname, msgname) < 0) edie("rename %s %s failed", tmpname, msgname); // Notify queue manager. We don't need an acknowledgment because // we've already "durably" queued the message. char notif[16]; snprintf(notif, sizeof notif, "%lu", (unsigned long)st.st_ino); if (sendto(notifyfd_, notif, strlen(notif), 0, (struct sockaddr*)¬ify_addr_, notify_len_) < 0) edie("send failed"); } void queue_exit() { const char *msg = "EXIT"; if (sendto(notifyfd_, msg, strlen(msg), 0, (struct sockaddr*)¬ify_addr_, notify_len_) < 0) edie("send exit failed"); } }; static void do_batch_mode(int fd, int resultfd, const char *recip, spool_writer *spool) { while (1) { uint64_t msg_len; size_t len = xread(fd, &msg_len, sizeof msg_len); if (len == 0) break; if (len != sizeof msg_len) die("short read of message length"); spool->queue(fd, recip, msg_len); // Indicate success uint64_t res = 0; xwrite(resultfd, &res, sizeof res); } } static void usage(const char *argv0) { fprintf(stderr, "Usage: %s spooldir recipient and\n" "a return code will be written to stdout after every delivery.\n" ); exit(2); } int main(int argc, char **argv) { if (argc == 3 && strcmp(argv[1], "--exit") == 0) { spool_writer spool{argv[2]}; spool.queue_exit(); return 0; } int opt; bool batch_mode = false; while ((opt = getopt(argc, argv, "b")) != -1) { switch (opt) { case 'b': batch_mode = true; break; default: usage(argv[0]); } } if (argc - optind != 2) usage(argv[0]); const char *spooldir = argv[optind]; const char *recip = argv[optind + 1]; spool_writer spool{spooldir}; if (batch_mode) do_batch_mode(0, 1, recip, &spool); else spool.queue(0, recip); return 0; }