skalibs: the djbunix library interface
Keywords: skalibs c unix djbunix library libstddjb

libstddjb
libskarnet
skalibs
Software
skarnet.org

# The `djbunix` library interface

The following functions are declared in the `skalibs/djbunix.h` header, and implemented in the `libskarnet.a` or `libskarnet.so` library.

## General information

`djbunix` is an alternative API to management of basic Unix concepts: file descriptors, files, environment, and so on. It is a rather chaotic mix of safe wrappers around Unix system calls, better reimplementations of standard libc functionalities, and higher-level manipulations of Unix concepts.

Understanding `djbunix` is essential to understanding any piece of code depending on skalibs.

## Functions

### Basic fd operations

`int coe (int fd)`
Sets the close-on-exec flag on fd. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int uncoe (int fd)`
Clears the close-on-exec flag on fd. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int ndelay_on (int fd)`
Sets the O_NONBLOCK flag on fd: sets it to non-blocking mode. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int ndelay_off (int fd)`
Clears the O_NONBLOCK flag on fd: sets it to blocking mode. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int pipenb (int *p)`
Like pipe(), but both ends of the created pipe are in non-blocking mode.

`int pipecoe (int *p)`
Like pipe(), but both ends of the created pipe are close-on-exec.

`int pipenbcoe (int *p)`
Like pipe(), but both ends of the created pipe are in non-blocking mode and close-on-exec.

`int fd_copy (int to, int from)`
Copies the open fd from to number to. If to was already open, it is closed before the copy. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int fd_copy2 (int to1, int from1, int to2, int from2)`
Copies the open fd from1 to number to2. Also copies from2 to to2 at the same time. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int fd_move (int to, int from)`
Moves the open fd from to number to. If to was already open, it is closed before the move. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`int fd_move2 (int to1, int from1, int to2, int from2)`
Moves the open fd from to number to. Also moves from2 to to2 at the same time. This is useful for instance when you want to swap two fds: `fd_move2` will handle the situation correctly. Returns 0 if it succeeds, or -1 (and sets errno) if it fails.

`void fd_close (int fd)`
Closes fd. This is a safe wrapper around close().

`int fd_chmod (int fd, unsigned int mode)`
Safe wrapper around fchmod().

`int fd_chown (int fd, uid_t uid, gid_t gid)`
Safe wrapper around fchown(). This function requires root privileges.

`int fd_sync (int fd)`
Safe wrapper around fsync().

`int fd_chdir (int fd)`
Safe wrapper around fchdir().

`off_t fd_cat (int from, int to)`
Synchronously copies data from fd from to fd to, until it encounters EOF or an error. Returns -1 (and sets errno) if it fails; returns the number of transmitted bytes if it gets an EOF.

When the underlying OS allows it, zero-copy transmission is performed. Currently, the following zero-copy implementations are supported:

- splice(), in Linux 2.6.17 and later

`off_t fd_catn (int from, int to, off_t n)`
Synchronously copies at most n bytes from fd from to fd to. Returns the total number of transmitted bytes; sets errno if this number is lesser than n. EOF is reported as EPIPE. See above for zero-copy transmission; zero-copy transmission is not attempted for less than 64k of data.

`int fd_ensure_open (int fd, int w)`
If fd is not open, opens it to `/dev/null`, for reading if w is zero, and for writing otherwise. Returns 1 if it succeeds and 0 if it fails.

`int fd_sanitize (void)`
Ensures stdin, stdout and stderr are open. If one of those file descriptors was closed, it now points to `/dev/null`. Returns 1 if it succeeds and 0 if it fails.

`void fd_shutdown (int fd, int w)`
Shuts down socket fd, for reading if w is zero, and for writing otherwise. Does not return an error code; does not modify errno. This is just a call to shutdown() with errno saved, used essentially to isolate application code from `sys/socket.h` which is a portability nightmare.

`int fd_lock (int fd, int w, int nb)`
Gets an advisory lock on fd: shared if w is zero, exclusive otherwise. fd must point to a regular file, open for writing or reading depending on whether you want an exclusive lock or not. If nb is zero, the function blocks until the lock can be obtained; otherwise it returns 0 immediately. On success, the function returns 1 ; it returns 0 if it cannot take the lock, or -1 (and sets errno) if an error occurs.

`void fd_unlock (int fd)`
Releases a previously held lock on fd.

`int fd_islocked (int fd)`
Returns 1 if a lock is currently held on fd, 0 otherwise. Returns -1 (and sets errno) if an error occurs.

`int open2 (char const *file, unsigned int flags)`
Safe wrapper around open() when it takes 2 arguments.

`int open3 (char const *file, unsigned int flags, unsigned int mode)`
Safe wrapper around open() when it takes 3 arguments.

`int open_read (char const *file)`
Opens file in read-only, non-blocking mode. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_read (char const *file)`
Opens file in read-only, non-blocking mode, close-on-exec. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openb_read (char const *file)`
Opens file in read-only, blocking mode. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openbc_read (char const *file)`
Opens file in read-only, blocking mode, close-on-exec. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int open_readb (char const *file)`
Opens file in read-only, blocking mode. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails. This call does not block. The open() system call is actually performed with the O_NONBLOCK option, and blocking mode is set afterwards; this behaviour allows for more transparent interactions with FIFOs.

`int openc_readb (char const *file)`
Same as above, but the file is opened close-on-exec.

`int open_excl (char const *file)`
Opens file in write-only, non-blocking mode, with the additional O_EXCL and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_excl (char const *file)`
Opens file in write-only, non-blocking mode, close-on-exec, with the additional O_EXCL and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int open_append (char const *file)`
Opens file in write-only, non-blocking mode, with the additional O_APPEND and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_append (char const *file)`
Opens file in write-only, non-blocking mode, close-on-exec, with the additional O_APPEND and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int open_trunc (char const *file)`
Opens file in write-only, non-blocking mode, with the additional O_TRUNC and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_trunc (char const *file)`
Opens file in write-only, non-blocking mode, close-on-exec, with the additional O_TRUNC and O_CREAT flags. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int open_create (char const *file)`
Opens file in write-only, non-blocking mode, with the additional O_CREAT flag. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_create (char const *file)`
Opens file in write-only, non-blocking mode, close-on-exec, with the additional O_CREAT flag. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int open_write (char const *file)`
Opens file in write-only, non-blocking mode. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

`int openc_write (char const *file)`
Opens file in write-only, non-blocking mode, close-on-exec. Returns a valid fd number if it succeeds, or -1 (and sets errno) if it fails.

### Forking children

`pid_t doublefork ()`
Performs a double fork. Returns -1 if it fails (and sets errno, EINTR meaning that the intermediate process was killed by a signal), 0 if the current process is the grandchild, and the grandchild's PID if the current process is the parent.

`pid_t child_spawn0 (char const *file, char const *const *argv, char const *const *envp)`
Forks and executes a child as with `exec_ae(file, argv, envp)`. Returns 0 if it fails, and the pid of the child if it succeeds. Implemented via posix_spawn() on systems that support it.

`pid_t child_spawn1_pipe (char const *file, char const *const *argv, char const *const *envp, int *fd, int w)`
Like `child_spawn0()`, except that a pipe is created between the child's stdin (if w is 0) or stdout (if w is nonzero) and the parent. The parent's end of the pipe will be stored in *fd.

`pid_t child_spawn1_socket (char const *file, char const *const *argv, char const *const *envp, int *fd)`
Like `child_spawn0`, except that a socket is created between the parent and the child. Both the child's stdin and stdout point to the socket; the parent has its end of the socket available in *fd.

`pid_t child_spawn2 (char const *file, char const *const *argv, char const *const *envp, int *fds)`
Like `child_spawn0()`, except that two pipes are created between the parent and the child. fds must point to an array of 2 ints: this array is read as well as written by `child_spawn2()`. On function call, the numbers in fds must describe what fds should be used in the child to contain the communication pipes (for instance, if the child will read from its parent on stdin and writes to its parent on stdout, fds must contain 0 and 1). On function return, fds contains the descriptors for the pipes that read from / write to the child.

`pid_t child_spawn (char const *file, char const *const *argv, char const *const *envp, int *fds, unsigned int nfds)`
More generic spawning function. fds must point to an array of at least nfds ints; file descriptors reading from or writing to the child will be stored there. The function returns 0 on failure or the pid of the child on success.

