Centralized configuration database.
Stores data in a tree like structure with nodes referenced by "paths" that have a file system-like format. i.e. /root/node1/node2/node3 = value
- SET - set the value for the specified path
- VALIDATE - accept / deny sets that match the specified path
- WATCH - watch for changes in the specified path
- GET - get the value stored at the specified path
- REFRESH - refresh database just in time when someone does a get for the specified path
- PROVIDE - provide the value stored at the specified path
- SEARCH - look for sub-paths that match the requested root path
- INDEX - provide search results for the specified root path
- PRUNE - from a requested root path, set values for all sub-paths to NULL
- PROXY - proxy gets and sets to the requested path via the specified URL
Apteryx paths are similar to unix paths.
- Use forward-slash / as a separator
- Start with a separator
- Spaces are prohibited
- Double separator is prohibited (i.e. "/test//example" is invalid)
- Some functions take a path and a key, this is treated as if they were joined with a separator, i.e. func(path, key, ...) called with ("/test/example", "name",...) would access "/test/example/name"
- Avoid collisions by selecting a starting path that is unique and not shorthand, i.e. "/av" is not acceptable, but "/antivirus" is, preferably the name of the library also matches the path used.
- Full paths include the Apteryx instance url e.g.
UNIX "unix:///<unix-path>[:<apteryx-path>]" e.g. unix:///tmp/apteryx:/system/hostname
TCP(IPv4) "tcp://<IPv4>:<port>[:<apteryx-path>]" e.g. tcp://192.168.1.2:9999:/system/hostname
TCP(IPv6) "tcp:[<IPv6>]:<port>[:<apteryx-path>]" e.g. tcp://[fc00::1]:9999:/system/hostname
Care must be taken when registering validation functions with apteryx_validate. Calls made to apteryx_set will block until the apteryx_validate callback is processed - this introduces a possible loop that can only be broken with a timeout. In order to avoid this, a process should avoid setting a value that it validates itself, and particularly avoid doing this from a watch callback.
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <apteryx.h>
#define SYSTEM_PATH "/system"
#define SYSTEM_HOSTNAME SYSTEM_PATH "/hostname"
#define SYSTEM_TIMEZONE SYSTEM_PATH "/timezone"
#define SYSTEM_TIME SYSTEM_PATH "/time"
bool watch_timezone (const char *path, const char *value)
{
char *cmd = NULL;
/* When the timezone is unset, use UTC */
if (value == NULL)
{
value = "UTC";
}
/* Create symlink from /etc/localtime to /usr/share/zoneinfo/(value) */
asprintf (&cmd, "ln -sf /usr/share/zoneinfo/%s /etc/localtime", value);
system (cmd);
free (cmd);
return true;
}
char *provide_time (const char *path)
{
char *ret = NULL;
time_t rawtime;
time ( &rawtime );
ret = strdup (ctime (&rawtime));
char *nl = strchr (ret, '\n');
nl[0] = '\0';
return ret;
}
int main (int argc, char *argv[])
{
char *value;
apteryx_init (false);
apteryx_watch (SYSTEM_TIMEZONE, watch_timezone);
apteryx_provide (SYSTEM_TIME, provide_time);
apteryx_set (SYSTEM_HOSTNAME, "host1");
value = apteryx_get (SYSTEM_TIME);
printf ("%s\n", value);
free ((void*)value);
while (1)
{
sleep (100);
}
}
sudo apt-get install libglib2.0-dev libcunit1-dev liblua5.2-dev
gcc -o clockd clockd.c -I. -L. -lapteryx -std=c99 `pkg-config --cflags glib-2.0` `pkg-config --libs glib-2.0`
LD_LIBRARY_PATH=. ./clockd &
apteryx -g /clock/time
apteryx -s /clock/timezone NZ # This might require you to run clockd as root
apteryx -g /clock/time
# Run unit tests on the release binary
make unit [TESTS="filter to match one or more tests"]
# Run the unit tests on the gcov and address sanitiser compiled binary
make test [TESTS="filter to match one or more tests"]
# GCOV output
google-chrome .test/gcov/index.html
Usage: apteryx [-h] [-s|-g|-f|-q|-t|-r|-w|-p|-x|-l|-m|-c|-u<filter>] [<path>] [<value>]
-h show this help
-d debug
-s set <path> to <value>
-g get <path>
-f find <path>
-q query <path>?<query>
-t traverse database from <path>
-r prune <path>
-w watch changes to the path <path>
-p provide <value> for <path>
-x proxy <path> via url <value>
-l last change <path>
-m display memory usage for <path>
-c display counters and statistics
-u run unit tests (optionally match only tests with <filter>)
Internal settings
/apteryx/debug
/apteryx/sockets
/apteryx/watchers
/apteryx/providers
/apteryx/validators
/apteryx/proxies
/apteryx/counters
Examples:
./apteryxd -b -p apteryx.pid
LD_LIBRARY_PATH=. ./apteryx -s /interfaces/eth0/description "our lan"
LD_LIBRARY_PATH=. ./apteryx -s /interfaces/eth0/state "up"
LD_LIBRARY_PATH=. ./apteryx -g /interfaces/eth0/description
/interfaces/eth0/description/ = our lan
LD_LIBRARY_PATH=. ./apteryx -t /interfaces/eth0/
/interfaces/eth0/description our lan
/interfaces/eth0/state up
./apteryxd -b -p apteryx2.pid -l tcp://127.0.0.1:9999
LD_LIBRARY_PATH=. ./apteryx -s tcp://127.0.0.1:9999:/test/dog cat
LD_LIBRARY_PATH=. ./apteryx -g /remote/node/test/dog
LD_LIBRARY_PATH=. ./apteryx -x /remote/node/* tcp://127.0.0.1:9999
LD_LIBRARY_PATH=. ./apteryx -g /remote/node/test/dog