/*-*- 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 "third_party/lua/lunix.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/makedev.h"
#include "libc/calls/pledge.h"
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/statfs.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/dns/dns.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/fmt.h"
#include "libc/mem/mem.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/clktck.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/linger.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/sockaddr.h"
#include "libc/sock/syslog.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/ip.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/log.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msg.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/sio.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/consts/st.h"
#include "libc/sysv/consts/tcp.h"
#include "libc/sysv/consts/utime.h"
#include "libc/sysv/consts/w.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lgc.h"
#include "third_party/lua/lua.h"
#include "third_party/lua/luaconf.h"
#include "third_party/nsync/futex.internal.h"
#include "tool/net/luacheck.h"

/**
 * @fileoverview UNIX system calls thinly wrapped for Lua
 * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD
 */

static lua_State *GL;

static void *LuaRealloc(lua_State *L, void *p, size_t n) {
  void *p2;
  if ((p2 = realloc(p, n))) {
    return p2;
  }
  if (IsLegalSize(n)) {
    WARNF("reacting to malloc() failure by running lua garbage collector...");
    luaC_fullgc(L, 1);
    p2 = realloc(p, n);
  }
  return p2;
}

static void *LuaAlloc(lua_State *L, size_t n) {
  return LuaRealloc(L, 0, n);
}

static void *LuaAllocOrDie(lua_State *L, size_t n) {
  void *p;
  if ((p = LuaAlloc(L, n))) {
    return p;
  } else {
    luaL_error(L, "out of memory");
    unreachable;
  }
}

static lua_Integer FixLimit(long x) {
  if (0 <= x && x < RLIM_INFINITY) {
    return x;
  } else {
    return -1;
  }
}

static void LuaPushSigset(lua_State *L, struct sigset set) {
  struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
  luaL_setmetatable(L, "unix.Sigset");
  *sp = set;
}

static void LuaPushStat(lua_State *L, struct stat *st) {
  struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1);
  luaL_setmetatable(L, "unix.Stat");
  *stp = *st;
}

static void LuaPushStatfs(lua_State *L, struct statfs *st) {
  struct statfs *stp = lua_newuserdatauv(L, sizeof(*stp), 1);
  luaL_setmetatable(L, "unix.Statfs");
  *stp = *st;
}

static void LuaPushRusage(lua_State *L, struct rusage *set) {
  struct rusage *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
  luaL_setmetatable(L, "unix.Rusage");
  *sp = *set;
}

static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
  lua_pushinteger(L, v);
  lua_setfield(L, -2, k);
}

static dontinline int ReturnInteger(lua_State *L, lua_Integer x) {
  lua_pushinteger(L, x);
  return 1;
}

static dontinline int ReturnBoolean(lua_State *L, int x) {
  lua_pushboolean(L, !!x);
  return 1;
}

static dontinline int ReturnString(lua_State *L, const char *x) {
  lua_pushstring(L, x);
  return 1;
}

int LuaUnixSysretErrno(lua_State *L, const char *call, int olderr) {
  struct UnixErrno *ep;
  int i, unixerr, winerr;
  unixerr = errno;
  winerr = GetLastError();
  if (!IsTiny() && !(0 < unixerr && unixerr < (!IsWindows() ? 4096 : 65536))) {
    WARNF("errno should not be %d", unixerr);
  }
  lua_pushnil(L);
  ep = lua_newuserdatauv(L, sizeof(*ep), 1);
  luaL_setmetatable(L, "unix.Errno");
  ep->errno_ = unixerr;
  ep->winerr = winerr;
  ep->call = call;
  errno = olderr;
  return 2;
}

static int SysretBool(lua_State *L, const char *call, int olderr, int rc) {
  if (!IsTiny() && (rc != 0 && rc != -1)) {
    WARNF("syscall supposed to return 0 / -1 but got %d", rc);
  }
  if (rc != -1) {
    lua_pushboolean(L, true);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, call, olderr);
  }
}

static int SysretInteger(lua_State *L, const char *call, int olderr,
                         int64_t rc) {
  if (rc != -1) {
    if (!IsTiny() && olderr != errno) {
      WARNF("errno unexpectedly changed %d â %d", olderr, errno);
    }
    lua_pushinteger(L, rc);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, call, olderr);
  }
}

static int MakeSockaddr(lua_State *L, int i, struct sockaddr_storage *ss,
                        uint32_t *salen) {
  bzero(ss, sizeof(*ss));
  if (!lua_isinteger(L, i)) {
    ((struct sockaddr_un *)ss)->sun_family = AF_UNIX;
    if (!memccpy(((struct sockaddr_un *)ss)->sun_path, luaL_checkstring(L, i),
                 0, sizeof(((struct sockaddr_un *)ss)->sun_path))) {
      luaL_error(L, "unix path too long");
      unreachable;
    }
    *salen = sizeof(struct sockaddr_un);
    return i + 1;
  } else {
    ((struct sockaddr_in *)ss)->sin_family = AF_INET;
    ((struct sockaddr_in *)ss)->sin_addr.s_addr =
        htonl(luaL_optinteger(L, i, 0));
    ((struct sockaddr_in *)ss)->sin_port = htons(luaL_optinteger(L, i + 1, 0));
    *salen = sizeof(struct sockaddr_in);
    return i + 2;
  }
}

static int PushSockaddr(lua_State *L, const struct sockaddr_storage *ss) {
  if (ss->ss_family == AF_INET) {
    lua_pushinteger(L,
                    ntohl(((const struct sockaddr_in *)ss)->sin_addr.s_addr));
    lua_pushinteger(L, ntohs(((const struct sockaddr_in *)ss)->sin_port));
    return 2;
  } else if (ss->ss_family == AF_UNIX) {
    lua_pushstring(L, ((const struct sockaddr_un *)ss)->sun_path);
    return 1;
  } else {
    luaL_error(L, "bad family");
    unreachable;
  }
}

static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) {
  if (!IsTiny()) {
    if (want == got) return;
    WARNF("getsockopt optvalsize should be %d but was %d", want, got);
  }
}

static void FreeStringList(char **p) {
  int i;
  if (p) {
    for (i = 0; p[i]; ++i) {
      free(p[i]);
    }
    free(p);
  }
}

static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
  int j, n;
  char **p, *s;
  luaL_checktype(L, i, LUA_TTABLE);
  lua_len(L, i);
  n = lua_tointeger(L, -1);
  lua_pop(L, 1);
  if ((p = LuaAlloc(L, (n + 1) * sizeof(*p)))) {
    for (j = 1; j <= n; ++j) {
      lua_geti(L, i, j);
      s = strdup(lua_tostring(L, -1));
      lua_pop(L, 1);
      if (s) {
        p[j - 1] = s;
      } else {
        FreeStringList(p);
        p = 0;
        break;
      }
    }
    p[j - 1] = 0;
  }
  return p;
}

////////////////////////////////////////////////////////////////////////////////
// System Calls

// unix.exit([exitcode:int])
//     âââ â¥
static wontreturn int LuaUnixExit(lua_State *L) {
  _Exit(luaL_optinteger(L, 1, 0));
}

static dontinline int LuaUnixGetid(lua_State *L, int f(void)) {
  return ReturnInteger(L, f());
}

static dontinline int LuaUnixGetUnsignedid(lua_State *L, unsigned f(void)) {
  return ReturnInteger(L, f());
}

// unix.getpid()
//     âââ pid:int
static int LuaUnixGetpid(lua_State *L) {
  return LuaUnixGetid(L, getpid);
}

// unix.getppid()
//     âââ pid:int
static int LuaUnixGetppid(lua_State *L) {
  return LuaUnixGetid(L, getppid);
}

// unix.getuid()
//     âââ uid:int
static int LuaUnixGetuid(lua_State *L) {
  return LuaUnixGetUnsignedid(L, getuid);
}

// unix.getgid()
//     âââ gid:int
static int LuaUnixGetgid(lua_State *L) {
  return LuaUnixGetUnsignedid(L, getgid);
}

// unix.geteuid()
//     âââ uid:int
static int LuaUnixGeteuid(lua_State *L) {
  return LuaUnixGetUnsignedid(L, geteuid);
}

// unix.getegid()
//     âââ gid:int
static int LuaUnixGetegid(lua_State *L) {
  return LuaUnixGetUnsignedid(L, getegid);
}

// unix.umask(newmask:int)
//     âââ oldmask:int
static int LuaUnixUmask(lua_State *L) {
  return ReturnInteger(L, umask(luaL_checkinteger(L, 1)));
}

// unix.access(path:str, how:int[, flags:int[, dirfd:int]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixAccess(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "access", olderr,
      faccessat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
                luaL_checkinteger(L, 2), luaL_optinteger(L, 4, 0)));
}

// unix.mkdir(path:str[, mode:int[, dirfd:int]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixMkdir(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "mkdir", olderr,
      mkdirat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
              luaL_optinteger(L, 2, 0755)));
}

// unix.makedirs(path:str[, mode:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixMakedirs(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "makedirs", olderr,
      makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)));
}

// unix.rmrf(path:str)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixRmrf(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "rmrf", olderr, rmrf(luaL_checkstring(L, 1)));
}

// unix.chdir(path:str)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixChdir(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "chdir", olderr, chdir(luaL_checkstring(L, 1)));
}

// unix.unlink(path:str[, dirfd:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixUnlink(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "unlink", olderr,
      unlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1), 0));
}

// unix.rmdir(path:str[, dirfd:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixRmdir(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "rmdir", olderr,
                    unlinkat(luaL_optinteger(L, 2, AT_FDCWD),
                             luaL_checkstring(L, 1), AT_REMOVEDIR));
}

// unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixRename(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "rename", olderr,
      renameat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
               luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 2)));
}

// unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixLink(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "link", olderr,
      linkat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
             luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 2),
             luaL_optinteger(L, 3, 0)));
}

// unix.symlink(target:str, linkpath:str[, newdirfd:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSymlink(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "symlink", olderr,
      symlinkat(luaL_checkstring(L, 1), luaL_optinteger(L, 3, AT_FDCWD),
                luaL_checkstring(L, 2)));
}

// unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixChown(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "chown", olderr,
      fchownat(luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 1),
               luaL_checkinteger(L, 2), luaL_checkinteger(L, 3),
               luaL_optinteger(L, 4, 0)));
}

// unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixChmod(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "chmod", olderr,
      fchmodat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
               luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0)));
}

// unix.readlink(path:str[, dirfd:int])
//     âââ content:str
//     âââ nil, unix.Errno
static int LuaUnixReadlink(lua_State *L) {
  size_t got;
  ssize_t rc;
  luaL_Buffer lb;
  int olderr = errno;
  if ((rc = readlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1),
                       luaL_buffinitsize(L, &lb, BUFSIZ), BUFSIZ)) != -1) {
    if ((got = rc) < BUFSIZ) {
      luaL_pushresultsize(&lb, got);
      return 1;
    } else {
      enametoolong();
    }
  }
  return LuaUnixSysretErrno(L, "readlink", olderr);
}

// unix.getcwd()
//     âââ path:str
//     âââ nil, unix.Errno
static int LuaUnixGetcwd(lua_State *L) {
  char *path;
  int olderr = errno;
  if ((path = getcwd(0, 0))) {
    lua_pushstring(L, path);
    free(path);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "getcwd", olderr);
  }
}

// unix.fork()
//     âââ¬ââ 0
//     â âââ childpid:int
//     âââ nil, unix.Errno
static int LuaUnixFork(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "fork", olderr, fork());
}

// unix.environ()
//     âââ {str,...}
static int LuaUnixEnviron(lua_State *L) {
  int i;
  char **e;
  lua_newtable(L);
  for (i = 0, e = environ; *e; ++e) {
    lua_pushstring(L, *e);
    lua_rawseti(L, -2, ++i);
  }
  return 1;
}

// unix.execve(prog:str[, args:List<*>, env:List<*>])
//     âââ nil, unix.Errno
static int LuaUnixExecve(lua_State *L) {
  int olderr;
  const char *prog;
  char **argv, **envp, **freeme1, **freeme2, *ezargs[2];
  olderr = errno;
  prog = luaL_checkstring(L, 1);
  if (!lua_isnoneornil(L, 2)) {
    if ((argv = ConvertLuaArrayToStringList(L, 2))) {
      freeme1 = argv;
      if (!lua_isnoneornil(L, 3)) {
        if ((envp = ConvertLuaArrayToStringList(L, 3))) {
          freeme2 = envp;
        } else {
          FreeStringList(argv);
          return LuaUnixSysretErrno(L, "execve", olderr);
        }
      } else {
        envp = environ;
        freeme2 = 0;
      }
    } else {
      return LuaUnixSysretErrno(L, "execve", olderr);
    }
  } else {
    ezargs[0] = prog;
    ezargs[1] = 0;
    argv = ezargs;
    envp = environ;
    freeme1 = 0;
    freeme2 = 0;
  }
  execve(prog, argv, envp);
  FreeStringList(freeme1);
  FreeStringList(freeme2);
  return LuaUnixSysretErrno(L, "execve", olderr);
}

// unix.commandv(prog:str)
//     âââ path:str
//     âââ nil, unix.Errno
static int LuaUnixCommandv(lua_State *L) {
  int olderr;
  const char *prog;
  char *pathbuf, *resolved;
  olderr = errno;
  prog = luaL_checkstring(L, 1);
  pathbuf = LuaAllocOrDie(L, PATH_MAX);
  if ((resolved = commandv(prog, pathbuf, PATH_MAX))) {
    lua_pushstring(L, resolved);
    free(pathbuf);
    return 1;
  } else {
    free(pathbuf);
    return LuaUnixSysretErrno(L, "commandv", olderr);
  }
}

// unix.realpath(path:str)
//     âââ path:str
//     âââ nil, unix.Errno
static int LuaUnixRealpath(lua_State *L) {
  char *resolved;
  int olderr;
  const char *path;
  olderr = errno;
  path = luaL_checkstring(L, 1);
  if ((resolved = realpath(path, 0))) {
    lua_pushstring(L, resolved);
    free(resolved);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "realpath", olderr);
  }
}

// unix.syslog(priority:str, msg:str)
static int LuaUnixSyslog(lua_State *L) {
  syslog(luaL_checkinteger(L, 1), "%s", luaL_checkstring(L, 2));
  return 0;
}

// unix.chroot(path:str)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixChroot(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "chroot", olderr, chroot(luaL_checkstring(L, 1)));
}

// unix.setrlimit(resource:int, soft:int[, hard:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetrlimit(lua_State *L) {
  int olderr = errno;
  int64_t soft = luaL_checkinteger(L, 2);
  return SysretBool(
      L, "setrlimit", olderr,
      setrlimit(luaL_checkinteger(L, 1),
                &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}));
}

