summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2023-04-19 17:59:01 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2023-05-03 23:11:35 -0400
commitfd142a53ebab66e6d2057a84485a1812499e9410 (patch)
tree3ab2901f14309dc042fb9a269b066d3f0bb35bf4 /src
parent5ca09e56a79a365d20f480e8900c12d442ecec4a (diff)
downloadlighttpd-git-fd142a53ebab66e6d2057a84485a1812499e9410.tar.gz
[core] use posix_spawn() where available
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
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/config.h.cmake2
-rw-r--r--src/fdevent.c99
-rw-r--r--src/meson.build2
4 files changed, 108 insertions, 1 deletions
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 <stdio.h> /* perror() rename() */
#include <signal.h> /* signal() kill() */
+#ifdef HAVE_POSIX_SPAWN
+#include <spawn.h> /* 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',