/*-*- 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 "ape/ape.h" #include "libc/atomic.h" #include "libc/calls/blockcancel.internal.h" #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/cosmo.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/bits.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/limits.h" #include "libc/mem/alloca.h" #include "libc/paths.h" #include "libc/proc/execve.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" #include "libc/sysv/errfuns.h" #define ELIBBAD_LINUX 80 #define EBADEXEC_XNU 85 #define EBADARCH_XNU 86 static struct { atomic_uint once; const char *home; const char *tmpdir; } g_execve; static bool IsApeFile(const char *path) { if (endswith(path, ".com")) { return true; } else { bool res = false; BLOCK_SIGNALS; int fd; char buf[8]; int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC; if ((fd = sys_openat(AT_FDCWD, path, flags, 0)) != -1) { res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf); sys_close(fd); } ALLOW_SIGNALS; return res; } } static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) { size_t n, m; if (a && *a) { n = strlen(a); m = strlen(b); if (n + m + 1 < PATH_MAX) { stpcpy(stpcpy(buf, a), b); return buf; } } return 0; } static void RetryExecve(const char *prog, char **argv, char *const *envp) { if ((argv[0] = (char *)prog)) { STRACE("execve(%#s, %s) due to %s", prog, DescribeStringList(argv), _strerrno(errno)); __sys_execve(prog, argv, envp); } } static void SetupExecve(void) { g_execve.home = getenv("HOME"); g_execve.tmpdir = getenv("TMPDIR"); } __attribute__((__constructor__)) static void InitExecve(void) { cosmo_once(&g_execve.once, SetupExecve); } int sys_execve(const char *prog, char *const argv[], char *const envp[]) { // try kernel // this also checks execute bit __sys_execve(prog, argv, envp); if (!(errno == ENOEXEC || (IsLinux() && errno == ELIBBAD_LINUX))) { return -1; } // allocate memory int argc; for (argc = 0; argv[argc];) ++argc; #pragma GCC push_options #pragma GCC diagnostic ignored "-Walloca-larger-than=" int nbytes = (argc + 4) * sizeof(char *); int ntotal = nbytes + PATH_MAX; if (__get_safe_size(ntotal, 4096) < ntotal) return enomem(); char **shargs = alloca(nbytes); CheckLargeStackAllocation(shargs, nbytes); #pragma GCC pop_options // try ape loader if (IsApeFile(prog)) { shargs[1] = (char *)"-"; shargs[2] = (char *)prog; memcpy(shargs + 3, argv, (argc + 1) * sizeof(char *)); RetryExecve("/usr/bin/ape", shargs, envp); char *buf = alloca(PATH_MAX); const char *name = "/.ape-" APE_VERSION_STR; InitExecve(); RetryExecve(Join(g_execve.tmpdir, name, buf), shargs, envp); RetryExecve(Join(g_execve.home, name, buf), shargs, envp); RetryExecve(Join(".", name, buf), shargs, envp); } // try bourne shell shargs[0] = _PATH_BSHELL; shargs[1] = (char *)prog; memcpy(shargs + 2, argv + 1, argc * sizeof(char *)); RetryExecve(shargs[0], shargs, envp); enoexec(); return -1; }