std/os/errno: add missing module (was untracked in prior aproc commit)
ober
8c39c3f3c9ea75fb14bb0a3c378ef33c0558199e
new file mode 100644 --- /dev/null +++ b/lib/std/os/errno.sls @@ -0,0 +1,162 @@ +#!chezscheme +;;; (std os errno) -- Cross-platform errno access and POSIX error constants +;;; +;;; Thin module so callers (aproc, debug-repl, future syscall wrappers) +;;; can read errno and check well-known values without pulling in the +;;; rest of (std os posix). Tries the three known C-side symbols: +;;; +;;; macOS / *BSD: __error (thread-local errno pointer) +;;; Linux glibc: __errno_location +;;; Bionic / musl: __errno +;;; +;;; Use: +;;; (errno) => current errno (int) +;;; (errno-strerror n) => string for errno N +;;; (errno-name n) => 'EINTR / 'ECHILD / ... or 'UNKNOWN +;;; E* constants exported by name (EINTR EAGAIN EWOULDBLOCK ECHILD EINVAL EPIPE +;;; ESRCH EPERM EBADF ENOENT EACCES EEXIST EIO +;;; ETIMEDOUT EBUSY ENOMEM EFAULT ENOTDIR) +;;; +;;; Numeric values differ between Linux and the BSDs; the constants are +;;; resolved at load time based on (machine-type). + +(library (std os errno) + (export + errno + errno-strerror + errno-name + errno-supported? + + EINTR EAGAIN EWOULDBLOCK ECHILD EINVAL EPIPE + ESRCH EPERM EBADF ENOENT EACCES EEXIST EIO + ETIMEDOUT EBUSY ENOMEM EFAULT ENOTDIR ENOSYS + EMFILE ENFILE ENOEXEC E2BIG) + + (import (chezscheme)) + + ;; Try to make libc symbols visible. (load-shared-object #f) maps the + ;; running image, which works for static binaries; the platform fallbacks + ;; handle dynamic builds where libc isn't already in the image. + (define _libc-loaded + (or (guard (e [#t #f]) (load-shared-object #f)) + (guard (e [#t #f]) (load-shared-object "libc.so.7")) + (guard (e [#t #f]) (load-shared-object "libc.so.6")) + (guard (e [#t #f]) (load-shared-object "libc.so")) + (guard (e [#t #f]) (load-shared-object "/usr/lib/libSystem.B.dylib")) + (guard (e [#t #f]) (load-shared-object "libSystem.dylib")) + (guard (e [#t #f]) (load-shared-object "libc.dylib")))) + + ;; ========== Platform detection ========== + (define machine-type-str (symbol->string (machine-type))) + + (define (ends-with? s suf) + (let ((sl (string-length s)) (fl (string-length suf))) + (and (>= sl fl) + (string=? (substring s (- sl fl) sl) suf)))) + + (define *freebsd?* (memq (machine-type) '(a6fb ta6fb i3fb ti3fb arm64fb))) + (define *openbsd?* (memq (machine-type) '(a6ob ta6ob arm64ob))) + (define *netbsd?* (memq (machine-type) '(a6nb ta6nb arm64nb))) + (define *macos?* (ends-with? machine-type-str "osx")) + (define *bsd-family?* (or *freebsd?* *openbsd?* *netbsd?* *macos?*)) + + ;; ========== errno location ========== + ;; Returns a thunk that yields a void* pointer to the current thread's + ;; errno cell — or #f if no known symbol resolved. + (define c-errno-location + (let try ((names (if *bsd-family?* + '("__error" "__errno_location" "__errno") + '("__errno_location" "__errno" "__error")))) + (cond + ((null? names) #f) + (else + (or (guard (e [#t #f]) + (foreign-procedure (car names) () void*)) + (try (cdr names))))))) + + (define (errno-supported?) + (and c-errno-location #t)) + + (define (errno) + (if c-errno-location + (foreign-ref 'int (c-errno-location) 0) + 0)) + + ;; ========== strerror ========== + (define c-strerror + (foreign-procedure "strerror" (int) string)) + + (define (errno-strerror n) + (or (guard (e [#t #f]) (c-strerror n)) + (string-append "errno=" (number->string n)))) + + ;; ========== Errno constants ========== + ;; Most are stable across platforms; the divergent ones (EAGAIN/EWOULDBLOCK + ;; on BSDs vs Linux, EDEADLK numbering, etc.) get a platform branch. + (define EPERM 1) + (define ENOENT 2) + (define ESRCH 3) + (define EINTR 4) + (define EIO 5) + (define E2BIG 7) + (define ENOEXEC 8) + (define EBADF 9) + (define ECHILD 10) + (define EACCES 13) + (define EFAULT 14) + (define EBUSY 16) + (define EEXIST 17) + (define ENOTDIR 20) + (define EINVAL 22) + (define ENFILE 23) + (define EMFILE 24) + (define ENOMEM (if *bsd-family?* 12 12)) + ;; EPIPE = 32 on Linux/glibc and the BSDs alike. + (define EPIPE 32) + + ;; EAGAIN/EWOULDBLOCK + ;; Linux: EAGAIN = EWOULDBLOCK = 11 + ;; *BSD: EAGAIN = EWOULDBLOCK = 35 + (define EAGAIN (if *bsd-family?* 35 11)) + (define EWOULDBLOCK EAGAIN) + + ;; ETIMEDOUT + ;; Linux: 110 + ;; FreeBSD: 60 macOS: 60 OpenBSD: 60 + (define ETIMEDOUT (if *bsd-family?* 60 110)) + + ;; ENOSYS + ;; Linux: 38 + ;; FreeBSD: 78 macOS: 78 + (define ENOSYS (if *bsd-family?* 78 38)) + + ;; ========== Name lookup ========== + ;; Reverse map: errno value -> symbol. Returns 'UNKNOWN for codes we + ;; haven't enumerated. Used by error messages, not by hot paths. + (define (errno-name n) + (cond + ((= n EPERM) 'EPERM) + ((= n ENOENT) 'ENOENT) + ((= n ESRCH) 'ESRCH) + ((= n EINTR) 'EINTR) + ((= n EIO) 'EIO) + ((= n E2BIG) 'E2BIG) + ((= n ENOEXEC) 'ENOEXEC) + ((= n EBADF) 'EBADF) + ((= n ECHILD) 'ECHILD) + ((= n EAGAIN) 'EAGAIN) + ((= n EACCES) 'EACCES) + ((= n EFAULT) 'EFAULT) + ((= n EBUSY) 'EBUSY) + ((= n EEXIST) 'EEXIST) + ((= n ENOTDIR) 'ENOTDIR) + ((= n EINVAL) 'EINVAL) + ((= n ENFILE) 'ENFILE) + ((= n EMFILE) 'EMFILE) + ((= n ENOMEM) 'ENOMEM) + ((= n EPIPE) 'EPIPE) + ((= n ETIMEDOUT) 'ETIMEDOUT) + ((= n ENOSYS) 'ENOSYS) + (else 'UNKNOWN))) + +) ;; end library