- If nfds is 0, then the function behaves like `child_spawn0`, except all ignored signals will be un-ignored in the child
- If nfds is 1 or more, then fds will contain pipes between the child and the parent. The parent will read on even-numbered ones (starting on fds[0]) and write on odd-numbered ones.

### Waiting for children

`unsigned int wait_reap ()`
Instantly reaps all the pending zombies, without blocking, without a look at the exit codes. Returns the number of reaped zombies.

`int waitn (pid_t *pids, unsigned int n)`
Waits until all processes whose PIDs are stored in the pids array, of size n, have died. Returns 1 if it succeeds, and 0 (and sets errno) if it fails. The pid array is not guaranteed to be unchanged.

`int waitn_posix (pid_t *pids, unsigned int n, int *wstat)`
Like `waitn`, but stores into *wstat the status of the last process in the pids array (i.e. `pids[n-1]`).

`int waitn_reap (pid_t *pids, unsigned int n)`
Instantly reaps all zombies whose PIDs are stored in the pids array, of size n. Returns -1 (and sets errno) if it fails, and the number of reaped zombies if it succeeds. The pid array is not guaranteed to be unchanged.

`int waitn_reap_posix (pid_t *pids, unsigned int n, int *wstat)`
Like `waitn_reap`, but stores into *wstat the status of the last process in the pids array (i.e. `pids[n-1]`), if applicable; otherwise *wstat is unchanged.

`pid_t wait_nohang (int *wstat)`
Instantly reaps one zombie, and stores the status information into *wstat. Returns the PID of the reaped zombie if it succeeds, 0 if there was nothing to reap (and the current process still has children), -1 ECHILD if there was nothing to reap (and the current process has no children), or -1 (and sets errno) if it fails.

`pid_t waitpid_nointr (pid_t pid, int *wstat, int flags)`
Safe wrapper around waitpid().

`pid_t wait_pid_nohang (pid_t pid, int *wstat)`
Instantly reaps an undetermined number of zombies until it finds pid. Stores the status information for dead pid into *wstat. Returns pid if it succeeds, 0 if there was nothing to reap (and the current process still has children), -1 ECHILD if there was nothing to reap (and the current process has no children), or -1 (and sets errno) if it fails.

`int wait_pids_nohang (pid_t const *pids, unsigned int len, int *wstat)`
Instantly reaps an undetermined number of zombies until it finds one whose PID is in the pids array, of size len. Stores the status information for that dead process into *wstat. Returns the index of the found PID in pids, starting at 1. Returns 0 if there was nothing to reap (and the current process still has children), -1 ECHILD if there was nothing to reap (and the current process has no children), or -1 (and sets errno) if it fails.

When asynchronously dealing with a child (resp. several children) and getting a SIGCHLD - which should be handled via a selfpipe - it is generally a good idea to use the `wait_pid_nohang()` (resp. `wait_pids_nohang()`) function over the basic Unix APIs. This allows a program to:

- Automatically and silently take care of children it does not know it has. This situation can happen when a process forks and the parent execs. When the child dies, the new parent process has to drag the "zombie bastard" along, which is ugly; `wait_pids_nohang()` prevents this.
- Still take appropriate care of its legitimate children, in any order.

### Reading and writing whole files

`int slurp (stralloc *sa, int fd)`
Slurps the contents of open descriptor fd into the *sa stralloc. If you are doing this, you should either have full control over the slurped file, or run your process with suitable limits to the amount of heap memory it can get. The function returns 1 if it succeeds, or 0 (and sets errno) if it fails.

`int openslurpclose (stralloc *sa, char const *file)`
Slurps the contents of file file into *sa. Returns 1 if it succeeds, and 0 (and sets errno) if it fails.

`ssize_t openreadnclose (char const *file, char *s, size_t n)`
Reads at most n bytes from file file into preallocated buffer s. Returns -1 (and sets errno) if it fails; else returns the number of read bytes.

