libslack - A UNIX/C library of general utilities for programmers with Slack
Slack(n.): The state in which you need nothing,
because you already have it.
-- J. R. "Bob" Dobbs, 1956
Compiling:
gcc -o app app.c `libslack-config --cflags --libs`
Headers:
/* Include this before all other header files */
#include <slack/lib.h>
or
/* Include this before all other header files */
#include <slack/std.h>
/* Then select what you want from the rest */
#include <slack/agent.h>
#include <slack/coproc.h>
#include <slack/daemon.h>
#include <slack/err.h>
#include <slack/fio.h>
#include <slack/hsort.h>
#include <slack/lim.h>
#include <slack/link.h>
#include <slack/list.h>
#include <slack/locker.h>
#include <slack/map.h>
#include <slack/mem.h>
#include <slack/msg.h>
#include <slack/net.h>
#include <slack/prog.h>
#include <slack/prop.h>
#include <slack/pseudo.h>
#include <slack/sig.h>
#include <slack/str.h>
#ifndef HAVE_SNPRINTF
#include <slack/snprintf.h>
#endif
#ifndef HAVE_VSSCANF
#include <slack/vsscanf.h>
#endif
Libslack is a library of general utilities designed to make UNIX/C programming a bit easier on the eye. It is a seemingly random collection of modules and functions that I find commonly useful.
It's a small library with lots of functionality, accurately documented and thoroughly tested. Good library naming conventions are not rigorously observed on the principle that common operations should always be easy to write and code should always be easy to read.
The "framework" parts of libslack are bound to be the most objectionable. Nobody likes frameworks. Don't worry, there isn't much to it. If you don't like it, pretend it's not there.
Libslack provides the ability for programs to identify themselves, perform command line option processing in a single line of code and produce "standard" GNU style --help
, --version
and usage messages. Debug and verbose messaging is also provided and is enabled by the use of "standard" --debug
and --verbose
command line options.
Messages (including error, debug and verbose messages) are provided with clean call syntax and flexible semantics. Messages can be directed to log files, file descriptors, syslog or multiplexed to any of the above (and to anywhere else (e.g. dialog boxes) if you implement your own message handlers) without complicating the call syntax.
ISO C imposes extreme restrictions on signal handlers. POSIX imposes less extreme but still annoying restrictions. Libslack contains functions that provide a level of abstraction between the signal handlers that you write and the real (ISO C compliant) signal handlers. This allows you to write unrestricted signal handlers.
Coarse grained persistence of simple configuration information is provided by the use of Java style properties files in "well-known" locations.
Libslack provides functions that make writing daemons trivial. It includes functions to become a daemon and to optionally create a locked pid file to ensure that only a single instance of a named daemon is ever active at the same time. The daemon function behaves appropriately if the client process is started by init(8) or inetd(8). There are also functions to streamline the parsing of simple configuration files (like those commonly found in the /etc
directory). There are also functions that help you write more secure daemons (i.e. revoke setuid/setgid privileges, prevent core file generation, change user and group, test the safety of a given file path).
Libslack provides functions to simplify the implementation of network servers and clients (TCP, UDP, unicast and multicast) and the (text or binary) application protocols that they use. Network servers and clients can be setup in a single line of code. There is transparent support for IPv4, IPv6 and UNIX domain socket addresses. There is support for reliability over UDP. Text protocols are implemented by sequences of expect and send functions. Binary protocol packets can be packed and unpacked (using functions similar to pack() and unpack() in Perl but network/storage friendly). There's also a function to send mail.
Libslack provides a generic agent-oriented programming model in the form of the Agent data type. Like objects, agents can react to external stimuli. Unlike objects, agents can can also take independent actions. This is performed by multiplexing I/O events on arbitrary file descriptors using poll() or select() and also multiplexing timers for scheduling actions. Connecting and disconnecting file descriptors is done in constant time. Scheduling and cancelling actions is done in constant time. Timer maintenance is performed in constant average time. This means that agents are scalable with respect to the number of outstanding timers and can support thousands of scheduled actions. A single agent may be used like a simple event loop, or multiple agents can be connected to each other in arbitrary networks across multiple threads, processes and/or hosts. A single agent isn't scalable with respect to the number of connected descriptors but multiple agents can be combined to achieve much higher scalability. They're useful for networked applications, distributed systems and simulations.
Libslack provides a generic growable pointer array data type called List, a generic growable hash table data type called Map and a decent String data type that comes with heaps of functions (many lifted from Perl). There are also abstract singly and doubly linked list data types with optional, "growable" freelists.
Libslack provides the Locker data type which decouples thread synchronisation strategies from client code. This allows code to be written that delegates the choice of synchronisation strategy to the client. This enables run time selection of locking strategies which means it's even possible to let the end user specify which locking strategy to use. It supports mutual exclusion locks, readers/writer locks and no locking. There are also debug versions that print messages to standard output to help clients locate deadlocks. See https://raf.org/papers/mt-disciplined.html
.
Libslack provides functions for creating pseudo terminals portably and for creating coprocesses that use either pipes or a pseudo terminal for communication with the client process.
Libslack contains convenience/shorthand functions for random things such as reading a line of text with any line ending (UNIX, DOS or Macintosh), fifo and file control, retrieving POSIX.1 limits, parsing syslog facility/priority pairs, dynamic allocation of multi-dimensional arrays, memory pools, secure memory, secure memory pools, There's also a heap sort function. And there are also implementations of GNU getopt_long(), strlcat(), strlcpy(), snprintf(), vsnprintf(), vsscanf(), asprinf() and vasprintf() for systems that don't have them.
Although there is a lot of functionality in libslack, the API itself is as low-level as possible. There are no gratuitous data structures that wrap around perfectly acceptable data structures that are provided by the system. For instance, the networking functions do not return some home grown Socket object. They return file descriptors just like underlying system calls. The coprocess API is similar in spirit to popen(3) and pclose(3). The fgetline(3) function interoperates perfectly with standard I/O. Errors are returned via errno
just like system calls and some standard library functions. You may not like errno
if you write multi-threaded programs but I figured that until errno
goes away, it's best to accept it and find a way to benefit from it. Unavoidably, the string module does wrap an object around perfectly good char pointers but the underlying char pointer is always accessible and many of the string functions have versions that work on ordinary C strings.
The purpose of this design is Laziness. It means I don't have to wrap every net related system call or standard string function in an extra function and you don't have to learn a load of new identifiers for functionality that you already know how to access. The same goes for error codes. I don't want to re-invent them and you don't need to re-learn them. It also means that libslack functions can be more easily incorporated into existing programs to enhance their functionality without huge code rewrites and without the need for a steep learning curve.
Every module has a manpage that explains every function in detail. The function signatures in the manpages are systematically checked against the source code to make sure that they never get out of sync.
There are plenty of examples in the manpages. Most of them are systematically compiled and executed to make sure that they never get out of sync with the source code.
The actual text of the manpages can't be systematically checked but it does get proofread regularly. If you find any errors of any kind in the documentation, please let me know.
All of the documentation takes the form of manpages because manpages are faster and more accessible than anything else.
Libslack is thoroughly tested. This doesn't mean that there are no bugs but it does mean that there is a large list of bugs that it doesn't have. Excluding the snprintf module, there are nearly 2,800 tests. Including the snprintf module, there are nearly 150,000 tests. These tests also serve as extra examples.
Libslack contains the following modules:
agent - agent-oriented programming
coproc - coprocesses using pipes or pseudo terminals
daemon - becoming a daemon
err - message/error/debug/verbosity/alert messaging
fio - fifo and file control and some I/O
getopt - GNU getopt_long() for systems that don't have it
hsort - generic heap sort
lim - POSIX.1 limits convenience functions
link - abstract linked lists with optional growable free lists
list - list (growable pointer array) data type
locker - abstract locking and reader/writer lock implementation
map - map (hash table) data type
mem - memory helper functions, secure memory, memory pools
msg - message handling and syslog helper functions
net - network functions (clients/servers, expect/send, pack/unpack, mail)
prog - program framework and flexible command line option handling
prop - program properties files
pseudo - pseudo terminals
sig - ISO C compliant signal handling
snprintf - safe sprintf() for systems that don't have it
str - string data type (tr, regexpr, regsub, fmt, trim, lc, uc, ...)
vsscanf - sscanf() with va_list argument for systems that don't have it
Each module, as well as each function, has its own section 3 manpage.
Each module has its own header file (see SYNOPSIS). If you want to include these header files individually, you must first include the <slack/std.h>
header file. Alternatively, you can just include the <slack/lib.h>
header file and get everything. Note: Make sure that <slack/lib.h>
or <slack/std.h>
is included before any system header files. Otherwise, the system headers won't be set up properly.
If libslack was installed on your system with a prefix added to each identifier, you may also want to include <slack/prefix.h>
which allows you to continue to use the original libslack identifiers and have them automatically translated into the prefixed identifiers.
Alternatively, if libslack was installed on your system with the original names and you wish to compile client code that does use a prefix, you can include "remove_prefix.h"
which allows you to continue to use the prefixed identifiers and have them automatically translated into the original identifiers. Note that you will probably have to generate this header file yourself (see the prefix program in the tools directory).
If your client code uses one prefix and the libslack installed on your current system uses a different prefix, you will need to include both of these header files. First include "remove_prefix.h"
. Then include <slack/prefix.h>
.
There are examples in all of the module manpages. If you can't find what you're looking for there, look at the test code which can be found at the bottom of each module's source file. The test code executes every function and thus serves as a treasure trove of examples. If you think of an example that really needs to be in the manpages, don't hesitate to let me know.
https://libslack.org
, https://raf.org/papers/mt-disciplined.html
, libslack-config(1), agent(3), coproc(3), daemon(3), err(3), fio(3), getopt(3), hsort(3), lim(3), link(3), list(3), locker(3), map(3), mem(3), msg(3), net(3), prog(3), prop(3), pseudo(3), sig(3), snprintf(3), str(3), vsscanf(3)
20230824 raf <raf@raf.org>