Add more apis to redbean unix module

- Document unix.fcntl()
- Add POSIX Advisory Locks
- Add mask parameter to unix.poll()
- Add lowest parameter to unix.dup()
This commit is contained in:
Justine Tunney 2022-08-16 23:23:34 -07:00
parent ce588dd56b
commit a1aaf23dc1
16 changed files with 432 additions and 102 deletions

View file

@ -110,8 +110,9 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
uint32_t flags; uint32_t flags;
if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) { if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) {
if (cmd == F_GETFL) { if (cmd == F_GETFL) {
return g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | return g_fds.p[fd].flags &
O_NOATIME | O_NONBLOCK); (O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME |
O_NONBLOCK | O_RANDOM | O_SEQUENTIAL);
} else if (cmd == F_SETFL) { } else if (cmd == F_SETFL) {
// O_APPEND doesn't appear to be tunable at cursory glance // O_APPEND doesn't appear to be tunable at cursory glance
// O_NONBLOCK might require we start doing all i/o in threads // O_NONBLOCK might require we start doing all i/o in threads
@ -131,7 +132,7 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
g_fds.p[fd].flags &= ~O_CLOEXEC; g_fds.p[fd].flags &= ~O_CLOEXEC;
return 0; return 0;
} }
} else if (cmd == F_SETLK || cmd == F_SETLKW) { } else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
return sys_fcntl_nt_lock(g_fds.p + fd, cmd, arg); return sys_fcntl_nt_lock(g_fds.p + fd, cmd, arg);
} else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) { } else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) {
return sys_fcntl_nt_dupfd(fd, cmd, arg); return sys_fcntl_nt_dupfd(fd, cmd, arg);

View file

@ -74,14 +74,12 @@ void flock2cosmo(uintptr_t memory) {
l_pid = u->linux.l_pid; l_pid = u->linux.l_pid;
l_type = u->linux.l_type; l_type = u->linux.l_type;
l_whence = u->linux.l_whence; l_whence = u->linux.l_whence;
l_sysid = 0;
} else if (IsXnu()) { } else if (IsXnu()) {
l_start = u->xnu.l_start; l_start = u->xnu.l_start;
l_len = u->xnu.l_len; l_len = u->xnu.l_len;
l_pid = u->xnu.l_pid; l_pid = u->xnu.l_pid;
l_type = u->xnu.l_type; l_type = u->xnu.l_type;
l_whence = u->xnu.l_whence; l_whence = u->xnu.l_whence;
l_sysid = 0;
} else if (IsFreebsd()) { } else if (IsFreebsd()) {
l_start = u->freebsd.l_start; l_start = u->freebsd.l_start;
l_len = u->freebsd.l_len; l_len = u->freebsd.l_len;
@ -89,20 +87,19 @@ void flock2cosmo(uintptr_t memory) {
l_type = u->freebsd.l_type; l_type = u->freebsd.l_type;
l_whence = u->freebsd.l_whence; l_whence = u->freebsd.l_whence;
l_sysid = u->freebsd.l_sysid; l_sysid = u->freebsd.l_sysid;
u->cosmo.l_sysid = l_sysid;
} else if (IsOpenbsd()) { } else if (IsOpenbsd()) {
l_start = u->openbsd.l_start; l_start = u->openbsd.l_start;
l_len = u->openbsd.l_len; l_len = u->openbsd.l_len;
l_pid = u->openbsd.l_pid; l_pid = u->openbsd.l_pid;
l_type = u->openbsd.l_type; l_type = u->openbsd.l_type;
l_whence = u->openbsd.l_whence; l_whence = u->openbsd.l_whence;
l_sysid = 0;
} else if (IsNetbsd()) { } else if (IsNetbsd()) {
l_start = u->netbsd.l_start; l_start = u->netbsd.l_start;
l_len = u->netbsd.l_len; l_len = u->netbsd.l_len;
l_pid = u->netbsd.l_pid; l_pid = u->netbsd.l_pid;
l_type = u->netbsd.l_type; l_type = u->netbsd.l_type;
l_whence = u->netbsd.l_whence; l_whence = u->netbsd.l_whence;
l_sysid = 0;
} else { } else {
return; return;
} }
@ -111,7 +108,6 @@ void flock2cosmo(uintptr_t memory) {
u->cosmo.l_pid = l_pid; u->cosmo.l_pid = l_pid;
u->cosmo.l_type = l_type; u->cosmo.l_type = l_type;
u->cosmo.l_whence = l_whence; u->cosmo.l_whence = l_whence;
u->cosmo.l_sysid = l_sysid;
} }
void cosmo2flock(uintptr_t memory) { void cosmo2flock(uintptr_t memory) {

View file

@ -61,17 +61,22 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
struct sys_pollfd_nt sockfds[64]; struct sys_pollfd_nt sockfds[64];
int pipeindices[ARRAYLEN(pipefds)]; int pipeindices[ARRAYLEN(pipefds)];
int sockindices[ARRAYLEN(sockfds)]; int sockindices[ARRAYLEN(sockfds)];
int i, sn, pn, failed, gotinvals, gotpipes, gotsocks, waitfor; int i, rc, sn, pn, gotinvals, gotpipes, gotsocks, waitfor;
// check for interrupts early before doing work // check for interrupts early before doing work
if (sigmask && __sig_mask(SIG_SETMASK, sigmask, &oldmask)) return -1; if (sigmask) {
if (_check_interrupts(false, g_fds.p)) return eintr(); __sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
goto ReturnPath;
}
// do the planning // do the planning
// we need to read static variables // we need to read static variables
// we might need to spawn threads and open pipes // we might need to spawn threads and open pipes
__fds_lock(); __fds_lock();
for (gotinvals = failed = sn = pn = i = 0; i < nfds; ++i) { for (gotinvals = rc = sn = pn = i = 0; i < nfds; ++i) {
if (fds[i].fd < 0) continue; if (fds[i].fd < 0) continue;
if (__isfdopen(fds[i].fd)) { if (__isfdopen(fds[i].fd)) {
if (__isfdkind(fds[i].fd, kFdSocket)) { if (__isfdkind(fds[i].fd, kFdSocket)) {
@ -85,7 +90,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
++sn; ++sn;
} else { } else {
// too many socket fds // too many socket fds
failed = enomem(); rc = enomem();
break; break;
} }
} else if (pn < ARRAYLEN(pipefds)) { } else if (pn < ARRAYLEN(pipefds)) {
@ -109,7 +114,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
++pn; ++pn;
} else { } else {
// too many non-socket fds // too many non-socket fds
failed = enomem(); rc = enomem();
break; break;
} }
} else { } else {
@ -117,9 +122,9 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
} }
} }
__fds_unlock(); __fds_unlock();
if (failed) { if (rc) {
// failed to create a polling solution // failed to create a polling solution
return failed; goto ReturnPath;
} }
// perform the i/o and sleeping and looping // perform the i/o and sleeping and looping
@ -164,7 +169,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms); POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms);
#endif #endif
if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) { if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) {
return __winsockerr(); rc = __winsockerr();
goto ReturnPath;
} }
*ms -= waitfor; *ms -= waitfor;
} else { } else {
@ -188,7 +194,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
// otherwise loop limitlessly for timeout to elapse while // otherwise loop limitlessly for timeout to elapse while
// checking for signal delivery interrupts, along the way // checking for signal delivery interrupts, along the way
if (_check_interrupts(false, g_fds.p)) { if (_check_interrupts(false, g_fds.p)) {
return eintr(); rc = eintr();
goto ReturnPath;
} }
} }
@ -209,5 +216,11 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
} }
// and finally return // and finally return
return gotinvals + gotpipes + gotsocks; rc = gotinvals + gotpipes + gotsocks;
ReturnPath:
if (sigmask) {
__sig_mask(SIG_SETMASK, &oldmask, 0);
}
return rc;
} }

