int rw_init_courier(const char *name) { unsigned i; int err=0; if (rw_init_verbose_flag) { clog_msg_start_info(); clog_msg_str("Loading STATIC transport module libraries."); clog_msg_send(); } if (rw_install_start()) return (-1); for (i=0; rw_static[i].name; i++) /* name != 0 really meaningless for static libs, but, ** what the heck... */ if ((name == 0 || strcmp(name, rw_static[i].name) == 0) && rw_install(rw_static[i].name, rw_static[i].rw_install, rw_static[i].rw_init)) err=1; if (err || rw_install_init()) { clog_msg_start_err(); clog_msg_str("Transport module installation has FAILED."); clog_msg_send(); return (-1); } return (0); }
static void kill1filter(struct filterinfo **p) { int waitstat; pid_t pid; struct filterinfo *pp= *p; *p=pp->next; clog_msg_start_info(); clog_msg_str("Stopping "); clog_msg_str(pp->filtername); clog_msg_send(); close(pp->fd0); while ((pid=wait(&waitstat)) >= 0 && pid != pp->p) ; free(pp->filtername); free(pp); }
static void purgedir(const char *dirname) { std::string buf; DIR *p; struct dirent *de; clog_msg_start_info(); clog_msg_str("Purging "); clog_msg_str(dirname); clog_msg_send(); p=opendir(dirname); if (!p) return; while ((de=readdir(p)) != NULL) { if (de->d_name[0] == '.') continue; buf=dirname; buf += '/'; buf += de->d_name; rmdir(buf.c_str()); // Just for the hell of it } closedir(p); }
static int faxsend_cleanup(int errcode, char *errmsg, void *vp) { struct faxconv_err_args *args=(struct faxconv_err_args *)vp; unsigned pages_sent=0; char *p, *q; int i; time_t now_time; unsigned coverpage_cnt=0; unsigned page_cnt=0; /* Check how many files sendfax renamed (were succesfully sent) */ while (args->file_list) { if (access(args->file_list->filename, 0) == 0) break; if (coverpage_cnt < args->n_cover_pages) ++coverpage_cnt; else ++pages_sent; args->file_list=args->file_list->next; } /* Strip out any blank lines in captured output from sendfax */ for (p=q=errmsg; *p; p++) { if (*p == '\n' && (p[1] == '\n' || p[1] == 0)) continue; *q++=*p; } *q=0; /* Find the last message from sendfax */ for (p=q=errmsg; *p; p++) { if (*p != '\n') continue; *p=0; /* Dump sendfax's output to the log */ if (*q) { clog_msg_start_info(); clog_msg_str("courierfax: " SENDFAX ": "); clog_msg_str(q); clog_msg_send(); } q=p+1; } if (*q) /* Last line of the error message */ { clog_msg_start_info(); clog_msg_str("courierfax: " SENDFAX ": "); clog_msg_str(q); clog_msg_send(); } else /* Default message */ { q=SENDFAX ": completed."; } /* ** Ugly hack: capture the following message from sendfax: ** ** /usr/sbin/sendfax: cannot access fax device(s) (locked?) */ #if 0 lockflag=0; p=strchr(q, ':'); if (p) { static const char msg1[]="cannot access fax device"; static const char msg2[]="locked"; ++p; while (*p && isspace((int)(unsigned char)*p)) p++; if (*p && strncasecmp(p, msg1, sizeof(msg1)-1) == 0) { p += sizeof(msg1); while (*p && !isspace((int)(unsigned char)*p)) ++p; p=strchr(p, '('); if (p && strncmp(p+1, msg2, sizeof(msg2)-1) == 0) { args->is_locked=1; clog_msg_start_info(); clog_msg_str("courierfax: detected locked" " modem line, sleeping..."); clog_msg_send(); sleep(60); return (-1); } } } #else if (errcode == 2) { args->is_locked=1; clog_msg_start_info(); clog_msg_str("courierfax: detected locked" " modem line, sleeping..."); clog_msg_send(); sleep(60); return (-1); } #endif ctlfile_append_connectioninfo(args->ctf, args->nreceip, COMCTLFILE_DELINFO_REPLY, q); sprintf(errmsg, "%u cover pages, %u document pages sent.", coverpage_cnt, page_cnt); i=ctlfile_searchfirst(args->ctf, COMCTLFILE_FAXEXPIRES); time(&now_time); ctlfile_append_reply(args->ctf, args->nreceip, errmsg, (pages_sent == 0 && i >= 0 && errcode < 10 && now_time < strtotime(args->ctf->lines[i]+1) ? COMCTLFILE_DELDEFERRED: COMCTLFILE_DELFAIL_NOTRACK), 0); return (-1); }
/* ** sighup attempts to synchronize everything. */ static void sighup() { struct filterinfo **pp; DIR *dirp; struct dirent *de; /* Start any new filters */ dirp=opendir(FILTERACTIVEDIR); while (dirp && (de=readdir(dirp)) != 0) { char *n; int pipefd[2]; struct filterinfo *newp; if (de->d_name[0] == '.') continue; for (pp= &filterlist; *pp; pp= &(*pp)->next) if (strcmp( (*pp)->filtername, de->d_name) == 0) break; if (*pp) continue; n=malloc(sizeof(FILTERACTIVEDIR "/")+strlen(de->d_name)); if (!n) { perror("malloc"); exit(1); } strcat(strcpy(n, FILTERACTIVEDIR "/"), de->d_name); if ( (newp=(struct filterinfo *) malloc(sizeof(struct filterinfo))) == 0 || (newp->filtername=malloc(strlen(de->d_name)+1)) == 0) { perror("malloc"); exit(1); } strcpy(newp->filtername, de->d_name); newp->next=filterlist; filterlist=newp; if (pipe(pipefd) < 0) { perror("pipe"); exit(1); } newp->fd0=pipefd[1]; while ((newp->p=fork()) < 0) { perror("fork"); sleep(3); } if ( newp->p == 0) { dup2(pipefd[0], 0); close(pipefd[0]); close(pipefd[1]); execl(n, de->d_name, (char *)0); perror("exec"); exit(1); } free(n); close(pipefd[0]); if (fcntl(newp->fd0, F_SETFD, FD_CLOEXEC) < 0) { perror("fcntl"); exit(1); } clog_msg_start_info(); clog_msg_str("Starting "); clog_msg_str(newp->filtername); clog_msg_send(); } if (dirp) closedir(dirp); /* Then, kill any filters that should not be running any more */ for (pp= &filterlist; *pp; ) { char *n=activename( (*pp)->filtername ); if (access(n, 0) == 0) { free(n); pp= &(*pp)->next; continue; } free(n); kill1filter(pp); } /* Finally, remove any sockets for filters that don't run any more */ { static const char *dirs[2]={FILTERSOCKETDIR, ALLFILTERSOCKETDIR}; int i; for (i=0; i<2; i++) { dirp=opendir(dirs[i]); while (dirp && (de=readdir(dirp)) != 0) { char *n; if (de->d_name[0] == '.') continue; for (pp= &filterlist; *pp; pp= &(*pp)->next) if (strcmp( (*pp)->filtername, de->d_name) == 0) break; if (*pp) continue; n=malloc(strlen(dirs[i])+strlen(de->d_name)+2); if (!n) { perror("malloc"); exit(1); } strcat(strcat(strcpy(n, dirs[i]), "/"), de->d_name); unlink(n); free(n); } if (dirp) closedir(dirp); } } }
void drvinfo::init() { unsigned cnt, i; struct rw_transport *p; module_dsn=0; queuelo=0; for (cnt=0, p=rw_transport_first; p; p=p->next) ++cnt; if ((modules=new drvinfo[cnt]) == 0) clog_msg_errno(); cnt=0; for (p=rw_transport_first; p; p=p->next) { char *namebuf=(char *)courier_malloc( sizeof( SYSCONFDIR "/module.")+strlen(p->name)); strcat(strcpy(namebuf, SYSCONFDIR "/module."), p->name); FILE *config=fopen(namebuf, "r"); if (!config) { clog_msg_start_err(); clog_msg_str("Cannot open "); clog_msg_str(namebuf); clog_msg_send(); exit(1); } free(namebuf); unsigned maxdels=1; unsigned maxhost=1; unsigned maxrcpt=1; char *prog=0; char buf[BUFSIZ]; while (fgets(buf, sizeof(buf), config) != 0) { char *p; if ((p=strchr(buf, '#')) != 0) *p=0; p=strtok(buf, " \t\r\n="); if (strcmp(p, "MAXDELS") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxdels=atoi(p); } else if (strcmp(p, "MAXHOST") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxhost=atoi(p); } else if (strcmp(p, "MAXRCPT") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxrcpt=atoi(p); } else if (strcmp(p, "PROG") == 0 && !prog) { p=strtok(NULL, "\t\r\n="); if (p) { prog=(char *) courier_malloc(strlen(p)+1); strcpy(prog, p); } } } fclose(config); if (!prog) continue; // Input only module modules[cnt].module=p; p->udata=(void *)&modules[cnt]; modules[cnt].delinfo_list.resize(maxdels); modules[cnt].hosts_list.resize(maxdels); queuelo += maxdels; modules[cnt].maxhost=maxhost; modules[cnt].maxrcpt=maxrcpt; modules[cnt].delpfreefirst=0; if (strcmp(p->name, DSN) == 0) module_dsn= &modules[cnt]; for (i=0; i<maxdels; i++) { modules[cnt].delinfo_list[i].delid=i; modules[cnt].delinfo_list[i].freenext= modules[cnt].delpfreefirst; modules[cnt].delpfreefirst= &modules[cnt].delinfo_list[i]; } modules[cnt].hdlvrpfree=0; for (i=0; i<maxdels; i++) { modules[cnt].hosts_list[i].next= modules[cnt].hdlvrpfree; modules[cnt].hdlvrpfree=&modules[cnt].hosts_list[i]; } modules[cnt].hdlvrpfirst=0; modules[cnt].hdlvrplast=0; int pipe0[2], pipe1[2]; if (pipe(pipe0) || pipe(pipe1)) clog_msg_errno(); switch (modules[cnt].module_pid=fork()) { case 0: #if 0 // replaced by close-on-exec, below for (i=cnt; i; ) { --i; fclose(modules[i].module_to); close(modules[i].module_from.fd); } #endif close(pipe0[1]); close(pipe1[0]); dup2(pipe0[0], 0); dup2(pipe1[1], 1); close(pipe0[0]); close(pipe1[1]); signal(SIGCHLD, SIG_DFL); if (chdir(MODULEDIR) || chdir(modules[cnt].module->name)) { clog_msg_start_err(); clog_msg_str(MODULEDIR "/"); clog_msg_str(modules[cnt].module->name); clog_msg_str(" does not exist."); clog_msg_send(); clog_msg_errno(); } { char maxdels_buf[MAXLONGSIZE+15]; char maxhost_buf[MAXLONGSIZE+15]; char maxrcpt_buf[MAXLONGSIZE+15]; const char *sh=getenv("SHELL"); sprintf(maxdels_buf, "MAXDELS=%ld", (long)maxdels); sprintf(maxhost_buf, "MAXHOST=%ld", (long)maxhost); sprintf(maxrcpt_buf, "MAXRCPT=%ld", (long)maxrcpt); putenv(maxdels_buf); putenv(maxhost_buf); putenv(maxrcpt_buf); const char *ch=courierdir(); char *p=(char *)courier_malloc(strlen(ch)+ sizeof("COURIER_HOME=")); strcat(strcpy(p,"COURIER_HOME="), ch); putenv(p); if (!sh) sh="/bin/sh"; execl(sh, sh, "-c", prog, (char *)0); clog_msg_start_err(); clog_msg_str(MODULEDIR "/"); clog_msg_str(modules[cnt].module->name); clog_msg_str(": "); clog_msg_str(prog); clog_msg_send(); clog_msg_errno(); } case -1: clog_msg_errno(); break; } close(pipe0[0]); close(pipe1[1]); if (fcntl(pipe0[1], F_SETFD, FD_CLOEXEC) || fcntl(pipe1[0], F_SETFD, FD_CLOEXEC)) clog_msg_errno(); if ((modules[cnt].module_to=fdopen(pipe0[1], "w")) == NULL) clog_msg_errno(); mybuf_init( &modules[cnt].module_from, pipe1[0] ); clog_msg_start_info(); clog_msg_str("Started "); clog_msg_str(prog); clog_msg_str(", pid="); clog_msg_int(modules[cnt].module_pid); clog_msg_str(", maxdels="); clog_msg_int(maxdels); clog_msg_str(", maxhost="); clog_msg_int(maxhost); clog_msg_str(", maxrcpt="); clog_msg_int(maxrcpt); clog_msg_send(); free(prog); cnt++; } nmodules=cnt; if (queuelo < 200) queuelo=200; char *cfname=config_localfilename("queuelo"); char *conf=config_read1l(cfname); if (conf) { queuelo=atoi(conf); free(conf); } free(cfname); if (queuelo < 20) queuelo=20; cfname=config_localfilename("queuehi"); conf=config_read1l(cfname); if (conf) { queuehi=atoi(conf); free(conf); } else queuehi=queuelo < 500 ? queuelo * 2:queuelo+1000; if (queuehi < queuelo) queuehi=queuelo; free(cfname); clog_msg_start_info(); clog_msg_str("queuelo="); clog_msg_int(queuelo); clog_msg_str(", queuehi="); clog_msg_int(queuehi); clog_msg_send(); if (module_dsn == 0) { clog_msg_start_err(); clog_msg_str("Driver dsn not found."); clog_msg_send(); exit(1); } }
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); }
void msgq::start_message() { // // Schedule deliveries for this message. // #if 0 clog_msg_start_info(); clog_msg_str("Start message "); logmsgid(this); clog_msg_send(); #endif size_t n=rcptinfo_list.size(); if (n == 0) { done(this, 0); return; } size_t i; for (i=0; i<n; i++) { rcptinfo &ri=rcptinfo_list[i]; drvinfo *drvp=ri.delmodule; dlvrhost *hostp, *freehostp=0; // // Check if the same host has any deliveries in progress, or pending. // Search the hdlvrpfree/hdlvrplast link list for this host. // for (hostp=drvp->hdlvrplast; hostp; hostp=hostp->prev) { if (hostp->hostname == ri.delhost) { #if 0 clog_msg_start_info(); clog_msg_str("Found host "); clog_msg_str(ri.delhost); clog_msg_str(", # of deliveries="); clog_msg_uint(hostp->dlvrcount); clog_msg_send(); #endif break; } // // Just in case we don't find it, remember the last host at the end of the // MRU list which does not have any deliveries in progress. // if (!freehostp && drvp->hdlvrpfree == 0 && hostp->dlvrcount == 0) freehostp=hostp; // Recycle a host struct used longest ago } if (!hostp) { // // Did not find this host. // if (drvp->hdlvrpfree) { // // There's an unused host structure, take it. // hostp=drvp->hdlvrpfree; drvp->hdlvrpfree=hostp->next; } else if (freehostp) { // // Recycle a host structure for a host without any current deliveries, // remove it from the MRU list. // hostp=freehostp; if (hostp->next) hostp->next->prev=hostp->prev; else drvp->hdlvrplast=hostp->prev; if (hostp->prev) hostp->prev->next=hostp->next; else drvp->hdlvrpfirst=hostp->next; } if (hostp) { // // Ok, initialize this host, and stick it at the end of the MRU list. // hostp->hostname=ri.delhost; hostp->dlvrcount=0; hostp->next=0; if ((hostp->prev=drvp->hdlvrplast) != 0) drvp->hdlvrplast->next=hostp; else drvp->hdlvrpfirst=hostp; drvp->hdlvrplast=hostp; if (hostp->pending_list) hostp->pending_list->hostp=0; hostp->pending_list=0; } } // // Determine if we can start a new delivery. The conditions are: // A) We found an unused host structure, and the number of deliveries in // progress to this host is less than the maximum set for the module, // // AND // // B) The total number of current deliveries to this host is less than // the maximum set for this module (there's an available delivery slot // structure) // if (hostp && hostp->dlvrcount < drvp->maxhost && drvp->delpfreefirst) { delinfo *newdi=drvp->delpfreefirst; drvp->delpfreefirst=newdi->freenext; newdi->dlvrphost=hostp; newdi->rcptlist=&ri; ri.pending=0; hostp->dlvrcount++; startdelivery(drvp, newdi); continue; } // // We are unable to start the delivery at this time. At the recipient list // to the list of pending deliveries for this host. // // The first thing is to make sure that this host structure has the pending // list structure allocated. // pendelinfo *pi; if (hostp) { if ((pi=hostp->pending_list) == 0) { drvp->pendelinfo_list.push_back(pendelinfo()); std::list<pendelinfo>::iterator pos= --drvp->pendelinfo_list.end(); pi=& *pos; pi->pos=pos; pi->drvp=drvp; pi->hostname=ri.delhost; pi->hostp=hostp; hostp->pending_list=pi; } } else { std::list<pendelinfo>::iterator pos; // // We don't even have a host structure. // That's ok, search the pendelinfo_list of this module anyway. // pi=0; for (pos=drvp->pendelinfo_list.begin(); pos != drvp->pendelinfo_list.end(); ++pos) { pi=& *pos; if (pi->hostname == ri.delhost) break; pi=0; } if (!pi) { drvp->pendelinfo_list.push_back(pendelinfo()); pos= --drvp->pendelinfo_list.end(); pi=& *pos; pi->pos=pos; pi->drvp=drvp; pi->hostname=ri.delhost; pi->hostp=0; } } ri.pending=pi; pi->receipient_list.push_back(&ri); ri.pendingpos= --pi->receipient_list.end(); } }
int msgq::queuescan3(std::string subdir, std::string name, const char *isnewmsg) { struct ctlfile ctlinfo; ino_t inum; time_t deltime, delsendtime; const char *p=name.c_str(); struct stat stat_buf; ++p; inum=strtoino(p); while (isdigit(*p)) p++; ++p; deltime=strtotime(p); name= subdir + '/' + name; if (ctlfile_openfn(name.c_str(), &ctlinfo, 0, 1)) { if (errno) { clog_msg_start_err(); clog_msg_str("Cannot read "); clog_msg_str(name.c_str()); clog_msg_send(); clog_msg_prerrno(); } return (0); } delsendtime=deltime; if (flushtime && ctlinfo.mtime < flushtime && flushtime < deltime) delsendtime=flushtime; if (!queuefree) { msgq *p; // // msgq array is full. See if we can remove the last message from the // pending queue. p=queuetail; if (p && p->nextsenddel > delsendtime) { #if 0 clog_msg_start_info(); clog_msg_str("Removing "); clog_msg_uint(p->msgnum); clog_msg_str(" to make room for this message."); clog_msg_send(); #endif p->removewq(); p->removeq(); } } msgq *newq=queuefree; if (!newq) { ctlfile_close(&ctlinfo); if (queuefill && nextqueuefill == 0) { nextqueuefill=current_time + queuefill; } return (1); } const char *cn=qmsgsctlname(inum); if ( stat(cn, &stat_buf) == -1 ) { unlink(name.c_str()); unlink(qmsgsdatname(inum)); unlink(cn); ctlfile_close(&ctlinfo); return (0); } #if 0 clog_msg_start_info(); clog_msg_str("Adding "); clog_msg_uint(inum); clog_msg_str(" to queue."); clog_msg_send(); #endif queuefree=newq->next; ++queuedelivering; ino_t hashbucket=inum % queuehashfirst.size(); if (queuehashfirst[hashbucket]) queuehashfirst[hashbucket]->prevhash=newq; else queuehashlast[hashbucket]=newq; newq->nexthash=queuehashfirst[hashbucket]; newq->prevhash=0; queuehashfirst[hashbucket]=newq; newq->nksize= (unsigned long)stat_buf.st_size; ctlinfo.starttime=stat_buf.st_mtime; int k=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_MSGID); newq->msgid= k < 0 ? "": ctlinfo.lines[k]+1; newq->msgnum=inum; newq->nextdel=deltime; newq->nextsenddel=delsendtime; newq->rcptinfo_list.resize(0); newq->rcptcount=0; newq->cancelled=ctlinfo.cancelled; if (isnewmsg) { int auth; clog_msg_start_info(); clog_msg_str("newmsg,id="); logmsgid(newq); auth=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_AUTHNAME); if (auth >= 0) { clog_msg_str(", auth="); clog_msg_str(ctlinfo.lines[auth]+1); } int m=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_FROMMTA); if (m >= 0) { clog_msg_str(": "); clog_msg_str(ctlinfo.lines[m]+1); } clog_msg_send(); } unsigned i, j; std::string host, addr; k=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_SENDER); struct rfc822t *sendert=rw_rewrite_tokenize(k < 0 ? "":ctlinfo.lines[k]+1); std::string errmsg; for (i=0; i<ctlinfo.nreceipients; i++) { for (j=0; ctlinfo.lines[j]; j++) { switch (ctlinfo.lines[j][0]) { case COMCTLFILE_DELSUCCESS: case COMCTLFILE_DELFAIL: if ((unsigned)atoi(ctlinfo.lines[j]+1) == i) break; // This one has been delivered default: continue; } break; } if (ctlinfo.lines[j]) continue; drvinfo *module=getdelinfo(sendert->tokens, ctlinfo.receipients[i], host, addr, errmsg); if (!module) { ctlfile_append_reply(&ctlinfo, i, errmsg.c_str(), SMTPREPLY_TYPE(errmsg.c_str()), 0); continue; } /* Check if it's time to move the message to a backup relay */ if (backup_relay_driver && ctlfile_searchfirst(&ctlinfo, COMCTLFILE_WARNINGSENT) >= 0 && strcmp(module->module->name, backup_relay_driver->module->name) == 0) { // module=backup_relay_driver; host=backup_relay; } /* Group all recipients for the same driver and host together */ for (j=0; j<newq->rcptcount; j++) if (strcmp(module->module->name, newq->rcptinfo_list[j].delmodule-> module->name) == 0 && newq->rcptinfo_list[j].delhost == host && newq->rcptinfo_list[j].addresses.size() < module->maxrcpt ) break; if (j == newq->rcptcount) { #if 0 clog_msg_start_info(); clog_msg_str("id="); logmsgid(newq); clog_msg_str(",new rcpt list - module="); clog_msg_str(module->module->name); clog_msg_str(", host="); clog_msg_str(host); clog_msg_send(); #endif newq->rcptinfo_list.resize(++newq->rcptcount); struct rw_info_rewrite rwir; struct rw_info rwi; rwir.buf=0; rwir.errmsg=0; rw_info_init(&rwi, sendert->tokens, rw_err_func); rwi.sender=0; rwi.mode=RW_OUTPUT|RW_ENVSENDER; rwi.udata= (void *)&rwir; rw_rewrite_module(module->module, &rwi, rw_rewrite_chksyn_print); char *address=((struct rw_info_rewrite *)rwi.udata)->buf; char *errmsg= ((struct rw_info_rewrite *)rwi.udata)->errmsg; if (!address) { ctlfile_append_reply(&ctlinfo, i, errmsg, SMTPREPLY_TYPE(errmsg), 0); newq->rcptinfo_list.resize(--newq->rcptcount); free(errmsg); continue; } if (errmsg) free(errmsg); newq->rcptinfo_list[j].init(newq, module, host, address); free(address); } #if 0 clog_msg_start_info(); clog_msg_str("id="); logmsgid(newq); clog_msg_str(",module="); clog_msg_str(module->module->name); clog_msg_str(", host="); clog_msg_str(host); clog_msg_str(", addr=<"); clog_msg_str(addr); clog_msg_str(">"); clog_msg_send(); #endif newq->rcptinfo_list[j].addresses.push_back(addr); newq->rcptinfo_list[j].addressesidx.push_back(i); } rfc822t_free(sendert); ctlfile_close(&ctlinfo); if (newq->nextsenddel <= current_time || /* ** If there are no more recipients, we want to call done() via ** start_message. HOWEVER, if DSN injection FAILED, we want to respect ** the rescheduled delivery attempt time. We can detect that if isnewmsg == 0 */ (newq->rcptinfo_list.size() == 0 && isnewmsg)) { newq->start_message(); return (0); } msgq *qp, *qprev; for (qprev=queuetail, qp=0; qprev; qp=qprev, qprev=qp->prev) if (qprev->nextsenddel < newq->nextsenddel) break; newq->next=qp; newq->prev=qprev; if (qprev) qprev->next=newq; else queuehead=newq; if (qp) qp->prev=newq; else queuetail=newq; ++queuewaiting; return (0); }
void msgq::queuescan() { static int queuescan_flag=0; if (queuescan_flag) return; // Recursive invocation if message just pulled into queue // has been delivered to all of its recipients. nextqueuefill=0; #if 0 clog_msg_start_info(); clog_msg_str("queue scan"); clog_msg_send(); #endif try { std::list<std::string> subdirlist; DIR *tmpdir=opendir(MSGQDIR); struct dirent *de; std::string s; queuescan_flag=1; if (!tmpdir) clog_msg_errno(); time(¤t_time); while ((de=readdir(tmpdir)) != 0) { const char *p; for (p=de->d_name; *p; p++) if (!isdigit((int)(unsigned char)*p)) break; if (*p) continue; // Subdirs must be named all digits p=de->d_name; time_t n=strtotime(p); std::list<std::string>::iterator sb, se; sb=subdirlist.begin(); se=subdirlist.end(); while (sb != se) { if (strtotime(sb->c_str()) > n) break; ++sb; } subdirlist.insert(sb, de->d_name); } closedir(tmpdir); while (!subdirlist.empty()) { s=MSGQDIR "/"; s += subdirlist.front(); subdirlist.pop_front(); if (queuescan2(s) <= 0) break; // Stop if we can't add any more msgs } queuescan_flag=0; } catch (...) { queuescan_flag=0; throw; } }