summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/smtpctl.c
diff options
context:
space:
mode:
authorEric Faurot <eric@cvs.openbsd.org>2013-07-19 13:41:24 +0000
committerEric Faurot <eric@cvs.openbsd.org>2013-07-19 13:41:24 +0000
commit3f682f32166e1fac78ffd6f2bf08d03774fe2f74 (patch)
tree5a303ff71988aaea3b6eba67fb55d9f5497e1171 /usr.sbin/smtpd/smtpctl.c
parent23185218495ab7acbd9aa2e4c6d6e38bed8e0650 (diff)
New implementation for smtpctl and the command line parser. Allows
richer syntax, and makes the code way simpler to follow and extend with new commands.
Diffstat (limited to 'usr.sbin/smtpd/smtpctl.c')
-rw-r--r--usr.sbin/smtpd/smtpctl.c1272
1 files changed, 687 insertions, 585 deletions
diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c
index ea1926fc129..c19bb7d012f 100644
--- a/usr.sbin/smtpd/smtpctl.c
+++ b/usr.sbin/smtpd/smtpctl.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: smtpctl.c,v 1.105 2013/07/19 11:14:08 eric Exp $ */
+/* $OpenBSD: smtpctl.c,v 1.106 2013/07/19 13:41:23 eric Exp $ */
/*
+ * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
* Copyright (c) 2006 Gilles Chehade <gilles@poolp.org>
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
@@ -30,6 +31,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <fts.h>
#include <imsg.h>
#include <inttypes.h>
#include <pwd.h>
@@ -43,43 +45,33 @@
#include "parser.h"
#include "log.h"
-#define PATH_CAT "/bin/cat"
#define PATH_GZCAT "/usr/bin/gzcat"
+#define PATH_CAT "/bin/cat"
#define PATH_QUEUE "/queue"
void usage(void);
-static void setup_env(struct smtpd *);
-static int show_command_output(struct imsg *);
-static void show_stats_output(void);
-static void show_queue(int);
static void show_queue_envelope(struct envelope *, int);
static void getflag(uint *, int, char *, char *, size_t);
static void display(const char *);
-static void show_envelope(const char *);
-static void show_message(const char *);
-static void show_monitor(struct stat_digest *);
-
-static int try_connect(void);
-static void flush(void);
-static void next_message(struct imsg *);
-static int action_schedule_all(void);
-
-static int action_show_queue(void);
-static int action_show_queue_message(uint32_t);
-static uint32_t trace_convert(uint32_t);
-static uint32_t profile_convert(uint32_t);
-
-int proctype;
+static int str_to_trace(const char *);
+static int str_to_profile(const char *);
+static void show_offline_envelope(uint64_t);
+static int is_gzip_fp(FILE *);
+static int is_encrypted_fp(FILE *);
+static int is_encrypted_buffer(const char *);
+static int is_gzip_buffer(const char *);
+
+extern char *__progname;
+int sendmail;
+struct smtpd *env;
struct imsgbuf *ibuf;
-
-int sendmail = 0;
-extern char *__progname;
-
-struct smtpd *env = NULL;
-
-time_t now;
+struct imsg imsg;
+char *rdata;
+size_t rlen;
+time_t now;
struct queue_backend queue_backend_null;
+struct queue_backend queue_backend_proc;
struct queue_backend queue_backend_ram;
__dead void
@@ -96,18 +88,16 @@ usage(void)
exit(1);
}
-static void
-setup_env(struct smtpd *smtpd)
+void stat_increment(const char *k, size_t v)
{
- bzero(smtpd, sizeof (*smtpd));
- env = smtpd;
+}
- if (!queue_init("fs", 0))
- errx(1, "invalid directory permissions");
+void stat_decrement(const char *k, size_t v)
+{
}
static int
-try_connect(void)
+srv_connect(void)
{
struct sockaddr_un sun;
int ctl_sock, saved_errno;
@@ -134,22 +124,41 @@ try_connect(void)
}
static void
-flush(void)
+srv_flush(void)
{
if (imsg_flush(ibuf) == -1)
err(1, "write error");
}
static void
-next_message(struct imsg *imsg)
+srv_send(int msg, const void *data, size_t len)
+{
+ if (ibuf == NULL && !srv_connect())
+ errx(1, "smtpd doesn't seem to be running");
+ imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len);
+}
+
+static void
+srv_recv(int type)
{
ssize_t n;
+ srv_flush();
+
while (1) {
- if ((n = imsg_get(ibuf, imsg)) == -1)
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
errx(1, "imsg_get error");
- if (n)
- return;
+ if (n) {
+ if (imsg.hdr.type == IMSG_CTL_FAIL &&
+ imsg.hdr.peerid != 0 &&
+ imsg.hdr.peerid != IMSG_VERSION)
+ errx(1, "incompatible smtpctl and smtpd");
+ if (type != -1 && type != (int)imsg.hdr.type)
+ errx(1, "bad message type");
+ rdata = imsg.data;
+ rlen = imsg.hdr.len - sizeof(imsg.hdr);
+ break;
+ }
if ((n = imsg_read(ibuf)) == -1)
errx(1, "imsg_read error");
@@ -158,475 +167,565 @@ next_message(struct imsg *imsg)
}
}
-int
-main(int argc, char *argv[])
-{
- struct parse_result *res = NULL;
- struct imsg imsg;
- struct smtpd smtpd;
- uint64_t ulval;
- char name[SMTPD_MAXLINESIZE];
- int done = 0;
- int verb = 0;
- int profile = 0;
- int action = -1;
-
- /* parse options */
- if (strcmp(__progname, "sendmail") == 0 ||
- strcmp(__progname, "send-mail") == 0) {
- sendmail = 1;
- if (try_connect())
- return (enqueue(argc, argv));
- return (enqueue_offline(argc, argv));
- }
+static void
+srv_read(void *dst, size_t sz)
+{
+ if (sz == 0)
+ return;
+ if (rlen < sz)
+ errx(1, "message too short");
+ if (dst)
+ memmove(dst, rdata, sz);
+ rlen -= sz;
+ rdata += sz;
+}
- if (geteuid())
- errx(1, "need root privileges");
+static void
+srv_end(void)
+{
+ if (rlen)
+ errx(1, "bogus data");
+ imsg_free(&imsg);
+}
- if (strcmp(__progname, "mailq") == 0)
- action = SHOW_QUEUE;
- else if (strcmp(__progname, "smtpctl") == 0) {
- if ((res = parse(argc - 1, argv + 1)) == NULL)
- exit(1);
- action = res->action;
- } else
- errx(1, "unsupported mode");
-
- if (action == SHOW_ENVELOPE ||
- action == SHOW_MESSAGE ||
- !try_connect()) {
- setup_env(&smtpd);
- switch (action) {
- case SHOW_QUEUE:
- show_queue(0);
- break;
- case SHOW_ENVELOPE:
- show_envelope(res->data);
- break;
- case SHOW_MESSAGE:
- show_message(res->data);
- break;
- default:
- errx(1, "smtpd doesn't seem to be running");
- }
+static int
+srv_check_result(void)
+{
+ srv_recv(-1);
+ srv_end();
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_OK:
+ printf("command succeeded\n");
return (0);
+ case IMSG_CTL_FAIL:
+ if (rlen)
+ printf("command failed: %s\n", rdata);
+ else
+ printf("command failed\n");
+ return (1);
+ default:
+ errx(1, "wrong message in response: %u", imsg.hdr.type);
}
+ return (0);
+}
+
+static int
+srv_iter_messages(uint32_t *res)
+{
+ static uint32_t *msgids = NULL, from = 0;
+ static size_t n, curr;
+ static int done = 0;
+
+ if (done)
+ return (0);
- /* process user request */
- switch (action) {
- case NONE:
- usage();
- /* not reached */
-
- case SCHEDULE:
- if (! strcmp(res->data, "all"))
- return action_schedule_all();
-
- if ((ulval = text_to_evpid(res->data)) == 0)
- errx(1, "invalid msgid/evpid");
- imsg_compose(ibuf, IMSG_CTL_SCHEDULE, IMSG_VERSION, 0, -1, &ulval,
- sizeof(ulval));
- break;
- case REMOVE:
- if ((ulval = text_to_evpid(res->data)) == 0)
- errx(1, "invalid msgid/evpid");
- imsg_compose(ibuf, IMSG_CTL_REMOVE, IMSG_VERSION, 0, -1, &ulval,
- sizeof(ulval));
- break;
- case SHOW_QUEUE:
- return action_show_queue();
- case SHUTDOWN:
- imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case PAUSE_MDA:
- imsg_compose(ibuf, IMSG_CTL_PAUSE_MDA, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case PAUSE_MTA:
- imsg_compose(ibuf, IMSG_CTL_PAUSE_MTA, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case PAUSE_SMTP:
- imsg_compose(ibuf, IMSG_CTL_PAUSE_SMTP, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case RESUME_MDA:
- imsg_compose(ibuf, IMSG_CTL_RESUME_MDA, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case RESUME_MTA:
- imsg_compose(ibuf, IMSG_CTL_RESUME_MTA, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case RESUME_SMTP:
- imsg_compose(ibuf, IMSG_CTL_RESUME_SMTP, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case SHOW_STATS:
- imsg_compose(ibuf, IMSG_STATS, IMSG_VERSION, 0, -1, NULL, 0);
- break;
- case UPDATE_TABLE:
- if (strlcpy(name, res->data, sizeof name) >= sizeof name)
- errx(1, "table name too long.");
- imsg_compose(ibuf, IMSG_LKA_UPDATE_TABLE, IMSG_VERSION, 0, -1,
- name, strlen(name) + 1);
- done = 1;
- break;
- case MONITOR:
- while (1) {
- imsg_compose(ibuf, IMSG_DIGEST, IMSG_VERSION, 0, -1, NULL, 0);
- flush();
- next_message(&imsg);
- show_monitor(imsg.data);
- imsg_free(&imsg);
- sleep(1);
+ if (msgids == NULL) {
+ srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from));
+ srv_recv(IMSG_CTL_LIST_MESSAGES);
+ if (rlen == 0) {
+ srv_end();
+ done = 1;
+ return (0);
}
- break;
- case LOG_VERBOSE:
- verb = TRACE_DEBUG;
- /* FALLTHROUGH */
- case LOG_BRIEF:
- imsg_compose(ibuf, IMSG_CTL_VERBOSE, IMSG_VERSION, 0, -1, &verb,
- sizeof(verb));
- printf("logging request sent.\n");
- done = 1;
- break;
-
- case LOG_TRACE_IMSG:
- case LOG_TRACE_IO:
- case LOG_TRACE_SMTP:
- case LOG_TRACE_MFA:
- case LOG_TRACE_MTA:
- case LOG_TRACE_BOUNCE:
- case LOG_TRACE_SCHEDULER:
- case LOG_TRACE_LOOKUP:
- case LOG_TRACE_STAT:
- case LOG_TRACE_RULES:
- case LOG_TRACE_MPROC:
- case LOG_TRACE_EXPAND:
- case LOG_TRACE_ALL:
- verb = trace_convert(action);
- imsg_compose(ibuf, IMSG_CTL_TRACE, IMSG_VERSION, 0, -1, &verb,
- sizeof(verb));
- done = 1;
- break;
-
- case LOG_UNTRACE_IMSG:
- case LOG_UNTRACE_IO:
- case LOG_UNTRACE_SMTP:
- case LOG_UNTRACE_MFA:
- case LOG_UNTRACE_MTA:
- case LOG_UNTRACE_BOUNCE:
- case LOG_UNTRACE_SCHEDULER:
- case LOG_UNTRACE_LOOKUP:
- case LOG_UNTRACE_STAT:
- case LOG_UNTRACE_RULES:
- case LOG_UNTRACE_MPROC:
- case LOG_UNTRACE_EXPAND:
- case LOG_UNTRACE_ALL:
- verb = trace_convert(action);
- imsg_compose(ibuf, IMSG_CTL_UNTRACE, IMSG_VERSION, 0, -1, &verb,
- sizeof(verb));
- done = 1;
- break;
-
- case LOG_PROFILE_IMSG:
- case LOG_PROFILE_QUEUE:
- profile = profile_convert(action);
- imsg_compose(ibuf, IMSG_CTL_PROFILE, IMSG_VERSION, 0, -1, &profile,
- sizeof(profile));
- done = 1;
- break;
-
- case LOG_UNPROFILE_IMSG:
- case LOG_UNPROFILE_QUEUE:
- profile = profile_convert(action);
- imsg_compose(ibuf, IMSG_CTL_UNPROFILE, IMSG_VERSION, 0, -1, &profile,
- sizeof(profile));
- done = 1;
- break;
+ msgids = malloc(rlen);
+ n = rlen / sizeof(*msgids);
+ srv_read(msgids, rlen);
+ srv_end();
- default:
- errx(1, "unknown request (%d)", action);
+ curr = 0;
+ from = msgids[n - 1] + 1;
+ if (from == 0)
+ done = 1;
}
- do {
- flush();
- next_message(&imsg);
+ *res = msgids[curr++];
+ if (curr == n) {
+ free(msgids);
+ msgids = NULL;
+ }
- /* ANY command can return IMSG_CTL_FAIL if version mismatch */
- if (imsg.hdr.type == IMSG_CTL_FAIL) {
- show_command_output(&imsg);
- break;
- }
+ return (1);
+}
- switch (action) {
- case REMOVE:
- case SCHEDULE:
- case SHUTDOWN:
- case PAUSE_MDA:
- case PAUSE_MTA:
- case PAUSE_SMTP:
- case RESUME_MDA:
- case RESUME_MTA:
- case RESUME_SMTP:
- case LOG_VERBOSE:
- case LOG_BRIEF:
- case LOG_TRACE_IMSG:
- case LOG_TRACE_IO:
- case LOG_TRACE_SMTP:
- case LOG_TRACE_MFA:
- case LOG_TRACE_MTA:
- case LOG_TRACE_BOUNCE:
- case LOG_TRACE_SCHEDULER:
- case LOG_TRACE_LOOKUP:
- case LOG_TRACE_STAT:
- case LOG_TRACE_RULES:
- case LOG_TRACE_MPROC:
- case LOG_TRACE_EXPAND:
- case LOG_TRACE_ALL:
- case LOG_UNTRACE_IMSG:
- case LOG_UNTRACE_IO:
- case LOG_UNTRACE_SMTP:
- case LOG_UNTRACE_MFA:
- case LOG_UNTRACE_MTA:
- case LOG_UNTRACE_BOUNCE:
- case LOG_UNTRACE_SCHEDULER:
- case LOG_UNTRACE_LOOKUP:
- case LOG_UNTRACE_STAT:
- case LOG_UNTRACE_RULES:
- case LOG_UNTRACE_MPROC:
- case LOG_UNTRACE_EXPAND:
- case LOG_UNTRACE_ALL:
- case LOG_PROFILE_IMSG:
- case LOG_PROFILE_QUEUE:
- case LOG_UNPROFILE_IMSG:
- case LOG_UNPROFILE_QUEUE:
- done = show_command_output(&imsg);
- break;
- case SHOW_STATS:
- show_stats_output();
- done = 1;
- break;
- case NONE:
- break;
- case UPDATE_TABLE:
- break;
- case MONITOR:
+static int
+srv_iter_envelopes(uint32_t msgid, struct envelope *evp)
+{
+ static uint32_t currmsgid = 0;
+ static uint64_t from = 0;
+ static int done = 0, need_send = 1, found;
+
+ if (currmsgid != msgid) {
+ if (currmsgid != 0 && !done)
+ errx(1, "must finish current iteration first");
+ currmsgid = msgid;
+ from = msgid_to_evpid(msgid);
+ done = 0;
+ found = 0;
+ need_send = 1;
+ }
- default:
- err(1, "unexpected reply (%d)", action);
- }
+ if (done)
+ return (0);
- imsg_free(&imsg);
- } while (!done);
- free(ibuf);
+ again:
+ if (need_send) {
+ found = 0;
+ srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from));
+ }
+ need_send = 0;
- return (0);
+ srv_recv(IMSG_CTL_LIST_ENVELOPES);
+ if (rlen == 0) {
+ srv_end();
+ if (!found || evpid_to_msgid(from) != msgid) {
+ done = 1;
+ return (0);
+ }
+ need_send = 1;
+ goto again;
+ }
+
+ srv_read(evp, sizeof(*evp));
+ srv_end();
+ from = evp->id + 1;
+ found++;
+ return (1);
}
+static int
+do_log_brief(int argc, struct parameter *argv)
+{
+ int v = 0;
+
+ srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
+ return srv_check_result();
+}
static int
-action_show_queue_message(uint32_t msgid)
+do_log_verbose(int argc, struct parameter *argv)
{
- struct imsg imsg;
- struct envelope *evp;
- uint64_t evpid;
- size_t found;
+ int v = TRACE_DEBUG;
- evpid = msgid_to_evpid(msgid);
+ srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
+ return srv_check_result();
+}
- nextbatch:
+static int
+do_monitor(int argc, struct parameter *argv)
+{
+ struct stat_digest last, digest;
+ size_t count;
- found = 0;
- imsg_compose(ibuf, IMSG_CTL_LIST_ENVELOPES, IMSG_VERSION, 0, -1,
- &evpid, sizeof evpid);
- flush();
+ bzero(&last, sizeof(last));
+ count = 0;
while (1) {
- next_message(&imsg);
- if (imsg.hdr.type != IMSG_CTL_LIST_ENVELOPES)
- errx(1, "unexpected message %i", imsg.hdr.type);
-
- if (imsg.hdr.len == sizeof imsg.hdr) {
- imsg_free(&imsg);
- if (!found || evpid_to_msgid(++evpid) != msgid)
- return (0);
- goto nextbatch;
+ srv_send(IMSG_DIGEST, NULL, 0);
+ srv_recv(IMSG_DIGEST);
+ srv_read(&digest, sizeof(digest));
+ srv_end();
+
+ if (count % 25 == 0) {
+ if (count != 0)
+ printf("\n");
+ printf("--- client --- "
+ "-- envelope -- "
+ "---- relay/delivery --- "
+ "------- misc -------\n"
+ "curr conn disc "
+ "curr enq deq "
+ "ok tmpfail prmfail loop "
+ "expire remove bounce\n");
}
- found++;
- evp = imsg.data;
- evpid = evp->id;
- show_queue_envelope(evp, 1);
- imsg_free(&imsg);
+ printf("%4zu %4zu %4zu "
+ "%4zu %4zu %4zu "
+ "%4zu %4zu %4zu %4zu "
+ "%4zu %4zu %4zu\n",
+ digest.clt_connect - digest.clt_disconnect,
+ digest.clt_connect - last.clt_connect,
+ digest.clt_disconnect - last.clt_disconnect,
+
+ digest.evp_enqueued - digest.evp_dequeued,
+ digest.evp_enqueued - last.evp_enqueued,
+ digest.evp_dequeued - last.evp_dequeued,
+
+ digest.dlv_ok - last.dlv_ok,
+ digest.dlv_tempfail - last.dlv_tempfail,
+ digest.dlv_permfail - last.dlv_permfail,
+ digest.dlv_loop - last.dlv_loop,
+
+ digest.evp_expired - last.evp_expired,
+ digest.evp_removed - last.evp_removed,
+ digest.evp_bounce - last.evp_bounce);
+
+ last = digest;
+ count++;
+ sleep(1);
}
+ return (0);
+}
+
+static int
+do_pause_mda(int argc, struct parameter *argv)
+{
+ srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0);
+ return srv_check_result();
}
static int
-action_show_queue(void)
+do_pause_mta(int argc, struct parameter *argv)
{
- struct imsg imsg;
- uint32_t *msgids, msgid;
- size_t i, n;
+ srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0);
+ return srv_check_result();
+}
- msgid = 0;
- now = time(NULL);
+static int
+do_pause_smtp(int argc, struct parameter *argv)
+{
+ srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0);
+ return srv_check_result();
+}
- do {
- imsg_compose(ibuf, IMSG_CTL_LIST_MESSAGES, IMSG_VERSION, 0, -1,
- &msgid, sizeof msgid);
- flush();
- next_message(&imsg);
- if (imsg.hdr.type != IMSG_CTL_LIST_MESSAGES)
- errx(1, "unexpected message type %i", imsg.hdr.type);
- msgids = imsg.data;
- n = (imsg.hdr.len - sizeof imsg.hdr) / sizeof (*msgids);
- if (n == 0) {
- imsg_free(&imsg);
- break;
+static int
+do_profile(int argc, struct parameter *argv)
+{
+ int v;
+
+ v = str_to_profile(argv[0].u.u_str);
+
+ srv_send(IMSG_CTL_PROFILE, &v, sizeof(v));
+ return srv_check_result();
+}
+
+static int
+do_remove(int argc, struct parameter *argv)
+{
+ struct envelope evp;
+ uint32_t msgid;
+
+ if (argc == 0) {
+ while (srv_iter_messages(&msgid)) {
+ while (srv_iter_envelopes(msgid, &evp)) {
+ srv_send(IMSG_CTL_REMOVE, &evp.id,
+ sizeof(evp.id));
+ srv_check_result();
+ }
}
- for (i = 0; i < n; i++) {
- msgid = msgids[i];
- action_show_queue_message(msgid);
+ } else if (argv[0].type == P_MSGID) {
+ while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) {
+ srv_send(IMSG_CTL_REMOVE, &evp.id, sizeof(evp.id));
+ srv_check_result();
}
- imsg_free(&imsg);
-
- } while (++msgid);
+ } else {
+ srv_send(IMSG_CTL_REMOVE, &argv[0].u.u_evpid, sizeof(evp.id));
+ srv_check_result();
+ }
return (0);
}
static int
-action_schedule_all(void)
+do_resume_mda(int argc, struct parameter *argv)
{
- struct imsg imsg;
- uint32_t *msgids, from;
- uint64_t evpid;
- size_t i, n;
-
- from = 0;
- while (1) {
- imsg_compose(ibuf, IMSG_CTL_LIST_MESSAGES, IMSG_VERSION, 0, -1,
- &from, sizeof from);
- flush();
- next_message(&imsg);
- if (imsg.hdr.type != IMSG_CTL_LIST_MESSAGES)
- errx(1, "unexpected message type %i", imsg.hdr.type);
- msgids = imsg.data;
- n = (imsg.hdr.len - sizeof imsg.hdr) / sizeof (*msgids);
- if (n == 0)
- break;
+ srv_send(IMSG_CTL_RESUME_MDA, NULL, 0);
+ return srv_check_result();
+}
- for (i = 0; i < n; i++) {
- evpid = msgids[i];
- imsg_compose(ibuf, IMSG_CTL_SCHEDULE, IMSG_VERSION,
- 0, -1, &evpid, sizeof(evpid));
- }
- from = msgids[n - 1] + 1;
+static int
+do_resume_mta(int argc, struct parameter *argv)
+{
+ srv_send(IMSG_CTL_RESUME_MTA, NULL, 0);
+ return srv_check_result();
+}
- imsg_free(&imsg);
- flush();
+static int
+do_resume_smtp(int argc, struct parameter *argv)
+{
+ srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0);
+ return srv_check_result();
+}
- for (i = 0; i < n; i++) {
- next_message(&imsg);
- if (imsg.hdr.type != IMSG_CTL_OK)
- errx(1, "unexpected message type %i",
- imsg.hdr.type);
+static int
+do_schedule(int argc, struct parameter *argv)
+{
+ struct envelope evp;
+ uint32_t msgid;
+
+ if (argc == 0) {
+ while (srv_iter_messages(&msgid)) {
+ while (srv_iter_envelopes(msgid, &evp)) {
+ srv_send(IMSG_CTL_SCHEDULE, &evp.id,
+ sizeof(evp.id));
+ srv_check_result();
+ }
}
- if (from == 0)
- break;
+ } else if (argv[0].type == P_MSGID) {
+ while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) {
+ srv_send(IMSG_CTL_SCHEDULE, &evp.id, sizeof(evp.id));
+ srv_check_result();
+ }
+ } else {
+ srv_send(IMSG_CTL_SCHEDULE, &argv[0].u.u_evpid, sizeof(evp.id));
+ srv_check_result();
}
return (0);
}
static int
-show_command_output(struct imsg *imsg)
+do_show_envelope(int argc, struct parameter *argv)
{
- switch (imsg->hdr.type) {
- case IMSG_CTL_OK:
- printf("command succeeded\n");
- break;
- case IMSG_CTL_FAIL:
- if (imsg->hdr.peerid != IMSG_VERSION)
- printf("command failed: incompatible smtpctl and smtpd\n");
- else
- printf("command failed\n");
- break;
- default:
- errx(1, "wrong message in summary: %u", imsg->hdr.type);
+ char buf[SMTPD_MAXPATHLEN];
+
+ if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64,
+ PATH_SPOOL,
+ PATH_QUEUE,
+ (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24,
+ evpid_to_msgid(argv[0].u.u_evpid),
+ argv[0].u.u_evpid))
+ errx(1, "unable to retrieve envelope");
+
+ display(buf);
+
+ return (0);
+}
+
+static int
+do_show_message(int argc, struct parameter *argv)
+{
+ char buf[SMTPD_MAXPATHLEN];
+ uint32_t msgid;
+
+ if (argv[0].type == P_EVPID)
+ msgid = evpid_to_msgid(argv[0].u.u_evpid);
+ else
+ msgid = argv[0].u.u_msgid;
+
+ if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message",
+ PATH_SPOOL,
+ PATH_QUEUE,
+ (msgid & 0xff000000) >> 24,
+ msgid))
+ errx(1, "unable to retrieve message");
+
+ display(buf);
+
+ return (0);
+}
+
+static int
+do_show_queue(int argc, struct parameter *argv)
+{
+ struct envelope evp;
+ uint32_t msgid;
+ FTS *fts;
+ FTSENT *ftse;
+ char *qpath[] = {"/queue", NULL};
+ char *tmp;
+ uint64_t evpid;
+
+ now = time(NULL);
+
+ if (!srv_connect()) {
+ log_init(1);
+ queue_init("fs", 0);
+ if (chroot(PATH_SPOOL) == -1 || chdir(".") == -1)
+ err(1, "%s", PATH_SPOOL);
+ fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL);
+ if (fts == NULL)
+ err(1, "%s/queue", PATH_SPOOL);
+
+ while ((ftse = fts_read(fts)) != NULL) {
+ switch (ftse->fts_info) {
+ case FTS_DP:
+ case FTS_DNR:
+ break;
+ case FTS_F:
+ tmp = NULL;
+ evpid = strtoull(ftse->fts_name, &tmp, 16);
+ if (tmp && *tmp != '\0')
+ break;
+ show_offline_envelope(evpid);
+ }
+ }
+
+ fts_close(fts);
+ /*
+ while ((r = queue_envelope_walk(&evp)) != -1)
+ if (r)
+ show_queue_envelope(&evp, 0);
+ */
+ return (0);
}
- return (1);
+
+ if (argc == 0) {
+ msgid = 0;
+ while (srv_iter_messages(&msgid))
+ while (srv_iter_envelopes(msgid, &evp))
+ show_queue_envelope(&evp, 1);
+ } else if (argv[0].type == P_MSGID) {
+ while (srv_iter_envelopes(argv[0].u.u_msgid, &evp))
+ show_queue_envelope(&evp, 1);
+ }
+
+ return (0);
}
-static void
-show_stats_output(void)
+static int
+do_show_stats(int argc, struct parameter *argv)
{
- struct stat_kv kv, *kvp;
- struct imsg imsg;
+ struct stat_kv kv;
time_t duration;
bzero(&kv, sizeof kv);
while (1) {
- imsg_compose(ibuf, IMSG_STATS_GET, IMSG_VERSION, 0, -1, &kv, sizeof kv);
- flush();
- next_message(&imsg);
- if (imsg.hdr.type != IMSG_STATS_GET)
- errx(1, "invalid imsg type");
-
- kvp = imsg.data;
- if (kvp->iter == NULL) {
- imsg_free(&imsg);
- return;
- }
+ srv_send(IMSG_STATS_GET, &kv, sizeof kv);
+ srv_recv(IMSG_STATS_GET);
+ srv_read(&kv, sizeof(kv));
+ srv_end();
- if (strcmp(kvp->key, "uptime") == 0) {
- duration = time(NULL) - kvp->val.u.counter;
- printf("uptime=%zd\n", (size_t)duration);
+ if (kv.iter == NULL)
+ break;
+
+ if (strcmp(kv.key, "uptime") == 0) {
+ duration = time(NULL) - kv.val.u.counter;
+ printf("uptime=%lld\n", (long long)duration);
printf("uptime.human=%s\n",
duration_to_text(duration));
}
else {
- switch (kvp->val.type) {
+ switch (kv.val.type) {
case STAT_COUNTER:
printf("%s=%zd\n",
- kvp->key, kvp->val.u.counter);
+ kv.key, kv.val.u.counter);
break;
case STAT_TIMESTAMP:
printf("%s=%" PRId64 "\n",
- kvp->key, (int64_t)kvp->val.u.timestamp);
+ kv.key, (int64_t)kv.val.u.timestamp);
break;
case STAT_TIMEVAL:
- printf("%s=%zd.%zd\n",
- kvp->key, kvp->val.u.tv.tv_sec,
- kvp->val.u.tv.tv_usec);
+ printf("%s=%lld.%lld\n",
+ kv.key, (long long)kv.val.u.tv.tv_sec,
+ (long long)kv.val.u.tv.tv_usec);
break;
case STAT_TIMESPEC:
- printf("%s=%li.%06li\n",
- kvp->key,
- kvp->val.u.ts.tv_sec * 1000000 +
- kvp->val.u.ts.tv_nsec / 1000000,
- kvp->val.u.ts.tv_nsec % 1000000);
+ printf("%s=%lli.%06li\n",
+ kv.key,
+ (long long)kv.val.u.ts.tv_sec * 1000000 +
+ kv.val.u.ts.tv_nsec / 1000000,
+ kv.val.u.ts.tv_nsec % 1000000);
break;
}
}
-
- kv = *kvp;
- imsg_free(&imsg);
}
+
+ return (0);
}
-static void
-show_queue(flags)
+static int
+do_stop(int argc, struct parameter *argv)
{
- struct envelope envelope;
- int r;
+ srv_send(IMSG_CTL_SHUTDOWN, NULL, 0);
+ return srv_check_result();
+}
- log_init(1);
+static int
+do_trace(int argc, struct parameter *argv)
+{
+ int v;
- if (chroot(PATH_SPOOL) == -1 || chdir(".") == -1)
- err(1, "%s", PATH_SPOOL);
+ v = str_to_trace(argv[0].u.u_str);
+
+ srv_send(IMSG_CTL_TRACE, &v, sizeof(v));
+ return srv_check_result();
+}
+
+static int
+do_unprofile(int argc, struct parameter *argv)
+{
+ int v;
+
+ v = str_to_profile(argv[0].u.u_str);
+
+ srv_send(IMSG_CTL_UNPROFILE, &v, sizeof(v));
+ return srv_check_result();
+}
+
+static int
+do_untrace(int argc, struct parameter *argv)
+{
+ int v;
+
+ v = str_to_trace(argv[0].u.u_str);
+
+ srv_send(IMSG_CTL_UNTRACE, &v, sizeof(v));
+ return srv_check_result();
+}
+
+static int
+do_update_table(int argc, struct parameter *argv)
+{
+ const char *name = argv[0].u.u_str;
+
+ srv_send(IMSG_LKA_UPDATE_TABLE, name, strlen(name) + 1);
+ return srv_check_result();
+}
+
+int
+main(int argc, char **argv)
+{
+ char *argv_mailq[] = { "show", "queue", NULL };
+
+ if (strcmp(__progname, "sendmail") == 0 ||
+ strcmp(__progname, "send-mail") == 0) {
+ sendmail = 1;
+ if (srv_connect())
+ return (enqueue(argc, argv));
+ return (enqueue_offline(argc, argv));
+ }
+
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ cmd_install("log brief", do_log_brief);
+ cmd_install("log verbose", do_log_verbose);
+ cmd_install("monitor", do_monitor);
+ cmd_install("pause mda", do_pause_mda);
+ cmd_install("pause mta", do_pause_mta);
+ cmd_install("pause smtp", do_pause_smtp);
+ cmd_install("profile <str>", do_profile);
+ cmd_install("remove <evpid>", do_remove);
+ cmd_install("remove <msgid>", do_remove);
+ cmd_install("resume mda", do_resume_mda);
+ cmd_install("resume mta", do_resume_mta);
+ cmd_install("resume smtp", do_resume_smtp);
+ cmd_install("schedule <msgid>", do_schedule);
+ cmd_install("schedule <evpid>", do_schedule);
+ cmd_install("schedule all", do_schedule);
+ cmd_install("show envelope <evpid>", do_show_envelope);
+ cmd_install("show message <msgid>", do_show_message);
+ cmd_install("show message <evpid>", do_show_message);
+ cmd_install("show queue", do_show_queue);
+ cmd_install("show queue <msgid>", do_show_queue);
+ cmd_install("show stats", do_show_stats);
+ cmd_install("stop", do_stop);
+ cmd_install("trace <str>", do_trace);
+ cmd_install("unprofile <str>", do_unprofile);
+ cmd_install("untrace <str>", do_untrace);
+ cmd_install("update table <str>", do_update_table);
+
+ if (strcmp(__progname, "mailq") == 0)
+ return cmd_run(2, argv_mailq);
+ if (strcmp(__progname, "smtpctl") == 0)
+ return cmd_run(argc - 1, argv + 1);
+
+ errx(1, "unsupported mode");
+ return (0);
- while ((r = queue_envelope_walk(&envelope)) != -1)
- if (r)
- show_queue_envelope(&envelope, flags);
}
static void
@@ -637,12 +736,9 @@ show_queue_envelope(struct envelope *e, int online)
status[0] = '\0';
- getflag(&e->flags, EF_BOUNCE, "bounce",
- status, sizeof(status));
- getflag(&e->flags, EF_AUTHENTICATED, "auth",
- status, sizeof(status));
- getflag(&e->flags, EF_INTERNAL, "internal",
- status, sizeof(status));
+ getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status));
+ getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status));
+ getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status));
if (online) {
if (e->flags & EF_PENDING)
@@ -711,197 +807,203 @@ getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len)
}
static void
-display(const char *s)
+show_offline_envelope(uint64_t evpid)
{
- pid_t pid;
- arglist args;
- char *cmd;
- int status;
-
- pid = fork();
- if (pid < 0)
- err(1, "fork");
- if (pid == 0) {
- cmd = PATH_GZCAT;
- bzero(&args, sizeof(args));
- addargs(&args, "%s", cmd);
- addargs(&args, "%s", s);
- execvp(cmd, args.list);
- err(1, "execvp");
- }
- wait(&status);
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
- exit(0);
- cmd = PATH_CAT;
- bzero(&args, sizeof(args));
- addargs(&args, "%s", cmd);
- addargs(&args, "%s", s);
- execvp(cmd, args.list);
- err(1, "execvp");
-}
-
-static void
-show_envelope(const char *s)
-{
- char buf[SMTPD_MAXPATHLEN];
- uint64_t evpid;
+ FILE *fp = NULL;
+ char pathname[SMTPD_MAXPATHLEN];
+ size_t plen;
+ char *p;
+ size_t buflen;
+ char buffer[sizeof(struct envelope)];
- if ((evpid = text_to_evpid(s)) == 0)
- errx(1, "invalid msgid/evpid");
+ struct envelope evp;
- if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64,
- PATH_SPOOL,
- PATH_QUEUE,
+ if (! bsnprintf(pathname, sizeof pathname,
+ "/queue/%02x/%08x/%016"PRIx64,
(evpid_to_msgid(evpid) & 0xff000000) >> 24,
- evpid_to_msgid(evpid),
- evpid))
- errx(1, "unable to retrieve envelope");
-
- display(buf);
-}
-
-static void
-show_message(const char *s)
-{
- char buf[SMTPD_MAXPATHLEN];
- uint32_t msgid;
- uint64_t evpid;
+ evpid_to_msgid(evpid), evpid))
+ goto end;
+ fp = fopen(pathname, "r");
+ if (fp == NULL)
+ goto end;
+
+ buflen = fread(buffer, 1, sizeof buffer, fp);
+ p = buffer;
+ plen = buflen;
+
+ if (is_encrypted_buffer(p)) {
+ warnx("offline encrypted queue is not supported yet");
+ goto end;
+ }
- if ((evpid = text_to_evpid(s)) == 0)
- errx(1, "invalid msgid/evpid");
+ if (is_gzip_buffer(p)) {
+ warnx("offline compressed queue is not supported yet");
+ goto end;
+ }
- msgid = evpid_to_msgid(evpid);
- if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message",
- PATH_SPOOL,
- PATH_QUEUE,
- (evpid_to_msgid(evpid) & 0xff000000) >> 24,
- msgid))
- errx(1, "unable to retrieve message");
+ if (! envelope_load_buffer(&evp, p, plen))
+ goto end;
+ evp.id = evpid;
+ show_queue_envelope(&evp, 0);
- display(buf);
+end:
+ if (fp)
+ fclose(fp);
}
static void
-show_monitor(struct stat_digest *d)
+display(const char *s)
{
- static int init = 0;
- static size_t count = 0;
- static struct stat_digest last;
-
- if (init == 0) {
- init = 1;
- bzero(&last, sizeof last);
- }
-
- if (count % 25 == 0) {
- if (count != 0)
- printf("\n");
- printf("--- client --- "
- "-- envelope -- "
- "---- relay/delivery --- "
- "------- misc -------\n"
- "curr conn disc "
- "curr enq deq "
- "ok tmpfail prmfail loop "
- "expire remove bounce\n");
- }
- printf("%4zu %4zu %4zu "
- "%4zu %4zu %4zu "
- "%4zu %4zu %4zu %4zu "
- "%4zu %4zu %4zu\n",
- d->clt_connect - d->clt_disconnect,
- d->clt_connect - last.clt_connect,
- d->clt_disconnect - last.clt_disconnect,
+ FILE *fp;
+ char *key;
+ int gzipped;
+ char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1;
+
+ if ((fp = fopen(s, "r")) == NULL)
+ err(1, "fopen");
+
+ if (is_encrypted_fp(fp)) {
+ int i;
+ int fd;
+ FILE *ofp;
+ char sfn[] = "/tmp/smtpd.XXXXXXXXXX";
+
+ if ((fd = mkstemp(sfn)) == -1 ||
+ (ofp = fdopen(fd, "w+")) == NULL) {
+ if (fd != -1) {
+ unlink(sfn);
+ close(fd);
+ }
+ err(1, "mkstemp");
+ }
+ unlink(sfn);
- d->evp_enqueued - d->evp_dequeued,
- d->evp_enqueued - last.evp_enqueued,
- d->evp_dequeued - last.evp_dequeued,
+ for (i = 0; i < 3; i++) {
+ key = getpass("key> ");
+ if (crypto_setup(key, strlen(key)))
+ break;
+ }
+ if (i == 3)
+ errx(1, "crypto-setup: invalid key");
- d->dlv_ok - last.dlv_ok,
- d->dlv_tempfail - last.dlv_tempfail,
- d->dlv_permfail - last.dlv_permfail,
- d->dlv_loop - last.dlv_loop,
+ if (! crypto_decrypt_file(fp, ofp)) {
+ printf("object is encrypted: %s\n", key);
+ exit(1);
+ }
- d->evp_expired - last.evp_expired,
- d->evp_removed - last.evp_removed,
- d->evp_bounce - last.evp_bounce);
+ fclose(fp);
+ fp = ofp;
+ fseek(fp, SEEK_SET, 0);
+ }
+ gzipped = is_gzip_fp(fp);
- last = *d;
- count++;
+ (void)dup2(fileno(fp), STDIN_FILENO);
+ if (gzipped)
+ execl(PATH_GZCAT, gzcat_argv0, NULL);
+ else
+ execl(PATH_CAT, "cat", NULL);
+ err(1, "execl");
}
-static uint32_t
-trace_convert(uint32_t trace)
+static int
+str_to_trace(const char *str)
{
- switch (trace) {
- case LOG_TRACE_IMSG:
- case LOG_UNTRACE_IMSG:
+ if (!strcmp(str, "imsg"))
return TRACE_IMSG;
-
- case LOG_TRACE_IO:
- case LOG_UNTRACE_IO:
+ if (!strcmp(str, "io"))
return TRACE_IO;
-
- case LOG_TRACE_SMTP:
- case LOG_UNTRACE_SMTP:
+ if (!strcmp(str, "smtp"))
return TRACE_SMTP;
-
- case LOG_TRACE_MFA:
- case LOG_UNTRACE_MFA:
+ if (!strcmp(str, "mfa"))
return TRACE_MFA;
-
- case LOG_TRACE_MTA:
- case LOG_UNTRACE_MTA:
+ if (!strcmp(str, "mta"))
return TRACE_MTA;
-
- case LOG_TRACE_BOUNCE:
- case LOG_UNTRACE_BOUNCE:
+ if (!strcmp(str, "bounce"))
return TRACE_BOUNCE;
-
- case LOG_TRACE_SCHEDULER:
- case LOG_UNTRACE_SCHEDULER:
+ if (!strcmp(str, "scheduler"))
return TRACE_SCHEDULER;
-
- case LOG_TRACE_LOOKUP:
- case LOG_UNTRACE_LOOKUP:
+ if (!strcmp(str, "lookup"))
return TRACE_LOOKUP;
-
- case LOG_TRACE_STAT:
- case LOG_UNTRACE_STAT:
+ if (!strcmp(str, "stat"))
return TRACE_STAT;
-
- case LOG_TRACE_RULES:
- case LOG_UNTRACE_RULES:
+ if (!strcmp(str, "rules"))
return TRACE_RULES;
-
- case LOG_TRACE_MPROC:
- case LOG_UNTRACE_MPROC:
+ if (!strcmp(str, "mproc"))
return TRACE_MPROC;
-
- case LOG_TRACE_EXPAND:
- case LOG_UNTRACE_EXPAND:
+ if (!strcmp(str, "expand"))
return TRACE_EXPAND;
-
- case LOG_TRACE_ALL:
- case LOG_UNTRACE_ALL:
+ if (!strcmp(str, "all"))
return ~TRACE_DEBUG;
- }
-
- return 0;
+ errx(1, "invalid trace keyword: %s", str);
+ return (0);
}
-static uint32_t
-profile_convert(uint32_t prof)
+static int
+str_to_profile(const char *str)
{
- switch (prof) {
- case LOG_PROFILE_IMSG:
- case LOG_UNPROFILE_IMSG:
+ if (!strcmp(str, "imsg"))
return PROFILE_IMSG;
-
- case LOG_PROFILE_QUEUE:
- case LOG_UNPROFILE_QUEUE:
+ if (!strcmp(str, "queue"))
return PROFILE_QUEUE;
- }
+ errx(1, "invalid profile keyword: %s", str);
+ return (0);
+}
+
+static int
+is_gzip_buffer(const char *buffer)
+{
+ uint16_t magic;
+
+ memcpy(&magic, buffer, sizeof magic);
+#define GZIP_MAGIC 0x8b1f
+ return (magic == GZIP_MAGIC);
+}
+
+static int
+is_gzip_fp(FILE *fp)
+{
+ uint8_t magic[2];
+ int ret = 0;
+
+ if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
+ goto end;
+
+ ret = is_gzip_buffer((const char *)&magic);
+end:
+ fseek(fp, SEEK_SET, 0);
+ return ret;
+}
+
+
+/* XXX */
+/*
+ * queue supports transparent encryption.
+ * encrypted chunks are prefixed with an API version byte
+ * which we ensure is unambiguous with gzipped / plain
+ * objects.
+ */
+
+static int
+is_encrypted_buffer(const char *buffer)
+{
+ uint8_t magic;
+
+ magic = *buffer;
+#define ENCRYPTION_MAGIC 0x1
+ return (magic == ENCRYPTION_MAGIC);
+}
+
+static int
+is_encrypted_fp(FILE *fp)
+{
+ uint8_t magic;
+ int ret = 0;
+
+ if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
+ goto end;
- return 0;
+ ret = is_encrypted_buffer((const char *)&magic);
+end:
+ fseek(fp, SEEK_SET, 0);
+ return ret;
}