From fd142a53ebab66e6d2057a84485a1812499e9410 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Wed, 19 Apr 2023 17:59:01 -0400 Subject: [core] use posix_spawn() where available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use posix_spawn() where available use posix_spawn_file_actions_addfchdir_np() where available Using posix_spawn() reduces initial execution overhead of CGI programs; posix_spawn() is often faster than application code wrapping and calling traditional fork(),execve(). (history: fdevent.c posix_spawn code based on fdio.c:fdio_ipc_spawn() from 2015 on one of my unpublished branches. The inability to chdir() delayed inclusion in lighttpd, as the CGI specification says: "The current working directory for the script SHOULD be set to the directory containing the script." e.g. chdir() to target program directory before CGI execution) posix_spawn_file_actions_addfchdir_np() is a new(er) extension supported in glibc 2.29+, musl libc, FreeBSD ≥ 13.1, macOS ≥ 10.15 according to https://cygwin.com/pipermail/cygwin/2023-April/253505.html https://cygwin.com/pipermail/cygwin/2023-April/253526.html https://sourceware.org/bugzilla/show_bug.cgi?id=17405 POSIX Issue 8 plans to include posix_spawn_file_actions_addfchdir(): https://www.austingroupbugs.net/view.php?id=1208 --- src/CMakeLists.txt | 6 ++++ src/config.h.cmake | 2 ++ src/fdevent.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/meson.build | 2 ++ 4 files changed, 108 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d469d75c..d8b1da3e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -209,6 +209,12 @@ check_include_files(sys/select.h HAVE_SYS_SELECT_H) check_function_exists(select HAVE_SELECT) endif() +check_include_files(spawn.h HAVE_SPAWN_H) +if(HAVE_SPAWN_H) +check_function_exists(posix_spawn HAVE_POSIX_SPAWN) +check_function_exists(posix_spawn_file_actions_addfchdir_np HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP) +endif() + set(CMAKE_EXTRA_INCLUDE_FILES time.h) check_function_exists(timegm HAVE_TIMEGM) set(CMAKE_EXTRA_INCLUDE_FILES) diff --git a/src/config.h.cmake b/src/config.h.cmake index 838792b4..99dfd954 100644 --- a/src/config.h.cmake +++ b/src/config.h.cmake @@ -178,6 +178,8 @@ #cmakedefine HAVE_MMAP #cmakedefine HAVE_PIPE2 #cmakedefine HAVE_POLL +#cmakedefine HAVE_POSIX_SPAWN +#cmakedefine HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP #cmakedefine HAVE_PORT_CREATE #cmakedefine HAVE_PREAD #cmakedefine HAVE_PREADV diff --git a/src/fdevent.c b/src/fdevent.c index 43d2622e..6fde8d97 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -432,6 +432,9 @@ int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) { #include /* perror() rename() */ #include /* signal() kill() */ +#ifdef HAVE_POSIX_SPAWN +#include /* posix_spawn*() */ +#endif int fdevent_rename(const char *oldpath, const char *newpath) { @@ -440,7 +443,101 @@ int fdevent_rename(const char *oldpath, const char *newpath) { pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) { - #ifdef HAVE_FORK + #ifdef HAVE_POSIX_SPAWN + + /* Caller must ensure that all fd* args are >= 3, i.e. > STDERR_FILENO (2), + * unless fd* arg is -1, in which case we preserve existing target fd, or + * unless fd does not have FD_CLOEXEC set *and* is not being replaced, + * e.g. if fd 1 is open to /dev/null and fdout is -1 and fderr is 1 so + * that fd 1 (STDOUT_FILENO) to /dev/null is dup2() to fd 2 (STDERR_FILENO). + * Caller must handle so that if any dup() is required to make fd* >= 3, + * then the caller has access to the new fds. The reason fd* args >= 3 + * is required is that we set FD_CLOEXEC on all fds (thread-safety) and + * a dup2() in child is used for dup2() side effect of removing FD_CLOEXEC. + * (posix_spawn() provides posix_spawn_file_actions_adddup2() whereas + * it does not provide a means to use fcntl() to remove FD_CLOEXEC) */ + + sigset_t sigs; + posix_spawn_file_actions_t file_actions; + posix_spawnattr_t attr; + int rc; + pid_t pid = -1; + if (0 != (rc = posix_spawn_file_actions_init(&file_actions))) + return pid; + if (0 != (rc = posix_spawnattr_init(&attr))) { + posix_spawn_file_actions_destroy(&file_actions); + return pid; + } + if ( 0 == (rc = (fdin >= 0) + ? posix_spawn_file_actions_adddup2( + &file_actions, fdin, STDIN_FILENO) + : 0) + && 0 == (rc = (fdout >= 0) + ? posix_spawn_file_actions_adddup2( + &file_actions, fdout, STDOUT_FILENO) + : 0) + && 0 == (rc = (fderr >= 0) + ? posix_spawn_file_actions_adddup2( + &file_actions, fderr, STDERR_FILENO) + : 0) + #ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP + && 0 == (rc = (-1 != dfd) + ? posix_spawn_file_actions_addfchdir_np( + &file_actions, dfd) + : 0) + #endif + && 0 == (rc = posix_spawnattr_setflags( + &attr, POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK)) + && 0 == (rc = sigemptyset(&sigs)) + && 0 == (rc = posix_spawnattr_setsigmask(&attr, &sigs)) + /*(force reset signals to SIG_DFL if server.c set to SIG_IGN)*/ + #ifdef SIGTTOU + && 0 == (rc = sigaddset(&sigs, SIGTTOU)) + #endif + #ifdef SIGTTIN + && 0 == (rc = sigaddset(&sigs, SIGTTIN)) + #endif + #ifdef SIGTSTP + && 0 == (rc = sigaddset(&sigs, SIGTSTP)) + #endif + && 0 == (rc = sigaddset(&sigs, SIGPIPE)) + && 0 == (rc = sigaddset(&sigs, SIGUSR1)) + && 0 == (rc = posix_spawnattr_setsigdefault(&attr, &sigs))) { + + #ifndef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP + /* not thread-safe, but ok since lighttpd not (currently) threaded + * (alternatively, check HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP + * along with HAVE_POSIX_SPAWN at top of block and use HAVE_FORK + * below if HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP not avail)*/ + if (-1 != dfd) { + int ndfd = dfd; + dfd = fdevent_open_dirname(".", 1); /* reuse dfd for cwd fd */ + if (-1 == dfd || 0 != fchdir(ndfd)) + rc = -1; /*(or could set to errno for posix consistency)*/ + } + if (0 == rc) + #endif + + rc = posix_spawn(&pid, name, &file_actions, &attr, + argv, envp ? envp : environ); + + if (0 != rc) + pid = -1; + + #ifndef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDFCHDIR_NP + if (-1 != dfd) { + if (0 != fchdir(dfd)) { /* ignore error; best effort */ + /*rc = errno;*/ + } + close(dfd); + } + #endif + } + posix_spawn_file_actions_destroy(&file_actions); + posix_spawnattr_destroy(&attr); + return pid; + + #elif defined(HAVE_FORK) pid_t pid = fork(); if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */ diff --git a/src/meson.build b/src/meson.build index 930d1cf5..db59f0f5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -136,6 +136,8 @@ functions = { 'mmap': 'sys/mman.h', 'pipe2': 'unistd.h', 'poll': 'poll.h', + 'posix_spawn': 'spawn.h', + 'posix_spawn_file_actions_addfchdir_np': 'spawn.h', 'pread': 'unistd.h', 'preadv': 'sys/uio.h', 'pwrite': 'unistd.h', -- cgit v1.2.1