// unix.getrlimit(resource:int)
//     âââ soft:int, hard:int
//     âââ nil, unix.Errno
static int LuaUnixGetrlimit(lua_State *L) {
  int olderr = errno;
  struct rlimit rlim;
  if (!getrlimit(luaL_checkinteger(L, 1), &rlim)) {
    lua_pushinteger(L, FixLimit(rlim.rlim_cur));
    lua_pushinteger(L, FixLimit(rlim.rlim_max));
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "getrlimit", olderr);
  }
}

// unix.getrusage([who:int])
//     âââ unix.Rusage
//     âââ nil, unix.Errno
static int LuaUnixGetrusage(lua_State *L) {
  struct rusage ru;
  int olderr = errno;
  if (!getrusage(luaL_optinteger(L, 1, RUSAGE_SELF), &ru)) {
    LuaPushRusage(L, &ru);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "getrusage", olderr);
  }
}

// unix.kill(pid:int, sig:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixKill(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "kill", olderr,
                    kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
}

// unix.raise(sig:int)
//     âââ rc:int
//     âââ nil, unix.Errno
static int LuaUnixRaise(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "raise", olderr, raise(luaL_checkinteger(L, 1)));
}

// unix.wait([pid:int, options:int])
//     âââ pid:int, wstatus:int, unix.Rusage
//     âââ nil, unix.Errno
static int LuaUnixWait(lua_State *L) {
  struct rusage ru;
  int pid, wstatus, olderr = errno;
  if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus,
                   luaL_optinteger(L, 2, 0), &ru)) != -1) {
    lua_pushinteger(L, pid);
    lua_pushinteger(L, wstatus);
    LuaPushRusage(L, &ru);
    return 3;
  } else {
    return LuaUnixSysretErrno(L, "wait", olderr);
  }
}

// unix.fcntl(fd:int, cmd:int[, ...])
//     âââ ...
//     âââ nil, unix.Errno
static int LuaUnixFcntl(lua_State *L) {
  struct flock lock;
  int rc, fd, cmd, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  cmd = luaL_checkinteger(L, 2);
  if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
    lock.l_type = luaL_optinteger(L, 3, F_RDLCK);
    lock.l_start = luaL_optinteger(L, 4, 0);
    lock.l_len = luaL_optinteger(L, 5, 0);
    lock.l_whence = luaL_optinteger(L, 6, SEEK_SET);
  }
  if (cmd == F_SETLK || cmd == F_SETLKW) {
    return SysretBool(L, "fcntl(F_SETLK*)", olderr, fcntl(fd, cmd, &lock));
  } else if (cmd == F_GETLK) {
    if (fcntl(fd, cmd, &lock) != -1) {
      if (lock.l_type == F_UNLCK) {
        lua_pushinteger(L, F_UNLCK);
        return 1;
      } else {
        lua_pushinteger(L, lock.l_type);
        lua_pushinteger(L, lock.l_start);
        lua_pushinteger(L, lock.l_len);
        lua_pushinteger(L, lock.l_whence);
        lua_pushinteger(L, lock.l_pid);
        return 5;
      }
    } else {
      return LuaUnixSysretErrno(L, "fcntl(F_GETLK)", olderr);
    }
  } else {
    return SysretBool(L, "fcntl", olderr,
                      fcntl(fd, cmd, luaL_optinteger(L, 3, 0)));
  }
}

// unix.dup(oldfd:int[, newfd:int[, flags:int[, lowest:int]]])
//     âââ newfd:int
//     âââ nil, unix.Errno
static int LuaUnixDup(lua_State *L) {
  int rc, oldfd, newfd, flags, lowno, olderr;
  olderr = errno;
  oldfd = luaL_checkinteger(L, 1);
  newfd = luaL_optinteger(L, 2, -1);
  flags = luaL_optinteger(L, 3, 0);
  lowno = luaL_optinteger(L, 4, 0);
  if (newfd < 0) {
    if (!flags && !lowno) {
      rc = dup(oldfd);
    } else if (!flags) {
      rc = fcntl(oldfd, F_DUPFD, lowno);
    } else if (flags == O_CLOEXEC) {
      rc = fcntl(oldfd, F_DUPFD_CLOEXEC, lowno);
    } else {
      rc = einval();
    }
  } else {
    rc = dup3(oldfd, newfd, flags);
  }
  return SysretInteger(L, "dup", olderr, rc);
}

// unix.pipe([flags:int])
//     âââ reader:int, writer:int
//     âââ nil, unix.Errno
static int LuaUnixPipe(lua_State *L) {
  int pipefd[2], olderr = errno;
  if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) {
    lua_pushinteger(L, pipefd[0]);
    lua_pushinteger(L, pipefd[1]);
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "pipe", olderr);
  }
}

// unix.getsid(pid:int)
//     âââ sid:int
//     âââ nil, unix.Errno
static int LuaUnixGetsid(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "getsid", olderr, getsid(luaL_checkinteger(L, 1)));
}

static dontinline int LuaUnixRc0(lua_State *L, const char *call, int f(void)) {
  int olderr = errno;
  return SysretInteger(L, call, olderr, f());
}

// unix.getpgrp()
//     âââ pgid:int
//     âââ nil, unix.Errno
static int LuaUnixGetpgrp(lua_State *L) {
  return LuaUnixRc0(L, "getpgrp", getpgrp);
}

// unix.setpgrp()
//     âââ pgid:int
//     âââ nil, unix.Errno
static int LuaUnixSetpgrp(lua_State *L) {
  return LuaUnixRc0(L, "setpgrp", setpgrp);
}

// unix.setsid()
//     âââ sid:int
//     âââ nil, unix.Errno
static int LuaUnixSetsid(lua_State *L) {
  return LuaUnixRc0(L, "setsid", setsid);
}

// unix.getpgid(pid:int)
//     âââ pgid:int
//     âââ nil, unix.Errno
static int LuaUnixGetpgid(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "getpgid", olderr, getpgid(luaL_checkinteger(L, 1)));
}

// unix.setpgid(pid:int, pgid:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetpgid(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "setpgid", olderr,
                    setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
}

static dontinline int LuaUnixSetid(lua_State *L, const char *call,
                                   int f(unsigned)) {
  int olderr = errno;
  return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1)));
}

// unix.setuid(uid:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetuid(lua_State *L) {
  return LuaUnixSetid(L, "setuid", setuid);
}

// unix.setgid(gid:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetgid(lua_State *L) {
  return LuaUnixSetid(L, "setgid", setgid);
}

// unix.setfsuid(fsuid:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetfsuid(lua_State *L) {
  return LuaUnixSetid(L, "setfsuid", setfsuid);
}

// unix.setfsgid(fsgid:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetfsgid(lua_State *L) {
  return LuaUnixSetid(L, "setfsgid", setfsgid);
}

static dontinline int LuaUnixSetresid(lua_State *L, const char *call,
                                      int f(uint32_t, uint32_t, uint32_t)) {
  int olderr = errno;
  return SysretBool(L, call, olderr,
                    f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2),
                      luaL_checkinteger(L, 3)));
}

// unix.setresuid(real:int, effective:int, saved:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetresuid(lua_State *L) {
  return LuaUnixSetresid(L, "setresuid", setresuid);
}

// unix.setresgid(real:int, effective:int, saved:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixSetresgid(lua_State *L) {
  return LuaUnixSetresid(L, "setresgid", setresgid);
}

// unix.utimensat(path[, asecs, ananos, msecs, mnanos[, dirfd[, flags]]])
//     âââ 0
//     âââ nil, unix.Errno
static int LuaUnixUtimensat(lua_State *L) {
  struct timespec ts;
  int olderr = errno;
  return SysretInteger(
      L, "utimensat", olderr,
      utimensat(
          luaL_optinteger(L, 6, AT_FDCWD), luaL_checkstring(L, 1),
          (struct timespec[2]){
              {luaL_optinteger(L, 2, 0), luaL_optinteger(L, 3, UTIME_NOW)},
              {luaL_optinteger(L, 4, 0), luaL_optinteger(L, 5, UTIME_NOW)},
          },
          luaL_optinteger(L, 7, 0)));
}

// unix.futimens(fd:int[, asecs, ananos, msecs, mnanos])
//     âââ 0
//     âââ nil, unix.Errno
static int LuaUnixFutimens(lua_State *L) {
  struct timespec ts;
  int olderr = errno;
  return SysretInteger(
      L, "futimens", olderr,
      futimens(luaL_checkinteger(L, 1),
               (struct timespec[2]){
                   {luaL_optinteger(L, 2, 0), luaL_optinteger(L, 3, UTIME_NOW)},
                   {luaL_optinteger(L, 4, 0), luaL_optinteger(L, 5, UTIME_NOW)},
               }));
}

// unix.clock_gettime([clock:int])
//     âââ seconds:int, nanos:int
//     âââ nil, unix.Errno
static int LuaUnixGettime(lua_State *L) {
  struct timespec ts;
  int rc, olderr = errno;
  if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) {
    lua_pushinteger(L, ts.tv_sec);
    lua_pushinteger(L, ts.tv_nsec);
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "clock_gettime", olderr);
  }
}

// unix.nanosleep(seconds:int[, nanos:int])
//     âââ remseconds:int, remnanos:int
//     âââ nil, unix.Errno
static int LuaUnixNanosleep(lua_State *L) {
  int olderr = errno;
  struct timespec req, rem;
  req.tv_sec = luaL_checkinteger(L, 1);
  req.tv_nsec = luaL_optinteger(L, 2, 0);
  if (!nanosleep(&req, &rem)) {
    lua_pushinteger(L, rem.tv_sec);
    lua_pushinteger(L, rem.tv_nsec);
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "nanosleep", olderr);
  }
}

// unix.sync()
static int LuaUnixSync(lua_State *L) {
  sync();
  return 0;
}

// unix.fsync(fd:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixFsync(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "fsync", olderr, fsync(luaL_checkinteger(L, 1)));
}

// unix.fdatasync(fd:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixFdatasync(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1)));
}

// unix.open(path:str[, flags:int[, mode:int[, dirfd:int]]])
//     âââ fd:int
//     âââ nil, unix.Errno
static int LuaUnixOpen(lua_State *L) {
  int olderr = errno;
  return SysretInteger(
      L, "open", olderr,
      openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
             luaL_optinteger(L, 2, O_RDONLY), luaL_optinteger(L, 3, 0)));
}

// unix.tmpfd()
//     âââ fd:int
//     âââ nil, unix.Errno
static int LuaUnixTmpfd(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "tmpfd", olderr, tmpfd());
}

// unix.close(fd:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixClose(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "close", olderr, close(luaL_checkinteger(L, 1)));
}

// unix.lseek(fd:int, offset:int[, whence:int])
//     âââ newposbytes:int
//     âââ nil, unix.Errno
static int LuaUnixLseek(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "lseek", olderr,
                       lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2),
                             luaL_optinteger(L, 3, SEEK_SET)));
}

// unix.truncate(path:str[, length:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixTruncate(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "truncate", olderr,
                    truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)));
}

// unix.ftruncate(fd:int[, length:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixFtruncate(lua_State *L) {
  int olderr = errno;
  return SysretBool(
      L, "ftruncate", olderr,
      ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)));
}

// unix.read(fd:int[, bufsiz:str[, offset:int]])
//     âââ data:str
//     âââ nil, unix.Errno
static int LuaUnixRead(lua_State *L) {
  char *buf;
  size_t got;
  ssize_t rc;
  int fd, olderr;
  lua_Integer bufsiz, offset;
  olderr = errno;
  fd = luaL_checkinteger(L, 1);
  bufsiz = luaL_optinteger(L, 2, BUFSIZ);
  offset = luaL_optinteger(L, 3, -1);
  bufsiz = MIN(bufsiz, 0x7ffff000);
  buf = LuaAllocOrDie(L, bufsiz);
  if (offset == -1) {
    rc = read(fd, buf, bufsiz);
  } else {
    rc = pread(fd, buf, bufsiz, offset);
  }
  if (rc != -1) {
    got = rc;
    lua_pushlstring(L, buf, got);
    free(buf);
    return 1;
  } else {
    free(buf);
    return LuaUnixSysretErrno(L, "read", olderr);
  }
}

// unix.write(fd:int, data:str[, offset:int])
//     âââ wrotebytes:int
//     âââ nil, unix.Errno
static int LuaUnixWrite(lua_State *L) {
  ssize_t rc;
  size_t size;
  int fd, olderr;
  const char *data;
  lua_Integer offset;
  olderr = errno;
  fd = luaL_checkinteger(L, 1);
  data = luaL_checklstring(L, 2, &size);
  offset = luaL_optinteger(L, 3, -1);
  if (offset == -1) {
    rc = write(fd, data, size);
  } else {
    rc = pwrite(fd, data, size, offset);
  }
  return SysretInteger(L, "write", olderr, rc);
}

// unix.stat(path:str[, flags:int[, dirfd:int]])
//     âââ unix.Stat
//     âââ nil, unix.Errno
static int LuaUnixStat(lua_State *L) {
  struct stat st;
  int olderr = errno;
  if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st,
               luaL_optinteger(L, 2, 0))) {
    LuaPushStat(L, &st);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "stat", olderr);
  }
}

// unix.fstat(fd:int)
//     âââ unix.Stat
//     âââ nil, unix.Errno
static int LuaUnixFstat(lua_State *L) {
  struct stat st;
  int olderr = errno;
  if (!fstat(luaL_checkinteger(L, 1), &st)) {
    LuaPushStat(L, &st);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "fstat", olderr);
  }
}

// unix.statfs(path:str)
//     âââ unix.Statfs
//     âââ nil, unix.Errno
static int LuaUnixStatfs(lua_State *L) {
  struct statfs f;
  int olderr = errno;
  if (!statfs(luaL_checkstring(L, 1), &f)) {
    LuaPushStatfs(L, &f);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "statfs", olderr);
  }
}

// unix.fstatfs(fd:int)
//     âââ unix.Stat
//     âââ nil, unix.Errno
static int LuaUnixFstatfs(lua_State *L) {
  struct statfs f;
  int olderr = errno;
  if (!fstatfs(luaL_checkinteger(L, 1), &f)) {
    LuaPushStatfs(L, &f);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "fstatfs", olderr);
  }
}

