int main (int argc, char **argv) { XML *xml; QUEUE *q; QUEUEROW *r; MIME *m; char *ch; int arg = 1; xml = xml_parse (PhineasConfig); loadpath (cfg_installdir (xml)); queue_init (xml); /* test folder polling */ if ((argc > arg) && (strcmp (argv[arg], "-f") == 0)) { fpoller_register ("ebxml", ebxml_fprocessor); fpoller_task (xml); arg++; ran = 0; } /* test message from queue */ if ((argc > arg) && (strcmp (argv[arg], "-m") == 0)) { if ((q = queue_find ("MemSendQ")) == NULL) error ("can't find MemSendQ"); else if ((r = queue_pop (q)) == NULL) error ("can't pop row\n"); /* else if ((m = ebxml_getsoap (xml, r)) == NULL) error ("can't get soap container\n"); */ else if ((m = ebxml_getmessage (xml, r)) == NULL) error ("can't get message\n"); else if ((ch = mime_format (m)) == NULL) error ("Can't format soap containter\n"); else debug ("message MIME\n%s\n", ch); free (ch); mime_free (m); queue_row_free (r); arg++; } /* test queue polling */ if ((argc > arg) && (strcmp (argv[arg], "-q") == 0)) { qpoller_register ("EbXmlSndQ", ebxml_qprocessor); qpoller_task (xml); arg++; ran = 0; } queue_shutdown (); xml_free (xml); info ("%s %s\n", argv[0], Errors ? "failed" : "passed"); exit (Errors); }
static void queue_sig_handler(int sig, short event, void *p) { switch (sig) { case SIGINT: case SIGTERM: queue_shutdown(); break; default: fatalx("queue_sig_handler: unexpected signal"); } }
void sploit_shutdown(void) { ssl_shutdown(); net_shutdown(); str_shutdown(); io_shutdown(); dlink_shutdown(); queue_shutdown(); log_shutdown(); timer_shutdown(); mem_shutdown(); syscall_exit(1); }
/* -------------------------------------------------------------------------- * * Clean things up. * * -------------------------------------------------------------------------- */ void servauth_shutdown(void) { log(servauth_log, L_status, "Shutting down servauth..."); syscall_exit(0); connect_shutdown(); io_shutdown(); queue_shutdown(); dlink_shutdown(); mem_shutdown(); log_shutdown(); timer_shutdown(); log_source_unregister(servauth_log); }
pid_t queue(void) { pid_t pid; struct passwd *pw; struct timeval tv; struct event ev_qload; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("queue: cannot fork"); case 0: post_fork(PROC_QUEUE); break; default: return (pid); } purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); env->sc_queue_flags |= QUEUE_EVPCACHE; env->sc_queue_evpcache_size = 1024; if (chroot(PATH_SPOOL) == -1) fatal("queue: chroot"); if (chdir("/") == -1) fatal("queue: chdir(\"/\")"); config_process(PROC_QUEUE); if (env->sc_queue_flags & QUEUE_COMPRESSION) log_info("queue: queue compression enabled"); if (env->sc_queue_key) { if (! crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key))) fatalx("crypto_setup: invalid key for queue encryption"); log_info("queue: queue encryption enabled"); } if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("queue: cannot drop privileges"); imsg_callback = queue_imsg; event_init(); signal_set(&ev_sigint, SIGINT, queue_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, queue_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_CONTROL); config_peer(PROC_LKA); config_peer(PROC_SCHEDULER); config_peer(PROC_PONY); config_done(); /* setup queue loading task */ evtimer_set(&ev_qload, queue_timeout, &ev_qload); tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(&ev_qload, &tv); if (event_dispatch() < 0) fatal("event_dispatch"); queue_shutdown(); return (0); }
static void queue_imsg(struct mproc *p, struct imsg *imsg) { struct delivery_bounce bounce; struct msg_walkinfo *wi; struct timeval tv; struct bounce_req_msg *req_bounce; struct envelope evp; struct msg m; const char *reason; uint64_t reqid, evpid, holdq; uint32_t msgid; time_t nexttry; size_t n_evp; int fd, mta_ext, ret, v, flags, code; if (imsg == NULL) queue_shutdown(); memset(&bounce, 0, sizeof(struct delivery_bounce)); if (p->proc == PROC_PONY) { switch (imsg->hdr.type) { case IMSG_SMTP_MESSAGE_CREATE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_end(&m); ret = queue_message_create(&msgid); m_create(p, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); m_add_id(p, reqid); if (ret == 0) m_add_int(p, 0); else { m_add_int(p, 1); m_add_msgid(p, msgid); } m_close(p); return; case IMSG_SMTP_MESSAGE_ROLLBACK: m_msg(&m, imsg); m_get_msgid(&m, &msgid); m_end(&m); queue_message_delete(msgid); m_create(p_scheduler, IMSG_QUEUE_MESSAGE_ROLLBACK, 0, 0, -1); m_add_msgid(p_scheduler, msgid); m_close(p_scheduler); return; case IMSG_SMTP_MESSAGE_COMMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); ret = queue_message_commit(msgid); m_create(p, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, (ret == 0) ? 0 : 1); m_close(p); if (ret) { m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1); m_add_msgid(p_scheduler, msgid); m_close(p_scheduler); } return; case IMSG_SMTP_MESSAGE_OPEN: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); fd = queue_message_fd_rw(msgid); m_create(p, IMSG_SMTP_MESSAGE_OPEN, 0, 0, fd); m_add_id(p, reqid); m_add_int(p, (fd == -1) ? 0 : 1); m_close(p); return; case IMSG_QUEUE_SMTP_SESSION: bounce_fd(imsg->fd); return; } } if (p->proc == PROC_LKA) { switch (imsg->hdr.type) { case IMSG_LKA_ENVELOPE_SUBMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_envelope(&m, &evp); m_end(&m); if (evp.id == 0) log_warnx("warn: imsg_queue_submit_envelope: evpid=0"); if (evpid_to_msgid(evp.id) == 0) log_warnx("warn: imsg_queue_submit_envelope: msgid=0, " "evpid=%016"PRIx64, evp.id); ret = queue_envelope_create(&evp); m_create(p_pony, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); m_add_id(p_pony, reqid); if (ret == 0) m_add_int(p_pony, 0); else { m_add_int(p_pony, 1); m_add_evpid(p_pony, evp.id); } m_close(p_pony); if (ret) { m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); m_add_envelope(p_scheduler, &evp); m_close(p_scheduler); } return; case IMSG_LKA_ENVELOPE_COMMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_end(&m); m_create(p_pony, IMSG_QUEUE_ENVELOPE_COMMIT, 0, 0, -1); m_add_id(p_pony, reqid); m_add_int(p_pony, 1); m_close(p_pony); return; } } if (p->proc == PROC_SCHEDULER) { switch (imsg->hdr.type) { case IMSG_SCHED_ENVELOPE_REMOVE: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); /* already removed by scheduler */ if (queue_envelope_load(evpid, &evp) == 0) return; queue_log(&evp, "Remove", "Removed by administrator"); queue_envelope_delete(evpid); return; case IMSG_SCHED_ENVELOPE_EXPIRE: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); /* already removed by scheduler*/ if (queue_envelope_load(evpid, &evp) == 0) return; bounce.type = B_ERROR; envelope_set_errormsg(&evp, "Envelope expired"); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED); queue_bounce(&evp, &bounce); queue_log(&evp, "Expire", "Envelope expired"); queue_envelope_delete(evpid); return; case IMSG_SCHED_ENVELOPE_BOUNCE: CHECK_IMSG_DATA_SIZE(imsg, sizeof *req_bounce); req_bounce = imsg->data; evpid = req_bounce->evpid; if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: bounce: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 0); /* not in-flight */ m_close(p_scheduler); return; } queue_bounce(&evp, &req_bounce->bounce); evp.lastbounce = req_bounce->timestamp; if (!queue_envelope_update(&evp)) log_warnx("warn: could not update envelope %016"PRIx64, evpid); return; case IMSG_SCHED_ENVELOPE_DELIVER: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: deliver: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } evp.lasttry = time(NULL); m_create(p_pony, IMSG_QUEUE_DELIVER, 0, 0, -1); m_add_envelope(p_pony, &evp); m_close(p_pony); return; case IMSG_SCHED_ENVELOPE_INJECT: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); bounce_add(evpid); return; case IMSG_SCHED_ENVELOPE_TRANSFER: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } evp.lasttry = time(NULL); m_create(p_pony, IMSG_QUEUE_TRANSFER, 0, 0, -1); m_add_envelope(p_pony, &evp); m_close(p_pony); return; case IMSG_CTL_LIST_ENVELOPES: if (imsg->hdr.len == sizeof imsg->hdr) { m_forward(p_control, imsg); return; } m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_int(&m, &flags); m_get_time(&m, &nexttry); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) return; /* Envelope is gone, drop it */ /* * XXX consistency: The envelope might already be on * its way back to the scheduler. We need to detect * this properly and report that state. */ if (flags & EF_INFLIGHT) { /* * Not exactly correct but pretty close: The * value is not recorded on the envelope unless * a tempfail occurs. */ evp.lasttry = nexttry; } m_create(p_control, IMSG_CTL_LIST_ENVELOPES, imsg->hdr.peerid, 0, -1); m_add_int(p_control, flags); m_add_time(p_control, nexttry); m_add_envelope(p_control, &evp); m_close(p_control); return; } } if (p->proc == PROC_PONY) { switch (imsg->hdr.type) { case IMSG_MDA_OPEN_MESSAGE: case IMSG_MTA_OPEN_MESSAGE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); fd = queue_message_fd_r(msgid); m_create(p, imsg->hdr.type, 0, 0, fd); m_add_id(p, reqid); m_close(p); return; case IMSG_MDA_DELIVERY_OK: case IMSG_MTA_DELIVERY_OK: m_msg(&m, imsg); m_get_evpid(&m, &evpid); if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK) m_get_int(&m, &mta_ext); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warn("queue: dsn: failed to load envelope"); return; } if (evp.dsn_notify & DSN_SUCCESS) { bounce.type = B_DSN; bounce.dsn_ret = evp.dsn_ret; envelope_set_esc_class(&evp, ESC_STATUS_OK); if (imsg->hdr.type == IMSG_MDA_DELIVERY_OK) queue_bounce(&evp, &bounce); else if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK && (mta_ext & MTA_EXT_DSN) == 0) { bounce.mta_without_dsn = 1; queue_bounce(&evp, &bounce); } } queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_OK, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_TEMPFAIL: case IMSG_MTA_DELIVERY_TEMPFAIL: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: tempfail: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } envelope_set_errormsg(&evp, "%s", reason); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, code); evp.retry++; if (!queue_envelope_update(&evp)) log_warnx("warn: could not update envelope %016"PRIx64, evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_TEMPFAIL, 0, 0, -1); m_add_envelope(p_scheduler, &evp); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_PERMFAIL: case IMSG_MTA_DELIVERY_PERMFAIL: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: permfail: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } bounce.type = B_ERROR; envelope_set_errormsg(&evp, "%s", reason); envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL); envelope_set_esc_code(&evp, code); queue_bounce(&evp, &bounce); queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_LOOP: case IMSG_MTA_DELIVERY_LOOP: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: loop: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } envelope_set_errormsg(&evp, "%s", "Loop detected"); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED); bounce.type = B_ERROR; queue_bounce(&evp, &bounce); queue_envelope_delete(evp.id); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_LOOP, 0, 0, -1); m_add_evpid(p_scheduler, evp.id); m_close(p_scheduler); return; case IMSG_MTA_DELIVERY_HOLD: case IMSG_MDA_DELIVERY_HOLD: imsg->hdr.type = IMSG_QUEUE_HOLDQ_HOLD; m_forward(p_scheduler, imsg); return; case IMSG_MTA_SCHEDULE: imsg->hdr.type = IMSG_QUEUE_ENVELOPE_SCHEDULE; m_forward(p_scheduler, imsg); return; case IMSG_MTA_HOLDQ_RELEASE: case IMSG_MDA_HOLDQ_RELEASE: m_msg(&m, imsg); m_get_id(&m, &holdq); m_get_int(&m, &v); m_end(&m); m_create(p_scheduler, IMSG_QUEUE_HOLDQ_RELEASE, 0, 0, -1); if (imsg->hdr.type == IMSG_MTA_HOLDQ_RELEASE) m_add_int(p_scheduler, D_MTA); else m_add_int(p_scheduler, D_MDA); m_add_id(p_scheduler, holdq); m_add_int(p_scheduler, v); m_close(p_scheduler); return; } } if (p->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_CTL_PAUSE_MDA: case IMSG_CTL_PAUSE_MTA: case IMSG_CTL_RESUME_MDA: case IMSG_CTL_RESUME_MTA: m_forward(p_scheduler, imsg); return; case IMSG_CTL_VERBOSE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); log_verbose(v); return; case IMSG_CTL_PROFILE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); profiling = v; return; case IMSG_CTL_DISCOVER_EVPID: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: discover: failed to load " "envelope %016" PRIx64, evpid); n_evp = 0; m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid, 0, -1, &n_evp, sizeof n_evp); return; } m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID, 0, 0, -1); m_add_envelope(p_scheduler, &evp); m_close(p_scheduler); m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID, 0, 0, -1); m_add_msgid(p_scheduler, evpid_to_msgid(evpid)); m_close(p_scheduler); n_evp = 1; m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid, 0, -1, &n_evp, sizeof n_evp); return; case IMSG_CTL_DISCOVER_MSGID: m_msg(&m, imsg); m_get_msgid(&m, &msgid); m_end(&m); /* handle concurrent walk requests */ wi = xcalloc(1, sizeof *wi, "queu_imsg"); wi->msgid = msgid; wi->peerid = imsg->hdr.peerid; evtimer_set(&wi->ev, queue_msgid_walk, wi); tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(&wi->ev, &tv); return; case IMSG_CTL_UNCORRUPT_MSGID: m_msg(&m, imsg); m_get_msgid(&m, &msgid); m_end(&m); ret = queue_message_uncorrupt(msgid); m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid, 0, -1, &ret, sizeof ret); return; } } errx(1, "queue_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }
//----------------------------------------------------------------------------- // When the SIGINT signal is received, we need to start shutting down the // service. This means that it needs to: // 1. Close the listening socket. // 2. Send a CLOSING message to every connected node. // 3. if there are any undelivered messages in the queues, we need to return them. // 4. Shutdown the nodes if we can. // 5. Shutdown the queues if we can. // 6. Tell the stats system to close its event as soon as there are no more nodes connected. void sigint_handler(evutil_socket_t fd, short what, void *arg) { system_data_t *sysdata = (system_data_t *) arg; server_t *server; stats_t *stats; queue_t *q; node_t *node; controller_t *ct; logger(sysdata->logging, 3, "SIGINT"); assert(sysdata); assert(sysdata->servers); // delete the sigint event, we dont need it anymore. assert(sysdata->sigint_event); event_free(sysdata->sigint_event); sysdata->sigint_event = NULL; // delete the sighup event, we dont need that either. assert(sysdata->sighup_event); event_free(sysdata->sighup_event); sysdata->sighup_event = NULL; // delete the sigusr1 event, we dont need that either. assert(sysdata->sigusr1_event); event_free(sysdata->sigusr1_event); sysdata->sigusr1_event = NULL; // delete the sigusr2 event, we dont need that either. assert(sysdata->sigusr2_event); event_free(sysdata->sigusr2_event); sysdata->sigusr2_event = NULL; logger(sysdata->logging, 2, "Shutting down servers."); while ((server = ll_pop_head(sysdata->servers))) { server_shutdown(server); server_free(server); free(server); } // All active nodes should be informed that we are shutting down. logger(sysdata->logging, 2, "Shutting down nodes."); assert(sysdata->nodelist); ll_start(sysdata->nodelist); while ((node = ll_next(sysdata->nodelist))) { assert(node->handle > 0); logger(sysdata->logging, 2, "Initiating shutdown of node %d.", node->handle); node_shutdown(node); } ll_finish(sysdata->nodelist); // Now attempt to shutdown all the queues. Basically, this just means that the queue will close down all the 'waiting' consumers. logger(sysdata->logging, 2, "Initiating shutdown of queues."); ll_start(sysdata->queues); while ((q = ll_next(sysdata->queues))) { assert(q->name != NULL); assert(q->qid > 0); logger(sysdata->logging, 2, "Initiating shutdown of queue %d ('%s').", q->qid, q->name); queue_shutdown(q); } ll_finish(sysdata->queues); // if we have controllers that are attempting to connect, we need to change their status so that they dont. logger(sysdata->logging, 2, "Stopping controllers that are connecting."); ll_start(sysdata->controllers); while ((ct = ll_next(sysdata->controllers))) { BIT_SET(ct->flags, FLAG_CONTROLLER_FAILED); if (ct->connect_event) { event_free(ct->connect_event); ct->connect_event = NULL; } } ll_finish(sysdata->controllers); // Put stats event on notice that we are shutting down, so that as soon as there are no more nodes, it needs to stop its event. assert(sysdata != NULL); assert(sysdata->stats != NULL); stats = sysdata->stats; assert(stats->shutdown == 0); stats->shutdown ++; // Tell the logging system not to use events anymore. log_direct(sysdata->logging); }