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)
20100612 raf <raf@raf.org>