static bool IsSockoptBool(int l, int x) {
  if (l == SOL_SOCKET) {
    return x == SO_TYPE ||        //
           x == SO_DEBUG ||       //
           x == SO_ERROR ||       //
           x == SO_BROADCAST ||   //
           x == SO_REUSEADDR ||   //
           x == SO_REUSEPORT ||   //
           x == SO_KEEPALIVE ||   //
           x == SO_ACCEPTCONN ||  //
           x == SO_DONTROUTE;     //
  } else if (l = SOL_TCP) {
    return x == TCP_NODELAY ||           //
           x == TCP_CORK ||              //
           x == TCP_QUICKACK ||          //
           x == TCP_SAVE_SYN ||          //
           x == TCP_FASTOPEN_CONNECT ||  //
           x == TCP_DEFER_ACCEPT;        //
  } else if (l = SOL_IP) {
    return x == IP_HDRINCL;  //
  } else {
    return false;
  }
}

static bool IsSockoptInt(int l, int x) {
  if (l == SOL_SOCKET) {
    return x == SO_SNDBUF ||    //
           x == SO_RCVBUF ||    //
           x == SO_RCVLOWAT ||  //
           x == SO_SNDLOWAT;    //
  } else if (l = SOL_TCP) {
    return x == TCP_FASTOPEN ||       //
           x == TCP_KEEPCNT ||        //
           x == TCP_MAXSEG ||         //
           x == TCP_SYNCNT ||         //
           x == TCP_NOTSENT_LOWAT ||  //
           x == TCP_WINDOW_CLAMP ||   //
           x == TCP_KEEPIDLE ||       //
           x == TCP_KEEPINTVL;        //
  } else if (l = SOL_IP) {
    return x == IP_TOS ||  //
           x == IP_MTU ||  //
           x == IP_TTL;    //
  } else {
    return false;
  }
}

static bool IsSockoptTimeval(int l, int x) {
  if (l == SOL_SOCKET) {
    return x == SO_RCVTIMEO ||  //
           x == SO_SNDTIMEO;    //
  } else {
    return false;
  }
}

static int LuaUnixSetsockopt(lua_State *L) {
  void *optval;
  struct linger l;
  uint32_t optsize;
  struct timeval tv;
  int rc, fd, level, optname, optint, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  level = luaL_checkinteger(L, 2);
  optname = luaL_checkinteger(L, 3);
  if (level == -1 || optname == 0) {
  NoProtocolOption:
    enoprotoopt();
    return LuaUnixSysretErrno(L, "setsockopt", olderr);
  }
  if (IsSockoptBool(level, optname)) {
    // unix.setsockopt(fd:int, level:int, optname:int, value:bool)
    //     âââ true
    //     âââ nil, unix.Errno
    optint = lua_toboolean(L, 4);
    optval = &optint;
    optsize = sizeof(optint);
  } else if (IsSockoptInt(level, optname)) {
    // unix.setsockopt(fd:int, level:int, optname:int, value:int)
    //     âââ true
    //     âââ nil, unix.Errno
    optint = luaL_checkinteger(L, 4);
    optval = &optint;
    optsize = sizeof(optint);
  } else if (IsSockoptTimeval(level, optname)) {
    // unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int])
    //     âââ true
    //     âââ nil, unix.Errno
    tv.tv_sec = luaL_checkinteger(L, 4);
    tv.tv_usec = luaL_optinteger(L, 5, 0) / 1000;
    optval = &tv;
    optsize = sizeof(tv);
  } else if (level == SOL_SOCKET && optname == SO_LINGER) {
    // unix.setsockopt(fd:int, level:int, optname:int, secs:int, enabled:bool)
    //     âââ true
    //     âââ nil, unix.Errno
    l.l_linger = luaL_checkinteger(L, 4);
    l.l_onoff = lua_toboolean(L, 5);
    optval = &l;
    optsize = sizeof(l);
  } else {
    goto NoProtocolOption;
  }
  return SysretBool(L, "setsockopt", olderr,
                    setsockopt(fd, level, optname, optval, optsize));
}

static int LuaUnixGetsockopt(lua_State *L) {
  char *p;
  uint32_t size;
  struct linger l;
  struct timeval tv;
  int rc, fd, level, optname, optval, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  level = luaL_checkinteger(L, 2);
  optname = luaL_checkinteger(L, 3);
  if (level == -1 || optname == 0) {
  NoProtocolOption:
    enoprotoopt();
    return LuaUnixSysretErrno(L, "setsockopt", olderr);
  }
  if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) {
    // unix.getsockopt(fd:int, level:int, optname:int)
    //     âââ value:int
    //     âââ nil, unix.Errno
    size = sizeof(optval);
    if (getsockopt(fd, level, optname, &optval, &size) != -1) {
      CheckOptvalsize(L, sizeof(optval), size);
      lua_pushinteger(L, optval);
      return 1;
    }
  } else if (IsSockoptTimeval(level, optname)) {
    // unix.getsockopt(fd:int, level:int, optname:int)
    //     âââ secs:int, nsecs:int
    //     âââ nil, unix.Errno
    size = sizeof(tv);
    if (getsockopt(fd, level, optname, &tv, &size) != -1) {
      CheckOptvalsize(L, sizeof(tv), size);
      lua_pushinteger(L, tv.tv_sec);
      lua_pushinteger(L, tv.tv_usec * 1000);
      return 2;
    }
  } else if (level == SOL_SOCKET && optname == SO_LINGER) {
    // unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER)
    //     âââ seconds:int, enabled:bool
    //     âââ nil, unix.Errno
    size = sizeof(l);
    if (getsockopt(fd, level, optname, &l, &size) != -1) {
      CheckOptvalsize(L, sizeof(l), size);
      lua_pushinteger(L, l.l_linger);
      lua_pushboolean(L, !!l.l_onoff);
      return 1;
    }
  } else if (level == SOL_TCP && optname == TCP_SAVED_SYN) {
    // unix.getsockopt(fd:int, unix.SOL_TCP, unix.SO_SAVED_SYN)
    //     âââ syn_packet_bytes:str
    //     âââ nil, unix.Errno
    if ((p = malloc((size = 1500)))) {
      if (getsockopt(fd, level, optname, p, &size) != -1) {
        lua_pushlstring(L, p, size);
        free(p);
        return 1;
      }
      free(p);
    }
  } else {
    goto NoProtocolOption;
  }
  return LuaUnixSysretErrno(L, "getsockopt", olderr);
}

// unix.socket([family:int[, type:int[, protocol:int]]])
//     âââ fd:int
//     âââ nil, unix.Errno
static int LuaUnixSocket(lua_State *L) {
  int olderr = errno;
  int family = luaL_optinteger(L, 1, AF_INET);
  return SysretInteger(L, "socket", olderr,
                       socket(family, luaL_optinteger(L, 2, SOCK_STREAM),
                              luaL_optinteger(L, 3, 0)));
}

// unix.socketpair([family:int[, type:int[, protocol:int]]])
//     âââ fd1:int, fd2:int
//     âââ nil, unix.Errno
static int LuaUnixSocketpair(lua_State *L) {
  int sv[2], olderr = errno;
  if (!socketpair(luaL_optinteger(L, 1, AF_UNIX),
                  luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, 0),
                  sv)) {
    lua_pushinteger(L, sv[0]);
    lua_pushinteger(L, sv[1]);
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "socketpair", olderr);
  }
}

// unix.bind(fd:int[, ip:uint32, port:uint16])
// unix.bind(fd:int[, unixpath:str])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixBind(lua_State *L) {
  uint32_t salen;
  struct sockaddr_storage ss;
  int olderr = errno;
  MakeSockaddr(L, 2, &ss, &salen);
  return SysretBool(
      L, "bind", olderr,
      bind(luaL_checkinteger(L, 1), (struct sockaddr *)&ss, salen));
}

// unix.connect(fd:int, ip:uint32, port:uint16)
// unix.connect(fd:int, unixpath:str)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixConnect(lua_State *L) {
  uint32_t salen;
  struct sockaddr_storage ss;
  int olderr = errno;
  MakeSockaddr(L, 2, &ss, &salen);
  return SysretBool(
      L, "connect", olderr,
      connect(luaL_checkinteger(L, 1), (struct sockaddr *)&ss, salen));
}

// unix.listen(fd:int[, backlog:int])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixListen(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "listen", olderr,
                    listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)));
}

static int LuaUnixGetname(lua_State *L, const char *name,
                          int func(int, struct sockaddr *, uint32_t *)) {
  int olderr;
  uint32_t addrsize;
  struct sockaddr_storage ss = {0};
  olderr = errno;
  addrsize = sizeof(ss) - 1;
  if (!func(luaL_checkinteger(L, 1), (struct sockaddr *)&ss, &addrsize)) {
    return PushSockaddr(L, &ss);
  } else {
    return LuaUnixSysretErrno(L, name, olderr);
  }
}

// unix.getsockname(fd:int)
//     âââ ip:uint32, port:uint16
//     âââ unixpath:str
//     âââ nil, unix.Errno
static int LuaUnixGetsockname(lua_State *L) {
  return LuaUnixGetname(L, "getsockname", getsockname);
}

// unix.getpeername(fd:int)
//     âââ ip:uint32, port:uint16
//     âââ unixpath:str
//     âââ nil, unix.Errno
static int LuaUnixGetpeername(lua_State *L) {
  return LuaUnixGetname(L, "getpeername", getpeername);
}

// unix.siocgifconf()
//     âââ {{name:str,ip:uint32,netmask:uint32}, ...}
//     âââ nil, unix.Errno
static int LuaUnixSiocgifconf(lua_State *L) {
  size_t n;
  char *data;
  int i, fd, olderr;
  struct ifreq *ifr;
  struct ifconf conf;
  olderr = errno;
  data = LuaAllocOrDie(L, (n = 4096));
  if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
    free(data);
    return LuaUnixSysretErrno(L, "siocgifconf", olderr);
  }
  conf.ifc_buf = data;
  conf.ifc_len = n;
  if (ioctl(fd, SIOCGIFCONF, &conf) == -1) {
    close(fd);
    free(data);
    return LuaUnixSysretErrno(L, "siocgifconf", olderr);
  }
  lua_newtable(L);
  i = 0;
  for (ifr = (struct ifreq *)data; (char *)ifr < data + conf.ifc_len; ++ifr) {
    if (ifr->ifr_addr.sa_family != AF_INET) continue;
    lua_createtable(L, 0, 3);
    lua_pushliteral(L, "name");
    lua_pushstring(L, ifr->ifr_name);
    lua_settable(L, -3);
    lua_pushliteral(L, "ip");
    lua_pushinteger(
        L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr));
    lua_settable(L, -3);
    if (ioctl(fd, SIOCGIFNETMASK, ifr) != -1) {
      lua_pushliteral(L, "netmask");
      lua_pushinteger(
          L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr));
      lua_settable(L, -3);
    }
    lua_rawseti(L, -2, ++i);
  }
  close(fd);
  free(data);
  return 1;
}

// sandbox.pledge([promises:str[, execpromises:str[, mode:int]]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixPledge(lua_State *L) {
  int olderr = errno;
  __pledge_mode = luaL_optinteger(L, 3, 0);
  return SysretBool(L, "pledge", olderr,
                    pledge(luaL_optstring(L, 1, 0), luaL_optstring(L, 2, 0)));
}

// sandbox.unveil([path:str[, permissions:str]])
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixUnveil(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "unveil", olderr,
                    unveil(luaL_optstring(L, 1, 0), luaL_optstring(L, 2, 0)));
}

// unix.gethostname()
//     âââ host:str
//     âââ nil, unix.Errno
static int LuaUnixGethostname(lua_State *L) {
  int rc, olderr;
  char buf[DNS_NAME_MAX + 1];
  olderr = errno;
  if ((rc = gethostname(buf, sizeof(buf))) != -1) {
    if (strnlen(buf, sizeof(buf)) < sizeof(buf)) {
      lua_pushstring(L, buf);
      return 1;
    } else {
      enomem();
    }
  }
  return LuaUnixSysretErrno(L, "gethostname", olderr);
}

// unix.accept(serverfd:int[, flags:int])
//     âââ clientfd:int, ip:uint32, port:uint16
//     âââ clientfd:int, unixpath:str
//     âââ nil, unix.Errno
static int LuaUnixAccept(lua_State *L) {
  uint32_t addrsize;
  struct sockaddr_storage ss;
  int clientfd, serverfd, olderr, flags;
  olderr = errno;
  addrsize = sizeof(ss);
  serverfd = luaL_checkinteger(L, 1);
  flags = luaL_optinteger(L, 2, 0);
  clientfd = accept4(serverfd, (struct sockaddr *)&ss, &addrsize, flags);
  if (clientfd != -1) {
    lua_pushinteger(L, clientfd);
    return 1 + PushSockaddr(L, &ss);
  } else {
    return LuaUnixSysretErrno(L, "accept", olderr);
  }
}

