#include #include #include #include #include #include #include #include #include #include #include #include extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); extern char** environ; static Result try_execvpe(const char* name, char* const* argv, char* const* envp) { if (strchr(name, '/')) return execve(name, argv, envp); char* path = getenv("PATH"); if (!path) path = const_cast("/bin:/sbin"); Vector paths = TRY(StringView { path }.split(":")); for (const auto& dir : paths) { StringBuilder sb; TRY(sb.add(dir)); TRY(sb.add('/')); TRY(sb.add(StringView { name })); String file = TRY(sb.string()); int err = errno; execve(file.chars(), argv, envp); if (errno != ENOENT && errno != EACCES && errno != ENOEXEC) return -1; if (errno == ENOEXEC) { Vector shell_argv; TRY(shell_argv.try_append(const_cast("sh"))); char* const* arg = argv; do { TRY(shell_argv.try_append(*arg)); } while (*(arg++)); execve("/bin/sh", shell_argv.data(), envp); errno = ENOEXEC; return -1; } if (err == EACCES) errno = err; } return -1; } static Result try_execl(const char* path, const char* arg, bool use_envp, va_list ap, int (*exec_function)(const char*, char* const*, char* const*)) { Vector args; TRY(args.try_append(const_cast(arg))); while (true) { char* str = va_arg(ap, char*); TRY(args.try_append(str)); if (!str) break; } if (use_envp) { char* const* envp = va_arg(ap, char* const*); return exec_function(path, args.data(), envp); } return exec_function(path, args.data(), environ); } extern "C" { pid_t fork(void) { long rc = syscall(SYS_fork); __errno_return(rc, int); } pid_t getpid(void) { return (pid_t)syscall(SYS_getpid); } pid_t getppid(void) { return (pid_t)syscall(SYS_getppid); } uid_t getuid(void) { return (uid_t)syscall(SYS_getuid); } uid_t geteuid(void) { return (uid_t)syscall(SYS_geteuid); } gid_t getgid(void) { return (gid_t)syscall(SYS_getgid); } gid_t getegid(void) { return (gid_t)syscall(SYS_getegid); } int setuid(uid_t uid) { long rc = syscall(SYS_setuid, uid); __errno_return(rc, int); } int seteuid(uid_t uid) { long rc = syscall(SYS_seteuid, uid); __errno_return(rc, int); } int setgid(gid_t gid) { long rc = syscall(SYS_setgid, gid); __errno_return(rc, int); } int setegid(gid_t gid) { long rc = syscall(SYS_setegid, gid); __errno_return(rc, int); } int chown(const char* path, uid_t uid, gid_t gid) { long rc = syscall(SYS_fchownat, AT_FDCWD, path, uid, gid, 0); __errno_return(rc, int); } int fchown(int fd, uid_t uid, gid_t gid) { long rc = syscall(SYS_fchownat, fd, "", uid, gid, AT_EMPTY_PATH); __errno_return(rc, int); } int execv(const char* path, char* const* argv) { return execve(path, argv, environ); } int execve(const char* path, char* const* argv, char* const* envp) { long rc = syscall(SYS_execve, path, argv, envp); __errno_return(rc, int); } int execvpe(const char* name, char* const* argv, char* const* envp) { auto rc = try_execvpe(name, argv, envp); if (rc.has_error()) { errno = rc.error(); return -1; } return rc.value(); } int execvp(const char* name, char* const* argv) { return execvpe(name, argv, environ); } int execl(const char* path, const char* arg, ...) { va_list ap; va_start(ap, arg); auto rc = try_execl(path, arg, false, ap, execve); va_end(ap); if (rc.has_error()) { errno = rc.error(); return -1; } return rc.value(); } int execlp(const char* path, const char* arg, ...) { va_list ap; va_start(ap, arg); auto rc = try_execl(path, arg, false, ap, execvpe); va_end(ap); if (rc.has_error()) { errno = rc.error(); return -1; } return rc.value(); } int execle(const char* path, const char* arg, ...) { va_list ap; va_start(ap, arg); auto rc = try_execl(path, arg, true, ap, execve); va_end(ap); if (rc.has_error()) { errno = rc.error(); return -1; } return rc.value(); } long syscall(long num, ...) { va_list ap; va_start(ap, num); uintptr_t arg0 = va_arg(ap, uintptr_t); uintptr_t arg1 = va_arg(ap, uintptr_t); uintptr_t arg2 = va_arg(ap, uintptr_t); uintptr_t arg3 = va_arg(ap, uintptr_t); uintptr_t arg4 = va_arg(ap, uintptr_t); long rc = arch_invoke_syscall(num, arg0, arg1, arg2, arg3, arg4); va_end(ap); return rc; } int usleep(useconds_t us) { long rc = syscall(SYS_usleep, us); __errno_return(rc, int); } unsigned long sleep(unsigned long seconds) { syscall(SYS_usleep, seconds * 1000000); return 0; } int close(int fd) { long rc = syscall(SYS_close, fd); __errno_return(rc, int); } ssize_t read(int fd, void* buf, size_t size) { long rc = syscall(SYS_read, fd, buf, size); __errno_return(rc, ssize_t); } ssize_t write(int fd, const void* buf, size_t size) { long rc = syscall(SYS_write, fd, buf, size); __errno_return(rc, ssize_t); } off_t lseek(int fd, off_t offset, int whence) { long rc = syscall(SYS_lseek, fd, offset, whence); __errno_return(rc, off_t); } int dup(int fd) { return fcntl(fd, F_DUPFD, 0); } int dup2(int oldfd, int newfd) { long rc = syscall(SYS_dup2, oldfd, newfd); __errno_return(rc, int); } int chdir(const char* path) { long rc = syscall(SYS_chdir, path); __errno_return(rc, int); } static ssize_t __getcwd_wrapper(char* buf, size_t size) { long rc = syscall(SYS_getcwd, buf, size); __errno_return(rc, ssize_t); } char* getcwd(char* buf, size_t size) { if (buf) { ssize_t rc = __getcwd_wrapper(buf, size); if (rc < 0) return nullptr; if (rc > (ssize_t)size) { errno = ERANGE; return nullptr; } return buf; } else if (size) { buf = (char*)malloc(size); if (!buf) return nullptr; ssize_t rc = __getcwd_wrapper(buf, size); if (rc < 0) { free(buf); return nullptr; } if (rc > (ssize_t)size) { free(buf); errno = ERANGE; return nullptr; } return buf; } else { buf = (char*)malloc(BUFSIZ); if (!buf) return nullptr; ssize_t rc = __getcwd_wrapper(buf, BUFSIZ); if (rc < 0) { free(buf); return nullptr; } if (rc > BUFSIZ) { free(buf); return getcwd(NULL, rc); } return buf; } } int unlink(const char* path) { return unlinkat(AT_FDCWD, path, 0); } int unlinkat(int dirfd, const char* path, int flags) { long rc = syscall(SYS_unlinkat, dirfd, path, flags); __errno_return(rc, int); } int rmdir(const char* path) { return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); } int gethostname(char* buf, size_t len) { struct utsname info; if (uname(&info) < 0) return -1; strlcpy(buf, info.nodename, len); return 0; } int sethostname(const char* buf, size_t len) { long rc = syscall(SYS_sethostname, buf, len); __errno_return(rc, int); } int pipe(int pfds[2]) { long rc = syscall(SYS_pipe, (int*)pfds); __errno_return(rc, int); } int symlink(const char* target, const char* linkpath) { long rc = syscall(SYS_symlinkat, target, AT_FDCWD, linkpath); __errno_return(rc, int); } int symlinkat(const char* target, int dirfd, const char* linkpath) { long rc = syscall(SYS_symlinkat, target, dirfd, linkpath); __errno_return(rc, int); } ssize_t readlink(const char* path, char* buf, size_t max) { long rc = syscall(SYS_readlinkat, AT_FDCWD, path, buf, max); __errno_return(rc, ssize_t); } ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t max) { long rc = syscall(SYS_readlinkat, dirfd, path, buf, max); __errno_return(rc, ssize_t); } int link(const char* oldpath, const char* newpath) { long rc = syscall(SYS_linkat, AT_FDCWD, oldpath, AT_FDCWD, newpath, 0); __errno_return(rc, int); } int linkat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath, int flags) { long rc = syscall(SYS_linkat, olddirfd, oldpath, newdirfd, newpath, flags); __errno_return(rc, int); } }