View file

@ -7,7 +7,7 @@ struct flock { /* cosmopolitan abi */
int16_t l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ int16_t l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
int16_t l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ int16_t l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
int64_t l_start; /* starting offset */ int64_t l_start; /* starting offset */
int64_t l_len; /* 0 means until end of file */ int64_t l_len; /* no. bytes (0 means to end of file) */
int32_t l_pid; /* lock owner */ int32_t l_pid; /* lock owner */
int32_t l_sysid; /* remote system id or zero for local */ int32_t l_sysid; /* remote system id or zero for local */
}; };

View file

@ -61,6 +61,7 @@ kOpenFlags:
.e O_SEQUENTIAL,"SEQUENTIAL" // windows .e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows .e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows .e O_INDEXED,"INDEXED" // windows
.e O_LARGEFILE,"LARGEFILE" //
.long MAGNUM_TERMINATOR .long MAGNUM_TERMINATOR
.endobj kOpenFlags,globl,hidden .endobj kOpenFlags,globl,hidden
.overrun .overrun

View file

@ -52,7 +52,7 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval tv, *tvp; struct timeval tv, *tvp;
struct timespec ts, *tsp; struct timespec ts, *tsp;
struct { struct {
sigset_t *s; const sigset_t *s;
size_t n; size_t n;
} ss; } ss;
if (nfds < 0) { if (nfds < 0) {

View file

@ -213,7 +213,7 @@ syscon open O_VERIFY 0 0 0x00200000 0 0 0 #
syscon open O_SHLOCK 0 0x00000010 0x00000010 0x00000010 0x00000010 0 # syscon open O_SHLOCK 0 0x00000010 0x00000010 0x00000010 0x00000010 0 #
syscon open O_EXLOCK 0 0x00000020 0x00000020 0x00000020 0x00000020 0 # syscon open O_EXLOCK 0 0x00000020 0x00000020 0x00000020 0x00000020 0 #
syscon open O_TTY_INIT 0 0 0x00080000 0 0 0 # syscon open O_TTY_INIT 0 0 0x00080000 0 0 0 #
syscon compat O_LARGEFILE 0 0 0 0 0 0 # syscon compat O_LARGEFILE 0100000 0 0 0 0 0 #
# mmap() flags # mmap() flags
# the revolutionary praxis of malloc() # the revolutionary praxis of malloc()

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon compat,O_LARGEFILE,0,0,0,0,0,0 .syscon compat,O_LARGEFILE,0100000,0,0,0,0,0

View file

@ -17,7 +17,10 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -25,15 +28,12 @@
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
char testlib_enable_tmp_setup_teardown; char testlib_enable_tmp_setup_teardown;
void SetUpOnce(void) {
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0));
}
TEST(fcntl_getfl, testRemembersAccessMode) { TEST(fcntl_getfl, testRemembersAccessMode) {
int fd; int fd;
ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644))); ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644)));
@ -71,3 +71,149 @@ TEST(fcntl, getfd) {
ASSERT_SYS(0, 0, close(4)); ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, close(3));
} }
void OnSig(int sig) {
}
TEST(posixAdvisoryLocks, oneProcess_unlockedFromOwnPerspectiveHuh) {
struct flock lock;
ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644));
ASSERT_SYS(0, 5, write(3, "hello", 5));
// set lock
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 1;
lock.l_len = 3;
lock.l_pid = -2;
ASSERT_SYS(0, 0, fcntl(3, F_SETLK, &lock));
EXPECT_EQ(F_WRLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(1, lock.l_start);
EXPECT_EQ(3, lock.l_len);
EXPECT_EQ(-2, lock.l_pid);
ASSERT_SYS(0, 4, open("foo", O_RDWR));
// try lock
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
ASSERT_SYS(0, 0, fcntl(4, F_SETLK, &lock));
EXPECT_EQ(F_WRLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(0, lock.l_start);
EXPECT_EQ(0, lock.l_len);
EXPECT_EQ(-1, lock.l_pid);
// get lock information
if (!IsWindows()) {
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -7;
ASSERT_SYS(0, 0, fcntl(4, F_GETLK, &lock));
EXPECT_EQ(F_UNLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(0, lock.l_start);
EXPECT_EQ(0, lock.l_len);
EXPECT_EQ(-7, lock.l_pid); // doesn't change due to F_UNLCK
}
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
}
TEST(posixAdvisoryLocks, twoProcesses) {
if (IsWindows()) return; // due to signals
int ws, pid;
struct flock lock;
sigset_t ss, oldss;
struct sigaction oldsa;
struct sigaction sa = {.sa_handler = OnSig};
ASSERT_SYS(0, 0, sigemptyset(&ss));
ASSERT_SYS(0, 0, sigaddset(&ss, SIGUSR1));
ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &ss, &oldss));
ASSERT_SYS(0, 0, sigdelset(&ss, SIGUSR1));
ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644));
ASSERT_SYS(0, 5, write(3, "hello", 5));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 1;
lock.l_len = 3;
lock.l_pid = -2;
ASSERT_SYS(0, 0, fcntl(3, F_SETLK, &lock));
EXPECT_EQ(F_WRLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(1, lock.l_start);
EXPECT_EQ(3, lock.l_len);
EXPECT_EQ(-2, lock.l_pid);
ASSERT_SYS(0, 0, kill(getppid(), SIGUSR1));
ASSERT_SYS(EINTR, -1, sigsuspend(&ss));
_Exit(0);
}
ASSERT_SYS(EINTR, -1, sigsuspend(&ss));
ASSERT_SYS(0, 3, open("foo", O_RDWR));
// try lock
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
ASSERT_SYS(EAGAIN, -1, fcntl(3, F_SETLK, &lock));
// get lock information
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
ASSERT_SYS(0, 0, fcntl(3, F_GETLK, &lock));
EXPECT_EQ(F_WRLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(1, lock.l_start);
EXPECT_EQ(3, lock.l_len);
EXPECT_EQ(pid, lock.l_pid);
// get lock information
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_END;
lock.l_start = -3;
lock.l_len = 0;
lock.l_pid = -1;
ASSERT_SYS(0, 0, fcntl(3, F_GETLK, &lock));
EXPECT_EQ(F_WRLCK, lock.l_type);
EXPECT_EQ(SEEK_SET, lock.l_whence);
EXPECT_EQ(1, lock.l_start);
EXPECT_EQ(3, lock.l_len);
EXPECT_EQ(pid, lock.l_pid);
// get lock information
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_END;
lock.l_start = -1;
lock.l_len = 0;
lock.l_pid = -1;
ASSERT_SYS(0, 0, fcntl(3, F_GETLK, &lock));
EXPECT_EQ(F_UNLCK, lock.l_type);
EXPECT_EQ(SEEK_END, lock.l_whence);
EXPECT_EQ(-1, lock.l_start);
EXPECT_EQ(0, lock.l_len);
EXPECT_EQ(-1, lock.l_pid);
ASSERT_SYS(0, 0, kill(pid, SIGUSR1));
EXPECT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0));
ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0));
}

