From 90087f07a7cfffe10b4958e13558a0052f85691d Mon Sep 17 00:00:00 2001 From: Gilles Chehade Date: Fri, 22 May 2020 14:35:02 +0200 Subject: Revert "moving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream" This reverts commit 90620a574d8824e5b2aa18709f2d5b5b6bb3cb38. --- smtpd/control.c | 817 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 817 insertions(+) create mode 100644 smtpd/control.c (limited to 'smtpd/control.c') diff --git a/smtpd/control.c b/smtpd/control.c new file mode 100644 index 00000000..0e35bbd1 --- /dev/null +++ b/smtpd/control.c @@ -0,0 +1,817 @@ +/* $OpenBSD: control.c,v 1.123 2018/05/31 21:06:12 gilles Exp $ */ + +/* + * Copyright (c) 2012 Gilles Chehade + * Copyright (c) 2008 Pierre-Yves Ritschard + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include /* needed for setgroups */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smtpd.h" +#include "log.h" + +#define CONTROL_BACKLOG 5 + +struct ctl_conn { + uint32_t id; + uint8_t flags; +#define CTL_CONN_NOTIFY 0x01 + struct mproc mproc; + uid_t euid; + gid_t egid; +}; + +struct { + struct event ev; + int fd; +} control_state; + +static void control_imsg(struct mproc *, struct imsg *); +static void control_shutdown(void); +static void control_listen(void); +static void control_accept(int, short, void *); +static void control_close(struct ctl_conn *); +static void control_dispatch_ext(struct mproc *, struct imsg *); +static void control_digest_update(const char *, size_t, int); +static void control_broadcast_verbose(int, int); + +static struct stat_backend *stat_backend = NULL; +extern const char *backend_stat; + +static uint64_t connid = 0; +static struct tree ctl_conns; +static struct tree ctl_count; +static struct stat_digest digest; + +#define CONTROL_FD_RESERVE 5 +#define CONTROL_MAXCONN_PER_CLIENT 32 + +static void +control_imsg(struct mproc *p, struct imsg *imsg) +{ + struct ctl_conn *c; + struct stat_value val; + struct msg m; + const char *key; + const void *data; + size_t sz; + + if (imsg == NULL) { + if (p->proc != PROC_CLIENT) + control_shutdown(); + return; + } + + switch (imsg->hdr.type) { + case IMSG_CTL_OK: + case IMSG_CTL_FAIL: + case IMSG_CTL_LIST_MESSAGES: + case IMSG_CTL_LIST_ENVELOPES: + case IMSG_CTL_DISCOVER_EVPID: + case IMSG_CTL_DISCOVER_MSGID: + case IMSG_CTL_MTA_SHOW_HOSTS: + case IMSG_CTL_MTA_SHOW_RELAYS: + case IMSG_CTL_MTA_SHOW_ROUTES: + case IMSG_CTL_MTA_SHOW_HOSTSTATS: + case IMSG_CTL_MTA_SHOW_BLOCK: + c = tree_get(&ctl_conns, imsg->hdr.peerid); + if (c == NULL) + return; + imsg->hdr.peerid = 0; + m_forward(&c->mproc, imsg); + return; + + case IMSG_CTL_SMTP_SESSION: + c = tree_get(&ctl_conns, imsg->hdr.peerid); + if (c == NULL) + return; + m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd, NULL, 0); + return; + + case IMSG_STAT_INCREMENT: + m_msg(&m, imsg); + m_get_string(&m, &key); + m_get_data(&m, &data, &sz); + m_end(&m); + if (sz != sizeof(val)) + fatalx("control: IMSG_STAT_INCREMENT size mismatch"); + memmove(&val, data, sz); + if (stat_backend) + stat_backend->increment(key, val.u.counter); + control_digest_update(key, val.u.counter, 1); + return; + + case IMSG_STAT_DECREMENT: + m_msg(&m, imsg); + m_get_string(&m, &key); + m_get_data(&m, &data, &sz); + m_end(&m); + if (sz != sizeof(val)) + fatalx("control: IMSG_STAT_DECREMENT size mismatch"); + memmove(&val, data, sz); + if (stat_backend) + stat_backend->decrement(key, val.u.counter); + control_digest_update(key, val.u.counter, 0); + return; + + case IMSG_STAT_SET: + m_msg(&m, imsg); + m_get_string(&m, &key); + m_get_data(&m, &data, &sz); + m_end(&m); + if (sz != sizeof(val)) + fatalx("control: IMSG_STAT_SET size mismatch"); + memmove(&val, data, sz); + if (stat_backend) + stat_backend->set(key, &val); + return; + } + + errx(1, "control_imsg: unexpected %s imsg", + imsg_to_str(imsg->hdr.type)); +} + +int +control_create_socket(void) +{ + struct sockaddr_un s_un; + int fd; + mode_t old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + fatal("control: socket"); + + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + if (strlcpy(s_un.sun_path, SMTPD_SOCKET, + sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path)) + fatal("control: socket name too long"); + + if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) + fatalx("control socket already listening"); + + if (unlink(SMTPD_SOCKET) == -1) + if (errno != ENOENT) + fatal("control: cannot unlink socket"); + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + (void)umask(old_umask); + fatal("control: bind"); + } + (void)umask(old_umask); + + if (chmod(SMTPD_SOCKET, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { + (void)unlink(SMTPD_SOCKET); + fatal("control: chmod"); + } + + io_set_nonblocking(fd); + control_state.fd = fd; + + return fd; +} + +int +control(void) +{ + struct passwd *pw; + + purge_config(PURGE_EVERYTHING); + + if ((pw = getpwnam(SMTPD_USER)) == NULL) + fatalx("unknown user " SMTPD_USER); + + stat_backend = env->sc_stat; + stat_backend->init(); + + if (chroot(PATH_CHROOT) == -1) + fatal("control: chroot"); + if (chdir("/") == -1) + fatal("control: chdir(\"/\")"); + + config_process(PROC_CONTROL); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("control: cannot drop privileges"); + + imsg_callback = control_imsg; + event_init(); + + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + tree_init(&ctl_conns); + tree_init(&ctl_count); + + memset(&digest, 0, sizeof digest); + digest.startup = time(NULL); + + config_peer(PROC_SCHEDULER); + config_peer(PROC_QUEUE); + config_peer(PROC_PARENT); + config_peer(PROC_LKA); + config_peer(PROC_PONY); + config_peer(PROC_CA); + + control_listen(); + +#if HAVE_PLEDGE + if (pledge("stdio unix recvfd sendfd", NULL) == -1) + err(1, "pledge"); +#endif + + event_dispatch(); + fatalx("exited event loop"); + + return (0); +} + +static void +control_shutdown(void) +{ + log_debug("debug: control agent exiting"); + _exit(0); +} + +static void +control_listen(void) +{ + if (listen(control_state.fd, CONTROL_BACKLOG) == -1) + fatal("control_listen"); + + event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST, + control_accept, NULL); + event_add(&control_state.ev, NULL); +} + +/* ARGSUSED */ +static void +control_accept(int listenfd, short event, void *arg) +{ + int connfd; + socklen_t len; + struct sockaddr_un s_un; + struct ctl_conn *c; + size_t *count; + uid_t euid; + gid_t egid; + +#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) + if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) + goto pause; +#else + if (available_fds(CONTROL_FD_RESERVE)) + goto pause; +#endif + + len = sizeof(s_un); + if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) { + if (errno == ENFILE || errno == EMFILE) + goto pause; + if (errno == EINTR || errno == EWOULDBLOCK || + errno == ECONNABORTED) + return; + fatal("control_accept: accept"); + } + + io_set_nonblocking(connfd); + + if (getpeereid(connfd, &euid, &egid) == -1) + fatal("getpeereid"); + + count = tree_get(&ctl_count, euid); + if (count == NULL) { + count = xcalloc(1, sizeof *count); + tree_xset(&ctl_count, euid, count); + } + + if (*count == CONTROL_MAXCONN_PER_CLIENT) { + close(connfd); + log_warnx("warn: too many connections to control socket " + "from user with uid %lu", (unsigned long int)euid); + return; + } + (*count)++; + + do { + ++connid; + } while (tree_get(&ctl_conns, connid)); + + c = xcalloc(1, sizeof(*c)); + c->euid = euid; + c->egid = egid; + c->id = connid; + c->mproc.proc = PROC_CLIENT; + c->mproc.handler = control_dispatch_ext; + c->mproc.data = c; + if ((c->mproc.name = strdup(proc_title(c->mproc.proc))) == NULL) + fatal("strdup"); + mproc_init(&c->mproc, connfd); + mproc_enable(&c->mproc); + tree_xset(&ctl_conns, c->id, c); + + stat_backend->increment("control.session", 1); + return; + +pause: + log_warnx("warn: ctl client limit hit, disabling new connections"); + event_del(&control_state.ev); +} + +static void +control_close(struct ctl_conn *c) +{ + size_t *count; + + count = tree_xget(&ctl_count, c->euid); + (*count)--; + if (*count == 0) { + tree_xpop(&ctl_count, c->euid); + free(count); + } + tree_xpop(&ctl_conns, c->id); + mproc_clear(&c->mproc); + free(c); + + stat_backend->decrement("control.session", 1); + +#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) + if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) + return; +#else + if (available_fds(CONTROL_FD_RESERVE)) + return; +#endif + + if (!event_pending(&control_state.ev, EV_READ, NULL)) { + log_warnx("warn: re-enabling ctl connections"); + event_add(&control_state.ev, NULL); + } +} + +static void +control_digest_update(const char *key, size_t value, int incr) +{ + size_t *p; + + p = NULL; + + if (!strcmp(key, "smtp.session")) { + if (incr) + p = &digest.clt_connect; + else + digest.clt_disconnect += value; + } + else if (!strcmp(key, "scheduler.envelope")) { + if (incr) + p = &digest.evp_enqueued; + else + digest.evp_dequeued += value; + } + else if (!strcmp(key, "scheduler.envelope.expired")) + p = &digest.evp_expired; + else if (!strcmp(key, "scheduler.envelope.removed")) + p = &digest.evp_removed; + else if (!strcmp(key, "scheduler.delivery.ok")) + p = &digest.dlv_ok; + else if (!strcmp(key, "scheduler.delivery.permfail")) + p = &digest.dlv_permfail; + else if (!strcmp(key, "scheduler.delivery.tempfail")) + p = &digest.dlv_tempfail; + else if (!strcmp(key, "scheduler.delivery.loop")) + p = &digest.dlv_loop; + + else if (!strcmp(key, "queue.bounce")) + p = &digest.evp_bounce; + + if (p) { + if (incr) + *p = *p + value; + else + *p = *p - value; + } +} + +/* ARGSUSED */ +static void +control_dispatch_ext(struct mproc *p, struct imsg *imsg) +{ + struct sockaddr_storage ss; + struct ctl_conn *c; + int v; + struct stat_kv *kvp; + char *key; + struct stat_value val; + size_t len; + uint64_t evpid; + uint32_t msgid; + + c = p->data; + + if (imsg == NULL) { + control_close(c); + return; + } + + if (imsg->hdr.peerid != IMSG_VERSION) { + m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0); + return; + } + + switch (imsg->hdr.type) { + case IMSG_CTL_SMTP_SESSION: + if (env->sc_flags & SMTPD_SMTP_PAUSED) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1, + &c->euid, sizeof(c->euid)); + return; + + case IMSG_CTL_GET_DIGEST: + if (c->euid) + goto badcred; + digest.timestamp = time(NULL); + m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest); + return; + + case IMSG_CTL_GET_STATS: + if (c->euid) + goto badcred; + kvp = imsg->data; + if (!stat_backend->iter(&kvp->iter, &key, &val)) + kvp->iter = NULL; + else { + (void)strlcpy(kvp->key, key, sizeof kvp->key); + kvp->val = val; + } + m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp); + return; + + case IMSG_CTL_VERBOSE: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) + goto badcred; + + memcpy(&v, imsg->data, sizeof(v)); + log_trace_verbose(v); + + control_broadcast_verbose(IMSG_CTL_VERBOSE, v); + + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_TRACE_ENABLE: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) + goto badcred; + + memcpy(&v, imsg->data, sizeof(v)); + tracing |= v; + log_trace_verbose(tracing); + + control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); + + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_TRACE_DISABLE: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) + goto badcred; + + memcpy(&v, imsg->data, sizeof(v)); + tracing &= ~v; + log_trace_verbose(tracing); + + control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); + + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_PROFILE_ENABLE: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) + goto badcred; + + memcpy(&v, imsg->data, sizeof(v)); + profiling |= v; + + control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); + + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_PROFILE_DISABLE: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) + goto badcred; + + memcpy(&v, imsg->data, sizeof(v)); + profiling &= ~v; + + control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); + + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_PAUSE_EVP: + if (c->euid) + goto badcred; + + imsg->hdr.peerid = c->id; + m_forward(p_scheduler, imsg); + return; + + case IMSG_CTL_PAUSE_MDA: + if (c->euid) + goto badcred; + + if (env->sc_flags & SMTPD_MDA_PAUSED) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: mda paused"); + env->sc_flags |= SMTPD_MDA_PAUSED; + m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_PAUSE_MTA: + if (c->euid) + goto badcred; + + if (env->sc_flags & SMTPD_MTA_PAUSED) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: mta paused"); + env->sc_flags |= SMTPD_MTA_PAUSED; + m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_PAUSE_SMTP: + if (c->euid) + goto badcred; + + if (env->sc_flags & SMTPD_SMTP_PAUSED) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: smtp paused"); + env->sc_flags |= SMTPD_SMTP_PAUSED; + m_compose(p_pony, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_RESUME_EVP: + if (c->euid) + goto badcred; + + imsg->hdr.peerid = c->id; + m_forward(p_scheduler, imsg); + return; + + case IMSG_CTL_RESUME_MDA: + if (c->euid) + goto badcred; + + if (!(env->sc_flags & SMTPD_MDA_PAUSED)) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: mda resumed"); + env->sc_flags &= ~SMTPD_MDA_PAUSED; + m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_RESUME_MTA: + if (c->euid) + goto badcred; + + if (!(env->sc_flags & SMTPD_MTA_PAUSED)) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: mta resumed"); + env->sc_flags &= ~SMTPD_MTA_PAUSED; + m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_RESUME_SMTP: + if (c->euid) + goto badcred; + + if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) { + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); + return; + } + log_info("info: smtp resumed"); + env->sc_flags &= ~SMTPD_SMTP_PAUSED; + m_forward(p_pony, imsg); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_RESUME_ROUTE: + if (c->euid) + goto badcred; + + m_forward(p_pony, imsg); + m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); + return; + + case IMSG_CTL_LIST_MESSAGES: + if (c->euid) + goto badcred; + m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1, + imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); + return; + + case IMSG_CTL_LIST_ENVELOPES: + if (c->euid) + goto badcred; + m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1, + imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); + return; + + case IMSG_CTL_MTA_SHOW_HOSTS: + case IMSG_CTL_MTA_SHOW_RELAYS: + case IMSG_CTL_MTA_SHOW_ROUTES: + case IMSG_CTL_MTA_SHOW_HOSTSTATS: + case IMSG_CTL_MTA_SHOW_BLOCK: + if (c->euid) + goto badcred; + + imsg->hdr.peerid = c->id; + m_forward(p_pony, imsg); + return; + + case IMSG_CTL_SHOW_STATUS: + if (c->euid) + goto badcred; + + m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags, + sizeof(env->sc_flags)); + return; + + case IMSG_CTL_MTA_BLOCK: + case IMSG_CTL_MTA_UNBLOCK: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss)) + goto invalid; + memmove(&ss, imsg->data, sizeof(ss)); + m_create(p_pony, imsg->hdr.type, c->id, 0, -1); + m_add_sockaddr(p_pony, (struct sockaddr *)&ss); + m_add_string(p_pony, (char *)imsg->data + sizeof(ss)); + m_close(p_pony); + return; + + case IMSG_CTL_SCHEDULE: + if (c->euid) + goto badcred; + + imsg->hdr.peerid = c->id; + m_forward(p_scheduler, imsg); + return; + + case IMSG_CTL_REMOVE: + if (c->euid) + goto badcred; + + imsg->hdr.peerid = c->id; + m_forward(p_scheduler, imsg); + return; + + case IMSG_CTL_UPDATE_TABLE: + if (c->euid) + goto badcred; + + /* table name too long */ + len = strlen(imsg->data); + if (len >= LINE_MAX) + goto invalid; + + imsg->hdr.peerid = c->id; + m_forward(p_lka, imsg); + return; + + case IMSG_CTL_DISCOVER_EVPID: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid) + goto invalid; + + memmove(&evpid, imsg->data, sizeof evpid); + m_create(p_queue, imsg->hdr.type, c->id, 0, -1); + m_add_evpid(p_queue, evpid); + m_close(p_queue); + return; + + case IMSG_CTL_DISCOVER_MSGID: + if (c->euid) + goto badcred; + + if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid) + goto invalid; + + memmove(&msgid, imsg->data, sizeof msgid); + m_create(p_queue, imsg->hdr.type, c->id, 0, -1); + m_add_msgid(p_queue, msgid); + m_close(p_queue); + return; + + default: + log_debug("debug: control_dispatch_ext: " + "error handling %s imsg", + imsg_to_str(imsg->hdr.type)); + return; + } +badcred: +invalid: + m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); +} + +static void +control_broadcast_verbose(int msg, int v) +{ + m_create(p_lka, msg, 0, 0, -1); + m_add_int(p_lka, v); + m_close(p_lka); + + m_create(p_pony, msg, 0, 0, -1); + m_add_int(p_pony, v); + m_close(p_pony); + + m_create(p_queue, msg, 0, 0, -1); + m_add_int(p_queue, v); + m_close(p_queue); + + m_create(p_ca, msg, 0, 0, -1); + m_add_int(p_ca, v); + m_close(p_ca); + + m_create(p_scheduler, msg, 0, 0, -1); + m_add_int(p_scheduler, v); + m_close(p_scheduler); + + m_create(p_parent, msg, 0, 0, -1); + m_add_int(p_parent, v); + m_close(p_parent); +} -- cgit v1.2.3-59-g8ed1b