diff --git a/api.c b/api.c index 03ceec34..37725dd0 100644 --- a/api.c +++ b/api.c @@ -346,7 +346,7 @@ int api_init(uev_ctx_t *ctx) .sun_path = INIT_SOCKET, }; - sd = socket(AF_UNIX, SOCK_STREAM, 0); + sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (-1 == sd) { _pe("Failed starting external API socket"); return 1; diff --git a/exec.c b/exec.c index 5ed08e73..76c937bb 100644 --- a/exec.c +++ b/exec.c @@ -245,9 +245,9 @@ pid_t run_getty(char *cmd, char *args[], int console) if (fd != STDIN_FILENO) exit(1); - dup2(0, STDIN_FILENO); - dup2(0, STDOUT_FILENO); - dup2(0, STDERR_FILENO); + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); prctl(PR_SET_NAME, "console", 0, 0, 0); } diff --git a/inetd.c b/inetd.c index 52671aca..93138799 100644 --- a/inetd.c +++ b/inetd.c @@ -127,8 +127,7 @@ static int get_stdin(svc_t *svc) } if (!inetd_is_allowed(&svc->inetd, ifname)) { - FLOG_INFO("Service %s on port %d not allowed from interface %s.", - svc->inetd.name, svc->inetd.port, ifname); + FLOG_INFO("Service %s on %s:%d is not allowed.", svc->inetd.name, ifname, svc->inetd.port); if (svc->inetd.type == SOCK_STREAM) close(stdin); @@ -144,10 +143,19 @@ static void socket_cb(uev_t *w, void *arg, int UNUSED(events)) svc_t *svc = (svc_t *)arg, *task; int stdin; + _d("Got event on %s socket ...", svc->cmd); stdin = get_stdin(svc); if (stdin < 0) { - FLOG_ERROR("%s: Unable to accept incoming connection", - svc->cmd); + FLOG_ERROR("%s: Unable to accept incoming connection", svc->cmd); + return; + } + + /* + * Make sure to disable O_NONBLOCK on the descriptor before + * passing it to the inetd service, that's what is expected. + */ + if (fcntl(stdin, F_SETFL, fcntl(stdin, F_GETFL, 0) & ~O_NONBLOCK) < 0) { + FLOG_ERROR("Failed disabling non-blocking on %s socket", svc->cmd); return; } @@ -158,22 +166,27 @@ static void socket_cb(uev_t *w, void *arg, int UNUSED(events)) return; } - /* Copy inherited attributes from inetd */ + if (!svc->inetd.forking) { + svc->block = SVC_BLOCK_INETD_BUSY; + service_step(svc); + } + + /* Copy inherited attributes from inetd service's svc */ task->runlevels = svc->runlevels; - task->inetd = svc->inetd; + + /* Only copy the most relevant parts of inetd, in particular we + * must *not* copy the watcher data to the clone! */ + task->inetd.svc = svc; + task->inetd.type = svc->inetd.type; + memcpy(task->cond, svc->cond, sizeof(task->cond)); memcpy(task->username, svc->username, sizeof(task->username)); memcpy(task->group, svc->group, sizeof(task->group)); memcpy(task->args, svc->args, sizeof(task->args)); - snprintf(task->desc, sizeof(task->desc), "%s Connection", svc->desc); + snprintf(task->desc, sizeof(task->desc), "%s connection", svc->desc); task->stdin = stdin; service_step(task); - - if (!svc->inetd.forking) { - svc->block = SVC_BLOCK_INETD_BUSY; - service_step(svc); - } } /* Launch Inet socket for service. @@ -190,7 +203,7 @@ static int spawn_socket(inetd_t *inetd) return -EINVAL; } - _d("Spawning server socket for inetd %s ...", inetd->name); + _d("Spawning server socket for inetd %s, type %s ...", inetd->name, inetd->type == SOCK_STREAM ? "stream" : "dgram"); sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto); if (-1 == sd) { FLOG_PERROR("Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto); @@ -215,7 +228,7 @@ static int spawn_socket(inetd_t *inetd) if (inetd->port) { if (inetd->type == SOCK_STREAM) { - if (-1 == listen(sd, 20)) { + if (-1 == listen(sd, 10)) { FLOG_PERROR("Failed listening to inetd service %s", inetd->name); close(sd); return -errno; @@ -232,19 +245,40 @@ static int spawn_socket(inetd_t *inetd) int inetd_start(inetd_t *inetd) { - if (inetd->watcher.fd == -1) + int sd; + char buf[BUFSIZ]; + ssize_t len; + + sd = inetd->watcher.fd; + if (sd == -1) return spawn_socket(inetd); - return -EEXIST; + /* Read anything lingering, or clean up socket after failure */ + len = recv(sd, buf, sizeof(buf), MSG_DONTWAIT); + _d("Read %d lingering bytes from socket before restarting %s ...", len, inetd->svc->cmd); + + /* Restore O_NONBLOCK for socket */ + fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK); + + _d("Re-starting %s socket watcher ...", inetd->svc->cmd); + uev_io_start(&inetd->watcher); + + return 0; } void inetd_stop(inetd_t *inetd) { if (inetd->watcher.fd != -1) { + _d("Stopping %s socket watcher ...", inetd->svc->cmd); uev_io_stop(&inetd->watcher); - shutdown(inetd->watcher.fd, SHUT_RDWR); - close(inetd->watcher.fd); - inetd->watcher.fd = -1; + + /* For dgram inetd services we block the parent SVC + * and halt the watcher, so don't close the socket! */ + if (inetd->svc->block != SVC_BLOCK_INETD_BUSY) { + shutdown(inetd->watcher.fd, SHUT_RDWR); + close(inetd->watcher.fd); + inetd->watcher.fd = -1; + } } } diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 7fdb4f6e..f9db3126 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -104,7 +104,7 @@ static plugin_t plugin = { PLUGIN_INIT(plugin_init) { - pidfile_ctx.fd = inotify_init(); + pidfile_ctx.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (pidfile_ctx.fd < 0) { _pe("inotify_init()"); return; diff --git a/svc.c b/svc.c index fb241b22..631da95f 100644 --- a/svc.c +++ b/svc.c @@ -97,7 +97,9 @@ svc_t *svc_new(char *cmd, int id, int type) */ int svc_del(svc_t *svc) { - svc->type = SVC_TYPE_FREE; + /* Clear any old references and set SVC_TYPE_FREE */ + memset(svc, 0, sizeof(*svc)); + return 0; }