summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/smtpd/enqueue.c50
-rw-r--r--usr.sbin/smtpd/runner.c75
-rw-r--r--usr.sbin/smtpd/smtpctl.c8
-rw-r--r--usr.sbin/smtpd/smtpd.c278
-rw-r--r--usr.sbin/smtpd/smtpd.h16
5 files changed, 394 insertions, 33 deletions
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index 24c27317646..3c7e79f9088 100644
--- a/usr.sbin/smtpd/enqueue.c
+++ b/usr.sbin/smtpd/enqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: enqueue.c,v 1.13 2009/04/17 16:26:18 jacekm Exp $ */
+/* $OpenBSD: enqueue.c,v 1.14 2009/04/21 18:12:05 jacekm Exp $ */
/*
* Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -60,7 +60,6 @@ void rcptto(char *);
void start_data(void);
void send_message(int);
void end_data(void);
-int enqueue(int, char **);
enum headerfields {
HDR_NONE,
@@ -710,3 +709,50 @@ end_data(void)
if ((status = read_reply()) != STATUS_QUIT)
errx(1, "server sends error after QUIT");
}
+
+int
+enqueue_offline(int argc, char *argv[])
+{
+ char path[MAXPATHLEN];
+ FILE *fp;
+ int i, fd, ch;
+
+ if (! bsnprintf(path, sizeof(path), "%s%s/%d,XXXXXXXXXX", PATH_SPOOL,
+ PATH_OFFLINE, time(NULL)))
+ err(1, "snprintf");
+
+ if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+ warn("cannot create temporary file %s", path);
+ if (fd != -1)
+ unlink(path);
+ exit(1);
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (strchr(argv[i], '|') != NULL) {
+ warnx("%s contains illegal character", argv[i]);
+ unlink(path);
+ exit(1);
+ }
+ fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]);
+ }
+
+ fprintf(fp, "\n");
+
+ while ((ch = fgetc(stdin)) != EOF)
+ if (fputc(ch, fp) == EOF) {
+ warn("write error");
+ unlink(path);
+ exit(1);
+ }
+
+ if (ferror(stdin)) {
+ warn("read error");
+ unlink(path);
+ exit(1);
+ }
+
+ fclose(fp);
+
+ return (0);
+}
diff --git a/usr.sbin/smtpd/runner.c b/usr.sbin/smtpd/runner.c
index a52abac2f74..986c90c8882 100644
--- a/usr.sbin/smtpd/runner.c
+++ b/usr.sbin/smtpd/runner.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: runner.c,v 1.41 2009/04/21 14:37:32 eric Exp $ */
+/* $OpenBSD: runner.c,v 1.42 2009/04/21 18:12:05 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -48,6 +48,7 @@
__dead void runner_shutdown(void);
void runner_sig_handler(int, short, void *);
+void runner_dispatch_parent(int, short, void *);
void runner_dispatch_control(int, short, void *);
void runner_dispatch_queue(int, short, void *);
void runner_dispatch_mda(int, short, void *);
@@ -57,6 +58,7 @@ void runner_setup_events(struct smtpd *);
void runner_disable_events(struct smtpd *);
void runner_reset_flags(void);
+void runner_process_offline(struct smtpd *);
void runner_timeout(int, short, void *);
@@ -95,6 +97,55 @@ runner_sig_handler(int sig, short event, void *p)
}
void
+runner_dispatch_parent(int sig, short event, void *p)
+{
+ struct smtpd *env = p;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = env->sc_ibufs[PROC_PARENT];
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read_error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("runner_dispatch_parent: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_PARENT_ENQUEUE_OFFLINE:
+ runner_process_offline(env);
+ break;
+ default:
+ log_warnx("runner_dispatch_parent: got imsg %d",
+ imsg.hdr.type);
+ fatalx("runner_dispatch_parent: unexpected imsg");
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
runner_dispatch_control(int sig, short event, void *p)
{
struct smtpd *env = p;
@@ -398,6 +449,7 @@ runner(struct smtpd *env)
struct event ev_sigterm;
struct peer peers[] = {
+ { PROC_PARENT, runner_dispatch_parent },
{ PROC_CONTROL, runner_dispatch_control },
{ PROC_MDA, runner_dispatch_mda },
{ PROC_MTA, runner_dispatch_mta },
@@ -448,11 +500,12 @@ runner(struct smtpd *env)
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
- config_pipes(env, peers, 5);
- config_peers(env, peers, 5);
+ config_pipes(env, peers, 6);
+ config_peers(env, peers, 6);
unlink(PATH_QUEUE "/envelope.tmp");
runner_reset_flags();
+ runner_process_offline(env);
runner_setup_events(env);
event_dispatch();
@@ -462,6 +515,22 @@ runner(struct smtpd *env)
}
void
+runner_process_offline(struct smtpd *env)
+{
+ char path[MAXPATHLEN];
+ struct qwalk *q;
+
+ q = qwalk_new(PATH_OFFLINE);
+
+ if (qwalk(q, path))
+ imsg_compose(env->sc_ibufs[PROC_PARENT],
+ IMSG_PARENT_ENQUEUE_OFFLINE, 0, 0, -1, path,
+ strlen(path) + 1);
+
+ qwalk_close(q);
+}
+
+void
runner_reset_flags(void)
{
char path[MAXPATHLEN];
diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c
index bcbf31df1ac..a50695c15e8 100644
--- a/usr.sbin/smtpd/smtpctl.c
+++ b/usr.sbin/smtpd/smtpctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpctl.c,v 1.22 2009/04/20 17:07:01 jacekm Exp $ */
+/* $OpenBSD: smtpctl.c,v 1.23 2009/04/21 18:12:05 jacekm Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -45,7 +45,6 @@
__dead void usage(void);
int show_command_output(struct imsg*);
int show_stats_output(struct imsg *);
-int enqueue(int, char **);
/*
struct imsgname {
@@ -133,8 +132,11 @@ connected:
bzero(&sun, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path));
- if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
+ if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ if (sendmail)
+ return enqueue_offline(argc, argv);
err(1, "connect: %s", SMTPD_SOCKET);
+ }
if ((ibuf = calloc(1, sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index cd19b080aee..e50e7311d35 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.54 2009/04/21 14:37:32 eric Exp $ */
+/* $OpenBSD: smtpd.c,v 1.55 2009/04/21 18:12:05 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -56,6 +56,7 @@ void parent_dispatch_lka(int, short, void *);
void parent_dispatch_mda(int, short, void *);
void parent_dispatch_mfa(int, short, void *);
void parent_dispatch_smtp(int, short, void *);
+void parent_dispatch_runner(int, short, void *);
void parent_dispatch_control(int, short, void *);
void parent_sig_handler(int, short, void *);
int parent_open_message_file(struct batch *);
@@ -66,9 +67,11 @@ int parent_mailfile_rename(struct batch *, struct path *);
int parent_maildir_open(char *, struct passwd *, struct batch *);
int parent_maildir_init(struct passwd *, char *);
int parent_external_mda(char *, struct passwd *, struct batch *);
+int parent_enqueue_offline(struct smtpd *, char *);
int parent_forward_open(char *);
int check_child(pid_t, const char *);
int setup_spool(uid_t, gid_t);
+int path_starts_with(char *, char *);
extern char **environ;
@@ -501,6 +504,57 @@ out:
}
void
+parent_dispatch_runner(int sig, short event, void *p)
+{
+ struct smtpd *env = p;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = env->sc_ibufs[PROC_RUNNER];
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read_error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("parent_dispatch_runner: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_PARENT_ENQUEUE_OFFLINE:
+ if (! parent_enqueue_offline(env, imsg.data))
+ imsg_compose(ibuf, IMSG_PARENT_ENQUEUE_OFFLINE,
+ 0, 0, -1, NULL, 0);
+ break;
+ default:
+ log_warnx("parent_dispatch_runner: got imsg %d",
+ imsg.hdr.type);
+ fatalx("parent_dispatch_runner: unexpected imsg");
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
parent_dispatch_control(int sig, short event, void *p)
{
struct smtpd *env = p;
@@ -600,19 +654,40 @@ parent_sig_handler(int sig, short event, void *p)
fatalx("unexpected SIGCHLD");
if (WIFEXITED(status) && !WIFSIGNALED(status)) {
- switch (WEXITSTATUS(status)) {
- case EX_OK:
- log_debug("DEBUG: external mda reported success");
+ switch (mdaproc->type) {
+ case CHILD_ENQUEUE_OFFLINE:
+ if (WEXITSTATUS(status) == 0)
+ log_debug("offline enqueue was successful");
+ else
+ log_debug("offline enqueue failed");
+ imsg_compose(env->sc_ibufs[PROC_RUNNER],
+ IMSG_PARENT_ENQUEUE_OFFLINE, 0, 0, -1,
+ NULL, 0);
break;
- case EX_TEMPFAIL:
- log_debug("DEBUG: external mda reported temporary failure");
+ case CHILD_MDA:
+ if (WEXITSTATUS(status) == EX_OK)
+ log_debug("DEBUG: external mda reported success");
+ else if (WEXITSTATUS(status) == EX_TEMPFAIL)
+ log_debug("DEBUG: external mda reported temporary failure");
+ else
+ log_warnx("external mda returned %d", WEXITSTATUS(status));
break;
default:
- log_warnx("external mda returned %d", WEXITSTATUS(status));
+ fatalx("invalid child type");
+ break;
+ }
+ } else {
+ switch (mdaproc->type) {
+ case CHILD_ENQUEUE_OFFLINE:
+ log_warnx("offline enqueue terminated abnormally");
+ break;
+ case CHILD_MDA:
+ log_warnx("external mda terminated abnormally");
+ break;
+ default:
+ fatalx("invalid child type");
+ break;
}
- }
- else {
- log_warnx("external mda terminated abnormally");
}
SPLAY_REMOVE(mdaproctree, &env->mdaproc_queue,
@@ -647,6 +722,7 @@ main(int argc, char *argv[])
{ PROC_MDA, parent_dispatch_mda },
{ PROC_MFA, parent_dispatch_mfa },
{ PROC_SMTP, parent_dispatch_smtp },
+ { PROC_RUNNER, parent_dispatch_runner }
};
opts = 0;
@@ -763,8 +839,8 @@ main(int argc, char *argv[])
signal_add(&ev_sighup, NULL);
signal(SIGPIPE, SIG_IGN);
- config_pipes(&env, peers, 5);
- config_peers(&env, peers, 5);
+ config_pipes(&env, peers, 6);
+ config_peers(&env, peers, 6);
evtimer_set(&env.sc_ev, parent_send_config, &env);
bzero(&tv, sizeof(tv));
@@ -802,7 +878,8 @@ setup_spool(uid_t uid, gid_t gid)
unsigned int n;
char *paths[] = { PATH_INCOMING, PATH_ENQUEUE, PATH_QUEUE,
PATH_RUNQUEUE, PATH_RUNQUEUELOW,
- PATH_RUNQUEUEHIGH, PATH_PURGE };
+ PATH_RUNQUEUEHIGH, PATH_PURGE,
+ PATH_OFFLINE };
char pathname[MAXPATHLEN];
struct stat sb;
int ret;
@@ -852,6 +929,20 @@ setup_spool(uid_t uid, gid_t gid)
ret = 1;
for (n = 0; n < sizeof(paths)/sizeof(paths[0]); n++) {
+ mode_t mode;
+ uid_t owner;
+ gid_t group;
+
+ if (paths[n] == PATH_OFFLINE) {
+ mode = 01777;
+ owner = 0;
+ group = 0;
+ } else {
+ mode = 0700;
+ owner = uid;
+ group = gid;
+ }
+
if (! bsnprintf(pathname, sizeof(pathname), "%s%s", PATH_SPOOL,
paths[n]))
fatal("snprintf");
@@ -863,16 +954,22 @@ setup_spool(uid_t uid, gid_t gid)
continue;
}
- if (mkdir(pathname, 0700) == -1) {
+ /* chmod is deffered to avoid umask effect */
+ if (mkdir(pathname, 0) == -1) {
ret = 0;
warn("mkdir: %s", pathname);
}
- if (chown(pathname, uid, gid) == -1) {
+ if (chown(pathname, owner, group) == -1) {
ret = 0;
warn("chown: %s", pathname);
}
+ if (chmod(pathname, mode) == -1) {
+ ret = 0;
+ warn("chmod: %s", pathname);
+ }
+
if (stat(pathname, &sb) == -1)
err(1, "stat: %s", pathname);
}
@@ -883,22 +980,24 @@ setup_spool(uid_t uid, gid_t gid)
warnx("%s is not a directory", pathname);
}
- /* check that it is owned by uid/gid */
- if (sb.st_uid != uid) {
+ /* check that it is owned by owner/group */
+ if (sb.st_uid != owner) {
ret = 0;
- warnx("%s is not owned by uid %d", pathname, uid);
+ warnx("%s is not owned by uid %d", pathname, owner);
}
- if (sb.st_gid != gid) {
+ if (sb.st_gid != group) {
ret = 0;
- warnx("%s is not owned by gid %d", pathname, gid);
+ warnx("%s is not owned by gid %d", pathname, group);
}
/* check permission */
- if ((sb.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR)) != (S_IRUSR|S_IWUSR|S_IXUSR) ||
- (sb.st_mode & (S_IRGRP|S_IWGRP|S_IXGRP)) ||
- (sb.st_mode & (S_IROTH|S_IWOTH|S_IXOTH))) {
+ if ((sb.st_mode & 07777) != mode) {
+ char mode_str[12];
+
ret = 0;
- warnx("%s must be rwx------ (0700)", pathname);
+ strmode(mode, mode_str);
+ mode_str[10] = '\0';
+ warnx("%s must be %s (%o)", pathname, mode_str + 1, mode);
}
}
return ret;
@@ -1034,6 +1133,7 @@ parent_mailbox_open(char *path, struct passwd *pw, struct batch *batchp)
if (mdaproc == NULL)
fatal("calloc");
mdaproc->pid = pid;
+ mdaproc->type = CHILD_MDA;
SPLAY_INSERT(mdaproctree, &batchp->env->mdaproc_queue, mdaproc);
@@ -1181,6 +1281,7 @@ parent_external_mda(char *path, struct passwd *pw, struct batch *batchp)
if (mdaproc == NULL)
fatal("calloc");
mdaproc->pid = pid;
+ mdaproc->type = CHILD_MDA;
SPLAY_INSERT(mdaproctree, &batchp->env->mdaproc_queue, mdaproc);
@@ -1189,6 +1290,123 @@ parent_external_mda(char *path, struct passwd *pw, struct batch *batchp)
}
int
+parent_enqueue_offline(struct smtpd *env, char *runner_path)
+{
+ char path[MAXPATHLEN];
+ struct passwd *pw;
+ struct mdaproc *mdaproc;
+ struct stat sb;
+ pid_t pid;
+
+ log_debug("parent_enqueue_offline: path %s", runner_path);
+
+ if (! bsnprintf(path, sizeof(path), "%s%s", PATH_SPOOL, runner_path))
+ fatalx("parent_enqueue_offline: filename too long");
+
+ if (! path_starts_with(path, PATH_SPOOL PATH_OFFLINE))
+ fatalx("parent_enqueue_offline: path outside offline dir");
+
+ if (lstat(path, &sb) == -1) {
+ if (errno == ENOENT) {
+ log_warn("parent_enqueue_offline: %s", path);
+ return (0);
+ }
+ fatal("parent_enqueue_offline: lstat");
+ }
+
+ if (chflags(path, 0) == -1) {
+ if (errno == ENOENT) {
+ log_warn("parent_enqueue_offline: %s", path);
+ return (0);
+ }
+ fatal("parent_enqueue_offline: chflags");
+ }
+
+ errno = 0;
+ if ((pw = safe_getpwuid(sb.st_uid)) == NULL) {
+ log_warn("parent_enqueue_offline: getpwuid for %d failed",
+ sb.st_uid);
+ unlink(path);
+ return (0);
+ }
+
+ if (! S_ISREG(sb.st_mode)) {
+ log_warnx("file %s (uid %d) not regular, removing", path, sb.st_uid);
+ if (S_ISDIR(sb.st_mode))
+ rmdir(path);
+ else
+ unlink(path);
+ return (0);
+ }
+
+ if ((pid = fork()) == -1)
+ fatal("parent_enqueue_offline: fork");
+
+ if (pid == 0) {
+ char *envp[2], *p, *tmp;
+ FILE *fp;
+ size_t len;
+ arglist args;
+
+ bzero(&args, sizeof(args));
+
+ 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) ||
+ closefrom(STDERR_FILENO + 1) == -1) {
+ unlink(path);
+ _exit(1);
+ }
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ unlink(path);
+ _exit(1);
+ }
+ unlink(path);
+
+ if (chdir(pw->pw_dir) == -1 && chdir("/") == -1)
+ _exit(1);
+
+ if (setsid() == -1 ||
+ signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
+ dup2(fileno(fp), STDIN_FILENO) == -1)
+ _exit(1);
+
+ if ((p = fgetln(fp, &len)) == NULL)
+ _exit(1);
+
+ if (p[len - 1] != '\n')
+ _exit(1);
+ p[len - 1] = '\0';
+
+ addargs(&args, "%s", _PATH_SENDMAIL);
+
+ while ((tmp = strsep(&p, "|")) != NULL)
+ addargs(&args, "%s", tmp);
+
+ if (lseek(fileno(fp), len, SEEK_SET) == -1)
+ _exit(1);
+
+ envp[0] = "PATH=" _PATH_DEFPATH;
+ envp[1] = (char *)NULL;
+ environ = envp;
+
+ execvp(args.list[0], args.list);
+ _exit(1);
+ }
+
+ mdaproc = calloc(1, sizeof (struct mdaproc));
+ if (mdaproc == NULL)
+ fatal("calloc");
+ mdaproc->pid = pid;
+ mdaproc->type = CHILD_ENQUEUE_OFFLINE;
+
+ SPLAY_INSERT(mdaproctree, &env->mdaproc_queue, mdaproc);
+
+ return (1);
+}
+
+int
parent_filename_open(char *path, struct passwd *pw, struct batch *batchp)
{
int fd;
@@ -1281,6 +1499,18 @@ err:
}
int
+path_starts_with(char *file, char *prefix)
+{
+ char rprefix[MAXPATHLEN];
+ char rfile[MAXPATHLEN];
+
+ if (realpath(file, rfile) == NULL || realpath(prefix, rprefix) == NULL)
+ return (-1);
+
+ return (strncmp(rfile, rprefix, strlen(rprefix)) == 0);
+}
+
+int
mdaproc_cmp(struct mdaproc *s1, struct mdaproc *s2)
{
if (s1->pid < s2->pid)
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index a556f079519..15e69dc586a 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.100 2009/04/20 17:07:01 jacekm Exp $ */
+/* $OpenBSD: smtpd.h,v 1.101 2009/04/21 18:12:05 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -63,6 +63,8 @@
#define PATH_RUNQUEUEHIGH "/runqueue-high"
#define PATH_RUNQUEUELOW "/runqueue-low"
+#define PATH_OFFLINE "/offline"
+
/* number of MX records to lookup */
#define MXARRAYSIZE 5
#define MAX_MX_COUNT 10
@@ -198,6 +200,7 @@ enum imsg_type {
IMSG_BATCH_APPEND,
IMSG_BATCH_CLOSE,
+ IMSG_PARENT_ENQUEUE_OFFLINE,
IMSG_PARENT_FORWARD_OPEN,
IMSG_PARENT_MAILBOX_OPEN,
IMSG_PARENT_MESSAGE_OPEN,
@@ -476,10 +479,17 @@ enum batch_flags {
F_BATCH_EXPIRED = 0x8,
};
+enum child_type {
+ CHILD_INVALID,
+ CHILD_MDA,
+ CHILD_ENQUEUE_OFFLINE
+};
+
struct mdaproc {
SPLAY_ENTRY(mdaproc) mdaproc_nodes;
pid_t pid;
+ enum child_type type;
};
struct batch {
@@ -873,6 +883,10 @@ pid_t mta(struct smtpd *);
pid_t control(struct smtpd *);
void session_socket_blockmode(int, enum blockmodes);
+/* enqueue.c */
+int enqueue(int, char **);
+int enqueue_offline(int, char **);
+
/* runner.c */
pid_t runner(struct smtpd *);
SPLAY_PROTOTYPE(batchtree, batch, b_nodes, batch_cmp);