/* ARGSUSED */ void control_accept(int listenfd, short event, void *arg) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; len = sizeof(sun); if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { if (errno == EINTR || errno == ECONNABORTED) return; fatal("control_accept: accept"); } session_socket_blockmode(connfd, BM_NONBLOCK); if ((c = calloc(1, sizeof(*c))) == NULL) fatal(NULL); imsg_init(&c->iev.ibuf, connfd); c->iev.handler = control_dispatch_ext; c->iev.events = EV_READ; event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, NULL); event_add(&c->iev.ev, NULL); TAILQ_INSERT_TAIL(&ctl_conns, c, entry); if (stat_increment(STATS_CONTROL_SESSION) >= env->sc_maxconn) { log_warnx("ctl client limit hit, disabling new connections"); event_del(&control_state.ev); } }
void init_pipes(void) { int i, j, sockpair[2]; for (i = 0; i < PROC_COUNT; i++) for (j = i + 1; j < PROC_COUNT; j++) { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sockpair) == -1) fatal("socketpair"); pipes[i][j] = sockpair[0]; pipes[j][i] = sockpair[1]; session_socket_blockmode(pipes[i][j], BM_NONBLOCK); session_socket_blockmode(pipes[j][i], BM_NONBLOCK); } }
void init_pipes(void) { int i; int j; int count; int sockpair[2]; for (i = 0; i < PROC_COUNT; i++) for (j = 0; j < PROC_COUNT; j++) { /* * find out how many instances of this peer there are. */ if (i >= j || env->sc_instances[i] == 0|| env->sc_instances[j] == 0) continue; if (env->sc_instances[i] > 1 && env->sc_instances[j] > 1) fatalx("N:N peering not supported"); count = env->sc_instances[i] * env->sc_instances[j]; env->sc_pipes[i][j] = xcalloc(count, sizeof(int), "init_pipes"); env->sc_pipes[j][i] = xcalloc(count, sizeof(int), "init_pipes"); while (--count >= 0) { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sockpair) == -1) fatal("socketpair"); env->sc_pipes[i][j][count] = sockpair[0]; env->sc_pipes[j][i][count] = sockpair[1]; session_socket_blockmode( env->sc_pipes[i][j][count], BM_NONBLOCK); session_socket_blockmode( env->sc_pipes[j][i][count], BM_NONBLOCK); } } }
/* ARGSUSED */ void control_accept(int listenfd, short event, void *bula) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; event_add(&control_state.ev, NULL); if ((event & EV_TIMEOUT)) return; len = sizeof(sun); if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. */ if (errno == ENFILE || errno == EMFILE) { struct timeval evtpause = { 1, 0 }; event_del(&control_state.ev); evtimer_add(&control_state.evt, &evtpause); } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) log_warn("control_accept: accept"); return; } session_socket_blockmode(connfd, BM_NONBLOCK); if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { log_warn("control_accept"); close(connfd); return; } imsg_init(&c->iev.ibuf, connfd); c->iev.handler = control_dispatch_imsg; c->iev.events = EV_READ; event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, &c->iev); event_add(&c->iev.ev, NULL); TAILQ_INSERT_TAIL(&ctl_conns, c, entry); }
int control_init(char *path) { struct sockaddr_un sun; int fd; mode_t old_umask; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { log_warn("control_init: socket"); return (-1); } bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); if (unlink(path) == -1) if (errno != ENOENT) { log_warn("control_init: unlink %s", path); close(fd); return (-1); } old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { log_warn("control_init: bind: %s", path); close(fd); umask(old_umask); return (-1); } umask(old_umask); if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { log_warn("control_init: chmod"); close(fd); (void)unlink(path); return (-1); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; return (0); }
int control_create_socket(void) { struct sockaddr_un sun; int fd; mode_t old_umask; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("control: socket"); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) fatal("control: socket name too long"); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 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 *)&sun, sizeof(sun)) == -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"); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; return fd; }
/* ARGSUSED */ static void control_accept(int listenfd, short event, void *arg) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) goto pause; len = sizeof(sun); if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { if (errno == ENFILE || errno == EMFILE) goto pause; if (errno == EINTR || errno == EWOULDBLOCK || errno == ECONNABORTED) return; fatal("control_accept: accept"); } session_socket_blockmode(connfd, BM_NONBLOCK); c = xcalloc(1, sizeof(*c), "control_accept"); if (getpeereid(connfd, &c->euid, &c->egid) == -1) fatal("getpeereid"); c->id = ++connid; c->mproc.proc = PROC_CLIENT; c->mproc.handler = control_dispatch_ext; c->mproc.data = c; 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); }
/* ARGSUSED */ static void control_accept(int listenfd, short event, void *arg) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; if (available_fds(CONTROL_FD_RESERVE)) goto pause; len = sizeof(sun); if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { if (errno == ENFILE || errno == EMFILE) goto pause; if (errno == EINTR || errno == ECONNABORTED) return; fatal("control_accept: accept"); } session_socket_blockmode(connfd, BM_NONBLOCK); c = xcalloc(1, sizeof(*c), "control_accept"); imsg_init(&c->iev.ibuf, connfd); c->iev.handler = control_dispatch_ext; c->iev.events = EV_READ; event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, NULL); event_add(&c->iev.ev, NULL); TAILQ_INSERT_TAIL(&ctl_conns, c, entry); stat_backend->increment("control.session", 1); return; pause: log_warnx("ctl client limit hit, disabling new connections"); event_del(&control_state.ev); }
pid_t control(void) { struct sockaddr_un sun; int fd; mode_t old_umask; pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct peer peers [] = { { PROC_RUNNER, imsg_dispatch }, { PROC_QUEUE, imsg_dispatch }, { PROC_SMTP, imsg_dispatch }, { PROC_MFA, imsg_dispatch }, { PROC_PARENT, imsg_dispatch }, }; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("control: socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) fatal("control: socket name too long"); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 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 *)&sun, sizeof(sun)) == -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"); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; if (chroot(pw->pw_dir) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); smtpd_process = PROC_CONTROL; setproctitle("%s", env->sc_title[smtpd_process]); 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_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); TAILQ_INIT(&ctl_conns); config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }
pid_t control(void) { struct sockaddr_un sun; int fd; mode_t old_umask; pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: break; default: return (pid); } purge_config(PURGE_EVERYTHING); pw = env->sc_pw; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("control: socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) fatal("control: socket name too long"); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 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 *)&sun, sizeof(sun)) == -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"); } session_socket_blockmode(fd, BM_NONBLOCK); control_state.fd = fd; 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_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); tree_init(&ctl_conns); bzero(&digest, sizeof digest); digest.startup = time(NULL); config_peer(PROC_SCHEDULER); config_peer(PROC_QUEUE); config_peer(PROC_SMTP); config_peer(PROC_MFA); config_peer(PROC_PARENT); config_peer(PROC_LKA); config_peer(PROC_MDA); config_peer(PROC_MTA); config_done(); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }