ffi
The C ABI boundary. This is the only place in the standard library where raw platform types are allowed — every other module works in length-typed Str and String.
| Item | Import |
|---|---|
| POSIX / glibc bindings, typed enums, constants | import std::ffi::libc; |
| OpenSSL bindings | import std::ffi::openssl; |
Strings across the boundary
Cryo strings carry a length and no terminator; C strings carry a terminator and no length. Translation happens at the boundary, and there is no CString wrapper type — String does the job.
Going out, build a NUL-terminated copy and pass its pointer:
import std::ffi::libc;
import std::collections::string;
mut path: String = String::with_null(name);
const fd: i32 = libc::open(path.as_ptr() as string, libc::O_RDONLY, 0);
String::with_null(source) produces an owned copy that keeps a trailing NUL. as_cstr(mut &this) does the same in place on a string you already have: it writes the terminator at index length, reserving a spare byte if needed, without changing the logical length. It is idempotent and allocation-free once that byte exists.
Coming back, restore the length:
const view: Str = Str::from(c_string); // length via strlen; the NUL is excluded
Check pointers for null before you do it.
libc
A single home for every extern "C" the standard library needs, plus broad coverage of the POSIX and glibc surface for downstream use. Modules call libc::read(...), libc::sin(...) and so on, which keeps the rest of the tree free of inline extern blocks.
Functions mirror the libc and POSIX headers verbatim — same names, same signatures, same errno semantics. Idiomatic wrappers that return Result, work in Slice<u8>, or retry on EINTR live at the call site, not here. io::fd is the canonical example.
Sections are grouped by header, so you can skim for a function the way you would in man 3 or /usr/include.
Enums versus constants
The split is deliberate and worth knowing before you go looking for a name.
Closed sets of mutually exclusive values are type enum Name : i32, so they pass to libc as typed parameters and the compiler catches a wrong-family argument. Variant names are kept verbatim from the C header — ClockId::CLOCK_MONOTONIC, not ClockId::Monotonic — so call sites read like the man pages.
Among them: Whence, ClockId, ItimerWhich, AddressFamily, Protocol, SocketLevel, ShutdownHow, EpollOp, DirentType, MutexType, DetachState, CancelState, CancelType, LockType, FadviseAdvice, MadviseAdvice, BufferingMode, SigprocmaskHow, LocaleCategory, Resource, RusageWho, PriorityWhich, and the termios TcAction / TcQueue / TcFlow.
Bit-flag families stay as plain consts, because they get OR'd together and compared against return values, which a closed enum does not fit: O_*, S_IF*, POLL*, MAP_*, EPOLL*, AI_*, signal numbers, and errno codes.
C structs
Layout-stable POSIX structs are defined here as Cryo types with matching field order, so you can construct them directly: timespec, timeval, pollfd, iovec, in_addr, in6_addr, rlimit, tm, winsize, regmatch_t.
Platform-variant structs are treated as opaque byte buffers that you pack and unpack yourself — sockaddr_in, struct stat, addrinfo, epoll_event, struct termios, and the pthread_* family. The accompanying SIZEOF_* constants give the Linux x86-64 size so a u8[SIZEOF_X] buffer comes out the right length. net::socket::tcp does exactly this for sockaddr_in.
Errno, signal, mode, fcntl, epoll, and ioctl values are the Linux x86-64 / glibc ABI. Other platforms re-derive them from the corresponding headers.
Floating-point classification — isnan, isinf, finite, signbit, fpclassify — is deliberately not declared here. It is implemented in pure Cryo over the IEEE-754 bit pattern instead, since those are macros rather than exported symbols in most libcs.
openssl
Raw extern "C" declarations for the subset of the OpenSSL 3.x API that net::tls drives. Opaque handles — SSL, SSL_CTX, SSL_METHOD — cross as void*, and you own the lifetime: pair every *_new with its *_free.
A binary that pulls in net::tls must add ssl and crypto to link_libs in its cryoconfig. Nothing here is referenced unless net::tls is used, so non-TLS binaries link without OpenSSL at all.
Several "functions" in
<openssl/ssl.h>are preprocessor macros overSSL_ctrlrather than exported symbols. The one that matters,SSL_set_tlsext_host_namefor SNI, is wrapped here asssl_set_sniover the realSSL_ctrlsymbol. Every extern in this file was checked againstnm -Don the shipped libraries.