summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/smtpd/parser.c439
-rw-r--r--usr.sbin/smtpd/parser.h83
-rw-r--r--usr.sbin/smtpd/smtpctl.c1272
-rw-r--r--usr.sbin/smtpd/smtpctl/Makefile8
-rw-r--r--usr.sbin/smtpd/smtpd.h10
5 files changed, 921 insertions, 891 deletions
diff --git a/usr.sbin/smtpd/parser.c b/usr.sbin/smtpd/parser.c
index a0e75473210..a8d174a9956 100644
--- a/usr.sbin/smtpd/parser.c
+++ b/usr.sbin/smtpd/parser.c
@@ -1,9 +1,7 @@
-/* $OpenBSD: parser.c,v 1.34 2013/05/24 17:03:14 eric Exp $ */
+/* $OpenBSD: parser.c,v 1.35 2013/07/19 13:41:23 eric Exp $ */
/*
- * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,280 +17,243 @@
*/
#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/queue.h>
-#include <sys/tree.h>
-#include <event.h>
-#include <imsg.h>
-
-#include <openssl/ssl.h>
-
-#include "smtpd.h"
+#include <err.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "parser.h"
-enum token_type {
- NOTOKEN,
- ENDTOKEN,
- KEYWORD,
- VARIABLE
-};
-
-struct token {
- enum token_type type;
- const char *keyword;
- int value;
- const struct token *next;
-};
-
-static const struct token t_log[];
-static const struct token t_main[];
-static const struct token t_pause[];
-static const struct token t_remove[];
-static const struct token t_resume[];
-static const struct token t_schedule[];
-static const struct token t_show[];
-static const struct token t_show_envelope[];
-static const struct token t_show_message[];
-static const struct token t_update[];
-static const struct token t_update_table[];
-static const struct token t_trace[];
-static const struct token t_untrace[];
-static const struct token t_profile[];
-static const struct token t_unprofile[];
-
-static const struct token t_main[] = {
- {KEYWORD, "schedule", NONE, t_schedule},
- {KEYWORD, "show", NONE, t_show},
- {KEYWORD, "monitor", MONITOR, NULL},
- {KEYWORD, "pause", NONE, t_pause},
- {KEYWORD, "remove", NONE, t_remove},
- {KEYWORD, "resume", NONE, t_resume},
- {KEYWORD, "stop", SHUTDOWN, NULL},
- {KEYWORD, "log", NONE, t_log},
- {KEYWORD, "profile", NONE, t_profile},
- {KEYWORD, "trace", NONE, t_trace},
- {KEYWORD, "unprofile", NONE, t_unprofile},
- {KEYWORD, "untrace", NONE, t_untrace},
- {KEYWORD, "update", NONE, t_update},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_remove[] = {
- {VARIABLE, "evpid", REMOVE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_schedule[] = {
- {VARIABLE, "msgid/evpid/all", SCHEDULE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_show[] = {
- {KEYWORD, "queue", SHOW_QUEUE, NULL},
- {KEYWORD, "stats", SHOW_STATS, NULL},
- {KEYWORD, "envelope", NONE, t_show_envelope},
- {KEYWORD, "message", SHOW_MESSAGE, t_show_message},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_show_envelope[] = {
- {VARIABLE, "evpid", SHOW_ENVELOPE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_show_message[] = {
- {VARIABLE, "evpid", SHOW_MESSAGE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_pause[] = {
- {KEYWORD, "mda", PAUSE_MDA, NULL},
- {KEYWORD, "mta", PAUSE_MTA, NULL},
- {KEYWORD, "smtp", PAUSE_SMTP, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_resume[] = {
- {KEYWORD, "mda", RESUME_MDA, NULL},
- {KEYWORD, "mta", RESUME_MTA, NULL},
- {KEYWORD, "smtp", RESUME_SMTP, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
+uint64_t text_to_evpid(const char *);
+uint32_t text_to_msgid(const char *);
-static const struct token t_log[] = {
- {KEYWORD, "verbose", LOG_VERBOSE, NULL},
- {KEYWORD, "brief", LOG_BRIEF, NULL},
- {ENDTOKEN, "", NONE, NULL}
+struct node {
+ int type;
+ const char *token;
+ struct node *parent;
+ TAILQ_ENTRY(node) entry;
+ TAILQ_HEAD(, node) children;
+ int (*cmd)(int, struct parameter*);
};
-static const struct token t_update[] = {
- {KEYWORD, "table", NONE, t_update_table},
- {ENDTOKEN, "", NONE, NULL}
-};
+static struct node *root;
-static const struct token t_update_table[] = {
- {VARIABLE, "name", UPDATE_TABLE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
+#define ARGVMAX 64
-static const struct token t_trace[] = {
- {KEYWORD, "imsg", LOG_TRACE_IMSG, NULL},
- {KEYWORD, "io", LOG_TRACE_IO, NULL},
- {KEYWORD, "smtp", LOG_TRACE_SMTP, NULL},
- {KEYWORD, "filter", LOG_TRACE_MFA, NULL},
- {KEYWORD, "transfer", LOG_TRACE_MTA, NULL},
- {KEYWORD, "bounce", LOG_TRACE_BOUNCE, NULL},
- {KEYWORD, "scheduler", LOG_TRACE_SCHEDULER, NULL},
- {KEYWORD, "lookup", LOG_TRACE_LOOKUP, NULL},
- {KEYWORD, "stat", LOG_TRACE_STAT, NULL},
- {KEYWORD, "rules", LOG_TRACE_RULES, NULL},
- {KEYWORD, "mproc", LOG_TRACE_MPROC, NULL},
- {KEYWORD, "expand", LOG_TRACE_EXPAND, NULL},
- {KEYWORD, "all", LOG_TRACE_ALL, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_untrace[] = {
- {KEYWORD, "imsg", LOG_UNTRACE_IMSG, NULL},
- {KEYWORD, "io", LOG_UNTRACE_IO, NULL},
- {KEYWORD, "smtp", LOG_UNTRACE_SMTP, NULL},
- {KEYWORD, "filter", LOG_UNTRACE_MFA, NULL},
- {KEYWORD, "transfer", LOG_UNTRACE_MTA, NULL},
- {KEYWORD, "bounce", LOG_UNTRACE_BOUNCE, NULL},
- {KEYWORD, "scheduler", LOG_UNTRACE_SCHEDULER, NULL},
- {KEYWORD, "lookup", LOG_UNTRACE_LOOKUP, NULL},
- {KEYWORD, "stat", LOG_UNTRACE_STAT, NULL},
- {KEYWORD, "rules", LOG_UNTRACE_RULES, NULL},
- {KEYWORD, "mproc", LOG_UNTRACE_MPROC, NULL},
- {KEYWORD, "expand", LOG_UNTRACE_EXPAND, NULL},
- {KEYWORD, "all", LOG_UNTRACE_ALL, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_profile[] = {
- {KEYWORD, "imsg", LOG_PROFILE_IMSG, NULL},
- {KEYWORD, "queue", LOG_PROFILE_QUEUE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_unprofile[] = {
- {KEYWORD, "imsg", LOG_UNPROFILE_IMSG, NULL},
- {KEYWORD, "queue", LOG_UNPROFILE_QUEUE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-
-static const struct token *match_token(const char *, const struct token [],
- struct parse_result *);
-static void show_valid_args(const struct token []);
-
-struct parse_result *
-parse(int argc, char *argv[])
+int
+cmd_install(const char *pattern, int (*cmd)(int, struct parameter*))
{
- static struct parse_result res;
- const struct token *table = t_main;
- const struct token *match;
+ struct node *node, *tmp;
+ char *s, *str, *argv[ARGVMAX], **ap;
+ int i, n;
+
+ /* Tokenize */
+ str = s = strdup(pattern);
+ if (str == NULL)
+ err(1, "strdup");
+ n = 0;
+ for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) {
+ if (**ap != '\0') {
+ ap++;
+ n++;
+ }
+ }
+ *ap = NULL;
- bzero(&res, sizeof(res));
+ if (root == NULL) {
+ root = calloc(1, sizeof (*root));
+ TAILQ_INIT(&root->children);
+ }
+ node = root;
- while (argc >= 0) {
- if ((match = match_token(argv[0], table, &res)) == NULL) {
- fprintf(stderr, "valid commands/args:\n");
- show_valid_args(table);
- return (NULL);
+ for (i = 0; i < n; i++) {
+ TAILQ_FOREACH(tmp, &node->children, entry) {
+ if (!strcmp(tmp->token, argv[i])) {
+ node = tmp;
+ break;
+ }
+ }
+ if (tmp == NULL) {
+ tmp = calloc(1, sizeof (*tmp));
+ TAILQ_INIT(&tmp->children);
+ if (!strcmp(argv[i], "<str>"))
+ tmp->type = P_STR;
+ else if (!strcmp(argv[i], "<int>"))
+ tmp->type = P_INT;
+ else if (!strcmp(argv[i], "<msgid>"))
+ tmp->type = P_MSGID;
+ else if (!strcmp(argv[i], "<evpid>"))
+ tmp->type = P_EVPID;
+ else if (!strcmp(argv[i], "<routeid>"))
+ tmp->type = P_ROUTEID;
+ else
+ tmp->type = P_TOKEN;
+ tmp->token = strdup(argv[i]);
+ tmp->parent = node;
+ TAILQ_INSERT_TAIL(&node->children, tmp, entry);
+ node = tmp;
}
+ }
- argc--;
- argv++;
+ if (node->cmd)
+ errx(1, "duplicate pattern: %s", pattern);
+ node->cmd = cmd;
- if (match->type == NOTOKEN || match->next == NULL)
- break;
+ free(s);
+ return (n);
+}
- table = match->next;
- }
+static void
+cmd_dump(struct node *node, int depth)
+{
+ struct node *n;
+ int i;
- if (argc > 0) {
- fprintf(stderr, "superfluous argument: %s\n", argv[0]);
- return (NULL);
- }
+ for(i = 0; i < depth; i++)
+ printf(" ");
+ printf("%s\n", node->token ? node->token : "");
- return (&res);
+ TAILQ_FOREACH(n, &node->children, entry)
+ cmd_dump(n, depth + 1);
}
-const struct token *
-match_token(const char *word, const struct token table[],
- struct parse_result *res)
+static int
+cmd_check(const char *str, struct node *node, struct parameter *res)
{
- uint i, match;
- const struct token *t = NULL;
-
- match = 0;
+ const char *e;
+
+ switch (node->type) {
+ case P_TOKEN:
+ if (!strcmp(str, node->token))
+ return (1);
+ return (0);
+
+ case P_STR:
+ res->u.u_str = str;
+ return (1);
+
+ case P_INT:
+ res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e);
+ if (e)
+ return (0);
+ return (1);
+
+ case P_MSGID:
+ if (strlen(str) != 8)
+ return (0);
+ res->u.u_msgid = text_to_msgid(str);
+ if (res->u.u_msgid == 0)
+ return (0);
+ return (1);
+
+ case P_EVPID:
+ if (strlen(str) != 16)
+ return (0);
+ res->u.u_evpid = text_to_evpid(str);
+ if (res->u.u_evpid == 0)
+ return (0);
+ return (1);
+
+ case P_ROUTEID:
+ res->u.u_int = strtonum(str, 1, LLONG_MAX, &e);
+ if (e)
+ return (0);
+ return (1);
+
+ default:
+ errx(1, "bad token type: %i", node->type);
+ return (0);
+ }
+}
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- if (word == NULL || strlen(word) == 0) {
- match++;
- t = &table[i];
- }
- break;
- case KEYWORD:
- if (word != NULL && strncmp(word, table[i].keyword,
- strlen(word)) == 0) {
- match++;
- t = &table[i];
- if (t->value)
- res->action = t->value;
+int
+cmd_run(int argc, char **argv)
+{
+ struct parameter param[ARGVMAX];
+ struct node *node, *tmp, *stack[ARGVMAX], *best;
+ int i, j, np;
+
+ node = root;
+ np = 0;
+
+ for (i = 0; i < argc; i++) {
+ TAILQ_FOREACH(tmp, &node->children, entry) {
+ if (cmd_check(argv[i], tmp, &param[np])) {
+ stack[i] = tmp;
+ node = tmp;
+ param[np].type = node->type;
+ if (node->type != P_TOKEN)
+ np++;
+ break;
}
- break;
- case VARIABLE:
- if (word != NULL && strlen(word) != 0) {
- match++;
- t = &table[i];
- if (t->value) {
- res->action = t->value;
- res->data = word;
- }
+ }
+ if (tmp == NULL) {
+ best = NULL;
+ TAILQ_FOREACH(tmp, &node->children, entry) {
+ if (tmp->type != P_TOKEN)
+ continue;
+ if (strstr(tmp->token, argv[i]) != tmp->token)
+ continue;
+ if (best)
+ goto fail;
+ best = tmp;
}
- break;
- case ENDTOKEN:
- break;
+ if (best == NULL)
+ goto fail;
+ stack[i] = best;
+ node = best;
+ param[np].type = node->type;
+ if (node->type != P_TOKEN)
+ np++;
}
}
- if (match != 1) {
- if (word == NULL)
- fprintf(stderr, "missing argument:\n");
- else if (match > 1)
- fprintf(stderr, "ambiguous argument: %s\n", word);
- else if (match < 1)
- fprintf(stderr, "unknown argument: %s\n", word);
- return (NULL);
+ if (node->cmd == NULL)
+ goto fail;
+
+ return (node->cmd(np, param));
+
+fail:
+ fprintf(stderr, "possibilities are:\n");
+ TAILQ_FOREACH(tmp, &node->children, entry) {
+ for (j = 0; j < i; j++)
+ fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
+ fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
}
- return (t);
+ return (-1);
}
-static void
-show_valid_args(const struct token table[])
+int
+cmd_show_params(int argc, struct parameter *argv)
{
int i;
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- fprintf(stderr, " <cr>\n");
+ for (i = 0; i < argc; i++) {
+ switch(argv[i].type) {
+ case P_STR:
+ printf(" str:\"%s\"", argv[i].u.u_str);
+ break;
+ case P_INT:
+ printf(" int:%i", argv[i].u.u_int);
break;
- case KEYWORD:
- fprintf(stderr, " %s\n", table[i].keyword);
+ case P_MSGID:
+ printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
break;
- case VARIABLE:
- fprintf(stderr, " %s\n", table[i].keyword);
+ case P_EVPID:
+ printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
break;
- case ENDTOKEN:
+ case P_ROUTEID:
+ printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
break;
+ default:
+ printf(" ???:%i", argv[i].type);
}
}
+ printf ("\n");
+ return (1);
}
diff --git a/usr.sbin/smtpd/parser.h b/usr.sbin/smtpd/parser.h
index ffdcc49c08a..905b1e78114 100644
--- a/usr.sbin/smtpd/parser.h
+++ b/usr.sbin/smtpd/parser.h
@@ -1,7 +1,7 @@
-/* $OpenBSD: parser.h,v 1.27 2013/05/24 17:03:14 eric Exp $ */
+/* $OpenBSD: parser.h,v 1.28 2013/07/19 13:41:23 eric Exp $ */
/*
- * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,67 +16,26 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-enum actions {
- NONE,
- SHUTDOWN,
- MONITOR,
- LOG_VERBOSE,
- LOG_BRIEF,
- SCHEDULE,
- SHOW_QUEUE,
- SHOW_STATS,
- SHOW_SIZES,
- SHOW_ENVELOPE,
- SHOW_MESSAGE,
- PAUSE_MDA,
- PAUSE_MTA,
- PAUSE_SMTP,
- REMOVE,
- RESUME_MDA,
- RESUME_MTA,
- RESUME_SMTP,
- UPDATE_TABLE,
- LOG_TRACE_IMSG,
- LOG_TRACE_IO,
- LOG_TRACE_SMTP,
- LOG_TRACE_MFA,
- LOG_TRACE_MTA,
- LOG_TRACE_BOUNCE,
- LOG_TRACE_SCHEDULER,
- LOG_TRACE_LOOKUP,
- LOG_TRACE_STAT,
- LOG_TRACE_RULES,
- LOG_TRACE_MPROC,
- LOG_TRACE_EXPAND,
- LOG_TRACE_ALL,
- LOG_UNTRACE_IMSG,
- LOG_UNTRACE_IO,
- LOG_UNTRACE_SMTP,
- LOG_UNTRACE_MFA,
- LOG_UNTRACE_MTA,
- LOG_UNTRACE_BOUNCE,
- LOG_UNTRACE_SCHEDULER,
- LOG_UNTRACE_LOOKUP,
- LOG_UNTRACE_STAT,
- LOG_UNTRACE_RULES,
- LOG_UNTRACE_MPROC,
- LOG_UNTRACE_EXPAND,
- LOG_UNTRACE_ALL,
- LOG_PROFILE_IMSG,
- LOG_PROFILE_QUEUE,
- LOG_UNPROFILE_IMSG,
- LOG_UNPROFILE_QUEUE,
+enum {
+ P_TOKEN,
+ P_STR,
+ P_INT,
+ P_MSGID,
+ P_EVPID,
+ P_ROUTEID,
};
-struct ctl_id {
- uint32_t id;
- char name[MAX_NAME_SIZE];
+struct parameter {
+ int type;
+ union {
+ const char *u_str;
+ int u_int;
+ uint32_t u_msgid;
+ uint64_t u_evpid;
+ uint64_t u_routeid;
+ } u;
};
-struct parse_result {
- struct ctl_id id;
- enum actions action;
- const char *data;
-};
-
-struct parse_result *parse(int, char *[]);
+int cmd_install(const char *, int (*)(int, struct parameter *));
+int cmd_run(int, char **);
+int cmd_show_params(int argc, struct parameter *argv);
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;
}
diff --git a/usr.sbin/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile
index e61249f252a..511c783dae9 100644
--- a/usr.sbin/smtpd/smtpctl/Makefile
+++ b/usr.sbin/smtpd/smtpctl/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.31 2013/05/24 17:03:14 eric Exp $
+# $OpenBSD: Makefile,v 1.32 2013/07/19 13:41:23 eric Exp $
.PATH: ${.CURDIR}/..
@@ -17,12 +17,12 @@ CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
CFLAGS+= -Wsign-compare -Wbounded
CFLAGS+= -DNO_IO
-SRCS= enqueue.c parser.c log.c envelope.c
+SRCS= enqueue.c parser.c log.c envelope.c crypto.c
SRCS+= queue_backend.c queue_fsqueue.c
SRCS+= smtpctl.c util.c
SRCS+= compress_backend.c compress_gzip.c
SRCS+= to.c expand.c tree.c
-LDADD+= -lutil -lz
-DPADD+= ${LIBUTIL} ${LIBZ}
+LDADD+= -lutil -lz -lcrypto
+DPADD+= ${LIBUTIL} ${LIBZ} ${LIBCRYPTO}
.include <bsd.prog.mk>
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index cec8ebfd036..752cba6188e 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.415 2013/07/19 11:14:08 eric Exp $ */
+/* $OpenBSD: smtpd.h,v 1.416 2013/07/19 13:41:23 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -1073,6 +1073,14 @@ void config_done(void);
pid_t control(void);
+/* crypto.c */
+int crypto_setup(const char *, size_t);
+int crypto_encrypt_file(FILE *, FILE *);
+int crypto_decrypt_file(FILE *, FILE *);
+size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t);
+size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t);
+
+
/* delivery.c */
struct delivery_backend *delivery_backend_lookup(enum action_type);