// unix.poll({[fd:int]=events:int, ...}[, timeoutms:int[, mask:unix.Sigset]])
//     âââ {[fd:int]=revents:int, ...}
//     âââ nil, unix.Errno
static int LuaUnixPoll(lua_State *L) {
  size_t nfds;
  struct sigset *mask;
  struct timespec ts, *tsp;
  struct pollfd *fds, *fds2;
  int i, fd, events, olderr = errno;
  luaL_checktype(L, 1, LUA_TTABLE);
  if (!lua_isnoneornil(L, 2)) {
    ts = timespec_frommillis(luaL_checkinteger(L, 2));
    tsp = &ts;
  } else {
    tsp = 0;
  }
  if (!lua_isnoneornil(L, 3)) {
    mask = luaL_checkudata(L, 3, "unix.Sigset");
  } else {
    mask = 0;
  }
  lua_pushnil(L);
  for (fds = 0, nfds = 0; lua_next(L, 1);) {
    if (lua_isinteger(L, -2)) {
      if ((fds2 = LuaRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) {
        fds2[nfds].fd = lua_tointeger(L, -2);
        fds2[nfds].events = lua_tointeger(L, -1);
        fds = fds2;
        ++nfds;
      } else {
        free(fds);
        return LuaUnixSysretErrno(L, "poll", olderr);
      }
    } else {
      // ignore non-integer key
    }
    lua_pop(L, 1);
  }
  olderr = errno;
  if ((events = ppoll(fds, nfds, tsp, mask)) != -1) {
    lua_createtable(L, events, 0);
    for (i = 0; i < nfds; ++i) {
      if (fds[i].revents && fds[i].fd >= 0) {
        lua_pushinteger(L, fds[i].revents);
        lua_rawseti(L, -2, fds[i].fd);
      }
    }
    free(fds);
    return 1;
  } else {
    free(fds);
    return LuaUnixSysretErrno(L, "poll", olderr);
  }
}

// unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
//     âââ data:str, ip:uint32, port:uint16
//     âââ data:str, unixpath:str
//     âââ nil, unix.Errno
static int LuaUnixRecvfrom(lua_State *L) {
  char *buf;
  size_t got;
  ssize_t rc;
  uint32_t addrsize;
  lua_Integer bufsiz;
  struct sockaddr_storage ss;
  int fd, flags, pushed, olderr = errno;
  addrsize = sizeof(ss);
  fd = luaL_checkinteger(L, 1);
  bufsiz = luaL_optinteger(L, 2, 1500);
  bufsiz = MIN(bufsiz, 0x7ffff000);
  flags = luaL_optinteger(L, 3, 0);
  buf = LuaAllocOrDie(L, bufsiz);
  if ((rc = recvfrom(fd, buf, bufsiz, flags, (struct sockaddr *)&ss,
                     &addrsize)) != -1) {
    got = rc;
    lua_pushlstring(L, buf, got);
    pushed = 1 + PushSockaddr(L, &ss);
    free(buf);
    return pushed;
  } else {
    free(buf);
    return LuaUnixSysretErrno(L, "recvfrom", olderr);
  }
}

// unix.recv(fd:int[, bufsiz:int[, flags:int]])
//     âââ data:str
//     âââ nil, unix.Errno
static int LuaUnixRecv(lua_State *L) {
  char *buf;
  size_t got;
  ssize_t rc;
  lua_Integer bufsiz;
  int fd, flags, pushed, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  bufsiz = luaL_optinteger(L, 2, 1500);
  bufsiz = MIN(bufsiz, 0x7ffff000);
  flags = luaL_optinteger(L, 3, 0);
  buf = LuaAllocOrDie(L, bufsiz);
  rc = recv(fd, buf, bufsiz, flags);
  if (rc != -1) {
    got = rc;
    lua_pushlstring(L, buf, got);
    free(buf);
    return 1;
  } else {
    free(buf);
    return LuaUnixSysretErrno(L, "recv", olderr);
  }
}

// unix.send(fd:int, data:str[, flags:int])
//     âââ sent:int
//     âââ nil, unix.Errno
static int LuaUnixSend(lua_State *L) {
  char *data;
  ssize_t rc;
  size_t sent, size;
  int fd, flags, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  data = luaL_checklstring(L, 2, &size);
  flags = luaL_optinteger(L, 3, 0);
  return SysretInteger(L, "send", olderr, send(fd, data, size, flags));
}

// unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int])
// unix.sendto(fd:int, data:str, unixpath:str[, flags:int])
//     âââ sent:int
//     âââ nil, unix.Errno
static int LuaUnixSendto(lua_State *L) {
  char *data;
  size_t size;
  uint32_t salen;
  struct sockaddr_storage ss;
  int i, fd, flags, olderr = errno;
  fd = luaL_checkinteger(L, 1);
  data = luaL_checklstring(L, 2, &size);
  i = MakeSockaddr(L, 3, &ss, &salen);
  flags = luaL_optinteger(L, i, 0);
  return SysretInteger(
      L, "sendto", olderr,
      sendto(fd, data, size, flags, (struct sockaddr *)&ss, salen));
}

// unix.shutdown(fd:int, how:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixShutdown(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "shutdown", olderr,
                    shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
}

// unix.sigprocmask(how:int, newmask:unix.Sigset)
//     âââ oldmask:unix.Sigset
//     âââ nil, unix.Errno
static int LuaUnixSigprocmask(lua_State *L) {
  uint64_t imask;
  int olderr = errno;
  struct sigset oldmask;
  if (!sigprocmask(luaL_checkinteger(L, 1),
                   luaL_checkudata(L, 2, "unix.Sigset"), &oldmask)) {
    LuaPushSigset(L, oldmask);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "sigprocmask", olderr);
  }
}

static void LuaUnixOnSignal(int sig, siginfo_t *si, void *ctx) {
  int type;
  lua_State *L = GL;
  STRACE("LuaUnixOnSignal(%G)", sig);
  lua_getglobal(L, "__signal_handlers");
  type = lua_rawgeti(L, -1, sig);
  lua_remove(L, -2);  // pop __signal_handlers
  if (type == LUA_TFUNCTION) {
    lua_pushinteger(L, sig);
    if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
      ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(L, -1));
      lua_pop(L, 1);  // pop error
    }
  } else {
    lua_pop(L, 1);  // pop handler
  }
}

// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]])
//     âââ oldhandler:func|int, flags:int, mask:unix.Sigset
//     âââ nil, unix.Errno
static int LuaUnixSigaction(lua_State *L) {
  struct sigset *mask;
  int i, n, sig, olderr = errno;
  struct sigaction sa, oldsa, *saptr = &sa;
  sigemptyset(&sa.sa_mask);
  sig = luaL_checkinteger(L, 1);
  if (!(1 <= sig && sig <= NSIG)) {
    luaL_argerror(L, 1, "signal number invalid");
    unreachable;
  }
  if (lua_isnoneornil(L, 2)) {
    // if handler/flags/mask aren't passed,
    // then we're quering the current state
    saptr = 0;
  } else if (lua_isinteger(L, 2)) {
    // bypass handling signals using lua code if possible
    sa.sa_sigaction = (void *)luaL_checkinteger(L, 2);
  } else if (lua_isfunction(L, 2)) {
    sa.sa_sigaction = LuaUnixOnSignal;
    // lua probably isn't async signal safe, so...
    // let's mask all the lua handlers during handling
    sigaddset(&sa.sa_mask, sig);
    lua_getglobal(L, "__signal_handlers");
    luaL_checktype(L, -1, LUA_TTABLE);
    lua_len(L, -1);
    n = lua_tointeger(L, -1);
    lua_pop(L, 1);
    for (i = 1; i <= MIN(n, NSIG); ++i) {
      if (lua_geti(L, -1, i) == LUA_TFUNCTION) {
        sigaddset(&sa.sa_mask, i);
      }
      lua_pop(L, 1);
    }
    lua_pop(L, 1);
  } else {
    luaL_argerror(L, 2, "sigaction handler not integer or function");
    unreachable;
  }
  if (!lua_isnoneornil(L, 4)) {
    mask = luaL_checkudata(L, 4, "unix.Sigset");
    sa.sa_mask.__bits[0] |= mask->__bits[0];
    sa.sa_mask.__bits[1] |= mask->__bits[1];
    lua_remove(L, 4);
  }
  if (lua_isnoneornil(L, 3)) {
    sa.sa_flags = 0;
  } else {
    sa.sa_flags = lua_tointeger(L, 3);
    lua_remove(L, 3);
  }
  if (!sigaction(sig, saptr, &oldsa)) {
    lua_getglobal(L, "__signal_handlers");
    // push the old handler result to stack. if the global lua handler
    // table has a real function, then we prefer to return that. if it's
    // absent or a raw integer value, then we're better off returning
    // what the kernel gave us in &oldsa.
    if (lua_rawgeti(L, -1, sig) != LUA_TFUNCTION) {
      lua_pop(L, 1);
      lua_pushinteger(L, (intptr_t)oldsa.sa_handler);
    }
    if (saptr) {
      // update the global lua table
      if (sa.sa_sigaction == LuaUnixOnSignal) {
        lua_pushvalue(L, -3);
      } else {
        lua_pushnil(L);
      }
      lua_rawseti(L, -3, sig);
    }
    // remove the signal handler table from stack
    lua_remove(L, -2);
    // finish pushing the last 2/3 results
    lua_pushinteger(L, oldsa.sa_flags);
    LuaPushSigset(L, oldsa.sa_mask);
    return 3;
  } else {
    return LuaUnixSysretErrno(L, "sigaction", olderr);
  }
}

// unix.sigsuspend([mask:Sigmask])
//     âââ nil, unix.Errno
static int LuaUnixSigsuspend(lua_State *L) {
  int olderr = errno;
  sigsuspend(!lua_isnoneornil(L, 1) ? luaL_checkudata(L, 1, "unix.Sigset") : 0);
  return LuaUnixSysretErrno(L, "sigsuspend", olderr);
}

// unix.sigpending()
//     âââ mask:unix.Sigset
//     âââ nil, unix.Errno
static int LuaUnixSigpending(lua_State *L) {
  int olderr = errno;
  struct sigset mask;
  if (!sigpending(&mask)) {
    LuaPushSigset(L, mask);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "sigpending", olderr);
  }
}

// unix.setitimer(which[, intervalsec, intns, valuesec, valuens])
//     âââ intervalsec:int, intervalns:int, valuesec:int, valuens:int
//     âââ nil, unix.Errno
static int LuaUnixSetitimer(lua_State *L) {
  int which, olderr = errno;
  struct itimerval it, oldit, *itptr;
  which = luaL_checkinteger(L, 1);
  if (!lua_isnoneornil(L, 2)) {
    itptr = &it;
    it.it_interval.tv_sec = luaL_optinteger(L, 2, 0);
    it.it_interval.tv_usec = luaL_optinteger(L, 3, 0) / 1000;
    it.it_value.tv_sec = luaL_optinteger(L, 4, 0);
    it.it_value.tv_usec = luaL_optinteger(L, 5, 0) / 1000;
  } else {
    itptr = 0;
  }
  if (!setitimer(which, itptr, &oldit)) {
    lua_pushinteger(L, oldit.it_interval.tv_sec);
    lua_pushinteger(L, oldit.it_interval.tv_usec * 1000);
    lua_pushinteger(L, oldit.it_value.tv_sec);
    lua_pushinteger(L, oldit.it_value.tv_usec * 1000);
    return 4;
  } else {
    return LuaUnixSysretErrno(L, "setitimer", olderr);
  }
}

static int LuaUnixStr(lua_State *L, char *f(int)) {
  return ReturnString(L, f(luaL_checkinteger(L, 1)));
}

// unix.strsignal(sig:int)
//     âââ symbol:str
static int LuaUnixStrsignal(lua_State *L) {
  return LuaUnixStr(L, strsignal);
}

// unix.WIFEXITED(wstatus)
//     âââ bool
static int LuaUnixWifexited(lua_State *L) {
  return ReturnBoolean(L, !!WIFEXITED(luaL_checkinteger(L, 1)));
}

// unix.WEXITSTATUS(wstatus)
//     âââ exitcode:uint8
static int LuaUnixWexitstatus(lua_State *L) {
  return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1)));
}

// unix.WIFSIGNALED(wstatus)
//     âââ bool
static int LuaUnixWifsignaled(lua_State *L) {
  return ReturnBoolean(L, !!WIFSIGNALED(luaL_checkinteger(L, 1)));
}

// unix.WTERMSIG(wstatus)
//     âââ sig:uint8
static int LuaUnixWtermsig(lua_State *L) {
  return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1)));
}

static dontinline int LuaUnixTime(lua_State *L, const char *call,
                                  struct tm *f(const time_t *, struct tm *)) {
  int64_t ts;
  struct tm tm;
  int olderr = errno;
  ts = luaL_checkinteger(L, 1);
  if (f(&ts, &tm)) {
    lua_pushinteger(L, tm.tm_year + 1900);
    lua_pushinteger(L, tm.tm_mon + 1);  // 1 â¤ mon  â¤ 12
    lua_pushinteger(L, tm.tm_mday);     // 1 â¤ mday â¤ 31
    lua_pushinteger(L, tm.tm_hour);     // 0 â¤ hour â¤ 23
    lua_pushinteger(L, tm.tm_min);      // 0 â¤ min  â¤ 59
    lua_pushinteger(L, tm.tm_sec);      // 0 â¤ sec  â¤ 60
    lua_pushinteger(L, tm.tm_gmtoff);   // Â±93600 seconds
    lua_pushinteger(L, tm.tm_wday);     // 0 â¤ wday â¤ 6
    lua_pushinteger(L, tm.tm_yday);     // 0 â¤ yday â¤ 365
    lua_pushinteger(L, tm.tm_isdst);    // daylight savings
    lua_pushstring(L, tm.tm_zone);
    return 11;
  } else {
    return LuaUnixSysretErrno(L, call, olderr);
  }
}

// unix.gmtime(unixsecs:int)
//     âââ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
//     âââ nil,unix.Errno
static int LuaUnixGmtime(lua_State *L) {
  return LuaUnixTime(L, "gmtime", gmtime_r);
}

// unix.localtime(unixts:int)
//     âââ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
//     âââ nil,unix.Errno
static int LuaUnixLocaltime(lua_State *L) {
  return LuaUnixTime(L, "localtime", localtime_r);
}

// unix.major(rdev:int)
//     âââ major:int
static int LuaUnixMajor(lua_State *L) {
  return ReturnInteger(L, major(luaL_checkinteger(L, 1)));
}

// unix.minor(rdev:int)
//     âââ minor:int
static int LuaUnixMinor(lua_State *L) {
  return ReturnInteger(L, minor(luaL_checkinteger(L, 1)));
}

