diff --git a/examples/spawn.c b/examples/spawn.c new file mode 100644 index 000000000..780a9ef9a --- /dev/null +++ b/examples/spawn.c @@ -0,0 +1,232 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif + +// posix_spawn() example +// +// This program demonstrates the use of posix_spawn() to run the command +// `ls --dired` and capture its output. It teaches several key features: +// +// - Changing the working directory for the child process +// - Redirecting stdout and stderr to pipes +// - Handling the output from the child process +// +// The primary advantage of using posix_spawn() instead of the +// traditional fork()/execve() combination for launching processes is +// safety, efficiency, and cross-platform compatibility. +// +// 1. On Linux, FreeBSD, and NetBSD: +// +// Cosmopolitan Libc's posix_spawn() uses vfork() under the hood on +// these platforms automatically, since it's faster than fork(). It's +// because vfork() creates a child process without needing to copy +// the parent's page tables, making it more efficient, especially for +// large processes. Furthermore, vfork() avoids the need to acquire +// every single mutex (see pthread_atfork() for more details) which +// makes it scalable in multi-threaded apps, since the other threads +// in your app can keep going while the spawning thread waits for the +// subprocess to call execve(). Normally vfork() is error-prone since +// there exists few functions that are @vforksafe. the posix_spawn() +// API is designed to offer maximum assurance that you can't shoot +// yourself in the foot. If you do, then file a bug with Cosmo. +// +// 2. On Windows: +// +// posix_spawn() avoids fork() entirely. Windows doesn't natively +// support fork(), and emulating it can be slow and memory-intensive. +// By using posix_spawn(), we get a much faster process creation on +// Windows systems, because it only needs to call CreateProcess(). +// Your file actions are replayed beforehand in a simulated way. Only +// Cosmopolitan Libc offers this level of quality. With Cygwin you'd +// have to use its proprietary APIs to achieve the same performance. +// +// 3. Simplified error handling: +// +// posix_spawn() combines process creation and program execution in a +// single call, reducing the points of failure and simplifying error +// handling. One important thing that happens with Cosmopolitan's +// posix_spawn() implementation is that the error code of execve() +// inside your subprocess, should it fail, will be propagated to your +// parent process. This will happen efficiently via vfork() shared +// memory in the event your Linux environment supports this. If it +// doesn't, then Cosmopolitan will fall back to a throwaway pipe(). +// The pipe is needed on platforms like XNU and OpenBSD which do not +// support vfork(). It's also needed under QEMU User. +// +// 4. Signal safety: +// +// posix_spawn() guarantees your signal handler callback functions +// won't be executed in the child process. By default, it'll remove +// sigaction() callbacks atomically. This ensures that if something +// like a SIGTERM or SIGHUP is sent to the child process before it's +// had a chance to call execve(), then the child process will simply +// be terminated (like the spawned process would) instead of running +// whatever signal handlers the spawning process has installed. If +// you've set some signals to SIG_IGN, then that'll be preserved for +// the child process by posix_spawn(), unless you explicitly call +// posix_spawnattr_setsigdefault() to reset them. +// +// 5. Portability: +// +// posix_spawn() is part of the POSIX standard, making it more +// portable across different UNIX-like systems and Windows (with +// appropriate libraries). Even the non-POSIX APIs we use here are +// portable; e.g. posix_spawn_file_actions_addchdir_np() is supported +// by glibc, musl, freebsd, and apple too. +// +// These benefits make posix_spawn() a preferred choice for efficient +// and portable process creation in many scenarios, especially when +// launching many processes or on systems where process creation +// performance is critical. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +int main() { + pid_t pid; + int status, ret; + posix_spawnattr_t attr; + posix_spawn_file_actions_t actions; + char *const argv[] = {"ls", "--dired", NULL}; + int pipe_stdout[2], pipe_stderr[2]; + + // Initialize file actions + ret = posix_spawnattr_init(&attr); + if (ret != 0) { + fprintf(stderr, "posix_spawnattr_init failed: %s\n", strerror(ret)); + return 1; + } + + // Explicitly request vfork() from posix_spawn() implementation + // + // This is currently the default for Cosmopolitan Libc, however you + // may want to set this anyway, for portability with other platforms. + // Please note that vfork() isn't officially specified by POSIX, so + // portable code may want to omit this and just use the default. + ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK); + if (ret != 0) { + fprintf(stderr, "posix_spawnattr_setflags failed: %s\n", strerror(ret)); + return 1; + } + + // Initialize file actions + ret = posix_spawn_file_actions_init(&actions); + if (ret != 0) { + fprintf(stderr, "posix_spawn_file_actions_init failed: %s\n", + strerror(ret)); + return 1; + } + + // Change directory to $HOME + ret = posix_spawn_file_actions_addchdir_np(&actions, getenv("HOME")); + if (ret != 0) { + fprintf(stderr, "posix_spawn_file_actions_addchdir_np failed: %s\n", + strerror(ret)); + return 1; + } + + // Create pipes for stdout and stderr + if (pipe(pipe_stdout) == -1 || pipe(pipe_stderr) == -1) { + perror("pipe"); + return 1; + } + + // Redirect child's stdout to pipe + ret = posix_spawn_file_actions_adddup2(&actions, pipe_stdout[PIPE_WRITE], + STDOUT_FILENO); + if (ret != 0) { + fprintf(stderr, "posix_spawn_file_actions_adddup2 (stdout) failed: %s\n", + strerror(ret)); + return 1; + } + + // Redirect child's stderr to pipe + ret = posix_spawn_file_actions_adddup2(&actions, pipe_stderr[PIPE_WRITE], + STDERR_FILENO); + if (ret != 0) { + fprintf(stderr, "posix_spawn_file_actions_adddup2 (stderr) failed: %s\n", + strerror(ret)); + return 1; + } + + // Close unused write ends of pipes in the child process + ret = posix_spawn_file_actions_addclose(&actions, pipe_stdout[PIPE_READ]); + if (ret != 0) { + fprintf(stderr, + "posix_spawn_file_actions_addclose (stdout read) failed: %s\n", + strerror(ret)); + return 1; + } + ret = posix_spawn_file_actions_addclose(&actions, pipe_stderr[PIPE_READ]); + if (ret != 0) { + fprintf(stderr, + "posix_spawn_file_actions_addclose (stderr read) failed: %s\n", + strerror(ret)); + return 1; + } + + // Spawn the child process + ret = posix_spawnp(&pid, "ls", &actions, NULL, argv, NULL); + if (ret != 0) { + fprintf(stderr, "posix_spawn failed: %s\n", strerror(ret)); + return 1; + } + + // Close unused write ends of pipes in the parent process + close(pipe_stdout[PIPE_WRITE]); + close(pipe_stderr[PIPE_WRITE]); + + // Read and print output from child process + char buffer[4096]; + ssize_t bytes_read; + + printf("Stdout from child process:\n"); + while ((bytes_read = read(pipe_stdout[PIPE_READ], buffer, sizeof(buffer))) > + 0) { + write(STDOUT_FILENO, buffer, bytes_read); + } + + printf("\nStderr from child process:\n"); + while ((bytes_read = read(pipe_stderr[PIPE_READ], buffer, sizeof(buffer))) > + 0) { + write(STDERR_FILENO, buffer, bytes_read); + } + + // Wait for the child process to complete + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return 1; + } + + // Clean up + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + close(pipe_stdout[PIPE_READ]); + close(pipe_stderr[PIPE_READ]); + + if (WIFEXITED(status)) { + printf("Child process exited with status %d\n", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("Child process terminated with signal %s\n", + strsignal(WTERMSIG(status))); + } else { + printf("Child process did not exit normally\n"); + } + + return 0; +} diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 81a80ecc8..090a9660a 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/strace.h" #include "libc/macros.h" #include "libc/nt/enum/fileflagandattributes.h" @@ -27,6 +28,18 @@ #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" +static int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +static bool IsAbsolutePathWin32(char16_t *path) { + if (path[0] == '\\') + return true; + if (IsAlpha(path[0]) && path[1] == ':') + return true; + return false; +} + static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path, int flags, char16_t file[hasatleast PATH_MAX]) { @@ -39,7 +52,7 @@ static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path, return -1; if (!filelen) return enoent(); - if (file[0] != u'\\' && dirhand != AT_FDCWD) { // ProTip: \\?\C:\foo + if (dirhand != AT_FDCWD && !IsAbsolutePathWin32(file)) { dirlen = GetFinalPathNameByHandle(dirhand, dir, ARRAYLEN(dir), kNtFileNameNormalized | kNtVolumeNameDos); if (!dirlen) @@ -49,7 +62,8 @@ static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path, dir[dirlen] = u'\\'; memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t)); memcpy(file, dir, ((n = dirlen + 1 + filelen) + 1) * sizeof(char16_t)); - return __normntpath(file, n); + int res = __normntpath(file, n); + return res; } else { return filelen; } diff --git a/libc/intrin/createfile.c b/libc/intrin/createfile.c index 3bac501f4..3063379b1 100644 --- a/libc/intrin/createfile.c +++ b/libc/intrin/createfile.c @@ -56,7 +56,7 @@ TryAgain: hHandle = __imp_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, opt_lpSecurity, dwCreationDisposition, dwFlagsAndAttributes, opt_hTemplateFile); - NTTRACE("CreateFile(%#hs, %s, %s, %s, %s, %s, %ld) → {%ld, %d}", lpFileName, + NTTRACE("CreateFile(%#!hs, %s, %s, %s, %s, %s, %ld) → {%ld, %d}", lpFileName, _DescribeNtFileAccessFlags(buf_accessflags, dwDesiredAccess), _DescribeNtFileShareFlags(buf_shareflags, dwShareMode), _DescribeNtSecurityAttributes(buf_secattr, opt_lpSecurity), diff --git a/libc/intrin/wsarecvfrom.c b/libc/intrin/wsarecvfrom.c index 170eb9977..0885c0eec 100644 --- a/libc/intrin/wsarecvfrom.c +++ b/libc/intrin/wsarecvfrom.c @@ -23,6 +23,7 @@ #include "libc/intrin/likely.h" #include "libc/intrin/strace.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/iovec.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" #include "libc/runtime/runtime.h" @@ -54,8 +55,8 @@ textwindows int WSARecvFrom( } if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) { kprintf(STRACE_PROLOGUE "WSARecvFrom(%lu, [", s); - DescribeIovNt(inout_lpBuffers, dwBufferCount, - rc != -1 ? NumberOfBytesRecvd : 0); + _DescribeIovNt(inout_lpBuffers, dwBufferCount, + rc != -1 ? NumberOfBytesRecvd : 0); kprintf("], %u, [%'u], %p, %p, %p, %s, %p) → %d %d\n", dwBufferCount, NumberOfBytesRecvd, opt_out_fromsockaddr, opt_inout_fromsockaddrlen, inout_lpFlags, DescribeNtOverlapped(opt_inout_lpOverlapped), diff --git a/libc/proc/posix_spawn.c b/libc/proc/posix_spawn.c index e0f13f5a5..310e1e73b 100644 --- a/libc/proc/posix_spawn.c +++ b/libc/proc/posix_spawn.c @@ -450,23 +450,101 @@ static textwindows dontinline errno_t posix_spawn_nt( * posix_spawnattr_destroy(&sa); * while (wait(&status) != -1); * - * This provides superior process creation performance across systems + * The posix_spawn() function may be used to launch subprocesses. The + * primary advantage of using posix_spawn() instead of the traditional + * fork() / execve() combination for launching processes is efficiency + * and cross-platform compatibility. * - * Processes are normally spawned by calling fork() and execve(), but - * that goes slow on Windows if the caller has allocated a nontrivial - * number of memory mappings, all of which need to be copied into the - * forked child, only to be destroyed a moment later. On UNIX systems - * fork() bears a similar cost that's 100x less bad, which is copying - * the page tables. So what this implementation does is on Windows it - * calls CreateProcess() directly and on UNIX it uses vfork() if it's - * possible (XNU and OpenBSD don't have it). On UNIX this API has the - * benefit of avoiding the footguns of using vfork() directly because - * this implementation will ensure signal handlers can't be called in - * the child process since that'd likely corrupt the parent's memory. - * On systems with a real vfork() implementation, the execve() status - * code is returned by this function via shared memory; otherwise, it - * gets passed via a temporary pipe (on systems like QEmu, Blink, and - * XNU/OpenBSD) whose support is auto-detected at runtime. + * 1. On Linux, FreeBSD, and NetBSD: + * + * Cosmopolitan Libc's posix_spawn() uses vfork() under the hood on + * these platforms automatically, since it's faster than fork(). It's + * because vfork() creates a child process without needing to copy + * the parent's page tables, making it more efficient, especially for + * large processes. Furthermore, vfork() avoids the need to acquire + * every single mutex (see pthread_atfork() for more details) which + * makes it scalable in multi-threaded apps, since the other threads + * in your app can keep going while the spawning thread waits for the + * subprocess to call execve(). Normally vfork() is error-prone since + * there exists few functions that are @vforksafe. the posix_spawn() + * API is designed to offer maximum assurance that you can't shoot + * yourself in the foot. If you do, then file a bug with Cosmo. + * + * 2. On Windows: + * + * posix_spawn() avoids fork() entirely. Windows doesn't natively + * support fork(), and emulating it can be slow and memory-intensive. + * By using posix_spawn(), we get a much faster process creation on + * Windows systems, because it only needs to call CreateProcess(). + * Your file actions are replayed beforehand in a simulated way. Only + * Cosmopolitan Libc offers this level of quality. With Cygwin you'd + * have to use its proprietary APIs to achieve the same performance. + * + * 3. Simplified error handling: + * + * posix_spawn() combines process creation and program execution in a + * single call, reducing the points of failure and simplifying error + * handling. One important thing that happens with Cosmopolitan's + * posix_spawn() implementation is that the error code of execve() + * inside your subprocess, should it fail, will be propagated to your + * parent process. This will happen efficiently via vfork() shared + * memory in the event your Linux environment supports this. If it + * doesn't, then Cosmopolitan will fall back to a throwaway pipe(). + * The pipe is needed on platforms like XNU and OpenBSD which do not + * support vfork(). It's also needed under QEMU User. + * + * 4. Signal safety: + * + * posix_spawn() guarantees your signal handler callback functions + * won't be executed in the child process. By default, it'll remove + * sigaction() callbacks atomically. This ensures that if something + * like a SIGTERM or SIGHUP is sent to the child process before it's + * had a chance to call execve(), then the child process will simply + * be terminated (like the spawned process would) instead of running + * whatever signal handlers the spawning process has installed. If + * you've set some signals to SIG_IGN, then that'll be preserved for + * the child process by posix_spawn(), unless you explicitly call + * posix_spawnattr_setsigdefault() to reset them. + * + * 5. Portability: + * + * posix_spawn() is part of the POSIX standard, making it more + * portable across different UNIX-like systems and Windows (with + * appropriate libraries). Even the non-POSIX APIs we use here are + * portable; e.g. posix_spawn_file_actions_addchdir_np() is supported + * by glibc, musl libc, and apple libc too. + * + * When using posix_spawn() you have the option of passing an attributes + * object that specifies how the child process should be created. These + * functions are provided by Cosmopolitan Libc for setting attributes: + * + * - posix_spawnattr_init() + * - posix_spawnattr_destroy() + * - posix_spawnattr_setflags() + * - posix_spawnattr_getflags() + * - posix_spawnattr_setsigmask() + * - posix_spawnattr_getsigmask() + * - posix_spawnattr_setpgroup() + * - posix_spawnattr_getpgroup() + * - posix_spawnattr_setrlimit_np() + * - posix_spawnattr_getrlimit_np() + * - posix_spawnattr_setschedparam() + * - posix_spawnattr_getschedparam() + * - posix_spawnattr_setschedpolicy() + * - posix_spawnattr_getschedpolicy() + * - posix_spawnattr_setsigdefault() + * - posix_spawnattr_getsigdefault() + * + * You can also pass an ordered list of file actions to perform. The + * following APIs are provided by Cosmopolitan Libc for doing that: + * + * - posix_spawn_file_actions_init() + * - posix_spawn_file_actions_destroy() + * - posix_spawn_file_actions_adddup2() + * - posix_spawn_file_actions_addopen() + * - posix_spawn_file_actions_addclose() + * - posix_spawn_file_actions_addchdir_np() + * - posix_spawn_file_actions_addfchdir_np() * * @param pid if non-null shall be set to child pid on success * @param path is resolved path of program which is not `$PATH` searched @@ -496,31 +574,30 @@ errno_t posix_spawn(int *pid, const char *path, sigset_t blockall, oldmask; int child, res, cs, e = errno; volatile bool can_clobber = false; + short flags = attrp && *attrp ? (*attrp)->flags : 0; sigfillset(&blockall); sigprocmask(SIG_SETMASK, &blockall, &oldmask); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); - if ((use_pipe = !atomic_load_explicit(&has_vfork, memory_order_acquire))) { + if ((use_pipe = (flags & POSIX_SPAWN_USEFORK) || + !atomic_load_explicit(&has_vfork, memory_order_acquire))) { if (pipe2(pfds, O_CLOEXEC)) { res = errno; goto ParentFailed; } } - if (!(child = vfork())) { + if (!(child = (flags & POSIX_SPAWN_USEFORK) ? fork() : vfork())) { can_clobber = true; sigset_t childmask; bool lost_cloexec = 0; struct sigaction dfl = {0}; - short flags = attrp && *attrp ? (*attrp)->flags : 0; if (use_pipe) close(pfds[0]); - for (int sig = 1; sig < _NSIG; sig++) { + for (int sig = 1; sig < _NSIG; sig++) if (__sighandrvas[sig] != (long)SIG_DFL && (__sighandrvas[sig] != (long)SIG_IGN || ((flags & POSIX_SPAWN_SETSIGDEF) && - sigismember(&(*attrp)->sigdefault, sig) == 1))) { + sigismember(&(*attrp)->sigdefault, sig) == 1))) sigaction(sig, &dfl, 0); - } - } if (flags & POSIX_SPAWN_SETSID) setsid(); if ((flags & POSIX_SPAWN_SETPGROUP) && setpgid(0, (*attrp)->pgroup)) @@ -585,7 +662,7 @@ errno_t posix_spawn(int *pid, const char *path, if (sched_setparam(0, &(*attrp)->schedparam)) goto ChildFailed; } - if (flags & POSIX_SPAWN_SETRLIMIT) { + if (flags & POSIX_SPAWN_SETRLIMIT_NP) { int rlimset = (*attrp)->rlimset; while (rlimset) { int resource = bsf(rlimset); @@ -618,9 +695,8 @@ errno_t posix_spawn(int *pid, const char *path, } _Exit(127); } - if (use_pipe) { + if (use_pipe) close(pfds[1]); - } if (child != -1) { if (!use_pipe) { res = status; diff --git a/libc/proc/posix_spawn.h b/libc/proc/posix_spawn.h index 9da96f721..2efa2258a 100644 --- a/libc/proc/posix_spawn.h +++ b/libc/proc/posix_spawn.h @@ -12,7 +12,8 @@ #define POSIX_SPAWN_SETSCHEDPARAM 16 #define POSIX_SPAWN_SETSCHEDULER 32 #define POSIX_SPAWN_SETSID 128 -#define POSIX_SPAWN_SETRLIMIT 256 +#define POSIX_SPAWN_SETRLIMIT_NP 256 +#define POSIX_SPAWN_USEFORK 512 COSMOPOLITAN_C_START_ @@ -55,10 +56,10 @@ int posix_spawnattr_getsigdefault(const posix_spawnattr_t *, sigset_t *) libcesque; int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *) libcesque; -int posix_spawnattr_getrlimit(const posix_spawnattr_t *, int, - struct rlimit *) libcesque; -int posix_spawnattr_setrlimit(posix_spawnattr_t *, int, - const struct rlimit *) libcesque; +int posix_spawnattr_getrlimit_np(const posix_spawnattr_t *, int, + struct rlimit *) libcesque; +int posix_spawnattr_setrlimit_np(posix_spawnattr_t *, int, + const struct rlimit *) libcesque; COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_STDIO_SPAWN_H_ */ diff --git a/libc/proc/posix_spawnattr_getrlimit.c b/libc/proc/posix_spawnattr_getrlimit.c index cc2265998..c45a3387c 100644 --- a/libc/proc/posix_spawnattr_getrlimit.c +++ b/libc/proc/posix_spawnattr_getrlimit.c @@ -31,8 +31,8 @@ * @raise EINVAL if `resource` is invalid or unsupported by host * @raise ENOENT if `resource` is absent */ -int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource, - struct rlimit *rlim) { +int posix_spawnattr_getrlimit_np(const posix_spawnattr_t *attr, int resource, + struct rlimit *rlim) { if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) { if (((*attr)->rlimset & (1u << resource))) { *rlim = (*attr)->rlim[resource]; diff --git a/libc/proc/posix_spawnattr_setflags.c b/libc/proc/posix_spawnattr_setflags.c index 13cb82f28..057306dec 100644 --- a/libc/proc/posix_spawnattr_setflags.c +++ b/libc/proc/posix_spawnattr_setflags.c @@ -25,6 +25,8 @@ * * @param attr was initialized by posix_spawnattr_init() * @param flags may have any of the following + * - `POSIX_SPAWN_USEFORK` + * - `POSIX_SPAWN_USEVFORK` * - `POSIX_SPAWN_RESETIDS` * - `POSIX_SPAWN_SETPGROUP` * - `POSIX_SPAWN_SETSIGDEF` @@ -32,12 +34,13 @@ * - `POSIX_SPAWN_SETSCHEDPARAM` * - `POSIX_SPAWN_SETSCHEDULER` * - `POSIX_SPAWN_SETSID` - * - `POSIX_SPAWN_SETRLIMIT` + * - `POSIX_SPAWN_SETRLIMIT_NP` * @return 0 on success, or errno on error * @raise EINVAL if `flags` has invalid bits */ int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { - if (flags & ~(POSIX_SPAWN_USEVFORK | // + if (flags & ~(POSIX_SPAWN_USEFORK | // + POSIX_SPAWN_USEVFORK | // POSIX_SPAWN_RESETIDS | // POSIX_SPAWN_SETPGROUP | // POSIX_SPAWN_SETSIGDEF | // @@ -45,7 +48,7 @@ int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { POSIX_SPAWN_SETSCHEDPARAM | // POSIX_SPAWN_SETSCHEDULER | // POSIX_SPAWN_SETSID | // - POSIX_SPAWN_SETRLIMIT)) { + POSIX_SPAWN_SETRLIMIT_NP)) { return EINVAL; } (*attr)->flags = flags; diff --git a/libc/proc/posix_spawnattr_setrlimit.c b/libc/proc/posix_spawnattr_setrlimit.c index dd15a74ec..60c68cdf2 100644 --- a/libc/proc/posix_spawnattr_setrlimit.c +++ b/libc/proc/posix_spawnattr_setrlimit.c @@ -26,14 +26,14 @@ /** * Sets resource limit on spawned process. * - * You also need to pass `POSIX_SPAWN_SETRLIMIT` to + * You also need to pass `POSIX_SPAWN_SETRLIMIT_NP` to * posix_spawnattr_setflags() for it to take effect. * * @return 0 on success, or errno on error * @raise EINVAL if resource is invalid */ -int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource, - const struct rlimit *rlim) { +int posix_spawnattr_setrlimit_np(posix_spawnattr_t *attr, int resource, + const struct rlimit *rlim) { if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) { (*attr)->rlimset |= 1u << resource; (*attr)->rlim[resource] = *rlim; diff --git a/tool/build/compile.c b/tool/build/compile.c index 37406b791..a81b119f7 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -529,7 +529,7 @@ void PlanResource(int resource, struct rlimit rlim) { return; rlim.rlim_cur = MIN(rlim.rlim_cur, prior.rlim_max); rlim.rlim_max = MIN(rlim.rlim_max, prior.rlim_max); - posix_spawnattr_setrlimit(&spawnattr, resource, &rlim); + posix_spawnattr_setrlimit_np(&spawnattr, resource, &rlim); } void SetCpuLimit(int secs) { @@ -651,7 +651,7 @@ int Launch(void) { posix_spawnattr_init(&spawnattr); posix_spawnattr_setsigmask(&spawnattr, &savemask); posix_spawnattr_setflags(&spawnattr, - POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETRLIMIT); + POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETRLIMIT_NP); SetCpuLimit(cpuquota); SetFszLimit(fszquota); SetMemLimit(memquota);