diff --git a/examples/examples.mk b/examples/examples.mk index 0f6973377..3d958f8e3 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -151,6 +151,14 @@ o/$(MODE)/examples/hello.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/examples/printargs.com.dbg: \ + $(EXAMPLES_DEPS) \ + o/$(MODE)/examples/printargs.o \ + o/$(MODE)/examples/examples.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m $(EXAMPLES_OBJS): examples/examples.mk diff --git a/examples/printargs.c b/examples/printargs.c index 60ce148ed..11c057ce2 100644 --- a/examples/printargs.c +++ b/examples/printargs.c @@ -7,8 +7,30 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" #include "libc/runtime/runtime.h" int main() { - __printargs(""); + int pip[2]; + char buf[PATH_MAX]; + char *args[2] = {0}; + if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && + ((args[0] = commandv("less", buf)) || + (args[0] = commandv("more", buf)))) { + close(0); + close(2); + pipe(pip); + if (!vfork()) { + close(2); + execv(args[0], args); + _Exit(127); + } + close(0); + __printargs(""); + close(2); + wait(0); + } else { + __printargs(""); + } } diff --git a/libc/calls/struct/bpf.h b/libc/calls/struct/bpf.h index 1dc96c763..1a1e896a8 100644 --- a/libc/calls/struct/bpf.h +++ b/libc/calls/struct/bpf.h @@ -1287,7 +1287,7 @@ struct bpf_spin_lock { }; struct bpf_sysctl { - uint32_t write; + uint32_t write_; uint32_t file_pos; }; diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index ab7554733..ddace70e8 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -599,6 +599,8 @@ syscon sicode POLL_MSG 3 3 3 3 3 3 # SIGIO; input message availab syscon sicode POLL_ERR 4 4 4 4 4 4 # SIGIO; i/o error; unix consensus syscon sicode POLL_PRI 5 5 5 5 5 5 # SIGIO; high priority input available; unix consensus syscon sicode POLL_HUP 6 6 6 6 6 6 # SIGIO; device disconnected; unix consensus +syscon sicode SYS_SECCOMP 1 -1 -1 -1 -1 -1 # SIGSYS; seccomp triggered +syscon sicode SYS_USER_DISPATCH 2 -1 -1 -1 -1 -1 # SIGSYS; syscall user dispatch triggered # sigaltstack() values # diff --git a/libc/sysv/consts/RB_DISABLE_CAD.S b/libc/sysv/consts/RB_DISABLE_CAD.S index 47830b0ed..e7e383360 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,-1,-1,-1,-1,-1 +.syscon reboot,RB_DISABLE_CAD,0,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff diff --git a/libc/sysv/consts/RB_ENABLE_CAD.S b/libc/sysv/consts/RB_ENABLE_CAD.S index 1e36b6494..32f69564c 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,-1,-1,-1,-1,-1 +.syscon reboot,RB_ENABLE_CAD,0x89abcdef,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff diff --git a/libc/sysv/consts/RB_KEXEC.S b/libc/sysv/consts/RB_KEXEC.S index be957a104..ae434fa28 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,-1,-1,-1,-1,-1 +.syscon reboot,RB_KEXEC,0x45584543,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff diff --git a/libc/sysv/consts/RB_POWERDOWN.S b/libc/sysv/consts/RB_POWERDOWN.S index 6163679fa..9758eebe7 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,-1,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWERDOWN,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_POWEROFF.S b/libc/sysv/consts/RB_POWEROFF.S index 1524d9e28..56e6b566c 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,-1,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWEROFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_POWER_OFF.S b/libc/sysv/consts/RB_POWER_OFF.S index c8db8d8c3..e260da637 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,-1,0x4000,0x1000,0x808,8 +.syscon reboot,RB_POWER_OFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8 diff --git a/libc/sysv/consts/RB_SW_SUSPEND.S b/libc/sysv/consts/RB_SW_SUSPEND.S index 721706413..c2528864c 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,-1,-1,-1,-1,0xd000fce2 +.syscon reboot,RB_SW_SUSPEND,0xd000fce2,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xd000fce2 diff --git a/libc/sysv/consts/SYS_SECCOMP.S b/libc/sysv/consts/SYS_SECCOMP.S new file mode 100644 index 000000000..c99380963 --- /dev/null +++ b/libc/sysv/consts/SYS_SECCOMP.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon sicode,SYS_SECCOMP,1,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/SYS_USER_DISPATCH.S b/libc/sysv/consts/SYS_USER_DISPATCH.S new file mode 100644 index 000000000..7daf28756 --- /dev/null +++ b/libc/sysv/consts/SYS_USER_DISPATCH.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon sicode,SYS_USER_DISPATCH,2,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/sicode.h b/libc/sysv/consts/sicode.h index 4353a1841..7b79cc44d 100644 --- a/libc/sysv/consts/sicode.h +++ b/libc/sysv/consts/sicode.h @@ -52,6 +52,8 @@ extern const long POLL_MSG; extern const long POLL_ERR; extern const long POLL_PRI; extern const long POLL_HUP; +extern const long SYS_SECCOMP; +extern const long SYS_USER_DISPATCH; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ @@ -80,30 +82,32 @@ COSMOPOLITAN_C_END_ #define POLL_PRI LITERALLY(5) #define POLL_HUP LITERALLY(6) -#define SI_USER SYMBOLIC(SI_USER) -#define SI_QUEUE SYMBOLIC(SI_QUEUE) -#define SI_TIMER SYMBOLIC(SI_TIMER) -#define SI_MESGQ SYMBOLIC(SI_MESGQ) -#define SI_ASYNCIO SYMBOLIC(SI_ASYNCIO) -#define SI_TKILL SYMBOLIC(SI_TKILL) -#define SI_ASYNCNL SYMBOLIC(SI_ASYNCNL) -#define SI_KERNEL SYMBOLIC(SI_KERNEL) -#define SI_NOINFO SYMBOLIC(SI_NOINFO) -#define SEGV_PKUERR SYMBOLIC(SEGV_PKUERR) -#define FPE_INTDIV SYMBOLIC(FPE_INTDIV) -#define FPE_INTOVF SYMBOLIC(FPE_INTOVF) -#define FPE_FLTDIV SYMBOLIC(FPE_FLTDIV) -#define FPE_FLTOVF SYMBOLIC(FPE_FLTOVF) -#define FPE_FLTUND SYMBOLIC(FPE_FLTUND) -#define FPE_FLTRES SYMBOLIC(FPE_FLTRES) -#define FPE_FLTINV SYMBOLIC(FPE_FLTINV) -#define FPE_FLTSUB SYMBOLIC(FPE_FLTSUB) -#define ILL_ILLOPN SYMBOLIC(ILL_ILLOPN) -#define ILL_ILLADR SYMBOLIC(ILL_ILLADR) -#define ILL_ILLTRP SYMBOLIC(ILL_ILLTRP) -#define ILL_PRVOPC SYMBOLIC(ILL_PRVOPC) -#define BUS_OOMERR SYMBOLIC(BUS_OOMERR) -#define BUS_MCEERR_AR SYMBOLIC(BUS_MCEERR_AR) -#define BUS_MCEERR_AO SYMBOLIC(BUS_MCEERR_AO) +#define SI_USER SYMBOLIC(SI_USER) +#define SI_QUEUE SYMBOLIC(SI_QUEUE) +#define SI_TIMER SYMBOLIC(SI_TIMER) +#define SI_MESGQ SYMBOLIC(SI_MESGQ) +#define SI_ASYNCIO SYMBOLIC(SI_ASYNCIO) +#define SI_TKILL SYMBOLIC(SI_TKILL) +#define SI_ASYNCNL SYMBOLIC(SI_ASYNCNL) +#define SI_KERNEL SYMBOLIC(SI_KERNEL) +#define SI_NOINFO SYMBOLIC(SI_NOINFO) +#define SEGV_PKUERR SYMBOLIC(SEGV_PKUERR) +#define FPE_INTDIV SYMBOLIC(FPE_INTDIV) +#define FPE_INTOVF SYMBOLIC(FPE_INTOVF) +#define FPE_FLTDIV SYMBOLIC(FPE_FLTDIV) +#define FPE_FLTOVF SYMBOLIC(FPE_FLTOVF) +#define FPE_FLTUND SYMBOLIC(FPE_FLTUND) +#define FPE_FLTRES SYMBOLIC(FPE_FLTRES) +#define FPE_FLTINV SYMBOLIC(FPE_FLTINV) +#define FPE_FLTSUB SYMBOLIC(FPE_FLTSUB) +#define ILL_ILLOPN SYMBOLIC(ILL_ILLOPN) +#define ILL_ILLADR SYMBOLIC(ILL_ILLADR) +#define ILL_ILLTRP SYMBOLIC(ILL_ILLTRP) +#define ILL_PRVOPC SYMBOLIC(ILL_PRVOPC) +#define BUS_OOMERR SYMBOLIC(BUS_OOMERR) +#define BUS_MCEERR_AR SYMBOLIC(BUS_MCEERR_AR) +#define BUS_MCEERR_AO SYMBOLIC(BUS_MCEERR_AO) +#define SYS_SECCOMP SYMBOLIC(SYS_SECCOMP) +#define SYS_USER_DISPATCH SYMBOLIC(SYS_USER_DISPATCH) #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SICODE_H_ */ diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 2f5d8f83b..fa079f0b2 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -148,19 +148,6 @@ noasan int main(int argc, char *argv[]) { EmptySignalMask(); ShowCrashReports(); - // so the test runner can terminate unknown children without - // accidentally killing parent processes - if (!IsWindows() && weaken(fork)) { - setpgrp(); - } - - // prevent runaway tests from bombing the computer - cpus = GetCpuCount(); - cpus = MAX(4, cpus); - SetLimit(RLIMIT_NOFILE, 32, 128); - SetLimit(RLIMIT_SIGPENDING, 16, 16384); - SetLimit(RLIMIT_NPROC, cpus * 8, 2048); - // now get down to business g_testlib_shoulddebugbreak = IsDebuggerPresent(false); if (!IsWindows()) sys_getpid(); // make strace easier to read diff --git a/third_party/mbedtls/test/test_suite_ssl.c b/third_party/mbedtls/test/test_suite_ssl.c index f93906842..eb6529aaa 100644 --- a/third_party/mbedtls/test/test_suite_ssl.c +++ b/third_party/mbedtls/test/test_suite_ssl.c @@ -615,14 +615,14 @@ typedef struct mbedtls_test_message_socket_context { mbedtls_test_message_queue* queue_input; mbedtls_test_message_queue* queue_output; - mbedtls_mock_socket* socket; + mbedtls_mock_socket* socket_; } mbedtls_test_message_socket_context; void mbedtls_message_socket_init( mbedtls_test_message_socket_context *ctx ) { ctx->queue_input = NULL; ctx->queue_output = NULL; - ctx->socket = NULL; + ctx->socket_ = NULL; } /* @@ -645,7 +645,7 @@ int mbedtls_message_socket_setup( mbedtls_test_message_queue* queue_input, return ret; ctx->queue_input = queue_input; ctx->queue_output = queue_output; - ctx->socket = socket; + ctx->socket_ = socket; mbedtls_mock_socket_init( socket ); return 0; @@ -661,7 +661,7 @@ void mbedtls_message_socket_close( mbedtls_test_message_socket_context* ctx ) return; mbedtls_test_message_queue_free( ctx->queue_input ); - mbedtls_mock_socket_close( ctx->socket ); + mbedtls_mock_socket_close( ctx->socket_ ); memset( ctx, 0, sizeof( *ctx ) ); } @@ -683,14 +683,14 @@ int mbedtls_mock_tcp_send_msg( void *ctx, const unsigned char *buf, size_t len ) mbedtls_mock_socket* socket; mbedtls_test_message_socket_context *context = (mbedtls_test_message_socket_context*) ctx; - if( context == NULL || context->socket == NULL + if( context == NULL || context->socket_ == NULL || context->queue_output == NULL ) { return MBEDTLS_TEST_ERROR_CONTEXT_ERROR; } queue = context->queue_output; - socket = context->socket; + socket = context->socket_; if( queue->num >= queue->capacity ) return MBEDTLS_ERR_SSL_WANT_WRITE; @@ -722,14 +722,14 @@ int mbedtls_mock_tcp_recv_msg( void *ctx, unsigned char *buf, size_t buf_len ) size_t msg_len; int ret; - if( context == NULL || context->socket == NULL + if( context == NULL || context->socket_ == NULL || context->queue_input == NULL ) { return MBEDTLS_TEST_ERROR_CONTEXT_ERROR; } queue = context->queue_input; - socket = context->socket; + socket = context->socket_; /* Peek first, so that in case of a socket error the data remains in * the queue. */ @@ -788,7 +788,7 @@ typedef struct mbedtls_endpoint mbedtls_ssl_config conf; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_context entropy; - mbedtls_mock_socket socket; + mbedtls_mock_socket socket_; mbedtls_endpoint_certificate cert; } mbedtls_endpoint; @@ -939,12 +939,12 @@ int mbedtls_endpoint_init( mbedtls_endpoint *ep, int endpoint_type, int pk_alg, if( dtls_context != NULL ) { TEST_ASSERT( mbedtls_message_socket_setup( input_queue, output_queue, - 100, &( ep->socket ), + 100, &( ep->socket_ ), dtls_context ) == 0 ); } else { - mbedtls_mock_socket_init( &( ep->socket ) ); + mbedtls_mock_socket_init( &( ep->socket_ ) ); } ret = mbedtls_ctr_drbg_seed( &( ep->ctr_drbg ), mbedtls_entropy_func, @@ -962,7 +962,7 @@ int mbedtls_endpoint_init( mbedtls_endpoint *ep, int endpoint_type, int pk_alg, } else { - mbedtls_ssl_set_bio( &( ep->ssl ), &( ep->socket ), + mbedtls_ssl_set_bio( &( ep->ssl ), &( ep->socket_ ), mbedtls_mock_tcp_send_nb, mbedtls_mock_tcp_recv_nb, NULL ); @@ -1020,7 +1020,7 @@ void mbedtls_endpoint_free( mbedtls_endpoint *ep, } else { - mbedtls_mock_socket_close( &( ep->socket ) ); + mbedtls_mock_socket_close( &( ep->socket_ ) ); } } @@ -1870,8 +1870,8 @@ void perform_handshake( handshake_test_options* options ) } #endif - TEST_ASSERT( mbedtls_mock_socket_connect( &(client.socket), - &(server.socket), + TEST_ASSERT( mbedtls_mock_socket_connect( &(client.socket_), + &(server.socket_), BUFFSIZE ) == 0 ); #if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) @@ -4375,8 +4375,8 @@ void test_move_handshake_to_state(int endpoint_type, int state, int need_pass) MBEDTLS_PK_RSA, NULL, NULL, NULL ); TEST_ASSERT( ret == 0 ); - ret = mbedtls_mock_socket_connect( &(base_ep.socket), - &(second_ep.socket), + ret = mbedtls_mock_socket_connect( &(base_ep.socket_), + &(second_ep.socket_), BUFFSIZE ); TEST_ASSERT( ret == 0 ); diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 0a75744bf..840617bb9 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -466,12 +466,12 @@ THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \ THIRD_PARTY_PYTHON_STAGE1_A_DEPS = \ $(call uniq,$(foreach x,$(THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS),$($(x)))) -o/$(MODE)/third_party/python/Python/importlib.inc: \ +o//third_party/python/Python/importlib.inc: \ o/$(MODE)/third_party/python/freeze.com \ third_party/python/Lib/importlib/_bootstrap.py @$(COMPILE) -AFREEZE -T$@ $^ $@ -o/$(MODE)/third_party/python/Python/importlib_external.inc: \ +o//third_party/python/Python/importlib_external.inc: \ o/$(MODE)/third_party/python/freeze.com \ third_party/python/Lib/importlib/_bootstrap_external.py @$(COMPILE) -AFREEZE -T$@ $^ $@ diff --git a/tool/build/jail.c b/tool/build/jail.c new file mode 100644 index 000000000..82fde2a88 --- /dev/null +++ b/tool/build/jail.c @@ -0,0 +1,160 @@ +/*-*- 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 2022 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 "libc/calls/calls.h" +#include "libc/calls/struct/filter.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/user_regs_struct.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/pr.h" +#include "libc/sysv/consts/ptrace.h" +#include "libc/sysv/consts/sig.h" +#include "tool/net/sandbox.h" + +#define __WALL 0x40000000 + +static const struct sock_filter kSandboxFilter[] = { + _SECCOMP_MACHINE(AUDIT_ARCH_X86_64), // + _SECCOMP_LOAD_SYSCALL_NR(), // + _SECCOMP_ALLOW_SYSCALL(0x000), // read + _SECCOMP_ALLOW_SYSCALL(0x001), // write + _SECCOMP_ALLOW_SYSCALL(0x013), // readv + _SECCOMP_ALLOW_SYSCALL(0x014), // writev + _SECCOMP_ALLOW_SYSCALL(0x005), // fstat + _SECCOMP_ALLOW_SYSCALL(0x007), // poll + _SECCOMP_ALLOW_SYSCALL(0x008), // lseek + _SECCOMP_ALLOW_SYSCALL(0x009), // mmap + _SECCOMP_ALLOW_SYSCALL(0x00b), // munmap + _SECCOMP_ALLOW_SYSCALL(0x003), // close + _SECCOMP_ALLOW_SYSCALL(0x010), // ioctl todo + _SECCOMP_ALLOW_SYSCALL(0x016), // pipe + _SECCOMP_ALLOW_SYSCALL(0x125), // pipe2 + _SECCOMP_ALLOW_SYSCALL(0x035), // socketpair + _SECCOMP_ALLOW_SYSCALL(0x020), // dup + _SECCOMP_ALLOW_SYSCALL(0x021), // dup2 + _SECCOMP_ALLOW_SYSCALL(0x124), // dup3 + _SECCOMP_ALLOW_SYSCALL(0x039), // fork + _SECCOMP_ALLOW_SYSCALL(0x03a), // vfork + _SECCOMP_ALLOW_SYSCALL(0x011), // pread + _SECCOMP_ALLOW_SYSCALL(0x012), // pwrite + _SECCOMP_ALLOW_SYSCALL(0x127), // preadv + _SECCOMP_ALLOW_SYSCALL(0x128), // pwritev + _SECCOMP_ALLOW_SYSCALL(0x0d9), // getdents + _SECCOMP_ALLOW_SYSCALL(0x028), // sendfile + _SECCOMP_ALLOW_SYSCALL(0x02d), // recvfrom + _SECCOMP_ALLOW_SYSCALL(0x033), // getsockname + _SECCOMP_ALLOW_SYSCALL(0x034), // getpeername + _SECCOMP_ALLOW_SYSCALL(0x00f), // rt_sigreturn + _SECCOMP_ALLOW_SYSCALL(0x0e4), // clock_gettime + _SECCOMP_ALLOW_SYSCALL(0x060), // gettimeofday + _SECCOMP_ALLOW_SYSCALL(0x027), // getpid + _SECCOMP_ALLOW_SYSCALL(0x03f), // uname + _SECCOMP_ALLOW_SYSCALL(0x03c), // exit + _SECCOMP_ALLOW_SYSCALL(0x0e7), // exit_group + _SECCOMP_TRACE_SYSCALL(0x03e, 0), // kill + _SECCOMP_TRACE_SYSCALL(0x101, 0), // openat + _SECCOMP_TRACE_SYSCALL(0x106, 0), // newfstatat + _SECCOMP_TRACE_SYSCALL(0x029, 0), // socket + _SECCOMP_TRACE_SYSCALL(0x031, 0), // bind + _SECCOMP_TRACE_SYSCALL(0x02a, 0), // connect + _SECCOMP_TRACE_SYSCALL(0x02c, 0), // sendto + _SECCOMP_TRACE_SYSCALL(0x036, 0), // setsockopt + _SECCOMP_TRACE_SYSCALL(0x048, 0), // fcntl + _SECCOMP_TRACE_SYSCALL(0x03b, 0), // execve + _SECCOMP_TRACE_SYSCALL(0x102, 0), // mkdirat + _SECCOMP_TRACE_SYSCALL(0x104, 0), // chownat + _SECCOMP_TRACE_SYSCALL(0x107, 0), // unlinkat + _SECCOMP_TRACE_SYSCALL(0x108, 0), // renameat + _SECCOMP_TRACE_SYSCALL(0x109, 0), // linkat + _SECCOMP_TRACE_SYSCALL(0x10a, 0), // symlinkat + _SECCOMP_TRACE_SYSCALL(0x10b, 0), // readlinkat + _SECCOMP_TRACE_SYSCALL(0x10c, 0), // fchmodat + _SECCOMP_TRACE_SYSCALL(0x10d, 0), // faccessat + _SECCOMP_TRACE_SYSCALL(0x0eb, 0), // utimes + _SECCOMP_TRACE_SYSCALL(0x105, 0), // futimesat + _SECCOMP_TRACE_SYSCALL(0x118, 0), // utimensat + _SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM +}; + +static const struct sock_fprog kSandbox = { + .len = ARRAYLEN(kSandboxFilter), + .filter = kSandboxFilter, +}; + +int main(int argc, char *argv[]) { + struct user_regs_struct regs; + int child, evpid, signal, wstatus; + + if (!IsLinux()) { + kprintf("error: %s is only supported on linux right now%n", argv[0]); + return 1; + } + if (argc < 2) { + kprintf("Usage: %s PROGRAM [ARGS...]%n", argv[0]); + return 1; + } + ShowCrashReports(); + + CHECK_NE(-1, (child = fork())); + if (!child) { + if (ptrace(PTRACE_TRACEME) == -1) _Exit(124); + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) _Exit(125); + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &kSandbox) == -1) _Exit(126); + execvp(argv[1], argv + 1); + _Exit(127); + } + + sigaction(SIGINT, &(struct sigaction){.sa_handler = SIG_IGN}, 0); + sigaction(SIGQUIT, &(struct sigaction){.sa_handler = SIG_IGN}, 0); + + // wait for ptrace(PTRACE_TRACEME) to be called + CHECK_EQ(child, waitpid(child, &wstatus, 0)); + + // configure linux process tracing + CHECK_NE(-1, ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP)); + + // continue child process + CHECK_NE(-1, ptrace(PTRACE_CONT, child, 0, 0)); + + for (;;) { + CHECK_NE(-1, (evpid = waitpid(-1, &wstatus, __WALL))); + if (WIFSTOPPED(wstatus)) { + signal = (wstatus >> 8) & 0xffff; + if (signal == SIGTRAP | PTRACE_EVENT_SECCOMP) { + // CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, evpid, 0, &msg)); + CHECK_NE(-1, ptrace(PTRACE_GETREGS, evpid, 0, regs)); + regs.rax = -EPERM; + CHECK_NE(-1, ptrace(PTRACE_GETREGS, evpid, 0, regs)); + ptrace(PTRACE_CONT, evpid, 0, 0); + } else { + ptrace(PTRACE_CONT, evpid, 0, signal & 127); + } + } else if (WIFEXITED(wstatus)) { + exit(WEXITSTATUS(wstatus)); + } else { + exit(128 + WTERMSIG(wstatus)); + } + } + + return 0; +} diff --git a/tool/build/package.c b/tool/build/package.c index 6e1ee212e..7742a4ba9 100644 --- a/tool/build/package.c +++ b/tool/build/package.c @@ -167,7 +167,7 @@ struct Packages { struct Symbol { uint32_t name; // pkg->strings.p[name] enum SectionKind kind : 8; - uint8_t bind : 4; + uint8_t bind_ : 4; uint8_t type : 4; uint16_t object; // pkg->objects.p[object] } * p; // persisted as pkg+RVA @@ -342,9 +342,9 @@ void LoadSymbols(struct Package *pkg, uint32_t object) { obj = &pkg->objects.p[object]; symbol.object = object; for (i = 0; i < obj->symcount; ++i) { - symbol.bind = ELF64_ST_BIND(obj->syms[i].st_info); + symbol.bind_ = ELF64_ST_BIND(obj->syms[i].st_info); symbol.type = ELF64_ST_TYPE(obj->syms[i].st_info); - if (symbol.bind != STB_LOCAL && + if (symbol.bind_ != STB_LOCAL && (symbol.type == STT_OBJECT || symbol.type == STT_FUNC || symbol.type == STT_COMMON || symbol.type == STT_NOTYPE)) { name = GetElfString(obj->elf, obj->size, obj->strs, obj->syms[i].st_name); @@ -448,7 +448,7 @@ void CheckStrictDeps(struct Package *pkg, struct Packages *deps) { struct Symbol *undef; for (i = 0; i < pkg->undefs.i; ++i) { undef = &pkg->undefs.p[i]; - if (undef->bind == STB_WEAK) continue; + if (undef->bind_ == STB_WEAK) continue; if (!FindSymbol(pkg->strings.p + undef->name, pkg, deps, NULL, NULL)) { fprintf(stderr, "%s: %`'s (%s) %s %s\n", "error", pkg->strings.p + undef->name, diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 97a2b31f5..2217aea8f 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -600,7 +600,11 @@ (when root (let ((default-directory root)) (save-buffer) - (cond ((file-executable-p file) + (cond ((save-excursion + (goto-char (point-min)) + (looking-at "#!")) + (compile (format "sh %s" file))) + ((file-executable-p file) (compile (if (cosmo-contains "/" file) file (format "./%s" file)))) @@ -610,6 +614,8 @@ (compile compile-command))) ((eq major-mode 'sh-mode) (compile (format "sh %s" file))) + ((eq major-mode 'lua-mode) + (compile (format "lua.com %s" file))) ((and (eq major-mode 'python-mode) (cosmo-startswith "third_party/python/Lib/test/" file)) (let ((mode (cosmo--make-mode arg))) @@ -673,6 +679,7 @@ (define-key c-mode-base-map (kbd "C-c C-r") 'cosmo-run) (define-key fortran-mode-map (kbd "C-c C-r") 'cosmo-run) (define-key sh-mode-map (kbd "C-c C-r") 'cosmo-run) + (define-key lua-mode-map (kbd "C-c C-r") 'cosmo-run) (define-key python-mode-map (kbd "C-c C-r") 'cosmo-run) (define-key c-mode-map (kbd "C-c C-s") 'cosmo-run-test) (define-key c-mode-map (kbd "C-c C-_") 'cosmo-run-win10)) diff --git a/tool/net/echo.c b/tool/net/echo.c new file mode 100644 index 000000000..109464f43 --- /dev/null +++ b/tool/net/echo.c @@ -0,0 +1,149 @@ +/*-*- 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 2020 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 "libc/calls/calls.h" +#include "libc/fmt/conv.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/check.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/sock.h" +#include "net/http/http.h" + +/** + * @fileoverview tcp/udp echo servers/clients + * use it to fill your network with junk data + */ + +int sock; +char buf[1000]; +struct sockaddr_in addr = {0}; +uint32_t addrsize = sizeof(struct sockaddr_in); + +void PrintUsage(char **argv) { + kprintf("usage: %s udp server [ip port]%n", argv[0]); + kprintf(" %s tcp server [ip port]%n", argv[0]); + kprintf(" %s udp client [ip port]%n", argv[0]); + kprintf(" %s tcp client [ip port]%n", argv[0]); + exit(1); +} + +void UdpServer(void) { + ssize_t rc; + struct sockaddr_in addr2; + uint32_t addrsize2 = sizeof(struct sockaddr_in); + CHECK_NE(-1, (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))); + CHECK_NE(-1, bind(sock, &addr, addrsize)); + CHECK_NE(-1, getsockname(sock, &addr2, &addrsize2)); + kprintf("udp server %hhu.%hhu.%hhu.%hhu %hu%n", addr2.sin_addr.s_addr >> 24, + addr2.sin_addr.s_addr >> 16, addr2.sin_addr.s_addr >> 8, + addr2.sin_addr.s_addr, addr2.sin_port); + for (;;) { + CHECK_NE(-1, + (rc = recvfrom(sock, buf, sizeof(buf), 0, &addr2, &addrsize2))); + CHECK_NE(-1, sendto(sock, buf, rc, 0, &addr2, addrsize2)); + } +} + +void UdpClient(void) { + CHECK_NE(-1, (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))); + CHECK_NE(-1, connect(sock, &addr, addrsize)); + for (;;) { + rngset(buf, sizeof(buf), rand64, -1); + CHECK_NE(-1, write(sock, &addr, addrsize)); + } +} + +void TcpServer(void) { + int client; + ssize_t rc; + struct sockaddr_in addr2; + uint32_t addrsize2 = sizeof(struct sockaddr_in); + CHECK_NE(-1, (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + CHECK_NE(-1, bind(sock, &addr, addrsize)); + CHECK_NE(-1, listen(sock, 10)); + CHECK_NE(-1, getsockname(sock, &addr2, &addrsize2)); + kprintf("tcp server %hhu.%hhu.%hhu.%hhu %hu%n", addr2.sin_addr.s_addr >> 24, + addr2.sin_addr.s_addr >> 16, addr2.sin_addr.s_addr >> 8, + addr2.sin_addr.s_addr, addr2.sin_port); + for (;;) { + addrsize2 = sizeof(struct sockaddr_in); + CHECK_NE(-1, (client = accept(sock, &addr2, &addrsize2))); + kprintf("got client %hhu.%hhu.%hhu.%hhu %hu%n", addr2.sin_addr.s_addr >> 24, + addr2.sin_addr.s_addr >> 16, addr2.sin_addr.s_addr >> 8, + addr2.sin_addr.s_addr, addr2.sin_port); + for (;;) { + CHECK_NE(-1, (rc = read(client, buf, sizeof(buf)))); + if (!rc) break; + CHECK_NE(-1, write(client, buf, rc)); + } + } +} + +void TcpClient(void) { + CHECK_NE(-1, (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + CHECK_NE(-1, connect(sock, &addr, addrsize)); + for (;;) { + rngset(buf, sizeof(buf), rand64, -1); + CHECK_NE(-1, write(sock, buf, sizeof(buf))); + CHECK_NE(-1, read(sock, buf, sizeof(buf))); + } +} + +int main(int argc, char *argv[]) { + int port = 0; + int64_t ip = 0; + if (argc < 3) PrintUsage(argv); + if (argc >= 4) { + if ((ip = ParseIp(argv[3], -1)) == -1) { + PrintUsage(argv); + } + } + if (argc >= 5) { + port = atoi(argv[4]); + if (port & 0xffff0000) { + PrintUsage(argv); + } + } + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(ip); + if (!strcmp(argv[1], "udp")) { + if (!strcmp(argv[2], "server")) { + UdpServer(); + } else if (!strcmp(argv[2], "client")) { + UdpClient(); + } else { + PrintUsage(argv); + } + } else if (!strcmp(argv[1], "tcp")) { + if (!strcmp(argv[2], "server")) { + TcpServer(); + } else if (!strcmp(argv[2], "client")) { + TcpClient(); + } else { + PrintUsage(argv); + } + } else { + PrintUsage(argv); + } + return 0; +} diff --git a/tool/net/echoserver.c b/tool/net/echoserver.c deleted file mode 100644 index 5d2aac821..000000000 --- a/tool/net/echoserver.c +++ /dev/null @@ -1,297 +0,0 @@ -/*-*- 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 2020 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 "libc/alg/arraylist.internal.h" -#include "libc/bits/safemacros.internal.h" -#include "libc/calls/calls.h" -#include "libc/calls/struct/iovec.h" -#include "libc/errno.h" -#include "libc/fmt/fmt.h" -#include "libc/log/check.h" -#include "libc/log/log.h" -#include "libc/runtime/gc.internal.h" -#include "libc/runtime/interruptiblecall.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/exit.h" -#include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/msg.h" -#include "libc/sysv/consts/poll.h" -#include "libc/sysv/consts/so.h" -#include "libc/sysv/consts/sock.h" -#include "libc/sysv/consts/sol.h" -#include "libc/x/x.h" -#include "third_party/getopt/getopt.h" - -/** - * @fileoverview Asynchronous TCP/UDP Echo Server. - * - * make -j8 o/default/tool/net/echoserver.com - * o/default/tool/net/echoserver.com udp:0.0.0.0:7 tcp:0.0.0.0:7 - */ - -enum SocketKind { - kSocketServer, - kSocketClient, -}; - -struct Message { - struct iovec data; - struct sockaddr_in dest; - uint32_t destsize; -}; - -struct Messages { - size_t i, n; - struct Message *p; -}; - -struct Socket { - int64_t fd; - enum SocketKind kind; - int type; - int protocol; - struct sockaddr_in addr; - struct Messages egress; /* LIFO */ -}; - -struct Sockets { - size_t i, n; - struct Socket *p; -}; - -struct Polls { - size_t i, n; - struct pollfd *p; -}; - -struct Sockets g_sockets; -struct Polls g_polls; - -dontdiscard char *DescribeAddress(struct sockaddr_in *addr) { - char ip4buf[16]; - return xasprintf("%s:%hu", - inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf, - sizeof(ip4buf)), - ntohs(addr->sin_port)); -} - -dontdiscard char *DescribeSocket(struct Socket *s) { - return xasprintf("%s:%s", s->protocol == IPPROTO_UDP ? "udp" : "tcp", - gc(DescribeAddress(&s->addr))); -} - -wontreturn void ShowUsageAndExit(bool iserror) { - FILE *f = iserror ? stderr : stdout; - int rc = iserror ? EXIT_FAILURE : EXIT_SUCCESS; - fprintf(f, "%s: %s %s\n", "Usage", __argv[0], "PROTOCOL:ADDR:PORT..."); - exit(rc); -} - -void GetFlags(int argc, char *argv[]) { - int opt; - while ((opt = getopt(argc, argv, "h")) != -1) { - switch (opt) { - case 'h': - ShowUsageAndExit(false); - default: - ShowUsageAndExit(true); - } - } - if (optind == argc) ShowUsageAndExit(true); -} - -void AddSocket(const struct Socket *s) { - struct pollfd pfd; - pfd.fd = s->fd; - pfd.events = POLLIN; - pfd.revents = 0; - CHECK_NE(-1L, append(&g_sockets, s)); - CHECK_NE(-1L, append(&g_polls, &pfd)); -} - -void RemoveSocket(size_t i) { - DCHECK_LT(i, g_sockets.i); - INFOF("removing: %s", gc(DescribeSocket(&g_sockets.p[i]))); - CHECK_NE(-1, close(g_sockets.p[i].fd)); - while (g_sockets.p[i].egress.i) { - free(g_sockets.p[i].egress.p[g_sockets.p[i].egress.i - 1].data.iov_base); - } - memcpy(&g_sockets.p[i], &g_sockets.p[i + 1], - (intptr_t)&g_sockets.p[g_sockets.i] - (intptr_t)&g_sockets.p[i + 1]); - memcpy(&g_polls.p[i], &g_polls.p[i + 1], - (intptr_t)&g_polls.p[g_polls.i] - (intptr_t)&g_polls.p[i + 1]); - g_sockets.i--; - g_polls.i--; -} - -void GetListeningAddressesFromCommandLine(int argc, char *argv[]) { - int i; - for (i = optind; i < argc; ++i) { - struct Socket server; - bzero(&server, sizeof(server)); - char scheme[4]; - unsigned char *ip4 = (unsigned char *)&server.addr.sin_addr.s_addr; - uint16_t port; - if (sscanf(argv[i], "%3s:%hhu.%hhu.%hhu.%hhu:%hu", scheme, &ip4[0], &ip4[1], - &ip4[2], &ip4[3], &port) != 6) { - fprintf(stderr, "error: bad ip4 uri\n"); - ShowUsageAndExit(true); - } - server.fd = -1; - server.kind = kSocketServer; - server.addr.sin_family = AF_INET; - server.addr.sin_port = htons(port); - if (strcasecmp(scheme, "tcp") == 0) { - server.type = SOCK_STREAM; - server.protocol = IPPROTO_TCP; - } else if (strcasecmp(scheme, "udp") == 0) { - server.type = SOCK_DGRAM; - server.protocol = IPPROTO_UDP; - } else { - fprintf(stderr, "%s: %s\n", "error", "bad scheme (should be tcp or udp)"); - ShowUsageAndExit(true); - } - AddSocket(&server); - } -} - -void BeginListeningForIncomingTraffic(void) { - size_t i; - for (i = 0; i < g_sockets.i; ++i) { - int yes = 1; - struct Socket *s = &g_sockets.p[i]; - CHECK_NE(-1L, (g_polls.p[i].fd = s->fd = - socket(s->addr.sin_family, s->type, s->protocol))); - CHECK_NE(-1L, - setsockopt(s->fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))); - CHECK_NE(-1, bind(s->fd, &s->addr, sizeof(s->addr))); - if (s->protocol == IPPROTO_TCP) { - CHECK_NE(-1, listen(s->fd, 1)); - } - uint32_t addrsize = sizeof(s->addr); - CHECK_NE(-1, getsockname(s->fd, &s->addr, &addrsize)); - INFOF("listening on %s", gc(DescribeSocket(s))); - } -} - -void AcceptConnection(size_t i) { - struct Socket *server = &g_sockets.p[i]; - struct Socket client; - bzero(&client, sizeof(client)); - client.kind = kSocketClient; - client.type = server->type; - client.protocol = server->protocol; - uint32_t addrsize = sizeof(client.addr); - CHECK_NE(-1L, (client.fd = accept(server->fd, &client.addr, &addrsize))); - INFOF("%s accepted %s", gc(DescribeSocket(server)), - gc(DescribeSocket(&client))); - AddSocket(&client); -} - -bool ReceiveData(size_t i) { - ssize_t got; - struct Message msg; - bool isudp = g_sockets.p[i].protocol == IPPROTO_UDP; - bzero(&msg, sizeof(msg)); - msg.destsize = sizeof(msg.dest); - msg.data.iov_len = PAGESIZE; - msg.data.iov_base = xmalloc(msg.data.iov_len); - CHECK_NE(-1L, (got = recvfrom(g_sockets.p[i].fd, msg.data.iov_base, - msg.data.iov_len, 0, isudp ? &msg.dest : NULL, - isudp ? &msg.destsize : NULL))); - if (0 < got && got <= msg.data.iov_len) { - INFOF("%s received %lu bytes from %s", gc(DescribeSocket(&g_sockets.p[i])), - got, gc(DescribeAddress(&msg.dest))); - msg.data.iov_base = xrealloc(msg.data.iov_base, (msg.data.iov_len = got)); - append(&g_sockets.p[i].egress, &msg); - g_polls.p[i].events |= POLLOUT; - return true; - } else { - RemoveSocket(i); - free_s(&msg.data.iov_base); - return false; - } -} - -void SendData(size_t i) { - ssize_t sent; - struct Socket *s = &g_sockets.p[i]; - struct Message *msg = &s->egress.p[s->egress.i - 1]; - bool isudp = s->protocol == IPPROTO_UDP; - DCHECK(s->egress.i); - CHECK_NE(-1L, (sent = sendto(s->fd, msg->data.iov_base, msg->data.iov_len, 0, - isudp ? &msg->dest : NULL, - isudp ? msg->destsize : 0))); - INFOF("%s sent %lu bytes to %s", gc(DescribeSocket(s)), msg->data.iov_len, - gc(DescribeAddress(&msg->dest))); - if (!(msg->data.iov_len -= min((size_t)sent, (size_t)msg->data.iov_len))) { - free_s(&msg->data.iov_base); - if (!--s->egress.i) { - g_polls.p[i].events &= ~POLLOUT; - } - } -} - -void HandleSomeNetworkTraffic(void) { - size_t i; - int eventcount; - CHECK_GE((eventcount = poll(g_polls.p, g_polls.i, -1)), 0); - for (i = 0; eventcount && i < g_sockets.i; ++i) { - if (!g_polls.p[i].revents) continue; - --eventcount; - if (g_polls.p[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { - CHECK_EQ(kSocketClient, g_sockets.p[i].kind); - RemoveSocket(i); - } else { - if (g_polls.p[i].revents & POLLIN) { - if (g_sockets.p[i].kind == kSocketServer && - g_sockets.p[i].protocol == IPPROTO_TCP) { - AcceptConnection(i); - } else { - if (!ReceiveData(i)) continue; - } - } - if (g_polls.p[i].revents & POLLOUT) { - SendData(i); - } - } - } -} - -void EchoServer(void) { - for (;;) HandleSomeNetworkTraffic(); -} - -int main(int argc, char *argv[]) { - STATIC_YOINK("isatty"); - GetFlags(argc, argv); - GetListeningAddressesFromCommandLine(argc, argv); - BeginListeningForIncomingTraffic(); - struct InterruptibleCall icall; - bzero(&icall, sizeof(icall)); - interruptiblecall(&icall, (void *)EchoServer, 0, 0, 0, 0); - fputc('\r', stderr); - INFOF("%s", "shutting down..."); - size_t i; - for (i = g_sockets.i; i; --i) RemoveSocket(i - 1); - return 0; -} diff --git a/tool/net/help.txt b/tool/net/help.txt index 1f9f18cd5..e3a45e91d 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1276,629 +1276,662 @@ MAXMIND MODULE UNIX MODULE - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, errno:int + This module exposes the low-level UNIX system call interface. The way + these functions work is they'll only throw a Lua exception if there's + some kind of error obtaining the required arguments. Once Lua reads + the arguments and the call is delegated to the system call interface, + all further errors won't be raised, but rather returned as errnos, + which should always be checked. For example, most calls follow: - Reads from file descriptor. + rc, errno = unix.foo(...) - unix.write(fd:int, data[, offset]) → rc:int[, errno:int] + Where the underlying call returning an rc of -1 will map to a Lua + value of nil, and if the system call doesn't return an error number + then errno will be nil and rc will be non-nil. To see which errnos are + possible for which system calls, please see the comprehensive index at + the bottom of this section. - Writes to file descriptor. + Your system calls are: - unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] + unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int] - Opens file. + Reads from file descriptor. - `flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`. - The following values may also be OR'd into `flags`: + unix.write(fd:int, data[, offset]) → rc:int[, errno:int] - - `O_CREAT`: create file if it doesn't exist - - `O_TRUNC` automatic ftruncate(fd,0) if exists - - `O_CLOEXEC`: automatic close() upon execve() - - `O_EXCL`: exclusive access (see below) - - `O_APPEND`: open file for append only - - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD) - - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT) - - `O_TMPFILE` try to make temp more secure (Linux and Windows only) - - `O_NOFOLLOW` fail if it's a symlink (zero on Windows) - - `O_DSYNC` it's complicated (zero on non-Linux/Apple) - - `O_RSYNC` it's complicated (zero on non-Linux/Apple) - - `O_PATH` it's complicated (zero on non-Linux) - - `O_VERIFY` it's complicated (zero on non-FreeBSD) - - `O_SHLOCK` it's complicated (zero on non-BSD) - - `O_EXLOCK` it's complicated (zero on non-BSD) - - `O_RANDOM` hint random access intent (zero on non-Windows) - - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows) - - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows) - - `O_INDEXED` turns on that slow performance (zero on non-Windows) + Writes to file descriptor. - There are three regular combinations for the above flags: + unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] - - `O_RDONLY`: Opens existing file for reading. If it doesn't - exist then nil is returned and errno will be `ENOENT` (or in - some other cases `ENOTDIR`). + Opens file. - - `O_WRONLY|O_CREAT|O_TRUNC`: Creates file. If it already - exists, then the existing copy is destroyed and the opened - file will start off with a length of zero. This is the - behavior of the traditional creat() system call. + `flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`. + The following values may also be OR'd into `flags`: - - `O_WRONLY|O_CREAT|O_EXCL`: Create file only if doesn't exist - already. If it does exist then `nil` is returned along with - `errno` set to `EEXIST`. + - `O_CREAT`: create file if it doesn't exist + - `O_TRUNC` automatic ftruncate(fd,0) if exists + - `O_CLOEXEC`: automatic close() upon execve() + - `O_EXCL`: exclusive access (see below) + - `O_APPEND`: open file for append only + - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD) + - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT) + - `O_TMPFILE` try to make temp more secure (Linux and Windows only) + - `O_NOFOLLOW` fail if it's a symlink (zero on Windows) + - `O_DSYNC` it's complicated (zero on non-Linux/Apple) + - `O_RSYNC` it's complicated (zero on non-Linux/Apple) + - `O_PATH` it's complicated (zero on non-Linux) + - `O_VERIFY` it's complicated (zero on non-FreeBSD) + - `O_SHLOCK` it's complicated (zero on non-BSD) + - `O_EXLOCK` it's complicated (zero on non-BSD) + - `O_RANDOM` hint random access intent (zero on non-Windows) + - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows) + - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows) + - `O_INDEXED` turns on that slow performance (zero on non-Windows) - unix.close(fd:int) → rc:int[, errno:int] - - Closes file descriptor. + There are three regular combinations for the above flags: - unix.exit([exitcode]) → ⊥ + - `O_RDONLY`: Opens existing file for reading. If it doesn't + exist then nil is returned and errno will be `ENOENT` (or in + some other cases `ENOTDIR`). - Invokes `_Exit(exitcode)` on the process. This will immediately - halt the current process. Memory will be freed. File descriptors - will be closed. Any open connections it owns will be reset. This - function never returns. + - `O_WRONLY|O_CREAT|O_TRUNC`: Creates file. If it already + exists, then the existing copy is destroyed and the opened + file will start off with a length of zero. This is the + behavior of the traditional creat() system call. - unix.fork() → childpid|0:int[, errno:int] + - `O_WRONLY|O_CREAT|O_EXCL`: Create file only if doesn't exist + already. If it does exist then `nil` is returned along with + `errno` set to `EEXIST`. - Creates a new process mitosis style. This returns twice. The - parent process gets the nonzero pid. The child gets zero. + unix.close(fd:int) → rc:int[, errno:int] + + Closes file descriptor. - unix.commandv(prog:str) → path:str[, errno:int] + unix.exit([exitcode]) → ⊥ - Performs `$PATH` lookup of executable. We automatically suffix - `.com` and `.exe` for all platforms when path searching. - By default, the current directory is not on the path. - If `prog` is an absolute path, then it's returned as-is. If - `prog` contains slashes then it's not path searched either and - will be returned if it exists. + Invokes `_Exit(exitcode)` on the process. This will immediately + halt the current process. Memory will be freed. File descriptors + will be closed. Any open connections it owns will be reset. This + function never returns. - 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(). `env` defaults to to the current `environ`. Here's - a basic usage example: - - unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"}) - unix.exit(127) - - `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. + unix.fork() → childpid|0:int[, errno:int] + + Creates a new process mitosis style. This returns twice. The + parent process gets the nonzero pid. The child gets zero. - Here's an example of how pipe(), fork(), dup(), etc. may be used - to serve an HTTP response containing the output of a subprocess. + unix.commandv(prog:str) → path:str[, errno:int] - 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 + Performs `$PATH` lookup of executable. We automatically suffix + `.com` and `.exe` for all platforms when path searching. + By default, the current directory is not on the path. + If `prog` is an absolute path, then it's returned as-is. If + `prog` contains slashes then it's not path searched either and + will be returned if it exists. + + 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(). `env` defaults to to the current `environ`. Here's + a basic usage example: + + unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"}) + unix.exit(127) + + `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. - unix.wait([pid:int, options:int]) - → pid:int, wstatus:int, nil, errno:int + Here's an example of how pipe(), fork(), dup(), etc. may be used + to serve an HTTP response containing the output of a subprocess. - Waits for subprocess to terminate. + 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 - `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 + unix.wait([pid:int, options:int]) + → pid:int, wstatus:int, nil, errno:int - 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. + Waits for subprocess to terminate. - Returns the process id of the child that terminated. In other - cases, the returned `pid` is nil and `errno` is non-nil. + `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 - 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: + 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. - -- 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 + Returns the process id of the child that terminated. In other + cases, the returned `pid` is nil and `errno` is non-nil. - unix.getpid() → pid + 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: - Returns process id of current process. + -- 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.getppid() → pid + unix.getpid() → pid - Returns process id of parent process. + Returns process id of current process. - unix.kill(pid, sig) → rc:int[, errno:int] + unix.getppid() → pid - Returns process id of current process. + Returns process id of parent process. - unix.raise(sig) → rc:int[, errno:int] + unix.kill(pid, sig) → rc:int[, errno:int] - Triggers signal in current process. - This is pretty much the same as `kill(getpid(), sig)`. + Returns process id of current process. - unix.access(path:str, how) → rc:int[, errno:int] + unix.raise(sig) → rc:int[, errno:int] - Checks if effective user of current process has permission to - access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to - check for read, write, execute, and existence respectively. + Triggers signal in current process. + This is pretty much the same as `kill(getpid(), sig)`. - unix.mkdir(path:str, mode) → rc:int[, errno:int] + unix.access(path:str, how) → rc:int[, errno:int] - Makes directory. `mode` should be octal, e.g. `0755`. + Checks if effective user of current process has permission to + access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to + check for read, write, execute, and existence respectively. - unix.chdir(path:str) → rc:int[, errno:int] + unix.mkdir(path:str, mode) → rc:int[, errno:int] - Changes current directory to `path`. + Makes directory. `mode` should be octal, e.g. `0755`. - unix.unlink(path:str) → rc:int[, errno:int] + unix.chdir(path:str) → rc:int[, errno:int] - Removes file at `path`. + Changes current directory to `path`. - unix.rmdir(path:str) → rc:int[, errno:int] + unix.unlink(path:str) → rc:int[, errno:int] - Removes empty directory at `path`. + Removes file at `path`. - unix.chroot(path:str) → rc:int[, errno:int] + unix.rmdir(path:str) → rc:int[, errno:int] - Changes root directory. Raises `ENOSYS` on Windows. + Removes empty directory at `path`. - unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] + unix.chroot(path:str) → rc:int[, errno:int] - Renames file. + Changes root directory. Raises `ENOSYS` on Windows. - unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] + unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] - Creates hard link, so your underlying inode has two names. + Renames file. - unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] + unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] - Creates soft link, or a symbolic link. + Creates hard link, so your underlying inode has two names. - unix.realpath(filename:str) → abspath:str[, errno:int] + unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] - Returns absolute path of filename, with `.` and `..` components - removed, and symlinks will be resolved. + Creates soft link, or a symbolic link. - unix.chown(path:str, uid, gid) → rc:int[, errno:int] + unix.realpath(filename:str) → abspath:str[, errno:int] - Changes user and gorup on file. + Returns absolute path of filename, with `.` and `..` components + removed, and symlinks will be resolved. - unix.chmod(path:str, mode) → rc:int[, errno:int] + unix.chown(path:str, uid, gid) → rc:int[, errno:int] - Changes mode bits on file. + Changes user and gorup on file. - unix.getcwd(path:str, mode) → rc:int[, errno:int] + unix.chmod(path:str, mode) → rc:int[, errno:int] - Returns current working directory. + Changes mode bits on file. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] + unix.getcwd(path:str, mode) → rc:int[, errno:int] - Manipulates file descriptor. + Returns current working directory. - 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`. + unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] - POSIX advisory locks can be controlled by setting `cmd` to - `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. + Manipulates file descriptor. - unix.getsid(pid) → sid, errno:int + 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`. - Gets session id. + POSIX advisory locks can be controlled by setting `cmd` to + `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getpgrp() → pgid, errno:int + unix.getsid(pid) → sid, errno:int - Gets process group id. + Gets session id. - unix.setpgrp() → pgid, errno:int + unix.getpgrp() → pgid, errno:int - Sets process group id. This is the same as `setpgid(0,0)`. + Gets process group id. - unix.setpgid(pid, pgid) → pgid, errno:int + unix.setpgrp() → pgid, errno:int - Sets process group id the modern way. + Sets process group id. This is the same as `setpgid(0,0)`. - unix.getpgid(pid) → pgid, errno:int + unix.setpgid(pid, pgid) → pgid, errno:int - Gets process group id the modern wayp. + Sets process group id the modern way. - unix.setsid() → sid, errno:int + unix.getpgid(pid) → pgid, errno:int - Sets session id. + Gets process group id the modern wayp. - This function can be used to create daemons. + unix.setsid() → sid, errno:int - unix.getuid() → uid:int + Sets session id. - Gets user id. + This function can be used to create daemons. - unix.getgid() → gid:int + unix.getuid() → uid:int - Sets group id. + Gets user id. - unix.setuid(uid:int) → rc:int[, errno:int] + unix.getgid() → gid:int - Sets user id. + Sets group id. - unix.setgid(gid:int) → rc:int[, errno:int] + unix.setuid(uid:int) → rc:int[, errno:int] - Sets group id. + Sets user id. - unix.umask(mask) → oldmask:int + unix.setgid(gid:int) → rc:int[, errno:int] - Sets file permission mask and returns the old one. + Sets group id. - unix.syslog(priority:int, msg:str) + unix.umask(mask) → oldmask:int - Generates a log message, which will be distributed by syslogd. + Sets file permission mask and returns the old one. - `priority` is a bitmask containing the facility value and the - level value. If no facility value is ORed into priority, then - the default value set by openlog() is used. If set to NULL, the - program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`, - `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, - `LOG_DEBUG`. + unix.syslog(priority:int, msg:str) - This function currently works on Linux, Windows, and NetBSD. On - WIN32 it uses the ReportEvent() facility. + Generates a log message, which will be distributed by syslogd. - unix.clock_gettime([clock]) → seconds, nanos, errno:int + `priority` is a bitmask containing the facility value and the + level value. If no facility value is ORed into priority, then + the default value set by openlog() is used. If set to NULL, the + program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`, + `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, + `LOG_DEBUG`. - Returns nanosecond precision timestamp from the system. + This function currently works on Linux, Windows, and NetBSD. On + WIN32 it uses the ReportEvent() facility. - `clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or - `CLOCK_MONOTONIC_RAW` since they work across platforms. - You may also try your luck with `CLOCK_REALTIME_COARSE`, - `CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`, - `CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`, - `CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, + unix.clock_gettime([clock]) → seconds, nanos, errno:int - unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int + Returns nanosecond precision timestamp from the system. - Sleeps with nanosecond precision. + `clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or + `CLOCK_MONOTONIC_RAW` since they work across platforms. + You may also try your luck with `CLOCK_REALTIME_COARSE`, + `CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`, + `CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`, + `CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, - unix.sync(fd:int) - unix.fsync(fd:int) → rc:int[, errno:int] - unix.fdatasync(fd:int) → rc:int[, errno:int] + unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int - These functions are used to make programs slower by asking the - operating system to flush data to the physical medium. + Sleeps with nanosecond precision. - unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] + unix.sync(fd:int) + unix.fsync(fd:int) → rc:int[, errno:int] + unix.fdatasync(fd:int) → rc:int[, errno:int] - Seeks to file position. + These functions are used to make programs slower by asking the + operating system to flush data to the physical medium. - `whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`. + unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] - unix.truncate(path:str, length) → rc:int[, errno:int] - unix.ftruncate(fd:int, length) → rc:int[, errno:int] + Seeks to file position. - Reduces or extends underlying physical medium of file. - If file was originally larger, content >length is lost. + `whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`. - unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int] + unix.truncate(path:str, length) → rc:int[, errno:int] + unix.ftruncate(fd:int, length) → rc:int[, errno:int] - `SOCK_CLOEXEC` may be or'd into type - `family` defaults to `AF_INET` but can be `AF_UNIX` - `type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM` - `protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP` + Reduces or extends underlying physical medium of file. + If file was originally larger, content >length is lost. - unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int + unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int] - `SOCK_CLOEXEC` may be or'd into type - `family` defaults to `AF_INET` - `type` defaults to `SOCK_STREAM` - `protocol` defaults to `IPPROTO_TCP` + `SOCK_CLOEXEC` may be or'd into type + `family` defaults to `AF_INET` but can be `AF_UNIX` + `type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM` + `protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → rc:int[, errno:int] + unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int - Binds socket. + `SOCK_CLOEXEC` may be or'd into type + `family` defaults to `AF_INET` + `type` defaults to `SOCK_STREAM` + `protocol` defaults to `IPPROTO_TCP` - `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(fd:int[, ip:uint32, port:uint16]) → rc:int[, errno:int] - unix.bind(sock, 10 << 24 | 0 << 16 | 0 << 8 | 1, 31337) + Binds socket. - 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: + `ip` and `port` are in host endian order. For example, if you + wanted to listen on `1.2.3.4:31337` you could do any of these - 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.bind(sock, 0x01020304, 31337) + unix.bind(sock, 1 << 24 | 0 << 16 | 0 << 8 | 1, 31337) - unix.listen(fd:int[, backlog]) → rc:int[, errno:int] + `ip` and `port` both default to zero. The meaning of bind(0, 0) + is to listen on all interfaces with a kernel-assigned ephemeral + port number, that can be retrieved and used as follows: - Listens for incoming connections on bound socket. + local sock = unix.socket() -- create ipv4 tcp socket + unix.bind(sock) -- all interfaces arbitrary port + 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.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] + Further note that calling `unix.bind(sock)` is equivalent to not + calling bind() at all, since the above behavior is the default. - unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] + unix.listen(fd:int[, backlog]) → rc:int[, errno:int] - unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] + Begins listening for incoming connections on a socket. - unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] - unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int + Accepts new client socket descriptor for a listening tcp socket. - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] - unix.recvfrom(fd:int[, bufsiz[, flags]]) → data, ip, port, errno:int + Connects a TCP socket to a remote host. - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + With TCP this is a blocking operation. For a UDP socket it simply + remembers the intended address so that send() or write() may be used + rather than sendto(). - unix.send(fd:int, data[, flags]) → sent, errno:int + unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] - This is the same as `write` except it has a `flags` argument - that's intended for sockets. `flags` can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + Retrieves the local address of a socket. - unix.sendto(fd:int, data, ip, port[, flags]) → sent, errno:int + unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] - This is useful for sending messages over UDP sockets to specific - addresses. The `flags` parameter can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + Retrieves the remote address of a socket. - unix.shutdown(fd:int, how:int) → rc:int[, errno:int] + unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int - Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or - `SHUT_RDWR`. + `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. - unix.sigprocmask(how[, mask]) → oldmask, errno:int + unix.recvfrom(fd:int[, bufsiz[, flags]]) → data, ip, port, errno:int - `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` + `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. - unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, flags:int, mask:int, errno:int + unix.send(fd:int, data[, flags]) → sent, errno:int - `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua - function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. - `flags` can have `SA_RESTART`, `SA_RESETHAND`, etc. Example: + This is the same as `write` except it has a `flags` argument + that's intended for sockets. `flags` can have `MSG_OOB`, + `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. - unix = require "unix" - unix.sigaction(unix.SIGUSR1, function(sig) - print(string.format("got %s", unix.strsignal(sig))) - end) - unix.sigprocmask(unix.SIG_SETMASK, -1) - unix.raise(unix.SIGUSR1) - unix.sigsuspend() + unix.sendto(fd:int, data, ip, port[, flags]) → sent, errno:int - It's a good idea to not do too much work in a signal handler. + This is useful for sending messages over UDP sockets to specific + addresses. The `flags` parameter can have `MSG_OOB`, + `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. - unix.sigsuspend([mask]) → errno + unix.shutdown(fd:int, how:int) → rc:int[, errno:int] - Waits for signal to be delivered. + Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or + `SHUT_RDWR`. - unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, intns, valsec, valns, errno:int + unix.sigprocmask(how[, mask]) → oldmask, errno:int - Causes `SIGALRM` signals to be generated at some point(s) in the - future. The `which` parameter should be `ITIMER_REAL`. + `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` - Here's an example of how to create a 400 ms interval timer: + unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) + → oldhandler:func|int, flags:int, mask:int, errno:int - ticks = 0 - unix.sigaction(unix.SIGALRM, function(sig) - print(string.format("tick no. %d", ticks)) - ticks = ticks + 1 - end) - unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) - while true do - unix.sigsuspend() - end + `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua + function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. + `flags` can have `SA_RESTART`, `SA_RESETHAND`, etc. Example: - Here's how you'd do a single-shot timeout in 1 second: + unix = require "unix" + unix.sigaction(unix.SIGUSR1, function(sig) + print(string.format("got %s", unix.strsignal(sig))) + end) + unix.sigprocmask(unix.SIG_SETMASK, -1) + unix.raise(unix.SIGUSR1) + unix.sigsuspend() - unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) - unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) + It's a good idea to not do too much work in a signal handler. - unix.reboot(how:int) → str + unix.sigsuspend([mask]) → errno - Changes power status of system. + Waits for signal to be delivered. - `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. + unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) + → intsec, intns, valsec, valns, errno:int - 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`. + Causes `SIGALRM` signals to be generated at some point(s) in the + future. The `which` parameter should be `ITIMER_REAL`. - unix.strerrno(errno:int) → str + Here's an example of how to create a 400 ms interval timer: - Turns `errno` code into its symbolic name, e.g. `"EINTR"`. + ticks = 0 + unix.sigaction(unix.SIGALRM, function(sig) + print(string.format("tick no. %d", ticks)) + ticks = ticks + 1 + end) + unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) + while true do + unix.sigsuspend() + end - unix.strerror(errno:int) → str + Here's how you'd do a single-shot timeout in 1 second: - Turns `errno` code into a longer string describing the error. + unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) + unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strsignal(sig:int) → str + unix.reboot(how:int) → str - Turns platform-specific `sig` code into its name, e.g. - `strsignal(9)` always returns `"SIGKILL"`. + Changes power status of system. - unix.stat(x) → UnixStat*, errno:int + `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. - Gets information about file or directory. `x` may be a file or - directory path string, or it may be a file descriptor int that - was made by open(). + 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.opendir(path:str) → UnixDir*, errno:int + unix.strerrno(errno:int) → str - Opens directory for listing its contents. + Turns `errno` code into its symbolic name, e.g. `"EINTR"`. - unix.opendir(fd:int) → UnixDir*, errno:int + unix.strerror(errno:int) → str - Opens directory for listing its contents, using a file - descriptor from `open(path, O_RDONLY|O_DIRECTORY)`. + Turns `errno` code into a longer string describing the error. + + unix.strsignal(sig:int) → str + + Turns platform-specific `sig` code into its name, e.g. + `strsignal(9)` always returns `"SIGKILL"`. + + unix.stat(x) → UnixStat*, errno:int + + Gets information about file or directory. `x` may be a file or + directory path string, or it may be a file descriptor int that + was made by open(). + + unix.opendir(path:str) → UnixDir*, errno:int + + Opens directory for listing its contents. + + unix.opendir(fd:int) → UnixDir*, errno:int + + Opens directory for listing its contents, using a file + descriptor from `open(path, O_RDONLY|O_DIRECTORY)`. UnixDir* Object - UnixDir:close() → rc:int[, errno:int] + UnixDir:close() → rc:int[, errno:int] - may be called multiple times - called by the garbage collector too + may be called multiple times + called by the garbage collector too - UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] + UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] - Returns `nil` if there are no more entries. Or error, `nil` will - be returned and `errno` will be non-nil. + Returns `nil` if there are no more entries. Or error, `nil` will + be returned and `errno` will be non-nil. - `kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`, - `DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`. + `kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`, + `DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`. - UnixDir:fd() → fd:int[, errno:int] + UnixDir:fd() → fd:int[, errno:int] - EOPNOTSUPP if using /zip/ - EOPNOTSUPP if IsWindows() + EOPNOTSUPP if using /zip/ + EOPNOTSUPP if IsWindows() - UnixDir:tell() → offset:int + UnixDir:tell() → offset:int - Returns current arbitrary offset into stream. + Returns current arbitrary offset into stream. - UnixDir:rewind() + UnixDir:rewind() - Resets stream back to beginning. + Resets stream back to beginning. - UnixStat* object. + UnixStat* object. - UnixStat:size() → bytes:int + UnixStat:size() → bytes:int - Size of file in bytes. + Size of file in bytes. - UnixStat:mode() → mode:int + UnixStat:mode() → mode:int - Contains file type and permissions. + Contains file type and permissions. - For example, `0010644` is what you might see for a file and - `0040755` is what you might see for a directory. + For example, `0010644` is what you might see for a file and + `0040755` is what you might see for a directory. - To determine the file type: + To determine the file type: - - `(st:mode() & 0170000) == 0010000` means fifo or pipe - - `(st:mode() & 0170000) == 0020000` means character device - - `(st:mode() & 0170000) == 0040000` means directory - - `(st:mode() & 0170000) == 0060000` means block device - - `(st:mode() & 0170000) == 0100000` means regular file - - `(st:mode() & 0170000) == 0120000` means symbolic link - - `(st:mode() & 0170000) == 0140000` means socket + - `(st:mode() & 0170000) == 0010000` means fifo or pipe + - `(st:mode() & 0170000) == 0020000` means character device + - `(st:mode() & 0170000) == 0040000` means directory + - `(st:mode() & 0170000) == 0060000` means block device + - `(st:mode() & 0170000) == 0100000` means regular file + - `(st:mode() & 0170000) == 0120000` means symbolic link + - `(st:mode() & 0170000) == 0140000` means socket - UnixStat:atim() → secs:int, nanos:int + UnixStat:atim() → secs:int, nanos:int - Size of file in bytes. + Size of file in bytes. - UnixStat:uid() → int + UnixStat:uid() → int - User ID of file owner. + User ID of file owner. - UnixStat:gid() → int + UnixStat:gid() → int - Group ID of file owner. + Group ID of file owner. - UnixStat:mtim() → secs:int, nanos:int + UnixStat:mtim() → secs:int, nanos:int - Last modified time. + Last modified time. - UnixStat:birthtim() → secs:int, nanos:int + UnixStat:birthtim() → secs:int, nanos:int - Creation time. Note that on Linux this is the mimimum of - atom/mtim/ctim. + Creation time. Note that on Linux this is the mimimum of + atom/mtim/ctim. - UnixStat:ctim() → secs:int, nanos:int + UnixStat:ctim() → secs:int, nanos:int - Complicated time. Means time file status was last changed on - UNIX. Means creation time on Windows. + Complicated time. Means time file status was last changed on + UNIX. Means creation time on Windows. - UnixStat:blocks() → int + UnixStat:blocks() → int - Number of blocks used by storage medium. + Number of blocks used by storage medium. - UnixStat:blksize() → int + UnixStat:blksize() → int - Block size is usually 4096 for file system files. + Block size is usually 4096 for file system files. - UnixStat:dev() → int - UnixStat:ino() → int - UnixStat:rdev() → int + UnixStat:dev() → int + UnixStat:ino() → int + UnixStat:rdev() → int Here are your error numbers: @@ -2007,8 +2040,7 @@ UNIX MODULE - `ENOTBLK`: Block device required. Raised by umount(). - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), - fcntl(), msync(), prctl(), ptrace(), rename(), - rmdir(). + fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). - `EEXIST`: File exists. Raised by bpf(), create_module(), inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), @@ -2077,8 +2109,8 @@ UNIX MODULE - `ENAMETOOLONG`: Filename too long. Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), execve(), gethostname(), inotify_add_watch(), link(), mkdir(), mknod(), open(), - readlink(), rename(), rmdir(), stat(), symlink(), - truncate(), u unlink(), utimensat() + readlink(), rename(), rmdir(), stat(), symlink(), truncate(), u + unlink(), utimensat() - `ENOLCK`: No locks available. Raised by fcntl(), flock(). @@ -2101,9 +2133,9 @@ UNIX MODULE - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), lseek(), mmap(), open(), stat(), statfs() - - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), - connect(), getpeername(), getsockname(), getsockopt(), - listen(), recv(), send(), shutdown(). + - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), + getpeername(), getsockname(), getsockopt(), listen(), recv(), + send(), shutdown(). - `EDESTADDRREQ`: Destination address required. Raised by send(), write(). @@ -2138,11 +2170,10 @@ UNIX MODULE - `EADDRNOTAVAIL`: address not available. Raised by bind(), connect(). - - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised - by accept() + - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() - - `ENETUNREACH`: host is unreachable; ; - WSAENETUNREACH. Raised by accept(), connect() + - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by + accept(), connect() - `ENETRESET`: connection reset by network @@ -2151,11 +2182,10 @@ UNIX MODULE - `ECONNRESET`: connection reset by client. Raised by send(), - - `ENOBUFS`: no buffer space available; - raised by getpeername(), getsockname(), send(), + - `ENOBUFS`: no buffer space available; raised by getpeername(), + getsockname(), send(), - - `EISCONN`: socket is connected. Raised by - connect(), send(). + - `EISCONN`: socket is connected. Raised by connect(), send(). - `ENOTCONN`: socket is not connected. Raised by getpeername(), recv(), send(), shutdown(), diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 0947ddbd5..0a3a758c2 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -381,23 +381,23 @@ static int LuaUnixExecve(lua_State *L) { // unix.commandv(prog:str) → path:str[, errno:int] static int LuaUnixCommandv(lua_State *L) { + int rc, olderr; const char *prog; - int rc, olderr, pushed; char *pathbuf, *resolved; olderr = errno; - pathbuf = xmalloc(PATH_MAX); - prog = luaL_checkstring(L, 1); - if ((resolved = commandv(prog, pathbuf))) { - lua_pushstring(L, resolved); - pushed = 1; + if ((pathbuf = malloc(PATH_MAX))) { + prog = luaL_checkstring(L, 1); + if ((resolved = commandv(prog, pathbuf))) { + lua_pushstring(L, resolved); + free(pathbuf); + return 1; + } else { + free(pathbuf); + return ReturnErrno(L, 1, olderr); + } } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - pushed = 2; + return ReturnErrno(L, 1, olderr); } - free(pathbuf); - return pushed; } // unix.realpath(path:str) → path:str[, errno:int] @@ -720,31 +720,31 @@ static int LuaUnixTruncate(lua_State *L) { static int LuaUnixRead(lua_State *L) { char *buf; size_t got; - int fd, olderr, pushed; + int fd, olderr; int64_t rc, 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 = xmalloc(bufsiz); - if (offset == -1) { - rc = read(fd, buf, bufsiz); + if ((buf = malloc(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 ReturnErrno(L, 1, olderr); + } } else { - rc = pread(fd, buf, bufsiz, offset); + return ReturnErrno(L, 1, olderr); } - if (rc != -1) { - got = rc; - lua_pushlstring(L, buf, got); - pushed = 1; - } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - pushed = 2; - } - free(buf); - return pushed; } // unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int] @@ -773,29 +773,31 @@ static int LuaUnixStat(lua_State *L) { int rc, fd, olderr; struct UnixStat **ust, *st; olderr = errno; - st = xmalloc(sizeof(struct UnixStat)); - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - rc = fstat(fd, &st->st); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - rc = stat(path, &st->st); + if ((st = malloc(sizeof(struct UnixStat)))) { + if (lua_isinteger(L, 1)) { + fd = luaL_checkinteger(L, 1); + rc = fstat(fd, &st->st); + } else if (lua_isstring(L, 1)) { + path = luaL_checkstring(L, 1); + rc = stat(path, &st->st); + } else { + free(st); + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + if (rc != -1) { + st->refs = 1; + ust = lua_newuserdatauv(L, sizeof(st), 1); + luaL_setmetatable(L, "UnixStat*"); + *ust = st; + return 1; + } else { + free(st); + return ReturnErrno(L, 1, olderr); + } } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; + return ReturnErrno(L, 1, olderr); } - if (rc == -1) { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - free(st); - return 2; - } - st->refs = 1; - ust = lua_newuserdatauv(L, sizeof(st), 1); - luaL_setmetatable(L, "UnixStat*"); - *ust = st; - return 1; } // unix.opendir(path:str) → UnixDir*[, errno] @@ -918,7 +920,6 @@ static int LuaUnixGetsockname(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; olderr = errno; - bzero(&sa, sizeof(sa)); addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); if (!getsockname(fd, &sa, &addrsize)) { @@ -936,7 +937,6 @@ static int LuaUnixGetpeername(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; olderr = errno; - bzero(&sa, sizeof(sa)); addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); if (!getpeername(fd, &sa, &addrsize)) { @@ -954,7 +954,6 @@ static int LuaUnixAccept(lua_State *L) { struct sockaddr_in sa; int clientfd, serverfd, olderr; olderr = errno; - bzero(&sa, sizeof(sa)); addrsize = sizeof(sa); serverfd = luaL_checkinteger(L, 1); clientfd = accept(serverfd, &sa, &addrsize); @@ -976,32 +975,29 @@ static int LuaUnixRecvfrom(lua_State *L) { ssize_t rc; uint32_t addrsize; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr, pushed; + int fd, flags, bufsiz, olderr; olderr = errno; - bzero(&sa, sizeof(sa)); addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - buf = xmalloc(bufsiz); - rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); - if (rc != -1) { - got = rc; - lua_pushlstring(L, buf, got); - lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushinteger(L, ntohs(sa.sin_port)); - pushed = 3; + if ((buf = malloc(bufsiz))) { + rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); + if (rc != -1) { + got = rc; + lua_pushlstring(L, buf, got); + lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushinteger(L, ntohs(sa.sin_port)); + free(buf); + return 3; + } else { + free(buf); + return ReturnErrno(L, 3, olderr); + } } else { - lua_pushnil(L); - lua_pushnil(L); - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - pushed = 4; + return ReturnErrno(L, 3, olderr); } - free(buf); - return pushed; } // unix.recv(fd[, bufsiz[, flags]]) → data[, errno] @@ -1015,20 +1011,20 @@ static int LuaUnixRecv(lua_State *L) { bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - buf = xmalloc(bufsiz); - rc = recv(fd, buf, bufsiz, flags); - if (rc != -1) { - got = rc; - lua_pushlstring(L, buf, got); - pushed = 1; + if ((buf = malloc(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 ReturnErrno(L, 1, olderr); + } } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - pushed = 2; + return ReturnErrno(L, 3, olderr); } - free(buf); - return pushed; } // unix.send(fd, data[, flags]) → sent, errno diff --git a/tool/net/net.mk b/tool/net/net.mk index c97461453..df426b8e3 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -16,16 +16,18 @@ TOOL_NET_BINS = \ TOOL_NET_COMS = \ o/$(MODE)/tool/net/dig.com \ - o/$(MODE)/tool/net/echoserver.com \ o/$(MODE)/tool/net/redbean.com \ o/$(MODE)/tool/net/redbean-demo.com \ o/$(MODE)/tool/net/redbean-static.com \ o/$(MODE)/tool/net/redbean-unsecure.com \ o/$(MODE)/tool/net/redbean-original.com \ o/$(MODE)/tool/net/redbean-assimilate.com \ - o/$(MODE)/tool/net/echoserver.com \ o/$(MODE)/tool/net/wb.com +TOOL_NET_CHECKS = \ + o/$(MODE)/tool/net/net.pkg \ + $(TOOL_NET_HDRS:%=o/$(MODE)/%.ok) + TOOL_NET_DIRECTDEPS = \ DSP_SCALE \ LIBC_ALG \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 8d1f76780..f67cd2512 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -2217,25 +2217,20 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) { } } -static const char *GetPagerPath(char path[PATH_MAX]) { - const char *s; - if ((s = commandv("less", path))) return s; - if ((s = commandv("more", path))) return s; - return 0; -} - static wontreturn void PrintUsage(int fd, int rc) { size_t n; int pip[2]; const char *p; struct Asset *a; + char buf[PATH_MAX]; char *args[2] = {0}; - char pathbuf[PATH_MAX]; if (!(a = GetAssetZip("/help.txt", 9)) || !(p = LoadAsset(a, &n))) { fprintf(stderr, "error: /help.txt is not a zip asset\n"); exit(1); } - if (isatty(0) && isatty(1) && (args[0] = GetPagerPath(pathbuf))) { + if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && + ((args[0] = commandv("less", buf)) || + (args[0] = commandv("more", buf)))) { sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0); close(0); pipe(pip); diff --git a/tool/net/sandbox.h b/tool/net/sandbox.h index 2d1da23d4..37a796a80 100644 --- a/tool/net/sandbox.h +++ b/tool/net/sandbox.h @@ -3,6 +3,7 @@ #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/seccomp.h" +#include "libc/sysv/consts/audit.h" // clang-format off #define _SECCOMP_MACHINE(MAGNUM) \ @@ -17,7 +18,15 @@ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, MAGNUM, 0, 1), \ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) +#define _SECCOMP_TRAP_SYSCALL(MAGNUM, DATA) \ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, MAGNUM, 0, 1), \ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP | ((DATA) & SECCOMP_RET_DATA)) + +#define _SECCOMP_TRACE_SYSCALL(MAGNUM, DATA) \ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, MAGNUM, 0, 1), \ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | ((DATA) & SECCOMP_RET_DATA)) + #define _SECCOMP_LOG_AND_RETURN_ERRNO(MAGNUM) \ - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (MAGNUM & SECCOMP_RET_DATA)) + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ((MAGNUM) & SECCOMP_RET_DATA)) #endif /* COSMOPOLITAN_TOOL_NET_SANDBOX_H_ */