// unix.S_ISDIR(mode:int)
//     âââ bool
static int LuaUnixSisdir(lua_State *L) {
  lua_pushboolean(L, S_ISDIR(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISCHR(mode:int)
//     âââ bool
static int LuaUnixSischr(lua_State *L) {
  lua_pushboolean(L, S_ISCHR(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISBLK(mode:int)
//     âââ bool
static int LuaUnixSisblk(lua_State *L) {
  lua_pushboolean(L, S_ISBLK(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISREG(mode:int)
//     âââ bool
static int LuaUnixSisreg(lua_State *L) {
  lua_pushboolean(L, S_ISREG(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISFIFO(mode:int)
//     âââ bool
static int LuaUnixSisfifo(lua_State *L) {
  lua_pushboolean(L, S_ISFIFO(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISLNK(mode:int)
//     âââ bool
static int LuaUnixSislnk(lua_State *L) {
  lua_pushboolean(L, S_ISLNK(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.S_ISSOCK(mode:int)
//     âââ bool
static int LuaUnixSissock(lua_State *L) {
  lua_pushboolean(L, S_ISSOCK(luaL_checkinteger(L, 1)));
  return 1;
}

// unix.isatty(fd:int)
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixIsatty(lua_State *L) {
  int olderr = errno;
  return SysretBool(L, "isatty", olderr, isatty(luaL_checkinteger(L, 1)));
}

// unix.tiocgwinsz(fd:int)
//     âââ rows:int, cols:int
//     âââ nil, unix.Errno
static int LuaUnixTiocgwinsz(lua_State *L) {
  struct winsize ws;
  int olderr = errno;
  if (!ioctl(luaL_checkinteger(L, 1), TIOCGWINSZ, &ws)) {
    lua_pushinteger(L, ws.ws_row);
    lua_pushinteger(L, ws.ws_col);
    return 2;
  } else {
    return LuaUnixSysretErrno(L, "tiocgwinsz", olderr);
  }
}

// unix.sched_yield()
static int LuaUnixSchedYield(lua_State *L) {
  sched_yield();
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// unix.Stat object

static struct stat *GetUnixStat(lua_State *L) {
  return luaL_checkudata(L, 1, "unix.Stat");
}

// unix.Stat:size()
//     âââ bytes:int
static int LuaUnixStatSize(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_size);
}

// unix.Stat:mode()
//     âââ mode:int
static int LuaUnixStatMode(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_mode);
}

// unix.Stat:dev()
//     âââ dev:int
static int LuaUnixStatDev(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_dev);
}

// unix.Stat:ino()
//     âââ inodeint
static int LuaUnixStatIno(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_ino);
}

// unix.Stat:nlink()
//     âââ count:int
static int LuaUnixStatNlink(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_nlink);
}

// unix.Stat:rdev()
//     âââ rdev:int
static int LuaUnixStatRdev(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_rdev);
}

// unix.Stat:uid()
//     âââ uid:int
static int LuaUnixStatUid(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_uid);
}

// unix.Stat:gid()
//     âââ gid:int
static int LuaUnixStatGid(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_gid);
}

// unix.Stat:blocks()
//     âââ count:int
static int LuaUnixStatBlocks(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_blocks);
}

// unix.Stat:blksize()
//     âââ bytes:int
static int LuaUnixStatBlksize(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_blksize);
}

static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) {
  lua_pushinteger(L, ts->tv_sec);
  lua_pushinteger(L, ts->tv_nsec);
  return 2;
}

// unix.Stat:atim()
//     âââ unixts:int, nanos:int
static int LuaUnixStatAtim(lua_State *L) {
  return ReturnTimespec(L, &GetUnixStat(L)->st_atim);
}

// unix.Stat:mtim()
//     âââ unixts:int, nanos:int
static int LuaUnixStatMtim(lua_State *L) {
  return ReturnTimespec(L, &GetUnixStat(L)->st_mtim);
}

// unix.Stat:ctim()
//     âââ unixts:int, nanos:int
static int LuaUnixStatCtim(lua_State *L) {
  return ReturnTimespec(L, &GetUnixStat(L)->st_ctim);
}

// unix.Stat:birthtim()
//     âââ unixts:int, nanos:int
static int LuaUnixStatBirthtim(lua_State *L) {
  return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim);
}

// unix.Stat:gen()
//     âââ gen:int [xnu/bsd]
static int LuaUnixStatGen(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_gen);
}

// unix.Stat:flags()
//     âââ flags:int [xnu/bsd]
static int LuaUnixStatFlags(lua_State *L) {
  return ReturnInteger(L, GetUnixStat(L)->st_flags);
}

static int LuaUnixStatToString(lua_State *L) {
  char ibuf[21];
  luaL_Buffer b;
  struct stat *st;
  st = GetUnixStat(L);
  luaL_buffinit(L, &b);
  luaL_addstring(&b, "unix.Stat({size=");
  FormatInt64(ibuf, st->st_size);
  luaL_addstring(&b, ibuf);
  if (st->st_mode) {
    luaL_addstring(&b, ", mode=");
    FormatOctal32(ibuf, st->st_mode, 1);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_ino) {
    luaL_addstring(&b, ", ino=");
    FormatUint64(ibuf, st->st_ino);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_nlink) {
    luaL_addstring(&b, ", nlink=");
    FormatUint64(ibuf, st->st_nlink);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_uid) {
    luaL_addstring(&b, ", uid=");
    FormatUint32(ibuf, st->st_uid);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_gid) {
    luaL_addstring(&b, ", gid=");
    FormatUint32(ibuf, st->st_gid);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_flags) {
    luaL_addstring(&b, ", flags=");
    FormatUint32(ibuf, st->st_flags);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_dev) {
    luaL_addstring(&b, ", dev=");
    FormatUint64(ibuf, st->st_dev);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_rdev) {
    luaL_addstring(&b, ", rdev=");
    FormatUint64(ibuf, st->st_rdev);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_blksize) {
    luaL_addstring(&b, ", blksize=");
    FormatInt64(ibuf, st->st_blksize);
    luaL_addstring(&b, ibuf);
  }
  if (st->st_blocks) {
    luaL_addstring(&b, ", blocks=");
    FormatInt64(ibuf, st->st_blocks);
    luaL_addstring(&b, ibuf);
  }
  luaL_addstring(&b, "})");
  luaL_pushresult(&b);
  return 1;
}

static const luaL_Reg kLuaUnixStatMeth[] = {
    {"atim", LuaUnixStatAtim},          //
    {"birthtim", LuaUnixStatBirthtim},  //
    {"blksize", LuaUnixStatBlksize},    //
    {"blocks", LuaUnixStatBlocks},      //
    {"ctim", LuaUnixStatCtim},          //
    {"dev", LuaUnixStatDev},            //
    {"gid", LuaUnixStatGid},            //
    {"ino", LuaUnixStatIno},            //
    {"mode", LuaUnixStatMode},          //
    {"mtim", LuaUnixStatMtim},          //
    {"nlink", LuaUnixStatNlink},        //
    {"rdev", LuaUnixStatRdev},          //
    {"size", LuaUnixStatSize},          //
    {"uid", LuaUnixStatUid},            //
    {"flags", LuaUnixStatFlags},        //
    {"gen", LuaUnixStatGen},            //
    {0},                                //
};

static const luaL_Reg kLuaUnixStatMeta[] = {
    {"__tostring", LuaUnixStatToString},  //
    {"__repr", LuaUnixStatToString},      //
    {0},                                  //
};

static void LuaUnixStatObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Stat");
  luaL_setfuncs(L, kLuaUnixStatMeta, 0);
  luaL_newlibtable(L, kLuaUnixStatMeth);
  luaL_setfuncs(L, kLuaUnixStatMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// unix.Statfs object

static struct statfs *GetUnixStatfs(lua_State *L) {
  return luaL_checkudata(L, 1, "unix.Statfs");
}

// unix.Statfs:type()
//     âââ bytes:int
static int LuaUnixStatfsType(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_type);
}

// unix.Statfs:bsize()
//     âââ bsize:int
static int LuaUnixStatfsBsize(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_bsize);
}

// unix.Statfs:blocks()
//     âââ blocks:int
static int LuaUnixStatfsBlocks(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_blocks);
}

// unix.Statfs:bfree()
//     âââ bfreedeint
static int LuaUnixStatfsBfree(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_bfree);
}

// unix.Statfs:bavail()
//     âââ count:int
static int LuaUnixStatfsBavail(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_bavail);
}

// unix.Statfs:files()
//     âââ files:int
static int LuaUnixStatfsFiles(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_files);
}

// unix.Statfs:ffree()
//     âââ ffree:int
static int LuaUnixStatfsFfree(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_ffree);
}

// unix.Statfs:fsid()
//     âââ x:int, y:int
static int LuaUnixStatfsFsid(lua_State *L) {
  struct statfs *f = GetUnixStatfs(L);
  lua_pushinteger(L, f->f_fsid.__val[0]);
  lua_pushinteger(L, f->f_fsid.__val[1]);
  return 2;
}

// unix.Statfs:namelen()
//     âââ count:int
static int LuaUnixStatfsNamelen(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_namelen);
}

// unix.Statfs:frsize()
//     âââ bytes:int
static int LuaUnixStatfsFrsize(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_frsize);
}

// unix.Statfs:flags()
//     âââ bytes:int
static int LuaUnixStatfsFlags(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_flags);
}

// unix.Statfs:owner()
//     âââ bytes:int
static int LuaUnixStatfsOwner(lua_State *L) {
  return ReturnInteger(L, GetUnixStatfs(L)->f_owner);
}

// unix.Statfs:fstypename()
//     âââ fstypename:str
static int LuaUnixStatfsFstypename(lua_State *L) {
  return ReturnString(L, GetUnixStatfs(L)->f_fstypename);
}

static int LuaUnixStatfsToString(lua_State *L) {
  char ibuf[21];
  luaL_Buffer b;
  struct statfs *f;
  f = GetUnixStatfs(L);
  luaL_buffinit(L, &b);
  luaL_addstring(&b, "unix.Statfs({type=");
  FormatInt64(ibuf, f->f_type);
  luaL_addstring(&b, ibuf);
  luaL_addstring(&b, ", fstypename=\"");
  luaL_addstring(&b, f->f_fstypename);
  luaL_addstring(&b, "\"");
  if (f->f_bsize) {
    luaL_addstring(&b, ", bsize=");
    FormatInt64(ibuf, f->f_bsize);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_blocks) {
    luaL_addstring(&b, ", blocks=");
    FormatInt64(ibuf, f->f_blocks);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_bfree) {
    luaL_addstring(&b, ", bfree=");
    FormatInt64(ibuf, f->f_bfree);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_bavail) {
    luaL_addstring(&b, ", bavail=");
    FormatInt64(ibuf, f->f_bavail);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_files) {
    luaL_addstring(&b, ", files=");
    FormatInt64(ibuf, f->f_files);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_ffree) {
    luaL_addstring(&b, ", ffree=");
    FormatInt64(ibuf, f->f_ffree);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_fsid.__val[0] || f->f_fsid.__val[1]) {
    luaL_addstring(&b, ", fsid={");
    FormatUint64(ibuf, f->f_fsid.__val[0]);
    luaL_addstring(&b, ibuf);
    luaL_addstring(&b, ", ");
    FormatUint64(ibuf, f->f_fsid.__val[1]);
    luaL_addstring(&b, ibuf);
    luaL_addstring(&b, "}");
  }
  if (f->f_namelen) {
    luaL_addstring(&b, ", namelen=");
    FormatUint64(ibuf, f->f_namelen);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_flags) {
    luaL_addstring(&b, ", flags=");
    FormatHex64(ibuf, f->f_flags, 2);
    luaL_addstring(&b, ibuf);
  }
  if (f->f_owner) {
    luaL_addstring(&b, ", owner=");
    FormatUint32(ibuf, f->f_owner);
    luaL_addstring(&b, ibuf);
  }
  luaL_addstring(&b, "})");
  luaL_pushresult(&b);
  return 1;
}

static const luaL_Reg kLuaUnixStatfsMeth[] = {
    {"type", LuaUnixStatfsType},              //
    {"bsize", LuaUnixStatfsBsize},            //
    {"blocks", LuaUnixStatfsBlocks},          //
    {"bfree", LuaUnixStatfsBfree},            //
    {"bavail", LuaUnixStatfsBavail},          //
    {"files", LuaUnixStatfsFiles},            //
    {"ffree", LuaUnixStatfsFfree},            //
    {"fsid", LuaUnixStatfsFsid},              //
    {"namelen", LuaUnixStatfsNamelen},        //
    {"frsize", LuaUnixStatfsFrsize},          //
    {"flags", LuaUnixStatfsFlags},            //
    {"owner", LuaUnixStatfsOwner},            //
    {"fstypename", LuaUnixStatfsFstypename},  //
    {0},                                      //
};

static const luaL_Reg kLuaUnixStatfsMeta[] = {
    {"__tostring", LuaUnixStatfsToString},  //
    {"__repr", LuaUnixStatfsToString},      //
    {0},                                    //
};

static void LuaUnixStatfsObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Statfs");
  luaL_setfuncs(L, kLuaUnixStatfsMeta, 0);
  luaL_newlibtable(L, kLuaUnixStatfsMeth);
  luaL_setfuncs(L, kLuaUnixStatfsMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// unix.Rusage object

static struct rusage *GetUnixRusage(lua_State *L) {
  return luaL_checkudata(L, 1, "unix.Rusage");
}

static dontinline int ReturnTimeval(lua_State *L, struct timeval *tv) {
  lua_pushinteger(L, tv->tv_sec);
  lua_pushinteger(L, tv->tv_usec * 1000);
  return 2;
}

// unix.Rusage:utime()
//     âââ unixts:int, nanos:int
static int LuaUnixRusageUtime(lua_State *L) {
  return ReturnTimeval(L, &GetUnixRusage(L)->ru_utime);
}

// unix.Rusage:stime()
//     âââ unixts:int, nanos:int
static int LuaUnixRusageStime(lua_State *L) {
  return ReturnTimeval(L, &GetUnixRusage(L)->ru_stime);
}

// unix.Rusage:maxrss()
//     âââ kilobytes:int
static int LuaUnixRusageMaxrss(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_maxrss);
}

// unix.Rusage:ixrss()
//     âââ integralkilobytes:int
static int LuaUnixRusageIxrss(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_ixrss);
}

// unid.Rusage:idrss()
//     âââ integralkilobytes:int
static int LuaUnixRusageIdrss(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_idrss);
}

// unis.Rusage:isrss()
//     âââ integralkilobytes:int
static int LuaUnixRusageIsrss(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_isrss);
}

// unix.Rusage:minflt()
//     âââ count:int
static int LuaUnixRusageMinflt(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_minflt);
}

// unix.Rusage:majflt()
//     âââ count:int
static int LuaUnixRusageMajflt(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_majflt);
}

// unix.Rusage:nswap()
//     âââ count:int
static int LuaUnixRusageNswap(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_nswap);
}

// unix.Rusage:inblock()
//     âââ count:int
static int LuaUnixRusageInblock(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_inblock);
}

// unix.Rusage:oublock()
//     âââ count:int
static int LuaUnixRusageOublock(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_oublock);
}

// unix.Rusage:msgsnd()
//     âââ count:int
static int LuaUnixRusageMsgsnd(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_msgsnd);
}

// unix.Rusage:msgrcv()
//     âââ count:int
static int LuaUnixRusageMsgrcv(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_msgrcv);
}

// unix.Rusage:nsignals()
//     âââ count:int
static int LuaUnixRusageNsignals(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_nsignals);
}

// unix.Rusage:nvcsw()
//     âââ count:int
static int LuaUnixRusageNvcsw(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_nvcsw);
}

// unix.Rusage:nivcsw()
//     âââ count:int
static int LuaUnixRusageNivcsw(lua_State *L) {
  return ReturnInteger(L, GetUnixRusage(L)->ru_nivcsw);
}

