← Back to AILP Home

sys() — Direct Syscalls

Nitpick provides direct syscall access at three safety levels to balance developer control with process integrity.

Function Tier Syscalls Available Return Type TOS Layer
sys(CONST, args...) Safe 72 curated whitelist Result<int64> or Result<string> Layer 1
sys!!(CONST, args...) Full All syscalls Result<int64> or Result<string> Layer 2
sys!!!(expr, args...) Raw Any int64 expression int64 (bare) Layer 3

Safe Tier — sys()

The safe tier uses a curated whitelist of 72 syscalls. This tier guarantees that process integrity cannot be compromised (e.g., you cannot accidentally fork or kill a process).

Note: The compiler requires a named constant as the first argument to sys(). These builtin constants (e.g., WRITE, OPEN) do not have the SYS_ prefix. The SYS_ prefixed constants are exported by stdlib/sys.npk for use in the raw tier. ```nitpick // Builtin safe tier usage: sys(WRITE, ...)

// Raw tier usage using stdlib constants: int64:nr = SYS_WRITE; sys!!!(nr, ...) ```

Safe Syscall Whitelist

Category Syscalls Count
File I/O READ, WRITE, OPEN, CLOSE, LSEEK, PREAD64, PWRITE64, OPENAT, READV, WRITEV 10
File Metadata FSTAT, NEWFSTATAT, FCHMOD, FCHOWN, UTIMENSAT, FACCESSAT 6
Memory MMAP, MUNMAP, MPROTECT, BRK, MREMAP 5
Directory & Links GETDENTS64, GETCWD, CHDIR, MKDIR, RMDIR, RENAME, RENAMEAT2, UNLINK, UNLINKAT, SYMLINK, READLINK 11
Process Info GETPID, GETPPID, GETTID, GETUID, GETGID, GETEUID, GETEGID 7
Time CLOCK_GETTIME, CLOCK_NANOSLEEP, NANOSLEEP 3
Networking SOCKET, BIND, LISTEN, ACCEPT4, CONNECT, SEND, RECV, SENDTO, RECVFROM, SETSOCKOPT, GETSOCKOPT, SHUTDOWN 12
Polling POLL, PPOLL, EPOLL_CREATE1, EPOLL_CTL, EPOLL_WAIT, SELECT, PSELECT6 7
Pipe / IPC PIPE2, DUP, DUP2, DUP3, EVENTFD2 5
Misc Safe IOCTL, FCNTL, FLOCK, FSYNC, FDATASYNC, GETRANDOM 6
Total 72

Inclusion Rationale

Exclusion Rationale (Moved to Full Tier)

Typed Returns

Some syscalls return structured data instead of a plain integer. The compiler automatically handles the buffer management.

Result — GETCWD, READLINK

// GETCWD: returns the current working directory as a string
Result<string>:cwd = sys(GETCWD);
string:dir = cwd ? "/unknown";
println(dir);

// READLINK: resolves a symbolic link target
Result<string>:target = sys(READLINK, "/proc/self/exe");
string:path = target ? "";

These syscalls internally: 1. Allocate a temporary 4096-byte buffer via mmap. 2. Execute the syscall writing into the buffer. 3. Copy the result into a managed Nitpick string. 4. Release the temporary buffer via munmap.

Variants: - sys(GETCWD) — uses the default 4096-byte buffer. - sys(GETCWD, size) — uses the specified buffer size in bytes.

Full Tier — sys!!()

All syscalls are available in this tier, including dangerous ones, but it still wraps the return value in a Result type:

int64:result = sys!!(SETUID, 0i64) ? -1i64;

Raw Tier Usage Guidelines

The sys!!!() tier accepts any int64 expression as the syscall number and returns a bare int64 (no Result wrapper).

When to use sys!!!: - Only for truly exotic syscalls not in any constant table. - For performance-critical hot paths where the Result wrapper overhead matters. - Calling a newer kernel syscall not yet present in Nitpick's builtin tables.

Risks: - No errno wrapping. - No compile-time argument validation.

Raw Tier Error Handling: The raw tier returns the exact kernel return value, which means errors are returned as negative errno values.

int64:ret = sys!!!(1i64, 1i64, "hello\n", 6i64); // raw WRITE
if (ret < 0i64) {
    // ret is -errno (e.g., -9 = EBADF)
    int64:errno = 0i64 - ret;
    println("Error!");
}

Known Issue

⚠️ Known Issue: String literals passed directly to sys() may fail to compile correctly due to an IR conversion issue.

Store the string in a variable first: ```nitpick // WRONG: // sys(WRITE, 1i64, "hello\n", 6i64);

// RIGHT: string:msg = "hello\n"; sys(WRITE, 1i64, msg, 6i64); ``` Note: This is tracked for a future fix and is not blocking.

Related