From: David Strauss <da...@davidstrauss.net> --- .gitignore | 1 + Makefile.am | 13 ++ src/socket-process-pool/Makefile | 1 + src/socket-process-pool/socket-process-poold.c | 247 +++++++++++++++++++++++++ src/socket-proxy/socket-proxyd.c | 3 +- 5 files changed, 264 insertions(+), 1 deletion(-) create mode 120000 src/socket-process-pool/Makefile create mode 100644 src/socket-process-pool/socket-process-poold.c
diff --git a/.gitignore b/.gitignore index 6253e0d..d3ab8bb 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ /systemd-reply-password /systemd-rfkill /systemd-run +/systemd-socket-process-poold /systemd-socket-proxyd /systemd-shutdown /systemd-shutdownd diff --git a/Makefile.am b/Makefile.am index 379e878..41e7012 100644 --- a/Makefile.am +++ b/Makefile.am @@ -319,6 +319,7 @@ rootlibexec_PROGRAMS = \ systemd-ac-power \ systemd-sysctl \ systemd-sleep \ + systemd-socket-process-poold \ systemd-socket-proxyd systemgenerator_PROGRAMS = \ @@ -3146,6 +3147,18 @@ EXTRA_DIST += \ # ------------------------------------------------------------------------------ +systemd_socket_process_poold_SOURCES = \ + src/socket-process-pool/socket-process-poold.c + +systemd_socket_process_poold_LDADD = \ + libsystemd-shared.la \ + libsystemd-logs.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la \ + libsystemd-daemon.la + +# ------------------------------------------------------------------------------ + systemd_socket_proxyd_SOURCES = \ src/socket-proxy/socket-proxyd.c diff --git a/src/socket-process-pool/Makefile b/src/socket-process-pool/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/src/socket-process-pool/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/socket-process-pool/socket-process-poold.c b/src/socket-process-pool/socket-process-poold.c new file mode 100644 index 0000000..57c1210 --- /dev/null +++ b/src/socket-process-pool/socket-process-poold.c @@ -0,0 +1,247 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 David Strauss + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. + ***/ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "sd-daemon.h" +#include "log.h" +#include "util.h" +#include "env-util.h" +#include "strv.h" + +struct pool { + int start_listen_fd; + size_t num_listen_fds; + bool ignore_env; + size_t num_cores; + size_t procs_per_core; + char *path; + char **argp; + char **envp; +}; + +static int help(void) { + + printf("%s [OPTIONS]... EXECUTABLE-PATH [ARGS-FOR-EXECUTABLE]...\n" + "Distribute an inherited socket to a process pool.\n\n" + " -h --help Show this help\n" + " --version Print version and exit\n" + /*" --num-procs=NUM Run an explicit number of processes\n"*/, + program_invocation_short_name); + + return 0; +} + +static void version(void) { + puts(PACKAGE_STRING " socket-process-poold"); +} + +static int parse_argv(int argc, char *argv[], struct pool *p) { + + enum { + ARG_VERSION = 0x100 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case '?': + return -EINVAL; + + case ARG_VERSION: + version(); + return 0; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + /* If there are no positional arguments, we have no path. */ + if (optind == argc) { + log_error("An executable path must provided."); + help(); + return -EINVAL; + } + + /* The first positional argument is the path. */ + p->path = argv[optind]; + + /* All positional arguments are arguments to the subprocess. */ + p->argp = new(char*, argc - optind); + for (int i = 0; i < argc - optind; ++i) { + p->argp[i] = argv[i + optind]; + } + p->argp[argc - optind] = NULL; + + return 1; +} + +static int fork_and_exec(struct pool *p) { + /* @TODO: Is there a cleaner way to do a stack allocation here? */ + char listen_pid[strlen("LISTEN_PID=") + 11]; + _cleanup_strv_free_ char **envp = NULL; + int r; + pid_t pid; + _cleanup_free_ int *fds = NULL; + + log_debug("About to fork off exec with path: %s", p->path); + + pid = fork(); + if (pid < 0) + return -errno; + + /* Do nothing if we're the parent. */ + if (pid > 0) { + log_debug("Started child %lu.", (unsigned long) pid); + return 0; + } + + log_debug("Now in child %lu pre-close.", (unsigned long) getpid()); + + /* Preserve only the socket fds. */ + /* @TODO: Should we run log_forget_fds() here? */ + fds = new(int, p->num_listen_fds); + if (fds == NULL && p->num_listen_fds > 0) + return log_oom(); + for (size_t i = 0; i < p->num_listen_fds; ++i) { + fd_cloexec(i + p->start_listen_fd, false); + fds[i] = i + p->start_listen_fd; + } + r = close_all_fds(fds, p->num_listen_fds); + if (r < 0) { + log_error("Failed to close extra file descriptors."); + return -1; + } + + log_debug("Now in process %lu post-close.", (unsigned long) getpid()); + + /* Preserve the environment, except for setting LISTEN_PID. */ + + r = snprintf(listen_pid, sizeof(listen_pid), "LISTEN_PID=%lu", (unsigned long) getpid()); + if (r < 0) + return log_oom(); + envp = strv_env_set(p->envp, listen_pid); + execve(p->path, p->argp, envp); + + /* We only reach here if execve() failed. */ + return errno; +} + +static int run_pool(struct pool *p) { + size_t to_start; + int r; + + to_start = p->num_cores * p->procs_per_core; + + log_debug("Starting %lu processes for the pool.", to_start); + + for (size_t started = 0; started < to_start; ++started) { + log_debug("Starting process %lu of %lu.", started, to_start); + r = fork_and_exec(p); + if (r < 0) + return r; + } + + log_debug("All pool members started."); + + return 0; +} + +static void wait_for_exit(size_t num_children) { + pid_t exited; + size_t remaining = num_children; + + log_debug("Waiting for pool members to stop."); + + while (remaining > 0) { + exited = waitpid(P_ALL, 0, WSTOPPED); + remaining--; + log_debug("Pool member pid=%lu has exited.", (unsigned long) exited); + } + + log_debug("All pool members stopped."); +} + +int main(int argc, char *argv[], char *envp[]) { + struct pool p = {}; + _cleanup_strv_free_ char **filtered_envp = NULL; + int r; + + log_parse_environment(); + log_open(); + + p.procs_per_core = 2; + + r = parse_argv(argc, argv, &p); + if (r <= 0) + goto finish; + + p.num_cores = sysconf(_SC_NPROCESSORS_ONLN); + log_info("Running %lu processes for each of %lu logical CPUs.", p.procs_per_core, p.num_cores); + + /* Import the environment, except for LISTEN_PID. */ + filtered_envp = strv_env_merge(1, envp, NULL); + filtered_envp = strv_env_unset(filtered_envp, "LISTEN_PID"); + p.envp = filtered_envp; + + /* Determine the set of inherited fds. */ + p.start_listen_fd = SD_LISTEN_FDS_START; + r = sd_listen_fds(1); + if (r < 0) { + log_error("Error %d while finding inheritable sockets: %s", r, strerror(-r)); + goto finish; + } + p.num_listen_fds = r; + + log_info("Starting the process pool with fd=%d through fd=%lu.", p.start_listen_fd, p.start_listen_fd + p.num_listen_fds - 1); + + r = run_pool(&p); + + wait_for_exit(p.num_cores * p.procs_per_core); + +finish: + strv_free(p.argp); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index d64b0d2..525f8bd 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -66,7 +66,8 @@ struct connection { static void free_connection(struct connection *c) { log_debug("Freeing fd=%d (conn %p).", c->fd, c); sd_event_source_unref(c->w); - close_nointr_nofail(c->fd); + if (c->fd > 0) + close_nointr_nofail(c->fd); free(c); } -- 1.8.3.1 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel