diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index d534dca64..e6e08df84 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -121,7 +121,7 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { if (rc == -1 && errno == EFAULT) { STRACE("pwritev(%d, %p, %d, %'ld) → %'zd% m", fd, iov, iovlen, off, rc); } else { - kprintf(STRACE_PROLOGUE "readv(%d, ", fd); + kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); kprintf(", %d, %'ld) → %'ld% m%n", iovlen, off, rc); } diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 7a32ddf6d..296fb5353 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -77,7 +77,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { if (rc == -1 && errno == EFAULT) { STRACE("writev(%d, %p, %d) → %'zd% m", fd, iov, iovlen, rc); } else { - kprintf(STRACE_PROLOGUE "readv(%d, ", fd); + kprintf(STRACE_PROLOGUE "writev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); kprintf(", %d) → %'ld% m%n", iovlen, rc); } diff --git a/libc/sysv/consts/RB_DISABLE_CAD.S b/libc/sysv/consts/RB_DISABLE_CAD.S index e7e383360..47830b0ed 100644 --- a/libc/sysv/consts/RB_DISABLE_CAD.S +++ b/libc/sysv/consts/RB_DISABLE_CAD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_DISABLE_CAD,0,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff +.syscon reboot,RB_DISABLE_CAD,0,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/RB_ENABLE_CAD.S b/libc/sysv/consts/RB_ENABLE_CAD.S index 32f69564c..1e36b6494 100644 --- a/libc/sysv/consts/RB_ENABLE_CAD.S +++ b/libc/sysv/consts/RB_ENABLE_CAD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_ENABLE_CAD,0x89abcdef,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff +.syscon reboot,RB_ENABLE_CAD,0x89abcdef,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/RB_KEXEC.S b/libc/sysv/consts/RB_KEXEC.S index ae434fa28..be957a104 100644 --- a/libc/sysv/consts/RB_KEXEC.S +++ b/libc/sysv/consts/RB_KEXEC.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_KEXEC,0x45584543,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff +.syscon reboot,RB_KEXEC,0x45584543,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/RB_POWERDOWN.S b/libc/sysv/consts/RB_POWERDOWN.S index 9758eebe7..6163679fa 100644 --- a/libc/sysv/consts/RB_POWERDOWN.S +++ b/libc/sysv/consts/RB_POWERDOWN.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_POWERDOWN,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWERDOWN,0x4321fedc,-1,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_POWEROFF.S b/libc/sysv/consts/RB_POWEROFF.S index 56e6b566c..1524d9e28 100644 --- a/libc/sysv/consts/RB_POWEROFF.S +++ b/libc/sysv/consts/RB_POWEROFF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_POWEROFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWEROFF,0x4321fedc,-1,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_POWER_OFF.S b/libc/sysv/consts/RB_POWER_OFF.S index e260da637..c8db8d8c3 100644 --- a/libc/sysv/consts/RB_POWER_OFF.S +++ b/libc/sysv/consts/RB_POWER_OFF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_POWER_OFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWER_OFF,0x4321fedc,-1,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_SW_SUSPEND.S b/libc/sysv/consts/RB_SW_SUSPEND.S index c2528864c..721706413 100644 --- a/libc/sysv/consts/RB_SW_SUSPEND.S +++ b/libc/sysv/consts/RB_SW_SUSPEND.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon reboot,RB_SW_SUSPEND,0xd000fce2,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xd000fce2 +.syscon reboot,RB_SW_SUSPEND,0xd000fce2,-1,-1,-1,-1,0xd000fce2 diff --git a/libc/sysv/consts/reboot.h b/libc/sysv/consts/reboot.h index f72bcadbc..ecd6c2c4b 100644 --- a/libc/sysv/consts/reboot.h +++ b/libc/sysv/consts/reboot.h @@ -4,17 +4,17 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern const uint32_t RB_AUTOBOOT; -extern const uint32_t RB_POWER_OFF; -extern const uint32_t RB_POWERDOWN; -extern const uint32_t RB_POWEROFF; -extern const uint32_t RB_HALT_SYSTEM; -extern const uint32_t RB_HALT; -extern const uint32_t RB_SW_SUSPEND; -extern const uint32_t RB_KEXEC; -extern const uint32_t RB_ENABLE_CAD; -extern const uint32_t RB_DISABLE_CAD; -extern const uint32_t RB_NOSYNC; +extern const int RB_AUTOBOOT; +extern const int RB_POWER_OFF; +extern const int RB_POWERDOWN; +extern const int RB_POWEROFF; +extern const int RB_HALT_SYSTEM; +extern const int RB_HALT; +extern const int RB_SW_SUSPEND; +extern const int RB_KEXEC; +extern const int RB_ENABLE_CAD; +extern const int RB_DISABLE_CAD; +extern const int RB_NOSYNC; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/net/demo/unix2.lua b/tool/net/demo/unix2.lua new file mode 100644 index 000000000..de95a2bca --- /dev/null +++ b/tool/net/demo/unix2.lua @@ -0,0 +1,26 @@ +-- example of how to run the ls command +-- and pipe its output to the http user +local unix = require "unix" +ls = unix.commandv("ls") +reader, writer = unix.pipe() +if unix.fork() == 0 then + unix.close(1) + unix.dup(writer) + unix.close(writer) + unix.close(reader) + unix.execve(ls, {ls, "-Shal"}) + unix.exit(127) +else + unix.close(writer) + SetHeader('Content-Type', 'text/plain') + while true do + data = unix.read(reader) + if data ~= "" then + Write(data) + else + break + end + end + unix.close(reader) + unix.wait(-1) +end diff --git a/tool/net/help.txt b/tool/net/help.txt index bfd84f8e2..1f9f18cd5 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -24,6 +24,17 @@ OVERVIEW itself is written as a single .c file. It embeds the Lua programming language and SQLite which let you write dynamic pages. +FEATURES + + - Lua v5.4 + - SQLite 3.35.5 + - TLS v1.2 / v1.1 / v1.0 + - HTTP v1.1 / v1.0 / v0.9 + - Chromium-Zlib Compression + - Statusz Monitoring Statistics + - Self-Modifying PKZIP Object Store + - Linux + Windows + Mac + FreeBSD + OpenBSD + NetBSD + FLAGS -h or -? help @@ -65,16 +76,53 @@ FLAGS --strace enables system call tracing --ftrace enables function call tracing -FEATURES +KEYBOARD - - Lua v5.4 - - SQLite 3.35.5 - - TLS v1.2 / v1.1 / v1.0 - - HTTP v1.1 / v1.0 / v0.9 - - Chromium-Zlib Compression - - Statusz Monitoring Statistics - - Self-Modifying PKZIP Object Store - - Linux + Windows + Mac + FreeBSD + OpenBSD + NetBSD + CTRL-D EXIT + CTRL-C CTRL-C EXIT + CTRL-E END + CTRL-A START + CTRL-B BACK + CTRL-F FORWARD + CTRL-L CLEAR + CTRL-H BACKSPACE + CTRL-D DELETE + CTRL-N NEXT HISTORY + CTRL-P PREVIOUS HISTORY + CTRL-R SEARCH HISTORY + CTRL-G CANCEL SEARCH + ALT-< BEGINNING OF HISTORY + ALT-> END OF HISTORY + ALT-F FORWARD WORD + ALT-B BACKWARD WORD + CTRL-K KILL LINE FORWARDS + CTRL-U KILL LINE BACKWARDS + ALT-H KILL WORD BACKWARDS + CTRL-W KILL WORD BACKWARDS + CTRL-ALT-H KILL WORD BACKWARDS + ALT-D KILL WORD FORWARDS + CTRL-Y YANK + ALT-Y ROTATE KILL RING AND YANK AGAIN + CTRL-T TRANSPOSE + ALT-T TRANSPOSE WORD + ALT-U UPPERCASE WORD + ALT-L LOWERCASE WORD + ALT-C CAPITALIZE WORD + CTRL-\ QUIT PROCESS + CTRL-S PAUSE OUTPUT + CTRL-Q UNPAUSE OUTPUT (IF PAUSED) + CTRL-Q ESCAPED INSERT + CTRL-ALT-F FORWARD EXPR + CTRL-ALT-B BACKWARD EXPR + ALT-RIGHT FORWARD EXPR + ALT-LEFT BACKWARD EXPR + ALT-SHIFT-B BARF EXPR + ALT-SHIFT-S SLURP EXPR + CTRL-SPACE SET MARK + CTRL-X CTRL-X GOTO MARK + CTRL-Z SUSPEND PROCESS + ALT-\ SQUEEZE ADJACENT WHITESPACE + PROTIP REMAP CAPS LOCK TO CTRL USAGE @@ -1303,23 +1351,139 @@ UNIX MODULE `prog` contains slashes then it's not path searched either and will be returned if it exists. - unix.realpath(filename:str) → abspath:str[, errno:int] - - Returns absolute path of filename, with `.` and `..` components - removed, and symlinks will be resolved. - - unix.execve(prog, argv[, envp]) → errno + unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see - commandv(). `envp` defaults to to the current `environ`. Both - `prog` and `envp` are arrays of strings. + commandv(). `env` defaults to to the current `environ`. Here's + a basic usage example: - unix.execve("/bin/ls", {"/bin/ls", "-hal"}) + unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"}) unix.exit(127) - The first element in `argv` should be `prog`. This function is - normally called after forking. + `prog` needs to be the resolved pathname of your executable. You + can use commandv() to search your `PATH`. + + `args` is a string list table. The first element in `args` + should be `prog`. Values are coerced to strings. This parameter + defaults to `{prog}`. + + `env` is a key/value table. Keys that aren't strings are + ignored. Values are coerced to strings. This parameter defaults + to environ() in a way that avoids copying. + + execve() function is normally called after fork() returns 0. If + that isn't the case, then your redbean worker will be destroyed. + + This function never returns on success. + + `EAGAIN` is returned if you've enforced a max number of + processes using `setrlimit(RLIMIT_NPROC)`. + + unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int + + Duplicates file descriptor. + + `newfd` defaults to the lowest number available file descriptor. + If the new number is specified and it's already open, then it'll + be silently closed before the duplication happens. + + `flags` can have `O_CLOEXEC` which means the returned file + descriptors will be automatically closed upon execve(). + + unix.pipe([flags]) → reader, writer, errno:int + + Creates fifo which enables communication between processes. + Returns two file descriptors: one for reading and one for + writing. `flags` can have `O_CLOEXEC`. On error, `reader` and + `writer` will be `nil` and `errno` will be set to non-nil. + + Here's an example of how pipe(), fork(), dup(), etc. may be used + to serve an HTTP response containing the output of a subprocess. + + local unix = require "unix" + ls = unix.commandv("ls") + reader, writer = unix.pipe() + if unix.fork() == 0 then + unix.close(1) + unix.dup(writer) + unix.close(writer) + unix.close(reader) + unix.execve(ls, {ls, "-Shal"}) + unix.exit(127) + else + unix.close(writer) + SetHeader('Content-Type', 'text/plain') + while true do + data = unix.read(reader) + if data ~= "" then + Write(data) + else + break + end + end + unix.close(reader) + unix.wait(-1) + end + + unix.wait([pid:int, options:int]) + → pid:int, wstatus:int, nil, errno:int + + Waits for subprocess to terminate. + + `pid` defaults to `-1` which means any child process. Setting + `pid` to `0` is equivalent to `-getpid()`. If `pid < -1` then + that means wait for any pid in the process group `-pid`. Then + lastly if `pid > 0` then this waits for a specific process id + + Options may have `WNOHANG` which means don't block, check for + the existence of processes that are already dead (technically + speaking zombies) and if so harvest them immediately. + + Returns the process id of the child that terminated. In other + cases, the returned `pid` is nil and `errno` is non-nil. + + The returned `wstatus` contains information about the process + exit status. It's a complicated integer and there's functions + that can help interpret it. For example: + + -- wait for zombies + -- traditional technique for SIGCHLD handlers + while true do + pid, wstatus, errno = unix.wait(-1, unix.WNOHANG) + if pid then + if unix.WIFEXITED(wstatus) then + print('child', pid, 'exited with', + unix.WEXITSTATUS(wstatus)) + elseif unix.WIFSIGNALED(wstatus) then + print('child', pid, 'crashed with', + unix.strsignal(unix.WTERMSIG(wstatus))) + end + elseif errno == unix.ECHILD then + print('no more zombies') + break + elseif errno == unix.ECHILD then + print('wait failed', unix.strerror(errno)) + break + end + end + + unix.getpid() → pid + + Returns process id of current process. + + unix.getppid() → pid + + Returns process id of parent process. + + unix.kill(pid, sig) → rc:int[, errno:int] + + Returns process id of current process. + + unix.raise(sig) → rc:int[, errno:int] + + Triggers signal in current process. + This is pretty much the same as `kill(getpid(), sig)`. unix.access(path:str, how) → rc:int[, errno:int] @@ -1347,17 +1511,6 @@ UNIX MODULE Changes root directory. Raises `ENOSYS` on Windows. - unix.dup(oldfd[, newfd[, flags]]) → newfd:int, errno:int - - Duplicates file descriptor. `flags` can have `O_CLOEXEC`. - - unix.pipe([flags]) → reader, writer, errno:int - - Creates fifo which enables communication between processes. - Returns two file descriptors: one for reading and one for - writing. `flags` can have `O_CLOEXEC`. On error, `reader` and - `writer` will be `nil` and `errno` will be set to non-nil. - unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] Renames file. @@ -1370,28 +1523,79 @@ UNIX MODULE Creates soft link, or a symbolic link. + unix.realpath(filename:str) → abspath:str[, errno:int] + + Returns absolute path of filename, with `.` and `..` components + removed, and symlinks will be resolved. + unix.chown(path:str, uid, gid) → rc:int[, errno:int] Changes user and gorup on file. unix.chmod(path:str, mode) → rc:int[, errno:int] + + Changes mode bits on file. + unix.getcwd(path:str, mode) → rc:int[, errno:int] - unix.getpid() → pid - unix.getppid() → pid - unix.kill(pid, sig) → rc:int[, errno:int] - unix.raise(sig) → rc:int[, errno:int] - unix.wait(pid[, options]) → pid, wstatus, nil, errno:int - unix.fcntl(fd:int, cmd[, arg]) → rc:int[, errno:int] + + Returns current working directory. + + unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] + + Manipulates file descriptor. + + Setting `cmd` to `F_GETFD`, `F_SETFD`, `F_GETFL` or `F_SETFL` + lets you query and/or change the status of file descriptors. For + example, it's possible using this to change `FD_CLOEXEC`. + + POSIX advisory locks can be controlled by setting `cmd` to + `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. + unix.getsid(pid) → sid, errno:int + + Gets session id. + unix.getpgrp() → pgid, errno:int - unix.getpgid(pid) → pgid, errno:int + + Gets process group id. + + unix.setpgrp() → pgid, errno:int + + Sets process group id. This is the same as `setpgid(0,0)`. + unix.setpgid(pid, pgid) → pgid, errno:int + + Sets process group id the modern way. + + unix.getpgid(pid) → pgid, errno:int + + Gets process group id the modern wayp. + unix.setsid() → sid, errno:int - unix.getuid() → uid, errno:int + + Sets session id. + + This function can be used to create daemons. + + unix.getuid() → uid:int + + Gets user id. + + unix.getgid() → gid:int + + Sets group id. + unix.setuid(uid:int) → rc:int[, errno:int] - unix.getgid() → gid, errno:int + + Sets user id. + unix.setgid(gid:int) → rc:int[, errno:int] - unix.umask(mask) → rc:int[, errno:int] + + Sets group id. + + unix.umask(mask) → oldmask:int + + Sets file permission mask and returns the old one. unix.syslog(priority:int, msg:str) @@ -1455,17 +1659,42 @@ UNIX MODULE `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int, ip, port) → rc:int[, errno:int] + unix.bind(fd:int[, ip:uint32, port:uint16]) → rc:int[, errno:int] - unix.connect(fd:int, ip, port) → rc:int[, errno:int] + Binds socket. + + `ip` and `port` are in host endian order. For example, if you + wanted to listen on `10.0.0.1:31337` you could do: + + unix.bind(sock, 10 << 24 | 0 << 16 | 0 << 8 | 1, 31337) + + By default, `ip` and `port` are set to zero, which means to + listen on all interfaces with a kernel-assigned port number that + can be retrieved and used as follows: + + local sock = unix.socket() + unix.bind(sock) + ip, port = unix.getsockname(sock) + print("listening on ip", FormatIp(ip), "port", port) + unix.listen(sock) + unix.accept(sock) + while true do + client, clientip, clientport = unix.accept(sock) + print("got client ip", FormatIp(clientip), "port", clientport) + unix.close(client) + end unix.listen(fd:int[, backlog]) → rc:int[, errno:int] - unix.getsockname(fd:int) → ip, port, errno:int + Listens for incoming connections on bound socket. - unix.getpeername(fd:int) → ip, port, errno:int + unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] - unix.accept(serverfd) → clientfd, ip, port, errno:int + unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] + + unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] + + unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int @@ -1496,7 +1725,8 @@ UNIX MODULE `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` - unix.sigaction(sig[, handler[, flags[, mask]]]) → handler, flags, mask, errno:int + unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) + → oldhandler:func|int, flags:int, mask:int, errno:int `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. @@ -1539,6 +1769,20 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) + unix.reboot(how:int) → str + + Changes power status of system. + + `how` may be `RB_AUTOBOOT` to reboot, `RB_POWER_OFF` to power + down, `RB_HALT_SYSTEM` to literally just stop the processor, or + `RB_SW_SUSPEND` to put the machine into a suspend state. These + magnums will be set to -1 if the method isn't supported on the + host platform. + + By default, an implicit sync() is performed. That's to help + prevent you from losing data. If you don't want to shutdown + gracefully, then you can bitwise or `RB_NOSYNC` into `how`. + unix.strerrno(errno:int) → str Turns `errno` code into its symbolic name, e.g. `"EINTR"`. diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 3be047d59..0947ddbd5 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -52,6 +52,7 @@ #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/reboot.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/shut.h" @@ -92,6 +93,11 @@ static dontinline int ReturnInteger(lua_State *L, lua_Integer x) { return 1; } +static dontinline int ReturnString(lua_State *L, const char *x) { + lua_pushstring(L, x); + return 1; +} + static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { lua_pushinteger(L, ts->tv_sec); lua_pushinteger(L, ts->tv_nsec); @@ -132,6 +138,23 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { return p; } +static char **ConvertLuaTableToEnvList(lua_State *L, int i) { + int j, n; + char **p, *s; + luaL_checktype(L, i, LUA_TTABLE); + p = xcalloc((n = 0) + 1, sizeof(char *)); + lua_pushnil(L); + for (n = 0; lua_next(L, i);) { + if (lua_type(L, -2) == LUA_TSTRING) { + p = xrealloc(p, (++n + 1) * sizeof(char *)); + p[n - 1] = xasprintf("%s=%s", lua_tostring(L, -2), lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + p[n] = 0; + return p; +} + static void FreeStringList(char **p) { int i; if (p) { @@ -145,6 +168,31 @@ static void FreeStringList(char **p) { //////////////////////////////////////////////////////////////////////////////// // System Calls +// unix.getpid() → pid:int +static int LuaUnixGetpid(lua_State *L) { + return ReturnInteger(L, getpid()); +} + +// unix.getppid() → pid:int +static int LuaUnixGetppid(lua_State *L) { + return ReturnInteger(L, getppid()); +} + +// unix.getuid() → uid:int +static int LuaUnixGetuid(lua_State *L) { + return ReturnInteger(L, getuid()); +} + +// unix.getgid() → gid:int +static int LuaUnixGetgid(lua_State *L) { + return ReturnInteger(L, getgid()); +} + +// unix.umask(mask:int) → oldmask:int +static int LuaUnixUmask(lua_State *L) { + return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); +} + // unix.exit([exitcode:int]) → ⊥ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); @@ -290,12 +338,11 @@ static int LuaUnixFork(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.execve(prog, argv[, envp]) → errno -// unix.exit(127) +// unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int // // unix = require "unix" // prog = unix.commandv("ls") -// unix.execve(prog, {prog, "-hal", "."}) +// unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) // unix.exit(127) // // prog needs to be absolute, see commandv() @@ -303,20 +350,30 @@ static int LuaUnixFork(lua_State *L) { static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; - char **argv, **envp, **freeme; + char **argv, **envp, **freeme1, **freeme2, *ezargs[2]; olderr = errno; prog = luaL_checkstring(L, 1); - argv = ConvertLuaArrayToStringList(L, 2); - if (!lua_isnoneornil(L, 3)) { - envp = ConvertLuaArrayToStringList(L, 3); - freeme = envp; + if (!lua_isnoneornil(L, 2)) { + argv = ConvertLuaArrayToStringList(L, 2); + freeme1 = argv; + if (!lua_isnoneornil(L, 3)) { + envp = ConvertLuaTableToEnvList(L, 3); + freeme2 = envp; + } else { + envp = environ; + freeme2 = 0; + } } else { + ezargs[0] = prog; + ezargs[1] = 0; + argv = ezargs; envp = environ; - freeme = 0; + freeme1 = 0; + freeme2 = 0; } execve(prog, argv, envp); - FreeStringList(freeme); - FreeStringList(argv); + FreeStringList(freeme1); + FreeStringList(freeme2); lua_pushinteger(L, errno); errno = olderr; return 1; @@ -401,18 +458,6 @@ static int LuaUnixGetrlimit(lua_State *L) { } } -// unix.getpid() → pid:int -static int LuaUnixGetpid(lua_State *L) { - lua_pushinteger(L, getpid()); - return 1; -} - -// unix.getppid() → pid:int -static int LuaUnixGetppid(lua_State *L) { - lua_pushinteger(L, getppid()); - return 1; -} - // unix.kill(pid, sig) → rc:int[, errno:int] static int LuaUnixKill(lua_State *L) { int rc, pid, sig, olderr; @@ -432,11 +477,11 @@ static int LuaUnixRaise(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.wait(pid[, options]) → pid, wstatus, nil, errno +// unix.wait([pid, options]) → pid, wstatus, nil, errno static int LuaUnixWait(lua_State *L) { int rc, pid, olderr, options, wstatus; olderr = errno; - pid = luaL_checkinteger(L, 1); + pid = luaL_optinteger(L, 1, -1); options = luaL_optinteger(L, 2, 0); rc = wait4(pid, &wstatus, options, 0); if (rc != -1) { @@ -517,15 +562,6 @@ static int LuaUnixGetpgid(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.umask(mask:int) → rc:int[, errno:int] -static int LuaUnixUmask(lua_State *L) { - int rc, mask, olderr; - olderr = errno; - mask = luaL_checkinteger(L, 1); - rc = umask(mask); - return ReturnRc(L, rc, olderr); -} - // unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] static int LuaUnixSetpgid(lua_State *L) { int rc, pid, pgid, olderr; @@ -554,28 +590,12 @@ static int LuaUnixSetsid(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.getuid() → uid[, errno] -static int LuaUnixGetuid(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = getuid(); - return ReturnRc(L, rc, olderr); -} - // unix.setuid(uid:int) → rc:int[, errno:int] static int LuaUnixSetuid(lua_State *L) { int olderr = errno; return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr); } -// unix.getgid() → gid:int[, errno:int] -static int LuaUnixGetgid(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = getgid(); - return ReturnRc(L, rc, olderr); -} - // unix.setgid(gid:int) → rc:int[, errno:int] static int LuaUnixSetgid(lua_State *L) { int olderr = errno; @@ -848,7 +868,7 @@ static int LuaUnixSocketpair(lua_State *L) { } } -// unix.bind(fd, ip, port) → rc:int[, errno:int] +// unix.bind(fd[, ip, port]) → rc:int[, errno:int] // SOCK_CLOEXEC may be or'd into type // family defaults to AF_INET // type defaults to SOCK_STREAM @@ -859,8 +879,8 @@ static int LuaUnixBind(lua_State *L) { bzero(&sa, sizeof(sa)); olderr = errno; fd = luaL_checkinteger(L, 1); - sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)); - sa.sin_port = htons(luaL_checkinteger(L, 3)); + sa.sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)); + sa.sin_port = htons(luaL_optinteger(L, 3, 0)); rc = bind(fd, &sa, sizeof(sa)); return ReturnRc(L, rc, olderr); } @@ -928,7 +948,7 @@ static int LuaUnixGetpeername(lua_State *L) { } } -// unix.accept(serverfd) → clientfd, ip, port, errno +// unix.accept(serverfd:int) → clientfd:int, ip:uint32, port:uint16, errno static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; @@ -1237,20 +1257,42 @@ static int LuaUnixSetitimer(lua_State *L) { // unix.strerror(errno) → str static int LuaUnixStrerror(lua_State *L) { - lua_pushstring(L, strerror(luaL_checkinteger(L, 1))); - return 1; + return ReturnString(L, strerror(luaL_checkinteger(L, 1))); } // unix.strerrno(errno) → str static int LuaUnixStrerrno(lua_State *L) { - lua_pushstring(L, strerror_short(luaL_checkinteger(L, 1))); - return 1; + return ReturnString(L, strerror_short(luaL_checkinteger(L, 1))); } // unix.strsignal(sig) → str static int LuaUnixStrsignal(lua_State *L) { - lua_pushstring(L, strsignal(luaL_checkinteger(L, 1))); - return 1; + return ReturnString(L, strsignal(luaL_checkinteger(L, 1))); +} + +// unix.WIFEXITED(wstatus) → int +static int LuaUnixWifexited(lua_State *L) { + return ReturnInteger(L, WIFEXITED(luaL_checkinteger(L, 1))); +} + +// unix.WEXITSTATUS(wstatus) → int +static int LuaUnixWexitstatus(lua_State *L) { + return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1))); +} + +// unix.WIFSIGNALED(wstatus) → int +static int LuaUnixWifsignaled(lua_State *L) { + return ReturnInteger(L, WIFSIGNALED(luaL_checkinteger(L, 1))); +} + +// unix.WTERMSIG(wstatus) → int +static int LuaUnixWtermsig(lua_State *L) { + return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1))); +} + +// unix.reboot(how:int) → rc:int[, errno:int] +static int LuaUnixReboot(lua_State *L) { + return ReturnInteger(L, reboot(luaL_checkinteger(L, 1))); } //////////////////////////////////////////////////////////////////////////////// @@ -1549,9 +1591,14 @@ static const luaL_Reg kLuaUnix[] = { {"sigprocmask", LuaUnixSigprocmask}, // change signal mask {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock + {"reboot", LuaUnixReboot}, // reboots system {"strerror", LuaUnixStrerror}, // turn errno into string {"strerrno", LuaUnixStrerrno}, // turn errno into string {"strsignal", LuaUnixStrsignal}, // turn signal into string + {"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status + {"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status + {"WIFSIGNALED", LuaUnixWifsignaled}, // determines if died due to signal + {"WTERMSIG", LuaUnixWtermsig}, // gets the signal code {0}, // }; @@ -1643,6 +1690,7 @@ int LuaUnix(lua_State *L) { // wait() options LuaSetIntField(L, "WNOHANG", WNOHANG); + LuaSetIntField(L, "WNOHANG", WNOHANG); // gettime() clocks LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable @@ -1721,5 +1769,12 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "LOG_INFO", LOG_INFO); LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG); + // reboot() howto + LuaSetIntField(L, "RB_AUTOBOOT", RB_AUTOBOOT); + LuaSetIntField(L, "RB_POWER_OFF", RB_POWER_OFF); + LuaSetIntField(L, "RB_HALT_SYSTEM", RB_HALT_SYSTEM); + LuaSetIntField(L, "RB_SW_SUSPEND", RB_SW_SUSPEND); + LuaSetIntField(L, "RB_NOSYNC", RB_NOSYNC); + return 1; } diff --git a/tool/net/net.mk b/tool/net/net.mk index 62ef2bc63..c97461453 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -212,6 +212,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/net.pkg \ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ o/$(MODE)/tool/net/demo/unix.lua.zip.o \ + o/$(MODE)/tool/net/demo/unix2.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ o/$(MODE)/tool/net/demo/redbean.lua.zip.o \ diff --git a/tool/viz/printimage.c b/tool/viz/printimage.c index 3e8fe45a7..3bc47b4ed 100644 --- a/tool/viz/printimage.c +++ b/tool/viz/printimage.c @@ -53,12 +53,15 @@ #include "third_party/stb/stb_image.h" #include "tool/viz/lib/graphic.h" +STATIC_YOINK("__zipos_get"); + static struct Flags { const char *out; bool subpixel; bool unsharp; bool dither; bool ruler; + bool magikarp; bool trailingnewline; long half; bool full; @@ -85,6 +88,7 @@ FLAGS\n\ -f display full size\n\ -s unsharp sharpening\n\ -x xterm256 color mode\n\ + -m use magikarp scaling\n\ -d hilbert curve dithering\n\ -r display pixel ruler on sides\n\ -p convert to subpixel layout\n\ @@ -94,7 +98,6 @@ FLAGS\n\ EXAMPLES\n\ \n\ printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\ -\n\ \n"); exit(rc); } @@ -118,7 +121,7 @@ static void GetOpts(int *argc, char *argv[]) { (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { PrintUsage(EXIT_SUCCESS, stdout); } - while ((opt = getopt(*argc, argv, "?vpfrtxads234o:w:h:")) != -1) { + while ((opt = getopt(*argc, argv, "?vpmfrtxads234o:w:h:")) != -1) { switch (opt) { case 'o': g_flags.out = optarg; @@ -146,6 +149,9 @@ static void GetOpts(int *argc, char *argv[]) { case 'r': g_flags.ruler = true; break; + case 'm': + g_flags.magikarp = true; + break; case 'p': g_flags.subpixel = true; break; @@ -173,10 +179,6 @@ static void GetOpts(int *argc, char *argv[]) { PrintUsage(EX_USAGE, stderr); } } - if (optind == *argc) { - if (!g_flags.out) g_flags.out = "-"; - argv[(*argc)++] = "-"; - } if (!g_flags.full && (!g_flags.width || !g_flags.width)) { ws.ws_col = 80; ws.ws_row = 24; @@ -398,6 +400,22 @@ void WithImageFile(const char *path, sxn = xn; dyn = g_flags.height; dxn = g_flags.width; + if (g_flags.magikarp) { + while (HALF(syn) > dyn || HALF(sxn) > dxn) { + if (HALF(sxn) > dxn) { + Magikarp2xX(yn, xn, data, syn, sxn); + Magikarp2xX(yn, xn, (char *)data + yn * xn, syn, sxn); + Magikarp2xX(yn, xn, (char *)data + yn * xn * 2, syn, sxn); + sxn = HALF(sxn); + } + if (HALF(syn) > dyn) { + Magikarp2xY(yn, xn, data, syn, sxn); + Magikarp2xY(yn, xn, (char *)data + yn * xn, syn, sxn); + Magikarp2xY(yn, xn, (char *)data + yn * xn * 2, syn, sxn); + syn = HALF(syn); + } + } + } data = EzGyarados(3, dyn, dxn, gc(memalign(32, dyn * dxn * 3)), cn, yn, xn, data, 0, cn, dyn, dxn, syn, sxn, 0, 0, 0, 0); yn = dyn; @@ -410,6 +428,7 @@ int main(int argc, char *argv[]) { int i; ShowCrashReports(); GetOpts(&argc, argv); + if (optind == argc) PrintUsage(0, stdout); stbi_set_unpremultiply_on_load(true); for (i = optind; i < argc; ++i) { WithImageFile(argv[i], ProcessImage); diff --git a/tool/viz/viz.mk b/tool/viz/viz.mk index 3fc7b5253..7dfba9880 100644 --- a/tool/viz/viz.mk +++ b/tool/viz/viz.mk @@ -45,6 +45,7 @@ TOOL_VIZ_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ + LIBC_ZIPOS \ NET_HTTP \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GDTOA \ @@ -71,6 +72,25 @@ o/$(MODE)/tool/viz/%.com.dbg: \ $(APE) @$(APELINK) +o/$(MODE)/tool/viz/printimage.com.dbg: \ + $(TOOL_VIZ_DEPS) \ + o/$(MODE)/tool/viz/printimage.o \ + o/$(MODE)/tool/viz/viz.pkg \ + o/$(MODE)/LICENSE.zip.o \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/tool/viz/printimage.com: \ + o/$(MODE)/tool/viz/printimage.com.dbg \ + o/$(MODE)/third_party/zip/zip.com \ + o/$(MODE)/tool/build/symtab.com + @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ + @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ + -o o/$(MODE)/tool/viz/.printimage/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + o/$(MODE)/tool/viz/.printimage/.symtab + o/$(MODE)/tool/viz/printvideo.com: \ o/$(MODE)/tool/viz/printvideo.com.dbg \ o/$(MODE)/third_party/zip/zip.com \