static int LuaUnixRusageToString(lua_State *L) {
  char *b = 0;
  struct rusage *ru = GetUnixRusage(L);
  appends(&b, "{");
  appendf(&b, "%s={%ld, %ld}", "utime", ru->ru_utime.tv_sec,
          ru->ru_utime.tv_usec * 1000);
  if (ru->ru_stime.tv_sec || ru->ru_stime.tv_usec) {
    appendw(&b, READ16LE(", "));
    appendf(&b, "%s={%ld, %ld}", "stime", ru->ru_stime.tv_sec,
            ru->ru_stime.tv_usec * 1000);
  }
  if (ru->ru_maxrss) appendf(&b, ", %s=%ld", "maxrss", ru->ru_maxrss);
  if (ru->ru_ixrss) appendf(&b, ", %s=%ld", "ixrss", ru->ru_ixrss);
  if (ru->ru_idrss) appendf(&b, ", %s=%ld", "idrss", ru->ru_idrss);
  if (ru->ru_isrss) appendf(&b, ", %s=%ld", "isrss", ru->ru_isrss);
  if (ru->ru_minflt) appendf(&b, ", %s=%ld", "minflt", ru->ru_minflt);
  if (ru->ru_majflt) appendf(&b, ", %s=%ld", "majflt", ru->ru_majflt);
  if (ru->ru_nswap) appendf(&b, ", %s=%ld", "nswap", ru->ru_nswap);
  if (ru->ru_inblock) appendf(&b, ", %s=%ld", "inblock", ru->ru_inblock);
  if (ru->ru_oublock) appendf(&b, ", %s=%ld", "oublock", ru->ru_oublock);
  if (ru->ru_msgsnd) appendf(&b, ", %s=%ld", "msgsnd", ru->ru_msgsnd);
  if (ru->ru_msgrcv) appendf(&b, ", %s=%ld", "msgrcv", ru->ru_msgrcv);
  if (ru->ru_nsignals) appendf(&b, ", %s=%ld", "nsignals", ru->ru_nsignals);
  if (ru->ru_nvcsw) appendf(&b, ", %s=%ld", "nvcsw", ru->ru_nvcsw);
  if (ru->ru_nivcsw) appendf(&b, ", %s=%ld", "nivcsw", ru->ru_nivcsw);
  appendw(&b, '}');
  lua_pushlstring(L, b, appendz(b).i);
  return 1;
}

static const luaL_Reg kLuaUnixRusageMeth[] = {
    {"utime", LuaUnixRusageUtime},        //
    {"stime", LuaUnixRusageStime},        //
    {"maxrss", LuaUnixRusageMaxrss},      //
    {"ixrss", LuaUnixRusageIxrss},        //
    {"idrss", LuaUnixRusageIdrss},        //
    {"isrss", LuaUnixRusageIsrss},        //
    {"minflt", LuaUnixRusageMinflt},      //
    {"majflt", LuaUnixRusageMajflt},      //
    {"nswap", LuaUnixRusageNswap},        //
    {"inblock", LuaUnixRusageInblock},    //
    {"oublock", LuaUnixRusageOublock},    //
    {"msgsnd", LuaUnixRusageMsgsnd},      //
    {"msgrcv", LuaUnixRusageMsgrcv},      //
    {"nsignals", LuaUnixRusageNsignals},  //
    {"nvcsw", LuaUnixRusageNvcsw},        //
    {"nivcsw", LuaUnixRusageNivcsw},      //
    {0},                                  //
};

static const luaL_Reg kLuaUnixRusageMeta[] = {
    {"__repr", LuaUnixRusageToString},      //
    {"__tostring", LuaUnixRusageToString},  //
    {0},                                    //
};

static void LuaUnixRusageObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Rusage");
  luaL_setfuncs(L, kLuaUnixRusageMeta, 0);
  luaL_newlibtable(L, kLuaUnixRusageMeth);
  luaL_setfuncs(L, kLuaUnixRusageMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// unix.Errno object

static struct UnixErrno *GetUnixErrno(lua_State *L) {
  return luaL_checkudata(L, 1, "unix.Errno");
}

// unix.Errno:errno()
//     âââ errno:int
static int LuaUnixErrnoErrno(lua_State *L) {
  return ReturnInteger(L, GetUnixErrno(L)->errno_);
}

static int LuaUnixErrnoWinerr(lua_State *L) {
  return ReturnInteger(L, GetUnixErrno(L)->winerr);
}

static int LuaUnixErrnoName(lua_State *L) {
  return ReturnString(L, _strerrno(GetUnixErrno(L)->errno_));
}

static int LuaUnixErrnoDoc(lua_State *L) {
  return ReturnString(L, _strerdoc(GetUnixErrno(L)->errno_));
}

static int LuaUnixErrnoCall(lua_State *L) {
  return ReturnString(L, GetUnixErrno(L)->call);
}

static int LuaUnixErrnoToString(lua_State *L) {
  char msg[256];
  struct UnixErrno *e;
  e = GetUnixErrno(L);
  if (e->call) {
    strerror_wr(e->errno_, e->winerr, msg, sizeof(msg));
    lua_pushfstring(L, "%s() failed: %s", e->call, msg);
  } else {
    lua_pushstring(L, _strerrno(e->errno_));
  }
  return 1;
}

static const luaL_Reg kLuaUnixErrnoMeth[] = {
    {"errno", LuaUnixErrnoErrno},    //
    {"winerr", LuaUnixErrnoWinerr},  //
    {"name", LuaUnixErrnoName},      //
    {"call", LuaUnixErrnoCall},      //
    {"doc", LuaUnixErrnoDoc},        //
    {0},                             //
};

static const luaL_Reg kLuaUnixErrnoMeta[] = {
    {"__tostring", LuaUnixErrnoToString},  //
    {0},                                   //
};

static void LuaUnixErrnoObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Errno");
  luaL_setfuncs(L, kLuaUnixErrnoMeta, 0);
  luaL_newlibtable(L, kLuaUnixErrnoMeth);
  luaL_setfuncs(L, kLuaUnixErrnoMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// unix.Memory object

struct Memory {
  union {
    char *bytes;
    atomic_long *words;
  } u;
  size_t size;
  void *map;
  size_t mapsize;
  pthread_mutex_t *lock;
};

// unix.Memory:read([offset:int[, bytes:int]])
//     âââ str
static int LuaUnixMemoryRead(lua_State *L) {
  size_t i, n;
  struct Memory *m;
  m = luaL_checkudata(L, 1, "unix.Memory");
  i = luaL_optinteger(L, 2, 0);
  if (lua_isnoneornil(L, 3)) {
    // unix.Memory:read([offset:int])
    // extracts nul-terminated string
    if (i > m->size) {
      luaL_error(L, "out of range");
      unreachable;
    }
    n = strnlen(m->u.bytes + i, m->size - i);
  } else {
    // unix.Memory:read(offset:int, bytes:int)
    // read binary data with boundary checking
    n = luaL_checkinteger(L, 3);
    if (i > m->size || n >= m->size || i + n > m->size) {
      luaL_error(L, "out of range");
      unreachable;
    }
  }
  pthread_mutex_lock(m->lock);
  lua_pushlstring(L, m->u.bytes + i, n);
  pthread_mutex_unlock(m->lock);
  return 1;
}

// unix.Memory:write([offset:int,] data:str[, bytes:int])
static int LuaUnixMemoryWrite(lua_State *L) {
  int b;
  const char *s;
  size_t i, n, j;
  struct Memory *m;
  m = luaL_checkudata(L, 1, "unix.Memory");
  if (!lua_isnumber(L, 2)) {
    // unix.Memory:write(data:str[, bytes:int])
    i = 0;
    s = luaL_checklstring(L, 2, &n);
    b = 3;
  } else {
    // unix.Memory:write(offset:int, data:str[, bytes:int])
    i = luaL_checkinteger(L, 2);
    s = luaL_checklstring(L, 3, &n);
    b = 4;
  }
  if (i > m->size) {
    luaL_error(L, "out of range");
    unreachable;
  }
  if (lua_isnoneornil(L, b)) {
    // unix.Memory:write(data:str[, offset:int])
    // writes binary data, plus a nul terminator
    if (i < n < m->size) {
      // include lua string's implicit nul so this round trips with
      // unix.Memory:read(offset:int) even when we're overwriting a
      // larger string that was previously inserted
      n += 1;
    } else {
      // there's no room to include the implicit nul-terminator so
      // leave it out which is safe b/c Memory:read() uses strnlen
    }
  } else {
    // unix.Memory:write(data:str, offset:int, bytes:int])
    // writes binary data without including nul-terminator
    j = luaL_checkinteger(L, b);
    if (j > n) {
      luaL_argerror(L, b, "bytes is more than what's in data");
      unreachable;
    }
    n = j;
  }
  if (i + n > m->size) {
    luaL_error(L, "out of range");
    unreachable;
  }
  pthread_mutex_lock(m->lock);
  memcpy(m->u.bytes + i, s, n);
  pthread_mutex_unlock(m->lock);
  return 0;
}

static atomic_long *GetWord(lua_State *L) {
  size_t i;
  struct Memory *m;
  m = luaL_checkudata(L, 1, "unix.Memory");
  i = luaL_checkinteger(L, 2);
  if (i >= m->size / sizeof(*m->u.words)) {
    luaL_error(L, "out of range");
    unreachable;
  }
  return m->u.words + i;
}

// unix.Memory:load(word_index:int)
//     âââ int
static int LuaUnixMemoryLoad(lua_State *L) {
  lua_pushinteger(L, atomic_load_explicit(GetWord(L), memory_order_relaxed));
  return 1;
}

// unix.Memory:store(word_index:int, value:int)
static int LuaUnixMemoryStore(lua_State *L) {
  atomic_store_explicit(GetWord(L), luaL_checkinteger(L, 3),
                        memory_order_relaxed);
  return 0;
}

// unix.Memory:xchg(word_index:int, value:int)
//     âââ int
static int LuaUnixMemoryXchg(lua_State *L) {
  lua_pushinteger(L, atomic_exchange(GetWord(L), luaL_checkinteger(L, 3)));
  return 1;
}

// unix.Memory:cmpxchg(word_index:int, old:int, new:int)
//     âââ success:bool, old:int
static int LuaUnixMemoryCmpxchg(lua_State *L) {
  long old = luaL_checkinteger(L, 3);
  lua_pushboolean(L, atomic_compare_exchange_strong(GetWord(L), &old,
                                                    luaL_checkinteger(L, 4)));
  lua_pushinteger(L, old);
  return 2;
}

// unix.Memory:add(word_index:int, value:int)
//     âââ old:int
static int LuaUnixMemoryAdd(lua_State *L) {
  lua_pushinteger(L, atomic_fetch_add(GetWord(L), luaL_checkinteger(L, 3)));
  return 1;
}

// unix.Memory:and(word_index:int, value:int)
//     âââ old:int
static int LuaUnixMemoryAnd(lua_State *L) {
  lua_pushinteger(L, atomic_fetch_and(GetWord(L), luaL_checkinteger(L, 3)));
  return 1;
}

// unix.Memory:or(word_index:int, value:int)
//     âââ old:int
static int LuaUnixMemoryOr(lua_State *L) {
  lua_pushinteger(L, atomic_fetch_or(GetWord(L), luaL_checkinteger(L, 3)));
  return 1;
}

// unix.Memory:xor(word_index:int, value:int)
//     âââ old:int
static int LuaUnixMemoryXor(lua_State *L) {
  lua_pushinteger(L, atomic_fetch_xor(GetWord(L), luaL_checkinteger(L, 3)));
  return 1;
}

// unix.Memory:wait(word_index:int, expect:int[, abs_deadline:int[, nanos:int]])
//     âââ 0
//     âââ nil, unix.Errno(unix.EINTR)
//     âââ nil, unix.Errno(unix.EAGAIN)
//     âââ nil, unix.Errno(unix.ETIMEDOUT)
static int LuaUnixMemoryWait(lua_State *L) {
  lua_Integer expect;
  int rc, olderr = errno;
  struct timespec ts, now, *deadline;
  expect = luaL_checkinteger(L, 3);
  if (!(INT32_MIN <= expect && expect <= INT32_MAX)) {
    luaL_argerror(L, 3, "must be an int32_t");
    unreachable;
  }
  if (lua_isnoneornil(L, 4)) {
    deadline = 0;  // wait forever
  } else {
    ts.tv_sec = luaL_checkinteger(L, 4);
    ts.tv_nsec = luaL_optinteger(L, 5, 0);
    deadline = &ts;
  }
  BEGIN_CANCELLATION_POINT;
  rc = nsync_futex_wait_((atomic_int *)GetWord(L), expect,
                         PTHREAD_PROCESS_SHARED, deadline);
  END_CANCELLATION_POINT;
  if (rc < 0) errno = -rc, rc = -1;
  return SysretInteger(L, "futex_wait", olderr, rc);
}

// unix.Memory:wake(index:int[, count:int])
//     âââ woken:int
static int LuaUnixMemoryWake(lua_State *L) {
  int count, woken;
  count = luaL_optinteger(L, 3, INT_MAX);
  woken = nsync_futex_wake_((atomic_int *)GetWord(L), count,
                            PTHREAD_PROCESS_SHARED);
  _npassert(woken >= 0);
  return ReturnInteger(L, woken);
}

static int LuaUnixMemoryTostring(lua_State *L) {
  char s[128];
  struct Memory *m;
  m = luaL_checkudata(L, 1, "unix.Memory");
  snprintf(s, sizeof(s), "unix.Memory(%zu)", m->size);
  lua_pushstring(L, s);
  return 1;
}

static int LuaUnixMemoryGc(lua_State *L) {
  struct Memory *m;
  m = luaL_checkudata(L, 1, "unix.Memory");
  if (m->u.bytes) {
    _npassert(!munmap(m->map, m->mapsize));
    m->u.bytes = 0;
  }
  return 0;
}

static const luaL_Reg kLuaUnixMemoryMeth[] = {
    {"read", LuaUnixMemoryRead},        //
    {"write", LuaUnixMemoryWrite},      //
    {"load", LuaUnixMemoryLoad},        //
    {"store", LuaUnixMemoryStore},      //
    {"xchg", LuaUnixMemoryXchg},        //
    {"cmpxchg", LuaUnixMemoryCmpxchg},  //
    {"fetch_add", LuaUnixMemoryAdd},    //
    {"fetch_and", LuaUnixMemoryAnd},    //
    {"fetch_or", LuaUnixMemoryOr},      //
    {"fetch_xor", LuaUnixMemoryXor},    //
    {"wait", LuaUnixMemoryWait},        //
    {"wake", LuaUnixMemoryWake},        //
    {0},                                //
};

static const luaL_Reg kLuaUnixMemoryMeta[] = {
    {"__tostring", LuaUnixMemoryTostring},  //
    {"__repr", LuaUnixMemoryTostring},      //
    {"__gc", LuaUnixMemoryGc},              //
    {0},                                    //
};

static void LuaUnixMemoryObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Memory");
  luaL_setfuncs(L, kLuaUnixMemoryMeta, 0);
  luaL_newlibtable(L, kLuaUnixMemoryMeth);
  luaL_setfuncs(L, kLuaUnixMemoryMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

static int LuaUnixMapshared(lua_State *L) {
  char *p;
  size_t n, c, g;
  struct Memory *m;
  pthread_mutexattr_t mattr;
  n = luaL_checkinteger(L, 1);
  if (!n) {
    luaL_error(L, "can't map empty region");
    unreachable;
  }
  if (n % sizeof(long)) {
    luaL_error(L, "size must be multiple of word size");
    unreachable;
  }
  if (!IsLegalSize(n)) {
    luaL_error(L, "map size too big");
    unreachable;
  }
  c = n;
  c += sizeof(*m->lock);
  g = sysconf(_SC_PAGESIZE);
  c = ROUNDUP(c, g);
  if (!(p = _mapshared(c))) {
    luaL_error(L, "out of memory");
    unreachable;
  }
  m = lua_newuserdatauv(L, sizeof(*m), 1);
  luaL_setmetatable(L, "unix.Memory");
  m->u.bytes = p + sizeof(*m->lock);
  m->size = n;
  m->map = p;
  m->mapsize = c;
  m->lock = (pthread_mutex_t *)p;
  pthread_mutexattr_init(&mattr);
  pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
  pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
  pthread_mutex_init(m->lock, &mattr);
  pthread_mutexattr_destroy(&mattr);
  return 1;
}

////////////////////////////////////////////////////////////////////////////////
// unix.Sigset object

// unix.Sigset(sig:int, ...)
//     âââ unix.Sigset
static int LuaUnixSigset(lua_State *L) {
  int i, n;
  lua_Integer sig;
  struct sigset set;
  sigemptyset(&set);
  n = lua_gettop(L);
  for (i = 1; i <= n; ++i) {
    sig = luaL_checkinteger(L, i);
    if (1 <= sig && sig <= NSIG) {
      set.__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
    }
  }
  LuaPushSigset(L, set);
  return 1;
}

// unix.Sigset:add(sig:int)
static int LuaUnixSigsetAdd(lua_State *L) {
  lua_Integer sig;
  struct sigset *set;
  set = luaL_checkudata(L, 1, "unix.Sigset");
  sig = luaL_checkinteger(L, 2);
  if (1 <= sig && sig <= NSIG) {
    set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
  }
  return 0;
}

// unix.Sigset:remove(sig:int)
static int LuaUnixSigsetRemove(lua_State *L) {
  lua_Integer sig;
  struct sigset *set;
  set = luaL_checkudata(L, 1, "unix.Sigset");
  sig = luaL_checkinteger(L, 2);
  if (1 <= sig && sig <= NSIG) {
    set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63));
  }
  return 0;
}

// unix.Sigset:fill()
static int LuaUnixSigsetFill(lua_State *L) {
  struct sigset *set;
  set = luaL_checkudata(L, 1, "unix.Sigset");
  memset(set, -1, sizeof(*set));
  return 0;
}

// unix.Sigset:clear()
static int LuaUnixSigsetClear(lua_State *L) {
  struct sigset *set;
  set = luaL_checkudata(L, 1, "unix.Sigset");
  bzero(set, sizeof(*set));
  return 0;
}

// unix.Sigset:contains(sig:int)
//     âââ bool
static int LuaUnixSigsetContains(lua_State *L) {
  lua_Integer sig;
  struct sigset *set;
  set = luaL_checkudata(L, 1, "unix.Sigset");
  sig = luaL_checkinteger(L, 2);
  return ReturnBoolean(
      L, (1 <= sig && sig <= NSIG)
             ? !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63)))
             : false);
}

