Module sys/unix

sys/unix
Stability: unstable — and Windows is a real capability question here, not a naming one. Windows has had `AF_UNIX` since 2017 but implements neither `AcceptEx` nor `GetAcceptExSockaddrs` for it, so the accept path in the generated runtime has to take a separate route, and `SOCK_DGRAM` over `AF_UNIX` does not exist there at all. `UnixAddr` also carries `std/sys/tcp`'s ownership problem in a slightly worse form — its `buf` is an `?*u8` whose `.None` case means "the platform reported a zero-size `sockaddr_un`", which is indistinguishable from "allocation refused" — and abstract (Linux `\0`-prefixed) socket names cannot be expressed through a NUL-terminated `*u8` path at all. Freezing needs the Windows matrix settled and an owning address type with a fallible constructor. — stable modules only change additively; this one may still change.

Async Unix-domain sockets — the raw syscall boundary.

The AF_UNIX socket calls plus a sockaddr_un builder. The transfer calls are the SAME externs std/sys/tcp uses — a connected socket is a connected socket at the syscall layer — so this module exists for the address family and the address construction, not for a different set of operations. std/net/unix.yo is the public surface (UnixStream, UnixListener).

Every operation returns an IoFuture resolving to a non-negative value (an fd for socket_* and accept, a byte count for send/recv, 0 for the rest) or a negative errno.

Stability

unstable — and Windows is a real capability question here, not a naming one. Windows has had AF_UNIX since 2017 but implements neither AcceptEx nor GetAcceptExSockaddrs for it, so the accept path in the generated runtime has to take a separate route, and SOCK_DGRAM over AF_UNIX does not exist there at all. UnixAddr also carries std/sys/tcp's ownership problem in a slightly worse form — its buf is an ?*u8 whose .None case means "the platform reported a zero-size sockaddr_un", which is indistinguishable from "allocation refused" — and abstract (Linux \0-prefixed) socket names cannot be expressed through a NUL-terminated *u8 path at all. Freezing needs the Windows matrix settled and an owning address type with a fallible constructor.

Types

UnixAddr struct
UnixAddr

A heap-allocated sockaddr_un buffer and its length.

Like std/sys/tcp's SockAddr it owns its allocation with no Dispose, so every make_sockaddr_un needs a matching free_addr. buf is optional because the constructor answers .None when the platform reports a zero-size sockaddr_un, which is how a target without Unix-domain sockets shows up.

Fields

NameTypeDescription
buf?(*(u8))

The sockaddr_un bytes, or .None if the platform has no sockaddr_un.

lenu32

Size of the buffer in bytes — the addrlen the socket calls want. 0 when buf is .None.

Functions

socket_stream function
fn() -> IoFuture

Create a stream-oriented Unix-domain socket — socket(AF_UNIX, SOCK_STREAM, 0). Resolves to the new fd, or a negative errno. This is the one to use: it is reliable, ordered and connection-based, like TCP but without the network.

Returns: IoFuture

socket_dgram function
fn() -> IoFuture

Create a datagram Unix-domain socket — socket(AF_UNIX, SOCK_DGRAM, 0). Resolves to the new fd, or a negative errno.

Unlike UDP, AF_UNIX datagrams ARE reliable and ordered — the kernel never drops them — so this is a message-boundary-preserving channel rather than a lossy one. It does not exist on Windows.

Returns: IoFuture

bind function
fn(sockfd : i32, addr : *u8, addrlen : u32) -> IoFuture

Bind the socket to a filesystem path — POSIX bind(2). Resolves to 0, or a negative errno.

