int main(int argc, char **argv) { if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%s\n", COURIER_COPYRIGHT); exit(0); } if (chdir(courierdir())) { perror("chdir"); return (1); } if (argc < 2) return (0); if (strcmp(argv[1], "stop") == 0) { int fd; trigger(TRIGGER_STOP); /* Wait until the exclusive lock goes away: */ signal(SIGHUP, SIG_DFL); if ((fd=open(TMPDIR "/courierd.lock", O_RDWR|O_CREAT, 0600)) < 0) clog_msg_errno(); alarm(15); /* But abort after 15 seconds. */ ll_lock_ex(fd); return (0); } if (strcmp(argv[1], "restart") == 0) { trigger(TRIGGER_RESTART); return (0); } if (strcmp(argv[1], "flush") == 0) { ino_t n; struct ctlfile ctf; if (getuid()) { /* ** We'll fail trying to open the pipe anyway, but let's ** give a meaningful error message now. */ fprintf(stderr, "courier flush can be executed only by the superuser.\n"); exit(1); } if (argc < 3) { trigger(TRIGGER_FLUSH); /* Everything */ exit(0); } if (comparseqid(argv[2], &n) == 0 && ctlfile_openi(n, &ctf, 1) == 0) { int c=ctlfile_searchfirst(&ctf, COMCTLFILE_MSGID); if (c >= 0 && strcmp(ctf.lines[c]+1, argv[2]) == 0) { char *s=courier_malloc(sizeof(TRIGGER_FLUSHMSG)+1+ strlen(argv[2])); strcat(strcat(strcpy(s, TRIGGER_FLUSHMSG), argv[2]), "\n"); trigger(s); ctlfile_close(&ctf); return (0); } ctlfile_close(&ctf); } fprintf(stderr, "No such message.\n"); exit(1); return (1); } /* Might as well... */ if (strcmp(argv[1], "start") == 0) { pid_t p; int waitstat; char dummy; /* ** Ok, courierd will close file descriptor 3 when it starts, so we ** put a pipe on there, and wait for it to close. */ int pipefd[2]; close(3); if (open("/dev/null", O_RDONLY) != 3 || pipe(pipefd)) { fprintf(stderr, "Cannot open pipe\n"); exit(1); } if (getuid()) { /* ** We'll fail trying to execute courierd anyway, but let's ** give a meaningful error message now. */ fprintf(stderr, "courier start can be executed only by the superuser.\n"); return (1); } signal(SIGCHLD, SIG_DFL); while ((p=fork()) == -1) { perror("fork"); sleep(10); } if (p == 0) { dup2(pipefd[1], 3); close(pipefd[1]); close(pipefd[0]); while ((p=fork()) == -1) { perror("fork"); sleep(10); } if (p == 0) { /* ** stdin from the bitbucket. stdout to ** /dev/null, or the bitbucket. */ signal(SIGHUP, SIG_IGN); close(0); open("/dev/null", O_RDWR); dup2(0, 1); dup2(0, 2); /* See if we can disconnect from the terminal */ #ifdef TIOCNOTTY { int fd=open("/dev/tty", O_RDWR); if (fd >= 0) { ioctl(fd, TIOCNOTTY, 0); close(fd); } } #endif /* Remove any process groups */ #if HAVE_SETPGRP #if SETPGRP_VOID setpgrp(); #else setpgrp(0, 0); #endif #endif execl( DATADIR "/courierctl.start", "courierctl.start", (char *)0); perror("exec"); _exit(1); } _exit(0); } close(pipefd[1]); while (wait(&waitstat) != p) ; if (read(pipefd[0], &dummy, 1) < 0) ; /* ignore */ close(pipefd[0]); close(3); return (0); } if (strcmp(argv[1], "clear") == 0 && argc > 2) { libmail_changeuidgid(MAILUID, MAILGID); if (strcmp(argv[2], "all") == 0) { courier_clear_all(); } else { track_save(argv[2], TRACK_ADDRACCEPTED); printf("%s cleared.\n", argv[2]); } return (0); } if (argc > 2 && strcmp(argv[1], "show") == 0 && strcmp(argv[2], "all") == 0) { libmail_changeuidgid(MAILUID, MAILGID); courier_show_all(); } return (0); }
int msgcancel(const char *qid, const char **reason, int nreason, int chkuid) { ino_t n; struct ctlfile ctf; struct stat stat_buf; uid_t u=getuid(); int c, d; struct iovec *iova; static const char default_cancel_msg[]="Message cancelled."; static const char *default_cancel_msgp[1]= { default_cancel_msg }; static const char x=COMCTLFILE_CANCEL_MSG; char *s; if (comparseqid(qid, &n)) return (-1); if (ctlfile_openi(n, &ctf, 0)) return (-1); if (fstat(ctf.fd, &stat_buf) || (chkuid && u && u != stat_buf.st_uid) || ctf.cancelled) { ctlfile_close(&ctf); return (-1); } c=ctlfile_searchfirst(&ctf, COMCTLFILE_MSGID); if (c < 0 || strcmp(ctf.lines[c]+1, qid)) { ctlfile_close(&ctf); return (-1); } if ((iova=(struct iovec *)malloc(sizeof(struct iovec) * 3 * (nreason == 0 ? 1:nreason))) == 0) { perror("enomem"); exit(1); } if (nreason == 0) { reason=default_cancel_msgp; nreason=1; } c=0; while (nreason) { iova[c].iov_base=(caddr_t)&x; iova[c].iov_len=1; c++; iova[c].iov_base=(caddr_t) *reason; iova[c].iov_len=strlen( *reason ); c++; iova[c].iov_base=(caddr_t)"\n"; iova[c].iov_len=1; c++; ++reason; --nreason; } s=malloc(sizeof(TRIGGER_FLUSHMSG)+strlen(qid)+3); if (!s) { perror("malloc"); exit(1); } strcat(strcat(strcpy(s, TRIGGER_FLUSHMSG " "), qid), "\n"); d=writev(ctf.fd, iova, c); ctlfile_close(&ctf); while (c) { --c; if (iova[c].iov_len > d) { perror("writev"); free(iova); free(s); return (0); } d -= iova[c].iov_len; } free(iova); trigger(s); free(s); return (0); }
int courierbmain() { fd_set fds, fdc; struct mybuf trigger_buf; clog_open_syslog("courierd"); rw_init_verbose(1); if (rw_init_courier(0)) return (1); drvinfo::init(); libmail_changeuidgid(MAILUID, MAILGID); signal(SIGPIPE, SIG_IGN); respawnlo=config_time_respawnlo(); respawnhi=config_time_respawnhi(); retryalpha=config_time_retryalpha(); retrybeta=config_retrybeta(); retrygamma=config_time_retrygamma(); retrymaxdelta=config_retrymaxdelta(); flushtime=0; delaytime=config_time_submitdelay(); queuefill=config_time_queuefill(); nextqueuefill=0; purgedir(MSGQDIR); purgedir(MSGSDIR); rmdir(MSGQDIR); mkdir(MSGQDIR, 0755); trackpurge(); if ((triggerw=open(triggername, O_WRONLY, 0)) < 0 || (triggerr=open(triggername, O_RDONLY, 0)) < 0) clog_msg_errno(); time(&start_time); courierbstart=start_time; msgq::init(queuehi); shutdown_flag=0; FD_ZERO(&fds); unsigned nd; int maxfd=triggerr; FD_SET(maxfd, &fds); mybuf_init(&trigger_buf, triggerr); for (nd=0; nd<drvinfo::nmodules; nd++) { int fd=drvinfo::modules[nd].module_from.fd; if (fd > maxfd) maxfd=fd; FD_SET(fd, &fds); } ++maxfd; time_t current_time, next_time, shutdown_time; current_time=start_time; shutdown_time=0; msgq::queuescan(); // Initial queue scan int doscantmp=1; //////////////////////////////////////////////////////////////////////////// // // Main loop: // // A) Scan tmp for new messages. // // B) Schedule messages for delivery, when their time comes. // // C) Update statistics. // // D) Stop scheduling messages when restart signal received. // // E) Restart if there's no activity for 5 mins after we've been running // for respawnlo amount of times. // // F) When we've been running for respawnhi amount of time, stop scheduling // messages, restart when all deliveries have been completed. // // G) Receive delivery completion messages from modules. // //////////////////////////////////////////////////////////////////////////// for (;;) { msgq *mp; if (shutdown_flag) doscantmp=0; if (doscantmp) doscantmp=msgq::tmpscan(); if (shutdown_flag && msgq::inprogress == 0) break; if (current_time >= start_time + respawnhi && !shutdown_flag) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: respawnhi limit reached."); clog_msg_send(); shutdown_flag=1; continue; } if (diskspacelow() && msgq::queuehead && msgq::queuehead->nextsenddel <= current_time) { clog_msg_start_err(); clog_msg_str("Low on disk space."); clog_msg_send(); for (mp=msgq::queuehead; mp && mp->nextsenddel <= current_time + 300; mp=mp->next) mp->nextsenddel=current_time+300; } if (!shutdown_flag) { while ((mp=msgq::queuehead) != 0 && mp->nextsenddel <= current_time) { mp->removewq(); mp->start_message(); } } if (nextqueuefill && nextqueuefill <= current_time) { nextqueuefill=0; if (!shutdown_flag) { msgq::queuescan(); } } next_time=0; if ( msgq::queuehead && msgq::queuehead->nextsenddel > current_time) next_time=msgq::queuehead->nextsenddel; if (!shutdown_flag && msgq::inprogress == 0 && shutdown_time == 0) { shutdown_time=current_time + 5 * 60; if (shutdown_time < start_time + respawnlo) shutdown_time=start_time + respawnlo; } if (shutdown_time && (next_time == 0 || shutdown_time < next_time)) next_time=shutdown_time; if (nextqueuefill) { /* Or, wake up next time we need to refill the queue */ if (next_time == 0 || nextqueuefill < next_time) { next_time=nextqueuefill; } } fdc=fds; struct timeval tv; if (next_time) { tv.tv_sec= next_time > current_time ? next_time - current_time:0; tv.tv_usec=0; } #if 1 { char buf[40]; clog_msg_start_info(); clog_msg_str("Waiting. shutdown time="); strcpy(buf, shutdown_time ? ctime(&shutdown_time):"none\n"); *strchr(buf, '\n')=0; clog_msg_str(buf); clog_msg_str(", wakeup time="); strcpy(buf, next_time ? ctime(&next_time):"none\n"); *strchr(buf, '\n')=0; clog_msg_str(buf); clog_msg_str(", queuedelivering="); clog_msg_int(msgq::queuedelivering); clog_msg_str(", inprogress="); clog_msg_int(msgq::inprogress); clog_msg_send(); } #endif while (select(maxfd, &fdc, (fd_set *)0, (fd_set *)0, (next_time && doscantmp == 0 ? &tv:(struct timeval *)0)) < 0) { if (errno != EINTR) clog_msg_errno(); } time(¤t_time); if (shutdown_time) { if (current_time >= shutdown_time && msgq::inprogress == 0) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: respawnlo limit reached, system inactive."); clog_msg_send(); shutdown_flag=1; } shutdown_time=0; } /* Handle commands sent via the trigger pipe */ if (FD_ISSET(triggerr, &fdc)) { unsigned l; char cmdbuf[256]; int c; do { l=0; do { c=mybuf_get(&trigger_buf); if (l < sizeof(cmdbuf)-1) cmdbuf[l++]=c; } while (c != '\n'); cmdbuf[l]=0; if (strcmp(cmdbuf, TRIGGER_NEWMSG) == 0) doscantmp=1; else if (strcmp(cmdbuf, TRIGGER_RESTART) == 0) { shutdown_flag=1; clog_msg_start_info(); clog_msg_str("SHUTDOWN: Restarting..."); clog_msg_send(); } else if (strcmp(cmdbuf, TRIGGER_STOP) == 0) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: Stopping..."); clog_msg_send(); _exit(99); } else if (strcmp(cmdbuf, TRIGGER_FLUSH) == 0) { flushtime=current_time; for (mp=msgq::queuehead; mp; mp=mp->next) mp->nextsenddel=current_time; } else if (strncmp(cmdbuf, TRIGGER_FLUSHMSG, sizeof(TRIGGER_FLUSHMSG)-1) == 0) { char *p; ino_t n; if ((p=strchr(cmdbuf, '\n')) != 0) *p=0; p=cmdbuf+sizeof(TRIGGER_FLUSHMSG)-1; while (*p == ' ') ++p; if (comparseqid(p, &n) == 0 && !shutdown_flag) msgq::flushmsg(n, p); } } while (mybuf_more(&trigger_buf)); } for (nd=0; nd<drvinfo::nmodules; nd++) { struct mybuf *p= &drvinfo::modules[nd].module_from; size_t delnum; int c; if (!FD_ISSET(p->fd, &fdc)) continue; do { delnum=0; while (errno=0, ((c=mybuf_get(p)) != '\n')) { if (c < 0) { if (errno == EINTR) continue; clog_msg_start_err(); clog_msg_str("MODULE "); clog_msg_str( drvinfo::modules[nd] .module->name); clog_msg_str(" ABORTED."); clog_msg_send(); _exit(1); } if (c >= '0' && c <= '9') delnum=delnum * 10 + (c-'0'); } msgq::completed(drvinfo::modules[nd], delnum); } while (mybuf_more(p)); if (!shutdown_flag) shutdown_time=0; } } close(triggerr); close(triggerw); drvinfo::cleanup(); return (0); }