View file

@ -100,6 +100,9 @@ o/$(MODE)/test/libc/calls/ioctl_siocgifconf_test.com.runs: \
o/$(MODE)/test/libc/calls/poll_test.com.runs: \ o/$(MODE)/test/libc/calls/poll_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet private .PLEDGE = stdio rpath wpath cpath fattr proc inet
o/$(MODE)/test/libc/calls/fcntl_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc flock
.PHONY: o/$(MODE)/test/libc/calls .PHONY: o/$(MODE)/test/libc/calls
o/$(MODE)/test/libc/calls: \ o/$(MODE)/test/libc/calls: \
$(TEST_LIBC_CALLS_BINS) \ $(TEST_LIBC_CALLS_BINS) \

View file

@ -24,6 +24,7 @@
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/calls/struct/bpf.h" #include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/dirent.h" #include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/rusage.h"
@ -704,27 +705,64 @@ static int LuaUnixWait(lua_State *L) {
} }
} }
// unix.fcntl(fd:int, cmd:int[, arg:int]) // unix.fcntl(fd:int, cmd:int[, ...])
// ├─→ true, ... // ├─→ ...
// └─→ nil, unix.Errno // └─→ nil, unix.Errno
static int LuaUnixFcntl(lua_State *L) { static int LuaUnixFcntl(lua_State *L) {
int olderr = errno; struct flock lock;
int rc, fd, cmd, olderr = errno;
fd = luaL_checkinteger(L, 1);
cmd = luaL_checkinteger(L, 2);
if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
lock.l_type = luaL_optinteger(L, 3, F_RDLCK);
lock.l_start = luaL_optinteger(L, 4, 0);
lock.l_len = luaL_optinteger(L, 5, 0);
lock.l_whence = luaL_optinteger(L, 6, SEEK_SET);
}
if (cmd == F_SETLK || cmd == F_SETLKW) {
return SysretBool(L, "fcntl(F_SETLK*)", olderr, fcntl(fd, cmd, &lock));
} else if (cmd == F_GETLK) {
if (fcntl(fd, cmd, &lock) != -1) {
if (lock.l_type == F_UNLCK) {
lua_pushinteger(L, F_UNLCK);
return 1;
} else {
lua_pushinteger(L, lock.l_type);
lua_pushinteger(L, lock.l_start);
lua_pushinteger(L, lock.l_len);
lua_pushinteger(L, lock.l_whence);
lua_pushinteger(L, lock.l_pid);
return 5;
}
} else {
return LuaUnixSysretErrno(L, "fcntl(F_GETLK)", olderr);
}
} else {
return SysretBool(L, "fcntl", olderr, return SysretBool(L, "fcntl", olderr,
fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), fcntl(fd, cmd, luaL_optinteger(L, 3, 0)));
luaL_optinteger(L, 3, 0))); }
} }
// unix.dup(oldfd:int[, newfd:int[, flags:int]]) // unix.dup(oldfd:int[, newfd:int[, flags:int[, lowest:int]]])
// ├─→ newfd:int // ├─→ newfd:int
// └─→ nil, unix.Errno // └─→ nil, unix.Errno
static int LuaUnixDup(lua_State *L) { static int LuaUnixDup(lua_State *L) {
int rc, oldfd, newfd, flags, olderr; int rc, oldfd, newfd, flags, lowno, olderr;
olderr = errno; olderr = errno;
oldfd = luaL_checkinteger(L, 1); oldfd = luaL_checkinteger(L, 1);
newfd = luaL_optinteger(L, 2, -1); newfd = luaL_optinteger(L, 2, -1);
flags = luaL_optinteger(L, 3, 0); flags = luaL_optinteger(L, 3, 0);
if (newfd == -1) { lowno = luaL_optinteger(L, 4, 0);
if (newfd < 0) {
if (!flags && !lowno) {
rc = dup(oldfd); rc = dup(oldfd);
} else if (!flags) {
rc = fcntl(oldfd, F_DUPFD, lowno);
} else if (flags == O_CLOEXEC) {
rc = fcntl(oldfd, F_DUPFD_CLOEXEC, lowno);
} else {
rc = einval();
}
} else { } else {
rc = dup3(oldfd, newfd, flags); rc = dup3(oldfd, newfd, flags);
} }
@ -1444,15 +1482,27 @@ static int LuaUnixAccept(lua_State *L) {
} }
} }
// unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) // unix.poll({[fd:int]=events:int, ...}[, timeoutms:int[, mask:unix.Sigset]])
// ├─→ {[fd:int]=revents:int, ...} // ├─→ {[fd:int]=revents:int, ...}
// └─→ nil, unix.Errno // └─→ nil, unix.Errno
static int LuaUnixPoll(lua_State *L) { static int LuaUnixPoll(lua_State *L) {
size_t nfds; size_t nfds;
struct sigset *mask;
struct timespec ts, *tsp;
struct pollfd *fds, *fds2; struct pollfd *fds, *fds2;
int i, fd, events, timeoutms, olderr = errno; int i, fd, events, olderr = errno;
timeoutms = luaL_optinteger(L, 2, -1);
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_isnoneornil(L, 2)) {
ts = _timespec_frommillis(luaL_checkinteger(L, 2));
tsp = &ts;
} else {
tsp = 0;
}
if (!lua_isnoneornil(L, 3)) {
mask = luaL_checkudata(L, 3, "unix.Sigset");
} else {
mask = 0;
}
lua_pushnil(L); lua_pushnil(L);
for (fds = 0, nfds = 0; lua_next(L, 1);) { for (fds = 0, nfds = 0; lua_next(L, 1);) {
if (lua_isinteger(L, -2)) { if (lua_isinteger(L, -2)) {
@ -1471,7 +1521,7 @@ static int LuaUnixPoll(lua_State *L) {
lua_pop(L, 1); lua_pop(L, 1);
} }
olderr = errno; olderr = errno;
if ((events = poll(fds, nfds, timeoutms)) != -1) { if ((events = ppoll(fds, nfds, tsp, mask)) != -1) {
lua_createtable(L, events, 0); lua_createtable(L, events, 0);
for (i = 0; i < nfds; ++i) { for (i = 0; i < nfds; ++i) {
if (fds[i].revents && fds[i].fd >= 0) { if (fds[i].revents && fds[i].fd >= 0) {
@ -2703,6 +2753,7 @@ int LuaUnix(lua_State *L) {
LuaSetIntField(L, "F_WRLCK", F_WRLCK); LuaSetIntField(L, "F_WRLCK", F_WRLCK);
LuaSetIntField(L, "F_SETLK", F_SETLK); LuaSetIntField(L, "F_SETLK", F_SETLK);
LuaSetIntField(L, "F_SETLKW", F_SETLKW); LuaSetIntField(L, "F_SETLKW", F_SETLKW);
LuaSetIntField(L, "F_GETLK", F_GETLK);
LuaSetIntField(L, "FD_CLOEXEC", FD_CLOEXEC); LuaSetIntField(L, "FD_CLOEXEC", FD_CLOEXEC);
// access() mode // access() mode

View file

@ -507,21 +507,11 @@
#define MAKE_HOST "x86_64-cosmopolitan" #define MAKE_HOST "x86_64-cosmopolitan"
/* Define to 1 to enable job server support in GNU make. */ /* Define to 1 to enable job server support in GNU make. */
/* #define MAKE_JOBSERVER 1
* TODO(jart): Why does job server not work? We don't need it, since the
* last thing we'd ever want is a recursive make, however it
* would be nice to confirm it's not a bug in our libc impl.
*/
/* #define MAKE_JOBSERVER 1 */
/* Define to 1 to enable symbolic link timestamp checking. */ /* Define to 1 to enable symbolic link timestamp checking. */
#define MAKE_SYMLINKS 1 #define MAKE_SYMLINKS 1
/* Use GNU style printf and scanf. */
#ifndef __USE_MINGW_ANSI_STDIO
#define __USE_MINGW_ANSI_STDIO 1
#endif
/* Define to 1 if the nlist n_name member is a pointer */ /* Define to 1 if the nlist n_name member is a pointer */
/* #undef N_NAME_POINTER */ /* #undef N_NAME_POINTER */

View file

@ -16,6 +16,7 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "third_party/make/makeint.inc" #include "third_party/make/makeint.inc"
/**/ /**/
#include "libc/sock/select.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
@ -231,17 +232,15 @@ unsigned int jobserver_acquire(int timeout) {
FD_SET(job_fds[0], &readfds); FD_SET(job_fds[0], &readfds);
r = pselect(job_fds[0] + 1, &readfds, NULL, NULL, specp, &empty); r = pselect(job_fds[0] + 1, &readfds, NULL, NULL, specp, &empty);
if (r < 0) switch (errno) { if (r < 0)
case EINTR: {
if (errno == EINTR)
/* SIGCHLD will show up as an EINTR. */ /* SIGCHLD will show up as an EINTR. */
return 0; return 0;
if (errno == EBADF)
case EBADF:
/* Someone closed the jobs pipe. /* Someone closed the jobs pipe.
That shouldn't happen but if it does we're done. */ That shouldn't happen but if it does we're done. */
O(fatal, NILF, _("job server shut down")); O(fatal, NILF, _("job server shut down"));
default:
pfatal_with_name(_("pselect jobs pipe")); pfatal_with_name(_("pselect jobs pipe"));
} }

View file

@ -195,23 +195,13 @@
(or (file-name-directory (or (file-name-directory
(file-relative-name this root)) (file-relative-name this root))
"")))) ""))))
((eq kind 'run-win7)
(format
(cosmo-join
" && "
`("m=%s; f=o/$m/%s.com"
,(concat "make -j12 -O $f MODE=$m")
"scp $f $f.dbg win7:"
"ssh win7 ./%s.com"))
mode name (file-name-nondirectory name)))
((eq kind 'run-win10) ((eq kind 'run-win10)
(format (format
(cosmo-join (cosmo-join
" && " " && "
`("m=%s; f=o/$m/%s.com" `("m=%s; f=o/$m/%s.com"
,(concat "make -j12 -O $f MODE=$m") ,(concat "make -j12 -O $f MODE=$m")
"scp $f $f.dbg win10:" "scp $f $f.dbg win10:; ssh win10 ./%s.com"))
"ssh win10 ./%s.com"))
mode name (file-name-nondirectory name))) mode name (file-name-nondirectory name)))
((eq kind 'run-xnu) ((eq kind 'run-xnu)
(format (format
@ -653,22 +643,6 @@
('t ('t
(error "cosmo-run: unknown major mode"))))))) (error "cosmo-run: unknown major mode")))))))
(defun cosmo-run-win7 (arg)
(interactive "P")
(let* ((this (or (buffer-file-name) dired-directory))
(proj (locate-dominating-file this "Makefile"))
(root (or proj default-directory))
(file (file-relative-name this root)))
(when root
(let ((default-directory root))
(save-buffer)
(cond ((memq major-mode '(c-mode c++-mode asm-mode fortran-mode))
(let* ((mode (cosmo--make-mode arg ""))
(compile-command (cosmo--compile-command this root 'run-win7 mode "" "" "")))
(compile compile-command)))
('t
(error "cosmo-run: unknown major mode")))))))
(defun cosmo-run-win10 (arg) (defun cosmo-run-win10 (arg)
(interactive "P") (interactive "P")
(let* ((this (or (buffer-file-name) dired-directory)) (let* ((this (or (buffer-file-name) dired-directory))

View file

@ -2590,19 +2590,30 @@ UNIX MODULE
`EAGAIN` is returned if you've enforced a max number of `EAGAIN` is returned if you've enforced a max number of
processes using `setrlimit(RLIMIT_NPROC)`. processes using `setrlimit(RLIMIT_NPROC)`.
unix.dup(oldfd:int[, newfd:int[, flags:int]]) unix.dup(oldfd:int[, newfd:int[, flags:int[, lowest:int]]])
├─→ newfd:int ├─→ newfd:int
└─→ nil, unix.Errno └─→ nil, unix.Errno
Duplicates file descriptor. Duplicates file descriptor.
`newfd` defaults to the lowest number available file descriptor. `newfd` may be specified to choose a specific number for the new
If the new number is specified and it's already open, then it'll file descriptor. If it's already open, then the preexisting one will
be silently closed before the duplication happens. be silently closed. `EINVAL` is returned if `newfd` equals `oldfd`.
`flags` can have `O_CLOEXEC` which means the returned file `flags` can have `O_CLOEXEC` which means the returned file
descriptors will be automatically closed upon execve(). descriptors will be automatically closed upon execve().
`lowest` defaults to zero and defines the lowest numbered file
descriptor that's acceptable to use. If `newfd` is specified then
`lowest` is ignored. For example, if you wanted to duplicate
standard input, then:
stdin2 = assert(unix.dup(0, nil, unix.O_CLOEXEC, 3))
Will ensure that, in the rare event standard output or standard
error are closed, you won't accidentally duplicate standard input to
those numbers.
unix.pipe([flags:int]) unix.pipe([flags:int])
├─→ reader:int, writer:int ├─→ reader:int, writer:int
└─→ nil, unix.Errno └─→ nil, unix.Errno
@ -2992,18 +3003,147 @@ UNIX MODULE
`path` is the file or directory path you wish to destroy. `path` is the file or directory path you wish to destroy.
unix.fcntl(fd:int, cmd:int, ...) unix.fcntl(fd:int, unix.F_GETFD)
├─→ ... ├─→ flags:int
└─→ nil, unix.Errno └─→ nil, unix.Errno
Manipulates file descriptor. Returns file descriptor flags.
Setting `cmd` to `F_GETFD`, `F_SETFD`, `F_GETFL` or `F_SETFL` The returned `flags` may include any of:
lets you query and/or change the status of file descriptors. For
example, it's possible using this to change `FD_CLOEXEC`.
[work in progress] POSIX advisory locks can be controlled by setting - `unix.FD_CLOEXEC` if `fd` was opened with `unix.O_CLOEXEC`.
`cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`.
Returns `EBADF` if `fd` isn't open.
unix.fcntl(fd:int, unix.F_SETFD, flags:int)
├─→ true
└─→ nil, unix.Errno
Sets file descriptor flags.
`flags` may include any of:
- `unix.FD_CLOEXEC` to re-open `fd` with `unix.O_CLOEXEC`.
Returns `EBADF` if `fd` isn't open.
unix.fcntl(fd:int, unix.F_GETFL)
├─→ flags:int
└─→ nil, unix.Errno
Returns file descriptor status flags.
`flags & unix.O_ACCMODE` includes one of:
- `O_RDONLY`
- `O_WRONLY`
- `O_RDWR`
Examples of values `flags & ~unix.O_ACCMODE` may include:
- `O_NONBLOCK`
- `O_APPEND`
- `O_SYNC`
- `O_ASYNC`
- `O_NOATIME` on Linux
- `O_RANDOM` on Windows
- `O_SEQUENTIAL` on Windows
- `O_DIRECT` on Linux/FreeBSD/NetBSD/Windows
Examples of values `flags & ~unix.O_ACCMODE` won't include:
- `O_CREAT`
- `O_TRUNC`
- `O_EXCL`
- `O_NOCTTY`
Returns `EBADF` if `fd` isn't open.
unix.fcntl(fd:int, unix.F_SETFL, flags:int)
├─→ true
└─→ nil, unix.Errno
Changes file descriptor status flags.
Examples of values `flags` may include:
- `O_NONBLOCK`
- `O_APPEND`
- `O_SYNC`
- `O_ASYNC`
- `O_NOATIME` on Linux
- `O_RANDOM` on Windows
- `O_SEQUENTIAL` on Windows
- `O_DIRECT` on Linux/FreeBSD/NetBSD/Windows
These values should be ignored:
- `O_RDONLY`, `O_WRONLY`, `O_RDWR`
- `O_CREAT`, `O_TRUNC`, `O_EXCL`
- `O_NOCTTY`
Returns `EBADF` if `fd` isn't open.
unix.fcntl(fd:int, unix.F_SETLK[, type[, start[, len[, whence]]]])
unix.fcntl(fd:int, unix.F_SETLKW[, type[, start[, len[, whence]]]])
├─→ true
└─→ nil, unix.Errno
Acquires lock on file interval.
POSIX Advisory Locks allow multiple processes to leave voluntary
hints to each other about which portions of a file they're using.
The command may be:
- `F_SETLK` to acquire lock if possible
- `F_SETLKW` to wait for lock if necessary
`fd` is file descriptor of open() file.
`type` may be one of:
- `F_RDLCK` for read lock (default)
- `F_WRLCK` for read/write lock
- `F_UNLCK` to unlock
`start` is 0-indexed byte offset into file. The default is zero.
`len` is byte length of interval. Zero is the default and it means
until the end of the file.
`whence` may be one of:
- `SEEK_SET` start from beginning (default)
- `SEEK_CUR` start from current position
- `SEEK_END` start from end
Returns `EAGAIN` if lock couldn't be acquired. POSIX says this
theoretically could also be `EACCES` but we haven't seen this
behavior on any of our supported platforms.
Returns `EBADF` if `fd` wasn't open.
unix.fcntl(fd:int, unix.F_GETLK[, type[, start[, len[, whence]]]])
├─→ unix.F_UNLCK
├─→ type, start, len, whence, pid
└─→ nil, unix.Errno
Acquires information about POSIX advisory lock on file.
This function accepts the same parameters as fcntl(F_SETLK) and
tells you if the lock acquisition would be successful for a given
range of bytes. If locking would have succeeded, then F_UNLCK is
returned. If the lock would not have succeeded, then information
about a conflicting lock is returned.
Returned `type` may be `F_RDLCK` or `F_WRLCK`.
Returned `pid` is the process id of the current lock owner.
This function is currently not supported on Windows.
Returns `EBADF` if `fd` wasn't open.
unix.getsid(pid:int) unix.getsid(pid:int)
├─→ sid:int ├─→ sid:int
@ -3477,7 +3617,7 @@ UNIX MODULE
`OnServerListen` hook to enable SYN saving in your Redbean. When the `OnServerListen` hook to enable SYN saving in your Redbean. When the
`TCP_SAVE_SYN` option isn't used, this may return empty string. `TCP_SAVE_SYN` option isn't used, this may return empty string.
unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) unix.poll({[fd:int]=events:int, ...}[, timeoutms:int[, mask:unix.Sigset]])
├─→ {[fd:int]=revents:int, ...} ├─→ {[fd:int]=revents:int, ...}
└─→ nil, unix.Errno └─→ nil, unix.Errno
@ -3504,9 +3644,25 @@ UNIX MODULE
peer closed its end of the channel. peer closed its end of the channel.
- `POLLNVAL` (revents): Invalid request. - `POLLNVAL` (revents): Invalid request.
`timeoutms` is the number of milliseconds to block. If this is set to `timeoutms` is the number of milliseconds to block. The default is
-1 then that means block as long as it takes until there's an event -1 which means block indefinitely until there's an event or an
or an interrupt. If the timeout expires, an empty table is returned. interrupt. If the timeout elapses without any such events, an empty
table is returned. A timeout of zero means non-blocking.
`mask` serves the purpose of enabling poll to listen for both file
descriptor events and signals. It's equivalent to saying:
oldmask = unix.sigprocmask(unix.SIG_SETMASK, mask);
unix.poll(fds, timeout);
unix.sigprocmask(unix.SIG_SETMASK, oldmask);
Except it'll happen atomically on supported platforms. The only
exceptions are MacOS and NetBSD where this behavior is simulated by
the polyfill. Atomicity is helpful for unit testing signal behavior.
`EINTR` is returned if the kernel decided to deliver a signal to a
signal handler instead during your call. This is a @norestart system
call that always returns `EINTR` even if `SA_RESTART` is in play.
unix.gethostname() unix.gethostname()
├─→ host:str ├─→ host:str