This CREATES a socket file at the path, and -EADDRINUSE means the file already exists — a stale one from a crashed process counts, so a server normally unlinks the path (std/sys/dir's unlink) before binding. Closing the socket does not remove the file; that is the owner's job too. Access is governed by the directory's write permission, which is what makes a socket in a mode-0700 directory a private channel.

Parameters

NameTypeNotes
sockfdi32
addr*u8
addrlenu32

Returns: IoFuture

listen function
fn(sockfd : i32, backlog : i32) -> IoFuture

Mark the socket as accepting connections — POSIX listen(2). Resolves to 0, or a negative errno. backlog bounds the pending-connection queue.

Parameters

NameTypeNotes
sockfdi32
backlogi32

Returns: IoFuture

accept function
fn(sockfd : i32, addr : *u8, addrlen : *u32) -> IoFuture

Accept the next connection — POSIX accept(2). Resolves to the new connection's fd, or a negative errno. addr/addrlen receive the peer's address, which for a Unix socket is usually EMPTY (an unbound client has no path), so do not rely on it to identify the caller — read the peer credentials with SO_PEERCRED/LOCAL_PEERCRED through std/sys/sockinfo if you need that.

Parameters

NameTypeNotes
sockfdi32
addr*u8
addrlen*u32

Returns: IoFuture

connect function
fn(sockfd : i32, addr : *u8, addrlen : u32) -> IoFuture

Connect to a bound Unix-domain path — POSIX connect(2). Resolves to 0, or a negative errno; -ENOENT when nothing has bound the path and -ECONNREFUSED when the file exists but no process is listening, which is exactly the stale-socket-file case.

Parameters

NameTypeNotes
sockfdi32
addr*u8
addrlenu32

Returns: IoFuture

send function
fn(sockfd : i32, buf : *u8, len : usize, flags : i32) -> IoFuture

Send on a connected socket — POSIX send(2). Resolves to the number of bytes accepted (which may be short on a SOCK_STREAM socket), or a negative errno. The SIGPIPE caveat from std/sys/tcp's send applies here too: the runtime does not ignore it.

Parameters

NameTypeNotesDescription
sockfdi32
buf*u8

The sockaddr_un bytes, or .None if the platform has no sockaddr_un.

lenusize

Size of the buffer in bytes — the addrlen the socket calls want. 0 when buf is .None.

flagsi32

Returns: IoFuture

recv function
fn(sockfd : i32, buf : *u8, len : usize, flags : i32) -> IoFuture

Receive on a connected socket — POSIX recv(2). Resolves to the number of bytes read, or a negative errno. On a SOCK_STREAM socket 0 means the peer closed; on a SOCK_DGRAM one it is a zero-length message.

Parameters

NameTypeNotesDescription
sockfdi32
buf*u8

The sockaddr_un bytes, or .None if the platform has no sockaddr_un.

lenusize

Size of the buffer in bytes — the addrlen the socket calls want. 0 when buf is .None.

flagsi32

Returns: IoFuture

close function
fn(fd : i32) -> IoFuture

Close the descriptor — POSIX close(2). Resolves to 0, or a negative errno. It does NOT unlink the socket file a bind created.

Parameters

NameTypeNotes
fdi32

Returns: IoFuture

make_sockaddr_un function
fn(path : *u8) -> UnixAddr

Build a sockaddr_un for the filesystem path path (a NUL-terminated C string). The caller must free_addr the result.

The path is copied into a fixed sun_path array — 104 bytes on macOS, 108 on Linux — and TRUNCATED to fit rather than reported as too long, so a deep path silently binds somewhere else. .None comes back when the platform reports a zero-size sockaddr_un.

Parameters

NameTypeNotes
path*u8

Returns: UnixAddr

get_path function
fn(addr_buf : *u8) -> *u8

Read the path out of a sockaddr_un buffer as a NUL-terminated C string. The pointer BORROWS the buffer — it is sun_path's address inside it, not a copy — so it dangles once the buffer is freed. An unbound peer's address reads as an empty string.

Parameters

NameTypeNotes
addr_buf*u8

Returns: *u8

free_addr function
fn(addr : UnixAddr) -> unit

Release a UnixAddr's buffer. A .None buffer is a no-op, so this is safe to call on any make_sockaddr_un result.

Parameters

NameTypeNotes
addrUnixAddr

Returns: unit