diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index 57a38808..3780b8c1 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -41,40 +41,36 @@ static textwindows noinline uint32_t GetUserNameHash(void) { return KnuthMultiplicativeHash32(buf, size >> 1); } -static uint32_t getuidgid(int at, uint32_t impl(void)) { +/** + * Returns real user id of process. + * + * This never fails. On Windows, which doesn't really have this concept, + * we return a deterministic value that's likely to work. + * + * @asyncsignalsafe + * @vforksafe + */ +uint32_t getuid(void) { if (!IsWindows()) { - if (at != -1) { - return getauxval(at); - } else { - return impl(); - } + return sys_getuid(); } else { return GetUserNameHash(); } } -/** - * Returns real user id of process. - * - * This never fails. On Windows, which doesn't really have this concept, - * we return a deterministic value that's likely to work. On Linux, this - * is fast. - * - * @asyncsignalsafe - */ -uint32_t getuid(void) { - return getuidgid(AT_UID, sys_getuid); -} - /** * Returns real group id of process. * * This never fails. On Windows, which doesn't really have this concept, - * we return a deterministic value that's likely to work. On Linux, this - * is fast. + * we return a deterministic value that's likely to work. * * @asyncsignalsafe + * @vforksafe */ uint32_t getgid(void) { - return getuidgid(AT_GID, sys_getgid); + if (!IsWindows()) { + return sys_getgid(); + } else { + return GetUserNameHash(); + } } diff --git a/libc/calls/sigfillset.c b/libc/calls/sigfillset.c index 7e1f6afe..e455ffc3 100644 --- a/libc/calls/sigfillset.c +++ b/libc/calls/sigfillset.c @@ -24,6 +24,7 @@ * * @return 0 on success, or -1 w/ errno * @asyncsignalsafe + * @vforksafe */ int sigfillset(sigset_t *set) { memset(set->__bits, -1, sizeof(set->__bits)); diff --git a/libc/calls/sigismember.c b/libc/calls/sigismember.c index 759da748..bbb57fe7 100644 --- a/libc/calls/sigismember.c +++ b/libc/calls/sigismember.c @@ -24,6 +24,7 @@ * * @return true, false, or -1 w/ errno * @asyncsignalsafe + * @vforksafe */ int sigismember(const sigset_t *set, int sig) { unsigned i = sig - 1; diff --git a/libc/stdio/spawn.c b/libc/stdio/spawn.c new file mode 100644 index 00000000..e7588640 --- /dev/null +++ b/libc/stdio/spawn.c @@ -0,0 +1,117 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/scheduler.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/stdio/spawn.h" +#include "libc/stdio/spawna.internal.h" +#include "libc/str/str.h" + +/** + * Spawns process the POSIX way. + * + * @param pid is non-NULL and will be set to child pid in parent + * @param path of executable that is not PATH searched + * @return 0 on success or error number on failure + */ +int posix_spawn(int *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[]) { + unsigned mode; + sigset_t allsigs; + struct sigaction dfl; + char *p, *q, opath[PATH_MAX]; + int s, fd, newfd, oflag, tempfd; + if (!(*pid = vfork())) { + if (attrp) { + if (attrp->posix_attr_flags & POSIX_SPAWN_SETPGROUP) { + if (setpgid(0, attrp->posix_attr_pgroup)) _exit(127); + } + if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGMASK) { + sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, NULL); + } + if (attrp->posix_attr_flags & POSIX_SPAWN_RESETIDS) { + setuid(getuid()); + setgid(getgid()); + } + if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGDEF) { + dfl.sa_handler = SIG_DFL; + dfl.sa_flags = 0; + sigfillset(&allsigs); + for (s = 0; sigismember(&allsigs, s); s++) { + if (sigismember(&attrp->posix_attr_sigdefault, s)) { + if (sigaction(s, &dfl, NULL) == -1) _exit(127); + } + } + } + } + if (file_actions) { + p = *file_actions; + while (*p != '\0') { + if (!strncmp(p, "close(", 6)) { + if (sscanf(p + 6, "%d)", &fd) != 1) _exit(127); + if (close(fd) == -1) _exit(127); + } else if (!strncmp(p, "dup2(", 5)) { + if (sscanf(p + 5, "%d,%d)", &fd, &newfd) != 2) _exit(127); + if (dup2(fd, newfd) == -1) _exit(127); + } else if (!strncmp(p, "open(", 5)) { + if (sscanf(p + 5, "%d,", &fd) != 1) _exit(127); + p = strchr(p, ',') + 1; + q = strchr(p, '*'); + if (!q || q - p + 1 > PATH_MAX) _exit(127); + strncpy(opath, p, q - p); + opath[q - p] = '\0'; + if (sscanf(q + 1, "%o,%o)", &oflag, &mode) != 2) _exit(127); + if (close(fd) == -1 && errno != EBADF) _exit(127); + tempfd = open(opath, oflag, mode); + if (tempfd == -1) _exit(127); + if (tempfd != fd) { + if (dup2(tempfd, fd) == -1) _exit(127); + if (close(tempfd) == -1) _exit(127); + } + } else { + _exit(127); + } + p = strchr(p, ')') + 1; + } + } + if (attrp) { + if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDULER) { + if (sched_setscheduler(0, attrp->posix_attr_schedpolicy, + &attrp->posix_attr_schedparam) == -1) { + _exit(127); + } + } + if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDPARAM) { + if (sched_setparam(0, &attrp->posix_attr_schedparam) == -1) { + _exit(127); + } + } + } + execve(path, argv, envp); + _exit(127); + } else { + if (*pid == -1) return errno; + return 0; + } +} diff --git a/libc/stdio/spawn.h b/libc/stdio/spawn.h new file mode 100644 index 00000000..d967646e --- /dev/null +++ b/libc/stdio/spawn.h @@ -0,0 +1,48 @@ +#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWN_H_ +#define COSMOPOLITAN_LIBC_STDIO_SPAWN_H_ +#include "libc/calls/struct/sched_param.h" +#include "libc/calls/struct/sigset.h" + +#define POSIX_SPAWN_RESETIDS 0x01 +#define POSIX_SPAWN_SETPGROUP 0x02 +#define POSIX_SPAWN_SETSIGDEF 0x04 +#define POSIX_SPAWN_SETSIGMASK 0x08 +#define POSIX_SPAWN_SETSCHEDPARAM 0x10 +#define POSIX_SPAWN_SETSCHEDULER 0x20 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef char *posix_spawn_file_actions_t; +typedef struct _posix_spawnattr posix_spawnattr_t; + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *); +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *); +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, int); +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int); +int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *, int, + const char *, int, unsigned); +int posix_spawnattr_init(posix_spawnattr_t *); +int posix_spawnattr_destroy(posix_spawnattr_t *); +int posix_spawnattr_getflags(const posix_spawnattr_t *, short *); +int posix_spawnattr_setflags(posix_spawnattr_t *, short); +int posix_spawnattr_getpgroup(const posix_spawnattr_t *, int *); +int posix_spawnattr_setpgroup(posix_spawnattr_t *, int); +int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *, int *); +int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int); +int posix_spawnattr_getschedparam(const posix_spawnattr_t *, + struct sched_param *); +int posix_spawnattr_setschedparam(posix_spawnattr_t *, + const struct sched_param *); +int posix_spawnattr_getsigmask(const posix_spawnattr_t *, sigset_t *); +int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *); +int posix_spawnattr_getdefault(const posix_spawnattr_t *, sigset_t *); +int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *); +int posix_spawn(int *, const char *, const posix_spawn_file_actions_t *, + const posix_spawnattr_t *, char *const[], char *const[]); +int posix_spawnp(int *, const char *, const posix_spawn_file_actions_t *, + const posix_spawnattr_t *, char *const[], char *const[]); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_STDIO_SPAWN_H_ */ diff --git a/libc/stdio/spawna.c b/libc/stdio/spawna.c new file mode 100644 index 00000000..125738ed --- /dev/null +++ b/libc/stdio/spawna.c @@ -0,0 +1,107 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/scheduler.h" +#include "libc/calls/sigbits.h" +#include "libc/stdio/spawn.h" +#include "libc/stdio/spawna.internal.h" + +/** + * Initialize object with default values. + */ +int posix_spawnattr_init(posix_spawnattr_t *attr) { + attr->posix_attr_flags = 0; + attr->posix_attr_pgroup = 0; + sigprocmask(0, NULL, &attr->posix_attr_sigmask); + sigemptyset(&attr->posix_attr_sigdefault); + attr->posix_attr_schedpolicy = sched_getscheduler(0); + sched_getparam(0, &attr->posix_attr_schedparam); + return 0; +} + +int posix_spawnattr_destroy(posix_spawnattr_t *attr) { + return 0; +} + +int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) { + *flags = attr->posix_attr_flags; + return 0; +} + +int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { + attr->posix_attr_flags = flags; + return 0; +} + +int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) { + *pgroup = attr->posix_attr_pgroup; + return 0; +} + +int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) { + attr->posix_attr_pgroup = pgroup; + return 0; +} + +int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, + int *schedpolicy) { + *schedpolicy = attr->posix_attr_schedpolicy; + return 0; +} + +int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) { + attr->posix_attr_schedpolicy = schedpolicy; + return 0; +} + +int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr, + struct sched_param *schedparam) { + *schedparam = attr->posix_attr_schedparam; + return 0; +} + +int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, + const struct sched_param *schedparam) { + attr->posix_attr_schedparam = *schedparam; + return 0; +} + +int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, + sigset_t *sigmask) { + *sigmask = attr->posix_attr_sigmask; + return 0; +} + +int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, + const sigset_t *sigmask) { + attr->posix_attr_sigmask = *sigmask; + return 0; +} + +int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr, + sigset_t *sigdefault) { + *sigdefault = attr->posix_attr_sigdefault; + return 0; +} + +int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, + const sigset_t *sigdefault) { + attr->posix_attr_sigdefault = *sigdefault; + return 0; +} diff --git a/libc/stdio/spawna.internal.h b/libc/stdio/spawna.internal.h new file mode 100644 index 00000000..2635aca1 --- /dev/null +++ b/libc/stdio/spawna.internal.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_ +#include "libc/calls/struct/sched_param.h" +#include "libc/calls/struct/sigset.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct _posix_spawnattr { + int16_t posix_attr_flags; + int32_t posix_attr_pgroup; + sigset_t posix_attr_sigmask; + sigset_t posix_attr_sigdefault; + int32_t posix_attr_schedpolicy; + struct sched_param posix_attr_schedparam; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_ */ diff --git a/libc/stdio/spawnf.c b/libc/stdio/spawnf.c new file mode 100644 index 00000000..3e893b15 --- /dev/null +++ b/libc/stdio/spawnf.c @@ -0,0 +1,85 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/mem/mem.h" +#include "libc/stdio/spawn.h" +#include "libc/str/str.h" + +/** + * Creates object with no actions. + */ +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) { + *file_actions = malloc(sizeof(char)); + if (!*file_actions) return ENOMEM; + strcpy(*file_actions, ""); + return 0; +} + +/** + * Frees object storage and make invalid. + */ +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) { + free(*file_actions); + *file_actions = NULL; + return 0; +} + +/** + * Adds new action string to object. + */ +static int add_to_file_actions(posix_spawn_file_actions_t *file_actions, + char *new_action) { + *file_actions = + realloc(*file_actions, strlen(*file_actions) + strlen(new_action) + 1); + if (!*file_actions) return ENOMEM; + strcat(*file_actions, new_action); + return 0; +} + +/** + * Add a close action to object. + */ +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, + int fildes) { + char temp[100]; + sprintf(temp, "close(%d)", fildes); + return add_to_file_actions(file_actions, temp); +} + +/** + * Add a dup2 action to object. + */ +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, + int fildes, int newfildes) { + char temp[100]; + sprintf(temp, "dup2(%d,%d)", fildes, newfildes); + return add_to_file_actions(file_actions, temp); +} + +/** + * Add an open action to object. + */ +int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions, + int fildes, const char *path, int oflag, + unsigned mode) { + char temp[100]; + sprintf(temp, "open(%d,%s*%o,%o)", fildes, path, oflag, mode); + return add_to_file_actions(file_actions, temp); +} diff --git a/libc/stdio/spawnp.c b/libc/stdio/spawnp.c new file mode 100644 index 00000000..2191b3c9 --- /dev/null +++ b/libc/stdio/spawnp.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/stdio/spawn.h" + +/** + * Spawns process the POSIX way w/ PATH search. + * + * @param pid is non-NULL and will be set to child pid in parent + * @param path of executable is PATH searched unless it contains a slash + * @return 0 on success or error number on failure + */ +int posix_spawnp(int *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[]) { + char pathbuf[PATH_MAX]; + if (!(path = commandv(path, pathbuf))) return errno; + return posix_spawn(pid, path, file_actions, attrp, argv, envp); +} diff --git a/test/libc/stdio/spawn_test.c b/test/libc/stdio/spawn_test.c new file mode 100644 index 00000000..b03c8c9f --- /dev/null +++ b/test/libc/stdio/spawn_test.c @@ -0,0 +1,88 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/fmt/conv.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/spawn.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +TEST(spawn, test) { + int rc, ws, pid; + char *prog = (char *)getauxval(AT_EXECFN); + char *args[] = {program_invocation_name, NULL}; + char *envs[] = {"THE_DOGE=42", NULL}; + if (atoi(nulltoempty(getenv("THE_DOGE"))) == 42) exit(42); + EXPECT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs)); + EXPECT_NE(-1, waitpid(pid, &ws, 0)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(42, WEXITSTATUS(ws)); +} + +/* + * BEST LINUX FORK+EXEC+EXIT+WAIT PERFORMANCE + * The fastest it can go with fork() is 40µs + * The fastest it can go with vfork() is 26µs + */ + +void BenchmarkProcessLifecycle(void) { + int rc, ws, pid; + char *prog = "/tmp/tiny64"; + char *args[] = {"tiny64", NULL}; + char *envs[] = {NULL}; + ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs)); + ASSERT_NE(-1, waitpid(pid, &ws, 0)); + ASSERT_TRUE(WIFEXITED(ws)); + ASSERT_EQ(42, WEXITSTATUS(ws)); +} + +const char kTinyLinuxExit[128] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, // ⌂ELF☻☺☺  + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //          + 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, // ☻ > ☺    + 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // x @      + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // @        + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //          + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, //     @ 8  + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ☺        + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // ☺   ♣    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //          + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, //   @      + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, //   @      + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç        + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ç        + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  ►       + 0x6a, 0x2a, 0x5f, 0x6a, 0x3c, 0x58, 0x0f, 0x05, // j*_j