static int LuaUnixSigsetTostring(lua_State *L) {
  char *b = 0;
  int sig, first;
  struct sigset *ss;
  ss = luaL_checkudata(L, 1, "unix.Sigset");
  appends(&b, "unix.Sigset");
  appendw(&b, '(');
  for (sig = first = 1; sig <= NSIG; ++sig) {
    if (sigismember(ss, sig) == 1) {
      if (!first) {
        appendw(&b, READ16LE(", "));
      } else {
        first = 0;
      }
      appendw(&b, READ64LE("unix.\0\0"));
      appends(&b, strsignal(sig));
    }
  }
  appendw(&b, ')');
  lua_pushlstring(L, b, appendz(b).i);
  free(b);
  return 1;
}

static const luaL_Reg kLuaUnixSigsetMeth[] = {
    {"add", LuaUnixSigsetAdd},            //
    {"fill", LuaUnixSigsetFill},          //
    {"clear", LuaUnixSigsetClear},        //
    {"remove", LuaUnixSigsetRemove},      //
    {"contains", LuaUnixSigsetContains},  //
    {0},                                  //
};

static const luaL_Reg kLuaUnixSigsetMeta[] = {
    {"__tostring", LuaUnixSigsetTostring},  //
    {"__repr", LuaUnixSigsetTostring},      //
    {0},                                    //
};

static void LuaUnixSigsetObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Sigset");
  luaL_setfuncs(L, kLuaUnixSigsetMeta, 0);
  luaL_newlibtable(L, kLuaUnixSigsetMeth);
  luaL_setfuncs(L, kLuaUnixSigsetMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// unix.Dir object

static DIR **GetUnixDirSelf(lua_State *L) {
  return luaL_checkudata(L, 1, "unix.Dir");
}

static DIR *GetDirOrDie(lua_State *L) {
  DIR **dirp;
  dirp = GetUnixDirSelf(L);
  if (*dirp) return *dirp;
  luaL_argerror(L, 1, "unix.UnixDir is closed");
  unreachable;
}

// unix.Dir:close()
//     âââ true
//     âââ nil, unix.Errno
static int LuaUnixDirClose(lua_State *L) {
  DIR **dirp;
  int rc, olderr;
  dirp = GetUnixDirSelf(L);
  if (*dirp) {
    olderr = errno;
    rc = closedir(*dirp);
    *dirp = 0;
    return SysretBool(L, "closedir", olderr, rc);
  } else {
    lua_pushboolean(L, true);
    return 1;
  }
}

// unix.Dir:read()
//     âââ name:str, kind:int, ino:int, off:int
//     âââ nil
static int LuaUnixDirRead(lua_State *L) {
  struct dirent *ent;
  if ((ent = readdir(GetDirOrDie(L)))) {
    lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name)));
    lua_pushinteger(L, ent->d_type);
    lua_pushinteger(L, ent->d_ino);
    lua_pushinteger(L, ent->d_off);
    return 4;
  } else {
    // end of directory stream condition
    // we make the assumption getdents() won't fail
    lua_pushnil(L);
    return 1;
  }
}

// unix.Dir:fd()
//     âââ fd:int
//     âââ nil, unix.Errno
static int LuaUnixDirFd(lua_State *L) {
  int fd, olderr = errno;
  fd = dirfd(GetDirOrDie(L));
  if (fd != -1) {
    lua_pushinteger(L, fd);
    return 1;
  } else {
    return LuaUnixSysretErrno(L, "dirfd", olderr);
  }
}

// unix.Dir:tell()
//     âââ off:int
//     âââ nil, unix.Errno
static int LuaUnixDirTell(lua_State *L) {
  int olderr = errno;
  return SysretInteger(L, "telldir", olderr, telldir(GetDirOrDie(L)));
}

// unix.Dir:rewind()
static int LuaUnixDirRewind(lua_State *L) {
  rewinddir(GetDirOrDie(L));
  return 0;
}

static int ReturnDir(lua_State *L, DIR *dir) {
  DIR **dirp;
  dirp = lua_newuserdatauv(L, sizeof(*dirp), 1);
  luaL_setmetatable(L, "unix.Dir");
  *dirp = dir;
  return 1;
}

// unix.opendir(path:str)
//     âââ state:unix.Dir
//     âââ nil, unix.Errno
static int LuaUnixOpendir(lua_State *L) {
  DIR *dir;
  int olderr = errno;
  if ((dir = opendir(luaL_checkstring(L, 1)))) {
    return ReturnDir(L, dir);
  } else {
    return LuaUnixSysretErrno(L, "opendir", olderr);
  }
}

// unix.fdopendir(fd:int)
//     âââ next:function, state:unix.Dir
//     âââ nil, unix.Errno
static int LuaUnixFdopendir(lua_State *L) {
  DIR *dir;
  int olderr = errno;
  if ((dir = fdopendir(luaL_checkinteger(L, 1)))) {
    return ReturnDir(L, dir);
  } else {
    return LuaUnixSysretErrno(L, "fdopendir", olderr);
  }
}

static const luaL_Reg kLuaUnixDirMeth[] = {
    {"close", LuaUnixDirClose},    //
    {"read", LuaUnixDirRead},      //
    {"fd", LuaUnixDirFd},          //
    {"tell", LuaUnixDirTell},      //
    {"rewind", LuaUnixDirRewind},  //
    {0},                           //
};

static const luaL_Reg kLuaUnixDirMeta[] = {
    {"__call", LuaUnixDirRead},  //
    {"__gc", LuaUnixDirClose},   //
    {0},                         //
};

static void LuaUnixDirObj(lua_State *L) {
  luaL_newmetatable(L, "unix.Dir");
  luaL_setfuncs(L, kLuaUnixDirMeta, 0);
  luaL_newlibtable(L, kLuaUnixDirMeth);
  luaL_setfuncs(L, kLuaUnixDirMeth, 0);
  lua_setfield(L, -2, "__index");
  lua_pop(L, 1);
}

////////////////////////////////////////////////////////////////////////////////
// UNIX module

static const luaL_Reg kLuaUnix[] = {
    {"S_ISBLK", LuaUnixSisblk},           // is st:mode() a block device?
    {"S_ISCHR", LuaUnixSischr},           // is st:mode() a character device?
    {"S_ISDIR", LuaUnixSisdir},           // is st:mode() a directory?
    {"S_ISFIFO", LuaUnixSisfifo},         // is st:mode() a fifo?
    {"S_ISLNK", LuaUnixSislnk},           // is st:mode() a symbolic link?
    {"S_ISREG", LuaUnixSisreg},           // is st:mode() a regular file?
    {"S_ISSOCK", LuaUnixSissock},         // is st:mode() a socket?
    {"Sigset", LuaUnixSigset},            // creates signal bitmask
    {"WEXITSTATUS", LuaUnixWexitstatus},  // gets exit status from wait status
    {"WIFEXITED", LuaUnixWifexited},      // gets exit code from wait status
    {"WIFSIGNALED", LuaUnixWifsignaled},  // determines if died due to signal
    {"WTERMSIG", LuaUnixWtermsig},        // gets the signal code
    {"accept", LuaUnixAccept},            // create client fd for client
    {"access", LuaUnixAccess},            // check my file authorization
    {"bind", LuaUnixBind},                // reserve network interface address
    {"chdir", LuaUnixChdir},              // change directory
    {"chmod", LuaUnixChmod},              // change mode of file
    {"chown", LuaUnixChown},              // change owner of file
    {"chroot", LuaUnixChroot},            // change root directory
    {"clock_gettime", LuaUnixGettime},    // get timestamp w/ nano precision
    {"close", LuaUnixClose},              // close file or socket
    {"commandv", LuaUnixCommandv},        // resolve program on $PATH
    {"connect", LuaUnixConnect},          // connect to remote address
    {"dup", LuaUnixDup},                  // copy fd to lowest empty slot
    {"environ", LuaUnixEnviron},          // get environment variables
    {"execve", LuaUnixExecve},            // replace process with program
    {"exit", LuaUnixExit},                // exit w/o atexit
    {"fcntl", LuaUnixFcntl},              // manipulate file descriptor
    {"fdatasync", LuaUnixFdatasync},      // flush open file w/o metadata
    {"fdopendir", LuaUnixFdopendir},      // read directory entry list
    {"fork", LuaUnixFork},                // make child process via mitosis
    {"fstat", LuaUnixFstat},              // get file info from fd
    {"fstatfs", LuaUnixFstatfs},          // get filesystem info from fd
    {"fsync", LuaUnixFsync},              // flush open file
    {"ftruncate", LuaUnixFtruncate},      // shrink or extend file medium
    {"futimens", LuaUnixFutimens},        // change access/modified time
    {"getcwd", LuaUnixGetcwd},            // get current directory
    {"getegid", LuaUnixGetegid},          // get effective group id of process
    {"geteuid", LuaUnixGeteuid},          // get effective user id of process
    {"getgid", LuaUnixGetgid},            // get real group id of process
    {"gethostname", LuaUnixGethostname},  // get hostname of this machine
    {"getpeername", LuaUnixGetpeername},  // get address of remote end
    {"getpgid", LuaUnixGetpgid},          // get process group id of pid
    {"getpgrp", LuaUnixGetpgrp},          // get process group id
    {"getpid", LuaUnixGetpid},            // get id of this process
    {"getppid", LuaUnixGetppid},          // get parent process id
    {"getrlimit", LuaUnixGetrlimit},      // query resource limits
    {"getrusage", LuaUnixGetrusage},      // query resource usages
    {"getsid", LuaUnixGetsid},            // get session id of pid
    {"getsockname", LuaUnixGetsockname},  // get address of local end
    {"getsockopt", LuaUnixGetsockopt},    // get socket tunings
    {"getuid", LuaUnixGetuid},            // get real user id of process
    {"gmtime", LuaUnixGmtime},            // destructure unix timestamp
    {"isatty", LuaUnixIsatty},            // detects pseudoteletypewriters
    {"kill", LuaUnixKill},                // signal child process
    {"link", LuaUnixLink},                // create hard link
    {"listen", LuaUnixListen},            // begin listening for clients
    {"localtime", LuaUnixLocaltime},      // localize unix timestamp
    {"lseek", LuaUnixLseek},              // seek in file
    {"major", LuaUnixMajor},              // extract device info
    {"makedirs", LuaUnixMakedirs},        // make directory and parents too
    {"mapshared", LuaUnixMapshared},      // mmap(MAP_SHARED) w/ mutex+atomics
    {"minor", LuaUnixMinor},              // extract device info
    {"mkdir", LuaUnixMkdir},              // make directory
    {"nanosleep", LuaUnixNanosleep},      // sleep w/ nano precision
    {"open", LuaUnixOpen},                // open file fd at lowest slot
    {"opendir", LuaUnixOpendir},          // read directory entry list
    {"pipe", LuaUnixPipe},                // create two anon fifo fds
    {"pledge", LuaUnixPledge},            // enables syscall sandbox
    {"poll", LuaUnixPoll},                // waits for file descriptor events
    {"raise", LuaUnixRaise},              // signal this process
    {"read", LuaUnixRead},                // read from file or socket
    {"readlink", LuaUnixReadlink},        // reads symbolic link
    {"realpath", LuaUnixRealpath},        // abspath without dots/symlinks
    {"recv", LuaUnixRecv},                // receive tcp from some address
    {"recvfrom", LuaUnixRecvfrom},        // receive udp from some address
    {"rename", LuaUnixRename},            // rename file or directory
    {"rmdir", LuaUnixRmdir},              // remove empty directory
    {"rmrf", LuaUnixRmrf},                // remove file recursively
    {"sched_yield", LuaUnixSchedYield},   // relinquish scheduled quantum
    {"send", LuaUnixSend},                // send tcp to some address
    {"sendto", LuaUnixSendto},            // send udp to some address
    {"setfsgid", LuaUnixSetfsgid},        // set/get group id for fs ops
    {"setfsuid", LuaUnixSetfsuid},        // set/get user id for fs ops
    {"setgid", LuaUnixSetgid},            // set real group id of process
    {"setitimer", LuaUnixSetitimer},      // set alarm clock
    {"setpgid", LuaUnixSetpgid},          // set process group id for pid
    {"setpgrp", LuaUnixSetpgrp},          // sets process group id
    {"setresgid", LuaUnixSetresgid},      // sets real/effective/saved gids
    {"setresuid", LuaUnixSetresuid},      // sets real/effective/saved uids
    {"setrlimit", LuaUnixSetrlimit},      // prevent cpu memory bombs
    {"setsid", LuaUnixSetsid},            // create a new session id
    {"setsockopt", LuaUnixSetsockopt},    // tune socket options
    {"setuid", LuaUnixSetuid},            // set real user id of process
    {"shutdown", LuaUnixShutdown},        // make socket half empty or full
    {"sigaction", LuaUnixSigaction},      // install signal handler
    {"sigpending", LuaUnixSigpending},    // get pending signals
    {"sigprocmask", LuaUnixSigprocmask},  // change signal mask
    {"sigsuspend", LuaUnixSigsuspend},    // wait for signal
    {"siocgifconf", LuaUnixSiocgifconf},  // get list of network interfaces
    {"socket", LuaUnixSocket},            // create network communication fd
    {"socketpair", LuaUnixSocketpair},    // create bidirectional pipe
    {"stat", LuaUnixStat},                // get file info from path
    {"statfs", LuaUnixStatfs},            // get filesystem info from path
    {"strsignal", LuaUnixStrsignal},      // turn signal into string
    {"symlink", LuaUnixSymlink},          // create symbolic link
    {"sync", LuaUnixSync},                // flushes files and disks
    {"syslog", LuaUnixSyslog},            // logs to system log
    {"tiocgwinsz", LuaUnixTiocgwinsz},    // pseudoteletypewriter dimensions
    {"tmpfd", LuaUnixTmpfd},              // create anonymous file
    {"truncate", LuaUnixTruncate},        // shrink or extend file medium
    {"umask", LuaUnixUmask},              // set default file mask
    {"unlink", LuaUnixUnlink},            // remove file
    {"unveil", LuaUnixUnveil},            // filesystem sandboxing
    {"utimensat", LuaUnixUtimensat},      // change access/modified time
    {"wait", LuaUnixWait},                // wait for child to change status
    {"write", LuaUnixWrite},              // write to file or socket
    {0},                                  //
};