`ssize_t openreadnclose_nb (char const *file, char *s, size_t n)`
Like `openreadnclose`, but can fail with EAGAIN if the file cannot be immediately read (for instance if it's a named pipe or other special file).

`int openreadfileclose (char const *file, stralloc *sa, size_t n)`
Reads at most n bytes from file file into the *sa stralloc, which is grown (if needed) to just accommodate the file size. Returns 1 if it succeeds and 0 (and sets errno) if it fails.

`int openwritenclose_unsafe_internal (char const *file, char const *s, size_t len, dev_t *dev, ino_t *ino, int dosync)`
Writes the n bytes stored at s into file file. The previous contents of file are destroyed even if the function fails. If dosync is nonzero, the new contents of file are synced to disk before the function returns. If dev and ino are not null, they're used to store the device and inode number of file. The function returns 1 if it succeeds, or 0 (and sets errno) if it fails.

`int openwritenclose_unsafe (char const *file, char const *s, size_t len)
int openwritenclose_unsafe_sync (char const *file, char const *s, size_t len)
int openwritenclose_unsafe_devino (char const *file, char const *s, size_t len, dev_t *dev, ino_t *ino)
int openwritenclose_unsafe_devino_sync (char const *file, char const *s, size_t len, dev_t *dev, ino_t *ino)`
Trivial shortcuts around `openwritenclose_unsafe_internal()`. The reader can easily figure out what they do.

`int openwritenclose_suffix_internal (char const *file, char const *s, size_t len, dev_t *dev, ino_t *ino, int dosync, char const *suffix)`
Writes the n bytes stored at s into file file, by first writing into filesuffix and atomically renaming filesuffix to file. IOW, the old contents of file are preserved if the operation fails, and are atomically replaced with the new contents if the operation succeeds. If dosync is nonzero, the new contents of filesuffix are synced to disk before the atomic replace. If dev and ino are not null, they're used to store the device and inode number of file. The function returns 1 if it succeeds, or 0 (and sets errno) if it fails.

`int openwritenclose_suffix (char const *file, char const *s, size_t len, char const *suffix)
int openwritenclose_suffix_sync (char const *file, char const *s, size_t len, char const *suffix)
int openwritenclose_suffix_devino (char const *file, char const *s, size_t len, char const *suffix, dev_t *dev, ino_t *ino)
int openwritenclose_suffix_devino_sync (char const *file, char const *s, size_t len, char const *suffix, dev_t *dev, ino_t *ino)`
Trivial shortcuts around `openwritenclose_suffix_internal()`. The reader can easily figure out what they do.

`int openwritevnclose_unsafe_internal (char const *file, struct iovec const *v, unsigned int vlen, dev_t *dev, ino_t *ino, int dosync)`
Like `openwritenclose_unsafe_internal`, but the content to write is taken from a scatter/gather array of vlen elements instead of a single string.

`int openwritevnclose_unsafe (char const *file, struct iovec const *v, unsigned int vlen)
int openwritevnclose_unsafe_sync (char const *file, struct iovec const *v, unsigned int vlen)
int openwritevnclose_unsafe_devino (char const *file, struct iovec const *v, unsigned int vlen, dev_t *dev, ino_t *ino)
int openwritevnclose_unsafe_devino_sync (char const *file, struct iovec const *v, unsigned int vlen, dev_t *dev, ino_t *ino)`
Trivial wrappers around `openwritevnclose_unsafe_internal()`.

`int openwritevnclose_suffix_internal (char const *file, struct iovec const *v, unsigned int vlen, dev_t *dev, ino_t *ino, int dosync, char const *suffix)`
Like `openwritenclose_suffix_internal`, but the content to write is taken from a scatter/gather array of vlen elements instead of a single string.

`int openwritevnclose_suffix (char const *file, struct iovec const *v, unsigned int vlen, char const *suffix)
int openwritevnclose_suffix_sync (char const *file, struct iovec const *v, unsigned int vlen, char const *suffix)
int openwritevnclose_suffix_devino (char const *file, struct iovec const *v, unsigned int vlen, char const *suffix, dev_t *dev, ino_t *ino)
int openwritevnclose_suffix_devino_sync (char const *file, struct iovec const *v, unsigned int vlen, char const *suffix, dev_t *dev, ino_t *ino)`
Trivial wrappers around `openwritevnclose_suffix_internal()`.

### Filesystem deletion

The following operations are not atomic, so if they fail, the relevant subtree might end up partially deleted.

`int rm_rf (char const *path)`
Deletes the filesystem subtree at path. Returns 0 if it succeeds or -1 (and sets errno) if it fails.

`int rm_rf_tmp (char const *path, stralloc *tmp)`
Deletes the filesystem subtree at path, using *tmp as heap-allocated temporary space. Returns 0 if it succeeds or -1 (and sets errno) if it fails.

`int rm_rf_in_tmp (stralloc *tmp, size_t n)`
Deletes a filesystem subtree, using *tmp as heap-allocated temporary space. Returns 0 if it succeeds or -1 (and sets errno) if it fails. When the function is called, *tmp must contain the null-terminated name of the subtree to delete at offset n.

`int rmstar (char const *dir)`
Deletes all the filesystem subtrees in directory dir. Returns 0 if it succeeds or -1 (and sets errno) if it fails.

`int rmstar_tmp (char const *dir, stralloc *tmp)`
Deletes all the filesystem subtrees in directory dir, using *tmp as heap-allocated temporary space. Returns 0 if it succeeds or -1 (and sets errno) if it fails.

### Filesystem copy

`int hiercopy_tmp (char const *src, char const *dst, stralloc *tmp)`
Recursively copies the filesystem hierarchy at src into dst, preserving modes, and also preserving the uids/gids if the process is running as the super-user. Uses *tmp as heap-allocated temporary space. Returns 1 if it succeeds or 0 (and sets errno) if it fails.

`int hiercopy (char const *src, char const *dst)`
Same as above, using the `satmp` global stralloc as heap-allocated temporary space.

### Directory listing

`int sals (char const *dir, stralloc *sa, size_t *maxlen)`
Appends the base names of all the files (save `.` and `..`) in dir to the stralloc *sa; each name is null-terminated. On error, returns -1 and sets errno. On success, returns the number of files it found, and writes to *maxlen the size of the largest file name it found (0 for an empty directory).

### Variable length wrappers around Single Unix calls

`int sarealpath (stralloc *sa, char const *path)`
Resolves path into a symlink-free absolute path, appending the result to the *sa stralloc. Returns 0 if it succeeds and -1 (and sets errno) if it fails.

`int sarealpath_tmp (stralloc *sa, char const *path, stralloc *tmp)`
Resolves path into a symlink-free absolute path, appending the result to *sa. Uses *tmp as heap-allocated temporary space. Returns 0 if it succeeds and -1 (and sets errno) if it fails.

`int sabasename (stralloc *sa, char const *s, size_t len)`
Appends the basename of filename s (of length len) to *sa. Returns 1 if it succeeds and 0 (and sets errno) if it fails.

`int sadirname (stralloc *sa, char const *s, size_t len)`
Appends the dirname of filename s (of length len) to *sa. Returns 1 if it succeeds and 0 (and sets errno) if it fails.

`int sagetcwd (stralloc *sa)`
Appends the current working directory to *sa. Returns 0 if it succeeds and -1 (and sets errno) if it fails.

`int sareadlink (stralloc *sa, char const *link)`
Appends the contents of symbolic link link to *sa. Returns 0 if it succeeds and -1 (and sets errno) if it fails.

`int sagethostname (stralloc *sa)`
Appends the machine's hostname to *sa. Returns 0 if it succeeds and -1 (and sets errno) if it fails.

### Temporization

`void deepsleepuntil (tain const *deadline, tain *stamp)`
Sleeps until the absolute time represented by the tain *deadline. *stamp must contain the current time. When the function returns, *stamp has been updated to reflect the new current time.

`void deepsleep (unsigned int n)`
Sleeps n seconds. Signals received during that time are handled, but do not interrupt the sleep.

`void deepmillisleep (unsigned long n)`
Sleeps n milliseconds. Signals received during that time are handled, but do not interrupt the sleep.

### Miscellaneous functions

`size_t path_canonicalize (char *out, char const *in, int check)`
Writes into out the canonical form of the Unix path given in in. The out array must be preallocated with at least `strlen(in)+2` bytes. Returns the length of the out path, without counting the terminating null byte. If check is nonzero, in is tested for every `foobar/..` element, and the function returns 0, and sets errno appropriately, if `foobar` is not a valid directory; in that case, out contains the problematic path.