1# syscall.tau - Low-level system call interface for Tau2# Provides a cross-platform abstraction layer for system calls3# Similar to Go's syscall package45ffi = import("ffi")67# The library is looked up next to the installed stdlib, dlopen() falls back8# to ~/.local/lib/tau and /lib/tau when the relative path doesn't exist.9lib = dlopen("libsyscall.so")1011# Every call is declared, so that a result comes back the width it really has12# rather than as a machine word to be read with int(x, 64) - and forgetting13# the 64 truncated it to an int of C, which is what a size or a millisecond14# does not fit in.15sys = ffi.Bind(lib, [16 "int64_t sys_open(const char *path, int64_t flags, int64_t mode)",17 "int64_t sys_close(int64_t fd)",18 "int64_t sys_read(int64_t fd, void *buf, int64_t count)",19 "int64_t sys_write(int64_t fd, const void *buf, int64_t count)",20 "int64_t sys_lseek(int64_t fd, int64_t offset, int64_t whence)",21 "int64_t sys_unlink(const char *path)",22 "int64_t sys_mkdir(const char *path, int64_t mode)",23 "int64_t sys_rmdir(const char *path)",24 "int64_t sys_chmod(const char *path, int64_t mode)",25 "int64_t sys_access(const char *path, int64_t mode)",26 "int64_t sys_socket(int64_t domain, int64_t type, int64_t protocol)",27 "int64_t sys_bind(int64_t sockfd, const void *addr, int64_t addrlen)",28 "int64_t sys_listen(int64_t sockfd, int64_t backlog)",29 "int64_t sys_accept(int64_t sockfd, void *addr, void *addrlen)",30 "int64_t sys_connect(int64_t sockfd, const void *addr, int64_t addrlen)",31 "int64_t sys_getsockname(int64_t sockfd, void *addr, void *addrlen)",32 "int64_t sys_send(int64_t sockfd, const void *buf, int64_t len, int64_t flags)",33 "int64_t sys_recv(int64_t sockfd, void *buf, int64_t len, int64_t flags)",34 "int64_t sys_sendto(int64_t sockfd, const void *buf, int64_t len, int64_t flags, const void *dest_addr, int64_t addrlen)",35 "int64_t sys_recvfrom(int64_t sockfd, void *buf, int64_t len, int64_t flags, void *src_addr, void *addrlen)",36 "int64_t sys_setsockopt(int64_t sockfd, int64_t level, int64_t optname, const void *optval, int64_t optlen)",37 "int64_t sys_getsockopt(int64_t sockfd, int64_t level, int64_t optname, void *optval, void *optlen)",38 "int64_t sys_shutdown(int64_t sockfd, int64_t how)",39 "uint16_t sys_htons(uint16_t hostshort)",40 "uint16_t sys_ntohs(uint16_t netshort)",41 "uint32_t sys_htonl(uint32_t hostlong)",42 "uint32_t sys_ntohl(uint32_t netlong)",43 "uint32_t sys_inet_addr(const char *cp)",44 "int64_t sys_sockaddr_in(void *buf, const char *ip, int64_t port)",45 "int64_t sys_sockaddr_in_size()",46 "int64_t sys_sockaddr_port(const void *buf)",47 "int64_t sys_sockaddr_ip(const void *buf, void *out, int64_t outlen)",48 "int64_t sys_int32(void *buf, int64_t v)",49 "int64_t sys_int32_size()",50 "int64_t sys_timeval(void *buf, int64_t ms)",51 "int64_t sys_timeval_size()",52 "int64_t sys_time_unix()",53 "int64_t sys_time_ms()",54 "int64_t sys_time_mono_ms()",55 "int64_t sys_sleep_ms(int64_t ms)",56 "int64_t sys_strerror(int64_t err, void *out, int64_t outlen)",57 "int64_t sys_errno()",58 "void sys_set_errno(int64_t err)",59 "int64_t sys_getenv(const char *name, void *out, int64_t outlen)",60 "int64_t sys_setenv(const char *name, const char *value, int64_t overwrite)",61 "int64_t sys_unsetenv(const char *name)",62 "int64_t sys_stat_size(const char *path)",63 "int64_t sys_stat_mode(const char *path)",64 "int64_t sys_stat_mtime(const char *path)",65 "int64_t sys_stat_isdir(const char *path)",66 "int64_t sys_rename(const char *from, const char *to)",67 "int64_t sys_getcwd(void *out, int64_t outlen)",68 "int64_t sys_chdir(const char *path)",69 "int64_t sys_opendir(const char *path)",70 "int64_t sys_readdir(int64_t dir, void *out, int64_t outlen)",71 "int64_t sys_closedir(int64_t dir)",72 "int64_t sys_tz_offset(int64_t t)",73 "int64_t sys_tz_name(int64_t t, void *out, int64_t outlen)",74 "int64_t sys_spawn_status(void)",75 "int64_t sys_spawn(const void *blob, int64_t bloblen, int64_t nargs, const void *in, int64_t inlen, void *out, int64_t outcap, int64_t flags)"76])7778# ========== Error Handling ==========7980Errno = fn() { sys.sys_errno() }81SetErrno = fn(err) { sys.sys_set_errno(err) }8283# Strerror returns the message of an errno value.84Strerror = fn(err) {85 buf = bytes(256)86 n = sys.sys_strerror(err, buf, 256)87 return string(slice(buf, 0, n))88}8990# Error builds an error for a failed call, with the message of the errno.91Error = fn(what) {92 err = Errno()93 return error("{what}: {Strerror(err)} (errno {err})")94}9596# ========== File Operation Constants ==========9798# open() flags - Linux/macOS values (POSIX)99O_RDONLY = 0x0000100O_WRONLY = 0x0001101O_RDWR = 0x0002102O_CREAT = 0x0040103O_EXCL = 0x0080104O_TRUNC = 0x0200105O_APPEND = 0x0400106O_NONBLOCK = 0x0800107O_SYNC = 0x1000108109# File permissions110S_IRWXU = 0x01C0 # User: rwx111S_IRUSR = 0x0100 # User: r--112S_IWUSR = 0x0080 # User: -w-113S_IXUSR = 0x0040 # User: --x114S_IRWXG = 0x0038 # Group: rwx115S_IRGRP = 0x0020 # Group: r--116S_IWGRP = 0x0010 # Group: -w-117S_IXGRP = 0x0008 # Group: --x118S_IRWXO = 0x0007 # Other: rwx119S_IROTH = 0x0004 # Other: r--120S_IWOTH = 0x0002 # Other: -w-121S_IXOTH = 0x0001 # Other: --x122123# lseek() whence124SEEK_SET = 0125SEEK_CUR = 1126SEEK_END = 2127128# access() mode129F_OK = 0 # Test for existence130X_OK = 1 # Test for execute permission131W_OK = 2 # Test for write permission132R_OK = 4 # Test for read permission133134# ========== Network Constants ==========135136# Address families137AF_INET = 2 # IPv4138AF_INET6 = 10 # IPv6139AF_UNIX = 1 # Unix domain sockets140141# Socket types142SOCK_STREAM = 1 # TCP143SOCK_DGRAM = 2 # UDP144SOCK_RAW = 3 # Raw socket145146# Protocols147IPPROTO_IP = 0148IPPROTO_TCP = 6149IPPROTO_UDP = 17150151# Socket options - level152SOL_SOCKET = 1153154# Socket options - names155SO_REUSEADDR = 2156SO_REUSEPORT = 15157SO_KEEPALIVE = 9158SO_BROADCAST = 6159SO_LINGER = 13160SO_RCVBUF = 8161SO_SNDBUF = 7162SO_RCVTIMEO = 20163SO_SNDTIMEO = 21164165# shutdown() how166SHUT_RD = 0167SHUT_WR = 1168SHUT_RDWR = 2169170# Listen backlog171SOMAXCONN = 128172173# The error numbers live in the errno module, which lists them all:174#175# errno = import("errno")176# if syscall.Errno() == errno.EAGAIN { ... }177178# ========== File Operations ==========179180Open = fn(path, flags, mode) {181 result = sys.sys_open(path, flags, mode)182 if result < 0 {183 return error("open failed: errno {Errno()}")184 }185 return result186}187188Close = fn(fd) {189 result = sys.sys_close(fd)190 if result < 0 {191 return error("close failed: errno {Errno()}")192 }193 return result194}195196Read = fn(fd, buf, count) {197 result = sys.sys_read(fd, buf, count)198 if result < 0 {199 return error("read failed: errno {Errno()}")200 }201 return result202}203204Write = fn(fd, buf, count) {205 result = sys.sys_write(fd, buf, count)206 if result < 0 {207 return error("write failed: errno {Errno()}")208 }209 return result210}211212Lseek = fn(fd, offset, whence) {213 result = sys.sys_lseek(fd, offset, whence)214 if result < 0 {215 return error("lseek failed: errno {Errno()}")216 }217 return result218}219220Unlink = fn(path) {221 result = sys.sys_unlink(path)222 if result < 0 {223 return error("unlink failed: errno {Errno()}")224 }225 return result226}227228Mkdir = fn(path, mode) {229 result = sys.sys_mkdir(path, mode)230 if result < 0 {231 return error("mkdir failed: errno {Errno()}")232 }233 return result234}235236Rmdir = fn(path) {237 result = sys.sys_rmdir(path)238 if result < 0 {239 return error("rmdir failed: errno {Errno()}")240 }241 return result242}243244Chmod = fn(path, mode) {245 result = sys.sys_chmod(path, mode)246 if result < 0 {247 return error("chmod failed: errno {Errno()}")248 }249 return result250}251252Access = fn(path, mode) {253 result = sys.sys_access(path, mode)254 if result < 0 {255 return error("access failed: errno {Errno()}")256 }257 return result258}259260# ========== Network Operations ==========261262Socket = fn(domain, socktype, protocol) {263 result = sys.sys_socket(domain, socktype, protocol)264 if result < 0 {265 return error("socket failed: errno {Errno()}")266 }267 return result268}269270Bind = fn(sockfd, addr, addrlen) {271 result = sys.sys_bind(sockfd, addr, addrlen)272 if result < 0 {273 return error("bind failed: errno {Errno()}")274 }275 return result276}277278Listen = fn(sockfd, backlog) {279 result = sys.sys_listen(sockfd, backlog)280 if result < 0 {281 return error("listen failed: errno {Errno()}")282 }283 return result284}285286Accept = fn(sockfd, addr, addrlen) {287 result = sys.sys_accept(sockfd, addr, addrlen)288 if result < 0 {289 return error("accept failed: errno {Errno()}")290 }291 return result292}293294Getsockname = fn(sockfd, addr, addrlen) {295 if sys.sys_getsockname(sockfd, addr, addrlen) < 0 {296 return error("getsockname failed: errno {Errno()}")297 }298 return null299}300301Connect = fn(sockfd, addr, addrlen) {302 result = sys.sys_connect(sockfd, addr, addrlen)303 if result < 0 {304 return error("connect failed: errno {Errno()}")305 }306 return result307}308309Send = fn(sockfd, buf, len, flags) {310 result = sys.sys_send(sockfd, buf, len, flags)311 if result < 0 {312 return error("send failed: errno {Errno()}")313 }314 return result315}316317Recv = fn(sockfd, buf, len, flags) {318 result = sys.sys_recv(sockfd, buf, len, flags)319 if result < 0 {320 return error("recv failed: errno {Errno()}")321 }322 return result323}324325Sendto = fn(sockfd, buf, len, flags, dest_addr, addrlen) {326 result = sys.sys_sendto(sockfd, buf, len, flags, dest_addr, addrlen)327 if result < 0 {328 return error("sendto failed: errno {Errno()}")329 }330 return result331}332333Recvfrom = fn(sockfd, buf, len, flags, src_addr, addrlen) {334 result = sys.sys_recvfrom(sockfd, buf, len, flags, src_addr, addrlen)335 if result < 0 {336 return error("recvfrom failed: errno {Errno()}")337 }338 return result339}340341Setsockopt = fn(sockfd, level, optname, optval, optlen) {342 result = sys.sys_setsockopt(sockfd, level, optname, optval, optlen)343 if result < 0 {344 return error("setsockopt failed: errno {Errno()}")345 }346 return result347}348349Getsockopt = fn(sockfd, level, optname, optval, optlen) {350 result = sys.sys_getsockopt(sockfd, level, optname, optval, optlen)351 if result < 0 {352 return error("getsockopt failed: errno {Errno()}")353 }354 return result355}356357Shutdown = fn(sockfd, how) {358 result = sys.sys_shutdown(sockfd, how)359 if result < 0 {360 return error("shutdown failed: errno {Errno()}")361 }362 return result363}364365# ========== Network Helper Functions ==========366367Htons = fn(hostshort) { sys.sys_htons(hostshort) }368Ntohs = fn(netshort) { sys.sys_ntohs(netshort) }369Htonl = fn(hostlong) { sys.sys_htonl(hostlong) }370Ntohl = fn(netlong) { sys.sys_ntohl(netlong) }371InetAddr = fn(cp) { sys.sys_inet_addr(cp) }372373# ========== Address Helpers ==========374375# SockaddrInSize is the length of a sockaddr_in.376SockaddrInSize = fn() { sys.sys_sockaddr_in_size() }377378# SockaddrIn returns a sockaddr_in filled for ip:port, ready for Bind,379# Connect and Sendto. An empty ip means "any address".380SockaddrIn = fn(ip, port) {381 buf = bytes(SockaddrInSize())382 sys.sys_sockaddr_in(buf, ip, port)383 return buf384}385386# SockaddrPort returns the port stored in a sockaddr_in.387SockaddrPort = fn(sa) { sys.sys_sockaddr_port(sa) }388389# SockaddrIP returns the dotted address stored in a sockaddr_in.390SockaddrIP = fn(sa) {391 buf = bytes(64)392 n = sys.sys_sockaddr_ip(sa, buf, 64)393 return string(slice(buf, 0, n))394}395396# ========== Small Structs ==========397398# Int32 returns the bytes of a native int32, for the calls that want a399# pointer to one.400Int32 = fn(n) {401 buf = bytes(sys.sys_int32_size())402 sys.sys_int32(buf, n)403 return buf404}405406# Timeval returns the bytes of a struct timeval of ms milliseconds, for the407# timeout options of setsockopt.408Timeval = fn(ms) {409 buf = bytes(sys.sys_timeval_size())410 sys.sys_timeval(buf, ms)411 return buf412}413414# ========== Time ==========415416# TimeUnix returns the wall clock time in seconds since the Unix epoch.417TimeUnix = fn() { sys.sys_time_unix() }418419# TimeMillis returns the wall clock time in milliseconds.420TimeMillis = fn() { sys.sys_time_ms() }421422# TimeMono returns a monotonic timestamp in milliseconds.423TimeMono = fn() { sys.sys_time_mono_ms() }424425# SleepMillis pauses the current tau routine for ms milliseconds.426SleepMillis = fn(ms) { sys.sys_sleep_ms(ms) }427428# ========== Environment ==========429430# Getenv returns the value of the variable name, or null when it isn't set.431Getenv = fn(name) {432 buf = bytes(4096)433 n = sys.sys_getenv(name, buf, 4096)434 if n < 0 {435 return null436 }437 return string(slice(buf, 0, n))438}439440# Setenv sets the variable name to value, overwriting what was there.441Setenv = fn(name, value) {442 if sys.sys_setenv(name, value, 1) < 0 {443 return Error("setenv")444 }445 return null446}447448# Unsetenv removes the variable name.449Unsetenv = fn(name) {450 if sys.sys_unsetenv(name) < 0 {451 return Error("unsetenv")452 }453 return null454}455456# ========== File Metadata ==========457458# The bits of a file mode, as returned by StatMode.459S_IFMT = 0170000460S_IFDIR = 0040000461S_IFREG = 0100000462S_IFLNK = 0120000463464# StatSize returns the size of the file at path in bytes.465StatSize = fn(path) {466 n = sys.sys_stat_size(path)467 if n < 0 {468 return Error("stat {path}")469 }470 return n471}472473# StatMode returns the mode of the file at path, permissions and type.474StatMode = fn(path) {475 n = sys.sys_stat_mode(path)476 if n < 0 {477 return Error("stat {path}")478 }479 return n480}481482# StatMtime returns when the file at path was last modified, in seconds since483# the Unix epoch.484StatMtime = fn(path) {485 n = sys.sys_stat_mtime(path)486 if n < 0 {487 return Error("stat {path}")488 }489 return n490}491492# StatIsDir reports whether path is a directory.493StatIsDir = fn(path) {494 n = sys.sys_stat_isdir(path)495 if n < 0 {496 return Error("stat {path}")497 }498 return n == 1499}500501Rename = fn(from, to) {502 if sys.sys_rename(from, to) < 0 {503 return Error("rename {from}")504 }505 return null506}507508# Getwd returns the working directory.509Getwd = fn() {510 buf = bytes(4096)511 n = sys.sys_getcwd(buf, 4096)512 if n < 0 {513 return Error("getcwd")514 }515 return string(slice(buf, 0, n))516}517518Chdir = fn(path) {519 if sys.sys_chdir(path) < 0 {520 return Error("chdir {path}")521 }522 return null523}524525# ========== Directories ==========526527# Readdirnames returns the names inside the directory at path, "." and ".."528# included, the way the system hands them over.529Readdirnames = fn(path) {530 d = sys.sys_opendir(path)531 if d == 0 {532 return Error("opendir {path}")533 }534535 names = []536 buf = bytes(4096)537 for {538 n = sys.sys_readdir(d, buf, 4096)539 if n < 0 {540 sys.sys_closedir(d)541 return names542 }543 names = append(names, string(slice(buf, 0, n)))544 }545}546547# ========== Time Zone ==========548549# TzOffset returns the seconds between local time and UTC at the instant sec,550# daylight saving included.551TzOffset = fn(sec) { sys.sys_tz_offset(sec) }552553# TzName returns the abbreviation of the local zone at the instant sec.554TzName = fn(sec) {555 buf = bytes(64)556 n = sys.sys_tz_name(sec, buf, 64)557 if n < 0 {558 return Error("tzname")559 }560 return string(slice(buf, 0, n))561}562563# ========== Processes ==========564565# Spawn runs a program and waits for it. blob holds the program and its566# arguments one after the other, each one ended by a zero byte, and nargs says567# how many there are. What the program writes on its standard output is copied568# into out, and the return value is how many bytes it wrote in all: more than569# len(out) means the rest was thrown away.570#571# input is written to the standard input of the program, or null for nothing.572# The flags are 1 to fold standard error into the output and 2 to leave both573# alone, inherited from this process.574#575# The exit status is read with SpawnStatus after the call. See the exec module576# for the usual way to reach all this.577Spawn = fn(blob, nargs, input, out, flags) {578 inlen = if input == null { 0 } else { len(input) }579 outcap = if out == null { 0 } else { len(out) }580581 n = sys.sys_spawn(blob, len(blob), nargs, input, inlen, out, outcap, flags)582 if n < 0 {583 return Error("spawn")584 }585 return n586}587588# SpawnStatus is the exit status of the last Spawn: what the program passed to589# exit, or 128 plus the signal that killed it, or 127 when it wasn't found.590SpawnStatus = fn() { sys.sys_spawn_status() }