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;
if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) {
if (cmd == F_GETFL) {
return g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT |
O_NOATIME | O_NONBLOCK);
return g_fds.p[fd].flags &
(O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME |
O_NONBLOCK | O_RANDOM | O_SEQUENTIAL);
} else if (cmd == F_SETFL) {
// O_APPEND doesn't appear to be tunable at cursory glance
// 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;
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);
} else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) {
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_type = u->linux.l_type;
l_whence = u->linux.l_whence;
l_sysid = 0;
} else if (IsXnu()) {
l_start = u->xnu.l_start;
l_len = u->xnu.l_len;
l_pid = u->xnu.l_pid;
l_type = u->xnu.l_type;
l_whence = u->xnu.l_whence;
l_sysid = 0;
} else if (IsFreebsd()) {
l_start = u->freebsd.l_start;
l_len = u->freebsd.l_len;
@ -89,20 +87,19 @@ void flock2cosmo(uintptr_t memory) {
l_type = u->freebsd.l_type;
l_whence = u->freebsd.l_whence;
l_sysid = u->freebsd.l_sysid;
u->cosmo.l_sysid = l_sysid;
} else if (IsOpenbsd()) {
l_start = u->openbsd.l_start;
l_len = u->openbsd.l_len;
l_pid = u->openbsd.l_pid;
l_type = u->openbsd.l_type;
l_whence = u->openbsd.l_whence;
l_sysid = 0;
} else if (IsNetbsd()) {
l_start = u->netbsd.l_start;
l_len = u->netbsd.l_len;
l_pid = u->netbsd.l_pid;
l_type = u->netbsd.l_type;
l_whence = u->netbsd.l_whence;
l_sysid = 0;
} else {
return;
}
@ -111,7 +108,6 @@ void flock2cosmo(uintptr_t memory) {
u->cosmo.l_pid = l_pid;
u->cosmo.l_type = l_type;
u->cosmo.l_whence = l_whence;
u->cosmo.l_sysid = l_sysid;
}
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];
int pipeindices[ARRAYLEN(pipefds)];
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
if (sigmask && __sig_mask(SIG_SETMASK, sigmask, &oldmask)) return -1;
if (_check_interrupts(false, g_fds.p)) return eintr();
if (sigmask) {
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
goto ReturnPath;
}
// do the planning
// we need to read static variables
// we might need to spawn threads and open pipes
__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 (__isfdopen(fds[i].fd)) {
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;
} else {
// too many socket fds
failed = enomem();
rc = enomem();
break;
}
} else if (pn < ARRAYLEN(pipefds)) {
@ -109,7 +114,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
++pn;
} else {
// too many non-socket fds
failed = enomem();
rc = enomem();
break;
}
} else {
@ -117,9 +122,9 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
}
__fds_unlock();
if (failed) {
if (rc) {
// failed to create a polling solution
return failed;
goto ReturnPath;
}
// 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);
#endif
if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) {
return __winsockerr();
rc = __winsockerr();
goto ReturnPath;
}
*ms -= waitfor;
} 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
// checking for signal delivery interrupts, along the way
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
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_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
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_sysid; /* remote system id or zero for local */
};

View file

@ -61,6 +61,7 @@ kOpenFlags:
.e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows
.e O_LARGEFILE,"LARGEFILE" //
.long MAGNUM_TERMINATOR
.endobj kOpenFlags,globl,hidden
.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 timespec ts, *tsp;
struct {
sigset_t *s;
const sigset_t *s;
size_t n;
} ss;
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_EXLOCK 0 0x00000020 0x00000020 0x00000020 0x00000020 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
# the revolutionary praxis of malloc()

View file

@ -1,2 +1,2 @@
#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.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/macros.internal.h"
@ -25,15 +28,12 @@
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
char testlib_enable_tmp_setup_teardown;
void SetUpOnce(void) {
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0));
}
TEST(fcntl_getfl, testRemembersAccessMode) {
int fd;
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(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: \
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
o/$(MODE)/test/libc/calls: \
$(TEST_LIBC_CALLS_BINS) \

View file

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

View file

@ -507,21 +507,11 @@
#define MAKE_HOST "x86_64-cosmopolitan"
/* Define to 1 to enable job server support in GNU make. */
/*
* 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 MAKE_JOBSERVER 1
/* Define to 1 to enable symbolic link timestamp checking. */
#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 */
/* #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 "libc/sock/select.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/sa.h"
@ -231,17 +232,15 @@ unsigned int jobserver_acquire(int timeout) {
FD_SET(job_fds[0], &readfds);
r = pselect(job_fds[0] + 1, &readfds, NULL, NULL, specp, &empty);
if (r < 0) switch (errno) {
case EINTR:
if (r < 0)
{
if (errno == EINTR)
/* SIGCHLD will show up as an EINTR. */
return 0;
case EBADF:
if (errno == EBADF)
/* Someone closed the jobs pipe.
That shouldn't happen but if it does we're done. */
O(fatal, NILF, _("job server shut down"));
default:
pfatal_with_name(_("pselect jobs pipe"));
}

View file

@ -195,23 +195,13 @@
(or (file-name-directory
(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)
(format
(cosmo-join
" && "
`("m=%s; f=o/$m/%s.com"
,(concat "make -j12 -O $f MODE=$m")
"scp $f $f.dbg win10:"
"ssh win10 ./%s.com"))
"scp $f $f.dbg win10:; ssh win10 ./%s.com"))
mode name (file-name-nondirectory name)))
((eq kind 'run-xnu)
(format
@ -653,22 +643,6 @@
('t
(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)
(interactive "P")
(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
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
└─→ nil, unix.Errno
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.
`newfd` may be specified to choose a specific number for the new
file descriptor. If it's already open, then the preexisting one will
be silently closed. `EINVAL` is returned if `newfd` equals `oldfd`.
`flags` can have `O_CLOEXEC` which means the returned file
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])
├─→ reader:int, writer:int
└─→ nil, unix.Errno
@ -2992,18 +3003,147 @@ UNIX MODULE
`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
Manipulates file descriptor.
Returns file descriptor flags.
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`.
The returned `flags` may include any of:
[work in progress] POSIX advisory locks can be controlled by setting
`cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`.
- `unix.FD_CLOEXEC` if `fd` was opened with `unix.O_CLOEXEC`.
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)
├─→ sid:int
@ -3477,7 +3617,7 @@ UNIX MODULE
`OnServerListen` hook to enable SYN saving in your Redbean. When the
`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, ...}
└─→ nil, unix.Errno
@ -3504,9 +3644,25 @@ UNIX MODULE
peer closed its end of the channel.
- `POLLNVAL` (revents): Invalid request.
`timeoutms` is the number of milliseconds to block. If this is set to
-1 then that means block as long as it takes until there's an event
or an interrupt. If the timeout expires, an empty table is returned.
`timeoutms` is the number of milliseconds to block. The default is
-1 which means block indefinitely until there's an event or an
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()
├─→ host:str