libslack(fio) - fifo and file control module and some I/O
#include <slack/std.h> #include <slack/fio.h>
char *fgetline(char *line, size_t size, FILE *stream); char *fgetline_unlocked(char *line, size_t size, FILE *stream); int read_timeout(int fd, long sec, long usec); int write_timeout(int fd, long sec, long usec); int rw_timeout(int fd, long sec, long usec); int nap(long sec, long usec); int fcntl_set_flag(int fd, int flag); int fcntl_clear_flag(int fd, int flag); int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len); int nonblock_set(int fd, int arg); int nonblock_on(int fd); int nonblock_off(int fd); int fifo_exists(const char *path, int prepare); int fifo_has_reader(const char *path, int prepare); int fifo_open(const char *path, mode_t mode, int lock, int *writefd);
This module provides various I/O related functions: reading a line of text no matter what line endings are used; timeouts for read/write operations without signals; exclusively opening a fifo for reading; and some random shorthand functions for manipulating file flags and locks.
char *fgetline(char *line, size_t size, FILE *stream)
Similar to fgets(3) except that it recognises UNIX ("\n"
), DOS ("\r\n"
) and Macintosh ("\r"
) line endings (even different line endings in the same file). Reads
characters from stream
and stores them in the buffer pointed to by line
. Reading stops after an EOF
or the end of the line is reached or when size - 1
characters have been stored. If the end of the line was reached, it is
stored as a "\n"
character. A
null
is stored after the last character in the buffer. On success, returns line
. On error, or when the end of file occurs while no characters have been
read, returns null
. Note that even when null
is returned, line
is modified and will always be nul
-terminated. So it is safe to examine line
even when this function returns null
. Calls to this function can be mixed with calls to other input functions
from the
stdio library for the same input stream. This is a drop-in replacement for fgets(3).
char line[BUFSIZ]; while (fgetline(line, BUFSIZ, stdin)) printf("%s", line);
char *fgetline_unlocked(char *line, size_t size, FILE *stream)
Equivalent to fgetline(3) except that stream
is not locked.
int read_timeout(int fd, long sec, long usec)
Performs a select(2) on a single file descriptor, fd
, for reading and exceptions (i.e. arrival of urgent data), that times out
after sec
seconds and usec
microseconds. This is just a shorthand function to provide a simple timed read(2) (or readv(2) or accept(2) or
recv(2) or recvfrom(2) or recvmsg(2) without resorting to
alarm(3) and SIGALRM
signals (best avoided). On success, returns 0
. On error, returns -1
with errno
set appropriately (ETIMEDOUT
if it timed out, otherwise set by select(2)). Usage:
if (read_timeout(fd, 5, 0) == -1 || (bytes = read(fd, buf, count)) == -1) return -1;
int write_timeout(int fd, long sec, long usec)
Performs a select(2) on a single file descriptor, fd
, for writing, that times out after sec
seconds and usec
microseconds. This is just a shorthand function to provide a simple timed write(2) (or writev(2) or
send(2) or sendto(2) or sendmsg(2)) without resorting to
alarm(3) and SIGALRM
signals (best avoided). On success, returns 0
. On error, returns -1
with errno
set appropriately (ETIMEDOUT
if it timed out, otherwise set by select(2)). Usage:
if (write_timeout(fd, 5, 0) == -1 || (bytes = write(fd, buf, count)) == -1) return -1;
int rw_timeout(int fd, long sec, long usec)
Performs a select(2) on a single file descriptor, fd
, for reading, writing and exceptions (i.e. arrival of urgent data), that
times out after
sec
seconds and usec
microseconds. This is just a shorthand function to provide a simple timed read(2) or write(2) without resorting to
alarm(3) and SIGALRM
signals (best avoided). On success, returns a bit mask indicating whether fd
is readable (R_OK
), writable (W_OK
) and/or has urgent data available (X_OK
). On error, returns -1
with
errno
set appropriately (ETIMEDOUT
if it timed out, otherwise set by
select(2)).
if ((mask = rw_timeout(fd, 5, 0)) == -1) return -1;
if ((mask & W_OK) && (bytes = write(fd, buf, count)) == -1) return -1;
if ((mask & R_OK) && (bytes = read(fd, buf, count)) == -1) return -1;
int nap(long sec, long usec)
Puts the process to sleep for sec
seconds and usec
microseconds. Note, however, that many systems' timers only have 10ms
resolution. On success, returns 0
. On error, returns -1
with errno
set appropriately.
nap(1, 500000); // Sleep for 1.5 seconds nap(0, 100000); // Sleep for 0.1 seconds
int fcntl_set_flag(int fd, int flag)
Shorthand for setting the file flag, flag
, on the file descriptor, fd
, using fcntl(2). All other flags are unaffected. On success, returns 0
. On error, returns -1
with errno
set by fcntl(2) with F_GETFL
or
F_SETFL
as the command.
int fcntl_clear_flag(int fd, int flag)
Shorthand for clearing the file flag, flag
, from the file descriptor,
fd
, using fcntl(2). All other flags are unaffected. On success, returns 0
. On error, returns -1
with errno
set by fcntl(2) with
F_GETFL
or F_SETFL
as the command.
int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len)
Shorthand for performing discretionary file locking operations on the file
descriptor, fd
. cmd
is the locking command and is passed to
fcntl(2). type
, whence
, start
and len
are used to fill a
flock structure which is passed to fcntl(2). Returns the same as
fcntl(2) with cmd
as the command.
if (fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) return -1;
int nonblock_set(int fd, int arg)
Sets non-blocking mode for the file descriptor, fd
, if arg
is non-zero. Sets blocking mode if arg
is zero. On success, returns 0
. On error, returns -1
with errno
set by fcntl(2) with F_GETFL
or
F_SETFL
as the command.
int nonblock_on(int fd)
Sets non-blocking mode for the file descriptor, fd
. On success, returns
0
. On error, returns -1
with errno
set by fcntl(2) with
F_GETFL
or F_SETFL
as the command.
int nonblock_off(int fd)
Sets blocking mode for the file descriptor, fd
. On success, returns 0
. On error, returns -1
with errno
set by fcntl(2) with F_GETFL
or
F_SETFL
as the command.
int fifo_exists(const char *path, int prepare)
Determines whether or not path
refers to a fifo. Returns 0
if path
doesn't exist or doesn't refer to a fifo. If path
refers to a fifo, returns 1. If prepare
is non-zero, and path
refers to a non-fifo, it will be unlinked. On error, returns -1
with errno
set by stat(2).
int fifo_has_reader(const char *path, int prepare)
Determines whether or not path
refers to a fifo that is being read by another process. If path
does not exist or does not refer to a fifo or if the fifo can't be opened
for non-blocking write(2), returns 0
. If
prepare
is non-zero, and path refers to a non-fifo, it will be unlinked. On error,
returns -1
with errno
set by stat(2) or open(2).
int fifo_open(const char *path, mode_t mode, int lock, int *writefd)
Creates a fifo named path
with creation mode mode
for reading. If
path
already exists, is a fifo and has a reader process, returns -1
with errno
set to EADDRINUSE
. If the fifo is created (or an existing one can be reused), two file
descriptors are opened to the fifo. A read descriptor and a write
descriptor. On success, returns the read descriptor. The write descriptor
only exists to ensure that there is always at least one writer process for
the fifo. This allows a read(2) on the read descriptor to block until another process writes to the fifo
rather than returning an
EOF
condition. This is done in a POSIX compliant way. If lock
is non-zero, the fifo is exclusively locked. If writefd
is not null
, the write descriptor is stored there. On error, returns -1
with errno
set by stat(2), open(2), mkfifo(2), fstat(2) or fcntl(2).
char *fifopath = "/tmp/fifo"; int fd, wfd;
if ((fd = fifo_open(fifopath, S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH, 1, &wfd)) == -1) return -1;
// Read from fd...
close(fd); close(wfd); unlink(fifopath);
These functions set errno
to the following values. errno
may also be set by the underlying system calls. See their manpages for
details.
ETIMEDOUT
The read_timeout(3), write_timeout(3) and rw_timeout(3) functions set this when a timeout occurs.
EADDRINUSE
fifo_open(3) sets this when the path refers to a fifo that already has another process reading from it.
MT-Safe
Mac OS X doesn't have flockfile(3), funlockfile(3) or getc_unlocked(3) so fgetline(3) is not MT-Safe on such platforms. You must guard all stdio calls in multi threaded programs with explicit synchronisation variables.
A paranoid fgetline() example:
#include <slack/std.h> #include <slack/fio.h>
int main() { char line[BUFSIZ];
while (fgetline(line, BUFSIZ, stdin)) printf("%s", line);
if (ferror(stdin)) { if (!*line) printf("%s\n", line);
return EXIT_FAILURE; }
return EXIT_SUCCESS; }
Read from stdin but give up after 5 seconds:
#include <slack/std.h> #include <slack/fio.h>
int main() { char buf[BUFSIZ]; ssize_t bytes;
if (read_timeout(STDIN_FILENO, 5, 0) == -1 || (bytes = read(STDIN_FILENO, buf, BUFSIZ)) == -1 || write(STDOUT_FILENO, buf, bytes) != bytes) return EXIT_FAILURE;
return EXIT_SUCCESS; }
A command line sub-second sleep command:
#include <slack/std.h> #include <slack/fio.h>
int main(int ac, char **av) { if (ac != 3) return EXIT_FAILURE;
nap(atoi(av[1]), atoi(av[2]));
return EXIT_SUCCESS; }
Setting file flags:
#include <slack/std.h> #include <slack/fio.h>
int main() { if (fcntl_set_flag(STDIN_FILENO, O_NONBLOCK | O_ASYNC) == -1) return EXIT_FAILURE;
if (fcntl_set_flag(STDOUT_FILENO, O_APPEND) == -1) return EXIT_FAILURE;
if (nonblock_on(STDOUT_FILENO) == -1) return EXIT_FAILURE;
if (fcntl_clear_flag(STDIN_FILENO, O_NONBLOCK | O_ASYNC) == -1) return EXIT_FAILURE;
if (fcntl_clear_flag(STDOUT_FILENO, O_APPEND) == -1) return EXIT_FAILURE;
if (nonblock_off(STDOUT_FILENO) == -1) return EXIT_FAILURE;
return EXIT_SUCCESS; }
File locking:
#include <slack/std.h> #include <slack/fio.h>
int main(int ac, char **av) { int fd;
if ((fd = open(av[1], O_RDWR)) == -1) return EXIT_FAILURE;
if (fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) return close(fd), EXIT_FAILURE;
// Write to the file...
if (fcntl_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0) == -1) return close(fd), EXIT_FAILURE;
close(fd);
return EXIT_SUCCESS; }
Turn a logfile into a fifo that sends log messages to syslog instead:
#include <slack/std.h> #include <slack/fio.h> #include <syslog.h>
int main() { char *fifopath = "/tmp/log2syslog"; char buf[BUFSIZ]; ssize_t bytes; int fd, wfd;
if ((fd = fifo_open(fifopath, S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH, 1, &wfd)) == -1) return EXIT_FAILURE;
while ((bytes = read(fd, buf, BUFSIZ)) > 0) { buf[bytes] = '\0'; syslog(LOG_DAEMON | LOG_ERR, "%s", buf); }
close(fd); close(wfd); unlink(fifopath); }
Some systems, such as Mac OS X, can't lock fifos. On these systems, fifo_open(3) ignores the locking failure and returns successfully. This means that there is no guarantee of a unique reader process on these systems. You will need to lock an ordinary file yourself to provide this guarantee.
libslack(3), fcntl(2), stat(2), fstat(2), open(2), write(2), read(2), mkfifo(2)
20040102 raf <raf@raf.org>