From 5da14042f70711ea5cf66e034699730335462f66 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 14:08:03 +0200 Subject: Merging upstream version 1.45.3+dfsg. Signed-off-by: Daniel Baumann --- src/web/server/h2o/libh2o/lib/common/serverutil.c | 317 ++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 src/web/server/h2o/libh2o/lib/common/serverutil.c (limited to 'src/web/server/h2o/libh2o/lib/common/serverutil.c') diff --git a/src/web/server/h2o/libh2o/lib/common/serverutil.c b/src/web/server/h2o/libh2o/lib/common/serverutil.c new file mode 100644 index 000000000..8226f6efc --- /dev/null +++ b/src/web/server/h2o/libh2o/lib/common/serverutil.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Nick Desaulniers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_SC_NPROCESSORS_ONLN) +#include +#endif +#include "cloexec.h" +#include "h2o/memory.h" +#include "h2o/serverutil.h" +#include "h2o/socket.h" +#include "h2o/string_.h" + +void h2o_set_signal_handler(int signo, void (*cb)(int signo)) +{ + struct sigaction action; + + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_handler = cb; + sigaction(signo, &action, NULL); +} + +int h2o_setuidgid(const char *user) +{ + struct passwd pwbuf, *pw; + char buf[65536]; /* should be large enough */ + + errno = 0; + if (getpwnam_r(user, &pwbuf, buf, sizeof(buf), &pw) != 0) { + perror("getpwnam_r"); + return -1; + } + if (pw == NULL) { + fprintf(stderr, "unknown user:%s\n", user); + return -1; + } + if (setgid(pw->pw_gid) != 0) { + fprintf(stderr, "setgid(%d) failed:%s\n", (int)pw->pw_gid, strerror(errno)); + return -1; + } + if (initgroups(pw->pw_name, pw->pw_gid) != 0) { + fprintf(stderr, "initgroups(%s, %d) failed:%s\n", pw->pw_name, (int)pw->pw_gid, strerror(errno)); + return -1; + } + if (setuid(pw->pw_uid) != 0) { + fprintf(stderr, "setuid(%d) failed:%s\n", (int)pw->pw_uid, strerror(errno)); + return -1; + } + + return 0; +} + +size_t h2o_server_starter_get_fds(int **_fds) +{ + const char *ports_env, *start, *end, *eq; + size_t t; + H2O_VECTOR(int) fds = {NULL}; + + if ((ports_env = getenv("SERVER_STARTER_PORT")) == NULL) + return 0; + if (ports_env[0] == '\0') { + fprintf(stderr, "$SERVER_STARTER_PORT is empty\n"); + return SIZE_MAX; + } + + /* ports_env example: 127.0.0.1:80=3;/tmp/sock=4 */ + for (start = ports_env; *start != '\0'; start = *end == ';' ? end + 1 : end) { + if ((end = strchr(start, ';')) == NULL) + end = start + strlen(start); + if ((eq = memchr(start, '=', end - start)) == NULL) { + fprintf(stderr, "invalid $SERVER_STARTER_PORT, an element without `=` in: %s\n", ports_env); + goto Error; + } + if ((t = h2o_strtosize(eq + 1, end - eq - 1)) == SIZE_MAX) { + fprintf(stderr, "invalid file descriptor number in $SERVER_STARTER_PORT: %s\n", ports_env); + goto Error; + } + h2o_vector_reserve(NULL, &fds, fds.size + 1); + fds.entries[fds.size++] = (int)t; + } + + *_fds = fds.entries; + return fds.size; +Error: + free(fds.entries); + return SIZE_MAX; +} + +static char **build_spawn_env(void) +{ + extern char **environ; + size_t num; + + /* calculate number of envvars, as well as looking for H2O_ROOT= */ + for (num = 0; environ[num] != NULL; ++num) + if (strncmp(environ[num], "H2O_ROOT=", sizeof("H2O_ROOT=") - 1) == 0) + return NULL; + + /* not found */ + char **newenv = h2o_mem_alloc(sizeof(*newenv) * (num + 2) + sizeof("H2O_ROOT=" H2O_TO_STR(H2O_ROOT))); + memcpy(newenv, environ, sizeof(*newenv) * num); + newenv[num] = (char *)(newenv + num + 2); + newenv[num + 1] = NULL; + strcpy(newenv[num], "H2O_ROOT=" H2O_TO_STR(H2O_ROOT)); + + return newenv; +} + +pid_t h2o_spawnp(const char *cmd, char *const *argv, const int *mapped_fds, int cloexec_mutex_is_locked) +{ +#if defined(__linux__) + + /* posix_spawnp of Linux does not return error if the executable does not exist, see + * https://gist.github.com/kazuho/0c233e6f86d27d6e4f09 + */ + extern char **environ; + int pipefds[2] = {-1, -1}, errnum; + pid_t pid; + + /* create pipe, used for sending error codes */ + if (pipe2(pipefds, O_CLOEXEC) != 0) + goto Error; + + /* fork */ + if (!cloexec_mutex_is_locked) + pthread_mutex_lock(&cloexec_mutex); + if ((pid = fork()) == 0) { + /* in child process, map the file descriptors and execute; return the errnum through pipe if exec failed */ + if (mapped_fds != NULL) { + for (; *mapped_fds != -1; mapped_fds += 2) { + if (mapped_fds[0] != mapped_fds[1]) { + if (mapped_fds[1] != -1) + dup2(mapped_fds[0], mapped_fds[1]); + close(mapped_fds[0]); + } + } + } + char **env = build_spawn_env(); + if (env != NULL) + environ = env; + execvp(cmd, argv); + errnum = errno; + write(pipefds[1], &errnum, sizeof(errnum)); + _exit(EX_SOFTWARE); + } + if (!cloexec_mutex_is_locked) + pthread_mutex_unlock(&cloexec_mutex); + if (pid == -1) + goto Error; + + /* parent process */ + close(pipefds[1]); + pipefds[1] = -1; + ssize_t rret; + errnum = 0; + while ((rret = read(pipefds[0], &errnum, sizeof(errnum))) == -1 && errno == EINTR) + ; + if (rret != 0) { + /* spawn failed */ + while (waitpid(pid, NULL, 0) != pid) + ; + pid = -1; + errno = errnum; + goto Error; + } + + /* spawn succeeded */ + close(pipefds[0]); + return pid; + +Error: + errnum = errno; + if (pipefds[0] != -1) + close(pipefds[0]); + if (pipefds[1] != -1) + close(pipefds[1]); + errno = errnum; + return -1; + +#else + + posix_spawn_file_actions_t file_actions; + pid_t pid; + extern char **environ; + char **env = build_spawn_env(); + posix_spawn_file_actions_init(&file_actions); + if (mapped_fds != NULL) { + for (; *mapped_fds != -1; mapped_fds += 2) { + if (mapped_fds[1] != -1) + posix_spawn_file_actions_adddup2(&file_actions, mapped_fds[0], mapped_fds[1]); + posix_spawn_file_actions_addclose(&file_actions, mapped_fds[0]); + } + } + if (!cloexec_mutex_is_locked) + pthread_mutex_lock(&cloexec_mutex); + errno = posix_spawnp(&pid, cmd, &file_actions, NULL, argv, env != NULL ? env : environ); + if (!cloexec_mutex_is_locked) + pthread_mutex_unlock(&cloexec_mutex); + free(env); + if (errno != 0) + return -1; + + return pid; + +#endif +} + +int h2o_read_command(const char *cmd, char **argv, h2o_buffer_t **resp, int *child_status) +{ + int respfds[2] = {-1, -1}; + pid_t pid = -1; + int mutex_locked = 0, ret = -1; + + h2o_buffer_init(resp, &h2o_socket_buffer_prototype); + + pthread_mutex_lock(&cloexec_mutex); + mutex_locked = 1; + + /* create pipe for reading the result */ + if (pipe(respfds) != 0) + goto Exit; + if (fcntl(respfds[0], F_SETFD, O_CLOEXEC) < 0) + goto Exit; + + /* spawn */ + int mapped_fds[] = {respfds[1], 1, /* stdout of the child process is read from the pipe */ + -1}; + if ((pid = h2o_spawnp(cmd, argv, mapped_fds, 1)) == -1) + goto Exit; + close(respfds[1]); + respfds[1] = -1; + + pthread_mutex_unlock(&cloexec_mutex); + mutex_locked = 0; + + /* read the response from pipe */ + while (1) { + h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192); + ssize_t r; + while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR) + ; + if (r <= 0) + break; + (*resp)->size += r; + } + +Exit: + if (mutex_locked) + pthread_mutex_unlock(&cloexec_mutex); + if (pid != -1) { + /* wait for the child to complete */ + pid_t r; + while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR) + ; + if (r == pid) { + /* success */ + ret = 0; + } + } + if (respfds[0] != -1) + close(respfds[0]); + if (respfds[1] != -1) + close(respfds[1]); + if (ret != 0) + h2o_buffer_dispose(resp); + + return ret; +} + +size_t h2o_numproc(void) +{ +#if defined(_SC_NPROCESSORS_ONLN) + return (size_t)sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(CTL_HW) && defined(HW_AVAILCPU) + int name[] = {CTL_HW, HW_AVAILCPU}; + int ncpu; + size_t ncpu_sz = sizeof(ncpu); + if (sysctl(name, sizeof(name) / sizeof(name[0]), &ncpu, &ncpu_sz, NULL, 0) != 0 || sizeof(ncpu) != ncpu_sz) { + fprintf(stderr, "[ERROR] failed to obtain number of CPU cores, assuming as one\n"); + ncpu = 1; + } + return ncpu; +#else + return 1; +#endif +} -- cgit v1.2.3