static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) {
  int i;
  char b[64], *p;
  p = stpcpy(b, pfx);
  for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) {
    stpcpy(p, MAGNUM_STRING(ms, i));
    LuaSetIntField(L, b, MAGNUM_NUMBER(ms, i));
  }
}

int LuaUnix(lua_State *L) {
  GL = L;
  luaL_newlib(L, kLuaUnix);
  LuaUnixSigsetObj(L);
  LuaUnixRusageObj(L);
  LuaUnixStatfsObj(L);
  LuaUnixMemoryObj(L);
  LuaUnixErrnoObj(L);
  LuaUnixStatObj(L);
  LuaUnixDirObj(L);
  lua_newtable(L);
  lua_setglobal(L, "__signal_handlers");

  LoadMagnums(L, kErrnoNames, "");
  LoadMagnums(L, kOpenFlags, "O_");
  LoadMagnums(L, kSignalNames, "");
  LoadMagnums(L, kIpOptnames, "IP_");
  LoadMagnums(L, kTcpOptnames, "TCP_");
  LoadMagnums(L, kSockOptnames, "SO_");
  LoadMagnums(L, kClockNames, "CLOCK_");

  // seek() whence
  LuaSetIntField(L, "SEEK_SET", SEEK_SET);
  LuaSetIntField(L, "SEEK_CUR", SEEK_CUR);
  LuaSetIntField(L, "SEEK_END", SEEK_END);

  // fcntl() stuff
  LuaSetIntField(L, "F_GETFD", F_GETFD);
  LuaSetIntField(L, "F_SETFD", F_SETFD);
  LuaSetIntField(L, "F_GETFL", F_GETFL);
  LuaSetIntField(L, "F_SETFL", F_SETFL);
  LuaSetIntField(L, "F_UNLCK", F_UNLCK);
  LuaSetIntField(L, "F_RDLCK", F_RDLCK);
  LuaSetIntField(L, "F_WRLCK", F_WRLCK);
  LuaSetIntField(L, "F_SETLK", F_SETLK);
  LuaSetIntField(L, "F_SETLKW", F_SETLKW);
  LuaSetIntField(L, "F_GETLK", F_GETLK);
  LuaSetIntField(L, "FD_CLOEXEC", FD_CLOEXEC);

  // access() mode
  LuaSetIntField(L, "R_OK", R_OK);
  LuaSetIntField(L, "W_OK", W_OK);
  LuaSetIntField(L, "X_OK", X_OK);
  LuaSetIntField(L, "F_OK", F_OK);

  // rlimit() resources
  LuaSetIntField(L, "RLIMIT_AS", RLIMIT_AS);
  LuaSetIntField(L, "RLIMIT_RSS", RLIMIT_RSS);
  LuaSetIntField(L, "RLIMIT_CPU", RLIMIT_CPU);
  LuaSetIntField(L, "RLIMIT_FSIZE", RLIMIT_FSIZE);
  LuaSetIntField(L, "RLIMIT_NPROC", RLIMIT_NPROC);
  LuaSetIntField(L, "RLIMIT_NOFILE", RLIMIT_NOFILE);

  // wait() options
  LuaSetIntField(L, "WNOHANG", WNOHANG);

  // socket() family
  LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC);
  LuaSetIntField(L, "AF_UNIX", AF_UNIX);
  LuaSetIntField(L, "AF_INET", AF_INET);

  // socket() type
  LuaSetIntField(L, "SOCK_STREAM", SOCK_STREAM);
  LuaSetIntField(L, "SOCK_DGRAM", SOCK_DGRAM);
  LuaSetIntField(L, "SOCK_RAW", SOCK_RAW);
  LuaSetIntField(L, "SOCK_RDM", SOCK_RDM);
  LuaSetIntField(L, "SOCK_SEQPACKET", SOCK_SEQPACKET);
  LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC);
  LuaSetIntField(L, "SOCK_NONBLOCK", SOCK_NONBLOCK);

  // socket() protocol
  LuaSetIntField(L, "IPPROTO_IP", IPPROTO_IP);
  LuaSetIntField(L, "IPPROTO_ICMP", IPPROTO_ICMP);
  LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP);
  LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP);
  LuaSetIntField(L, "IPPROTO_RAW", IPPROTO_RAW);

  // shutdown() how
  LuaSetIntField(L, "SHUT_RD", SHUT_RD);
  LuaSetIntField(L, "SHUT_WR", SHUT_WR);
  LuaSetIntField(L, "SHUT_RDWR", SHUT_RDWR);

  // recvfrom() / sendto() flags
  LuaSetIntField(L, "MSG_WAITALL", MSG_WAITALL);
  LuaSetIntField(L, "MSG_DONTROUTE", MSG_DONTROUTE);
  LuaSetIntField(L, "MSG_PEEK", MSG_PEEK);
  LuaSetIntField(L, "MSG_OOB", MSG_OOB);
  LuaSetIntField(L, "MSG_NOSIGNAL", MSG_NOSIGNAL);
  LuaSetIntField(L, "MSG_MORE", MSG_MORE);

  // readdir() type
  LuaSetIntField(L, "DT_UNKNOWN", DT_UNKNOWN);
  LuaSetIntField(L, "DT_REG", DT_REG);
  LuaSetIntField(L, "DT_DIR", DT_DIR);
  LuaSetIntField(L, "DT_BLK", DT_BLK);
  LuaSetIntField(L, "DT_LNK", DT_LNK);
  LuaSetIntField(L, "DT_CHR", DT_CHR);
  LuaSetIntField(L, "DT_FIFO", DT_FIFO);
  LuaSetIntField(L, "DT_SOCK", DT_SOCK);

  // poll() flags
  LuaSetIntField(L, "POLLIN", POLLIN);
  LuaSetIntField(L, "POLLPRI", POLLPRI);
  LuaSetIntField(L, "POLLOUT", POLLOUT);
  LuaSetIntField(L, "POLLERR", POLLERR);
  LuaSetIntField(L, "POLLHUP", POLLHUP);
  LuaSetIntField(L, "POLLNVAL", POLLNVAL);
  LuaSetIntField(L, "POLLRDBAND", POLLRDBAND);
  LuaSetIntField(L, "POLLRDNORM", POLLRDNORM);
  LuaSetIntField(L, "POLLWRBAND", POLLWRBAND);
  LuaSetIntField(L, "POLLWRNORM", POLLWRNORM);
  LuaSetIntField(L, "POLLRDHUP", POLLRDHUP);

  // i/o options
  LuaSetIntField(L, "AT_FDCWD", AT_FDCWD);
  LuaSetIntField(L, "AT_EACCESS", AT_EACCESS);
  LuaSetIntField(L, "AT_SYMLINK_NOFOLLOW", AT_SYMLINK_NOFOLLOW);

  // sigprocmask() handlers
  LuaSetIntField(L, "SIG_BLOCK", SIG_BLOCK);
  LuaSetIntField(L, "SIG_UNBLOCK", SIG_UNBLOCK);
  LuaSetIntField(L, "SIG_SETMASK", SIG_SETMASK);

  // sigaction() handlers
  LuaSetIntField(L, "SIG_DFL", (intptr_t)SIG_DFL);
  LuaSetIntField(L, "SIG_IGN", (intptr_t)SIG_IGN);

  // utimensat() magnums
  LuaSetIntField(L, "UTIME_NOW", UTIME_NOW);
  LuaSetIntField(L, "UTIME_OMIT", UTIME_OMIT);

  // setitimer() which
  LuaSetIntField(L, "ITIMER_REAL", ITIMER_REAL);  // portable
  LuaSetIntField(L, "ITIMER_PROF", ITIMER_PROF);
  LuaSetIntField(L, "ITIMER_VIRTUAL", ITIMER_VIRTUAL);

  // syslog() stuff
  LuaSetIntField(L, "LOG_EMERG", LOG_EMERG);
  LuaSetIntField(L, "LOG_ALERT", LOG_ALERT);
  LuaSetIntField(L, "LOG_CRIT", LOG_CRIT);
  LuaSetIntField(L, "LOG_ERR", LOG_ERR);
  LuaSetIntField(L, "LOG_WARNING", LOG_WARNING);
  LuaSetIntField(L, "LOG_NOTICE", LOG_NOTICE);
  LuaSetIntField(L, "LOG_INFO", LOG_INFO);
  LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG);

  // setsockopt() level
  LuaSetIntField(L, "SOL_IP", SOL_IP);
  LuaSetIntField(L, "SOL_SOCKET", SOL_SOCKET);
  LuaSetIntField(L, "SOL_TCP", SOL_TCP);
  LuaSetIntField(L, "SOL_UDP", SOL_UDP);

  // sigaction() flags
  LuaSetIntField(L, "SA_RESTART", SA_RESTART);
  LuaSetIntField(L, "SA_RESETHAND", SA_RESETHAND);
  LuaSetIntField(L, "SA_NODEFER", SA_NODEFER);
  LuaSetIntField(L, "SA_NOCLDWAIT", SA_NOCLDWAIT);
  LuaSetIntField(L, "SA_NOCLDSTOP", SA_NOCLDSTOP);

  // getrusage() who
  LuaSetIntField(L, "RUSAGE_SELF", RUSAGE_SELF);
  LuaSetIntField(L, "RUSAGE_THREAD", RUSAGE_THREAD);
  LuaSetIntField(L, "RUSAGE_CHILDREN", RUSAGE_CHILDREN);
  LuaSetIntField(L, "RUSAGE_BOTH", RUSAGE_BOTH);

  LuaSetIntField(L, "ARG_MAX", __arg_max());
  LuaSetIntField(L, "BUFSIZ", BUFSIZ);
  LuaSetIntField(L, "CLK_TCK", CLK_TCK);
  LuaSetIntField(L, "NAME_MAX", _NAME_MAX);
  LuaSetIntField(L, "NSIG", _NSIG);
  LuaSetIntField(L, "PATH_MAX", _PATH_MAX);
  LuaSetIntField(L, "PIPE_BUF", PIPE_BUF);

  // pledge() flags
  LuaSetIntField(L, "PLEDGE_PENALTY_KILL_THREAD", PLEDGE_PENALTY_KILL_THREAD);
  LuaSetIntField(L, "PLEDGE_PENALTY_KILL_PROCESS", PLEDGE_PENALTY_KILL_PROCESS);
  LuaSetIntField(L, "PLEDGE_PENALTY_RETURN_EPERM", PLEDGE_PENALTY_RETURN_EPERM);
  LuaSetIntField(L, "PLEDGE_STDERR_LOGGING", PLEDGE_STDERR_LOGGING);

  // statfs::f_flags
  LuaSetIntField(L, "ST_RDONLY", ST_RDONLY);
  LuaSetIntField(L, "ST_NOSUID", ST_NOSUID);
  LuaSetIntField(L, "ST_NODEV", ST_NODEV);
  LuaSetIntField(L, "ST_NOEXEC", ST_NOEXEC);
  LuaSetIntField(L, "ST_SYNCHRONOUS", ST_SYNCHRONOUS);
  LuaSetIntField(L, "ST_NOATIME", ST_NOATIME);
  LuaSetIntField(L, "ST_RELATIME", ST_RELATIME);
  LuaSetIntField(L, "ST_APPEND", ST_APPEND);
  LuaSetIntField(L, "ST_IMMUTABLE", ST_IMMUTABLE);
  LuaSetIntField(L, "ST_MANDLOCK", ST_MANDLOCK);
  LuaSetIntField(L, "ST_NODIRATIME", ST_NODIRATIME);
  LuaSetIntField(L, "ST_WRITE", ST_WRITE);

  return 1;
}
