Need DFS in trie.c, process() in main.c, and the rest
parent
ceb4a4dec5
commit
3cf8ae170b
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* config.h: compilation config file
|
||||||
|
* Edit as needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
const int cfg_log_facility = LOG_CRON;
|
||||||
|
const char cfg_log_ident[] = "kairos";
|
||||||
|
const int cfg_log_opt = LOG_PID | LOG_NDELAY;
|
||||||
|
|
||||||
|
const char cfg_trie_file[] = "/home/ledoian/.kairos/trie"
|
||||||
|
const char cfg_tasks_file[] = "/home/ledoian/.kairos/tasks"
|
||||||
|
const char cfg_socket[] = "/tmp/kairos.sock"
|
||||||
|
|
||||||
|
const int cfg_sock_maxclients = 16;
|
||||||
|
const int cfg_var_name_max_len = 16;
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* daemonize.c -- provides daemonize() function to daemonize a process
|
||||||
|
* according to daemon(7) and given flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "daemonize.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define unless(x) if(!((x)))
|
||||||
|
|
||||||
|
bool daemonize(uint32_t flags) {
|
||||||
|
if (flags & NODAEMONIZE) return true;
|
||||||
|
|
||||||
|
bool retval = true;
|
||||||
|
|
||||||
|
unless (flags & NOCLOSEFD) {
|
||||||
|
struct rlimit maxfd;
|
||||||
|
if(getrlimit(RLIMIT_NOFILE, &maxfd) == -1) {
|
||||||
|
//Shouldn't ever happen
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: getrlimit failed: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
for (rlim_t fd = 3; fd < maxfd.rlim_cur; fd++) {
|
||||||
|
char tries = 0;
|
||||||
|
while(close(fd) == -1) { // Try 3 times to close fd, retry only if interrupted
|
||||||
|
unless (errno == EINTR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
|
if (tries > 3) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not close fd %d: %m", fd);
|
||||||
|
retval = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NORSTSIG) {
|
||||||
|
for (int sig = 0; sig <= SIGRTMAX; sig++) { //FIXME: This may not be the best way
|
||||||
|
signal(sig, SIG_DFL); //EINVAL is not important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NORSTSIGMASK) {
|
||||||
|
sigset_t *set = malloc(sizeof(sigset_t));
|
||||||
|
if (set == NULL) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not malloc sigset_t: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
sigemptyset(set); //shall not have errors
|
||||||
|
if (sigprocmask(SIG_SETMASK, set, NULL) == -1) { //Shall not fail
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: sigprocmask failed: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOSANITY) {
|
||||||
|
// We don't sanitize environment, the user does.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipe for notification in step 14
|
||||||
|
int pipe_fds[2];
|
||||||
|
if(pipe(pipe_fds) == -1) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not create pipe: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOBGFORK) {
|
||||||
|
pid_t val = fork();
|
||||||
|
if (val == -1) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: fork failed: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
if (val > 0) {
|
||||||
|
// We're the grandparent
|
||||||
|
// We only wait for report from our grandson and then exit.
|
||||||
|
close(pipe_fds[1]);
|
||||||
|
char buf[2] = {0,0};
|
||||||
|
int tries = 0;
|
||||||
|
while (read(pipe_fds[0], buf, 1) == -1) {
|
||||||
|
tries++;
|
||||||
|
if (errno == EINTR && tries <= 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: grandpa: cannot receive data through pipe: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
if (buf[0] != 'S') { //Not success
|
||||||
|
syslog(cfg_log_facility | LOG_NOTICE, "daemonize: grandpa: grandson didn't report success");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(retval?0:1);
|
||||||
|
}
|
||||||
|
// We're the parent and we continue running at this point.
|
||||||
|
close(pipe_fds[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unless(flags & NOSETSID) {
|
||||||
|
if(setsid() == -1)
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: father: setsid failed: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOSECONDFORK) {
|
||||||
|
pid_t val = fork();
|
||||||
|
if (val == -1) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "daemonize: second fork failed: %m");
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
if (val > 0) {
|
||||||
|
//We're the parent, we exit
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
// We're the son and we continue running.
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOCONNECTIO) {
|
||||||
|
int fd = open("/dev/null", O_RDWR);
|
||||||
|
dup2(fd, STDIN_FILENO); //These shall not fail, maybe exept EINTR (FIXME)
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
unless(flags & NORSTUMASK) {
|
||||||
|
umask(0000); //No errors are defined.
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOCHANGEWD) {
|
||||||
|
chdir("/"); //No errors are applicable, maybe except EACCESS (FIXME)
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOPIDFILE) {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (flags & NOPRIVDROP) {
|
||||||
|
// We don't drop privileges, as we want to exec programs with privileges of the user.
|
||||||
|
}
|
||||||
|
|
||||||
|
write(pipe_fds[1], retval?"S":"F", 1);
|
||||||
|
syslog(cfg_log_facility | LOG_INFO, "Now runing as a daemon");
|
||||||
|
close(pipe_fds[1]);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* daemonize.h: a function to daemonize a process
|
||||||
|
* according to daemon(7) (SysV style)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DAEMONIZE_H
|
||||||
|
#define DAEMONIZE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
bool daemonize(uint32_t flags);
|
||||||
|
|
||||||
|
// the indices correspond to items in list to do in daemon(7)
|
||||||
|
// except the first one
|
||||||
|
// some of them will never be used
|
||||||
|
|
||||||
|
#define NODAEMONIZE 1<<0 /*Do not daemonize at all*/
|
||||||
|
#define NOCLOSEFD 1<<1
|
||||||
|
#define NORSTSIG 1<<2
|
||||||
|
#define NORSTSIGMASK 1<<3
|
||||||
|
#define NOSANITY 1<<4 // Sanitizing not implemented
|
||||||
|
#define NOBGFORK 1<<5
|
||||||
|
#define NOSETSID 1<<6
|
||||||
|
#define NOSECONDFORK 1<<7
|
||||||
|
#define NOEXITCHLD 1<<8 // Not implemented -- always exit
|
||||||
|
#define NOCONNECTIO 1<<9
|
||||||
|
#define NORSTUMASK 1<<10
|
||||||
|
#define NOCHANGEWD 1<<11
|
||||||
|
#define NOPIDFILE 1<<12 // Creating PID file not implemented
|
||||||
|
#define NOPRIVDROP 1<<13 // Not needed
|
||||||
|
#define NONOTIFY 1<<14 // Not implemented -- always notify
|
||||||
|
#define NOPARENTEXIT 1<<15 // Not implemented -- always exit
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,525 @@
|
|||||||
|
DAEMON(7) daemon DAEMON(7)
|
||||||
|
|
||||||
|
NAME
|
||||||
|
daemon - Writing and packaging system daemons
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
A daemon is a service process that runs in the background and
|
||||||
|
supervises the system or provides functionality to other processes.
|
||||||
|
Traditionally, daemons are implemented following a scheme originating
|
||||||
|
in SysV Unix. Modern daemons should follow a simpler yet more powerful
|
||||||
|
scheme (here called "new-style" daemons), as implemented by systemd(1).
|
||||||
|
This manual page covers both schemes, and in particular includes
|
||||||
|
recommendations for daemons that shall be included in the systemd init
|
||||||
|
system.
|
||||||
|
|
||||||
|
SysV Daemons
|
||||||
|
When a traditional SysV daemon starts, it should execute the following
|
||||||
|
steps as part of the initialization. Note that these steps are
|
||||||
|
unnecessary for new-style daemons (see below), and should only be
|
||||||
|
implemented if compatibility with SysV is essential.
|
||||||
|
|
||||||
|
1. Close all open file descriptors except standard input, output, and
|
||||||
|
error (i.e. the first three file descriptors 0, 1, 2). This ensures
|
||||||
|
that no accidentally passed file descriptor stays around in the
|
||||||
|
daemon process. On Linux, this is best implemented by iterating
|
||||||
|
through /proc/self/fd, with a fallback of iterating from file
|
||||||
|
descriptor 3 to the value returned by getrlimit() for
|
||||||
|
RLIMIT_NOFILE.
|
||||||
|
|
||||||
|
2. Reset all signal handlers to their default. This is best done by
|
||||||
|
iterating through the available signals up to the limit of _NSIG
|
||||||
|
and resetting them to SIG_DFL.
|
||||||
|
|
||||||
|
3. Reset the signal mask using sigprocmask().
|
||||||
|
|
||||||
|
4. Sanitize the environment block, removing or resetting environment
|
||||||
|
variables that might negatively impact daemon runtime.
|
||||||
|
|
||||||
|
5. Call fork(), to create a background process.
|
||||||
|
|
||||||
|
6. In the child, call setsid() to detach from any terminal and create
|
||||||
|
an independent session.
|
||||||
|
|
||||||
|
7. In the child, call fork() again, to ensure that the daemon can
|
||||||
|
never re-acquire a terminal again.
|
||||||
|
|
||||||
|
8. Call exit() in the first child, so that only the second child (the
|
||||||
|
actual daemon process) stays around. This ensures that the daemon
|
||||||
|
process is re-parented to init/PID 1, as all daemons should be.
|
||||||
|
|
||||||
|
9. In the daemon process, connect /dev/null to standard input, output,
|
||||||
|
and error.
|
||||||
|
|
||||||
|
10. In the daemon process, reset the umask to 0, so that the file modes
|
||||||
|
passed to open(), mkdir() and suchlike directly control the access
|
||||||
|
mode of the created files and directories.
|
||||||
|
|
||||||
|
11. In the daemon process, change the current directory to the root
|
||||||
|
directory (/), in order to avoid that the daemon involuntarily
|
||||||
|
blocks mount points from being unmounted.
|
||||||
|
|
||||||
|
12. In the daemon process, write the daemon PID (as returned by
|
||||||
|
getpid()) to a PID file, for example /run/foobar.pid (for a
|
||||||
|
hypothetical daemon "foobar") to ensure that the daemon cannot be
|
||||||
|
started more than once. This must be implemented in race-free
|
||||||
|
fashion so that the PID file is only updated when it is verified at
|
||||||
|
the same time that the PID previously stored in the PID file no
|
||||||
|
longer exists or belongs to a foreign process.
|
||||||
|
|
||||||
|
13. In the daemon process, drop privileges, if possible and applicable.
|
||||||
|
|
||||||
|
14. From the daemon process, notify the original process started that
|
||||||
|
initialization is complete. This can be implemented via an unnamed
|
||||||
|
pipe or similar communication channel that is created before the
|
||||||
|
first fork() and hence available in both the original and the
|
||||||
|
daemon process.
|
||||||
|
|
||||||
|
15. Call exit() in the original process. The process that invoked the
|
||||||
|
daemon must be able to rely on that this exit() happens after
|
||||||
|
initialization is complete and all external communication channels
|
||||||
|
are established and accessible.
|
||||||
|
|
||||||
|
The BSD daemon() function should not be used, as it implements only a
|
||||||
|
subset of these steps.
|
||||||
|
|
||||||
|
A daemon that needs to provide compatibility with SysV systems should
|
||||||
|
implement the scheme pointed out above. However, it is recommended to
|
||||||
|
make this behavior optional and configurable via a command line
|
||||||
|
argument to ease debugging as well as to simplify integration into
|
||||||
|
systems using systemd.
|
||||||
|
|
||||||
|
New-Style Daemons
|
||||||
|
Modern services for Linux should be implemented as new-style daemons.
|
||||||
|
This makes it easier to supervise and control them at runtime and
|
||||||
|
simplifies their implementation.
|
||||||
|
|
||||||
|
For developing a new-style daemon, none of the initialization steps
|
||||||
|
recommended for SysV daemons need to be implemented. New-style init
|
||||||
|
systems such as systemd make all of them redundant. Moreover, since
|
||||||
|
some of these steps interfere with process monitoring, file descriptor
|
||||||
|
passing and other functionality of the init system, it is recommended
|
||||||
|
not to execute them when run as new-style service.
|
||||||
|
|
||||||
|
Note that new-style init systems guarantee execution of daemon
|
||||||
|
processes in a clean process context: it is guaranteed that the
|
||||||
|
environment block is sanitized, that the signal handlers and mask is
|
||||||
|
reset and that no left-over file descriptors are passed. Daemons will
|
||||||
|
be executed in their own session, with standard input connected to
|
||||||
|
/dev/null and standard output/error connected to the systemd-
|
||||||
|
journald.service(8) logging service, unless otherwise configured. The
|
||||||
|
umask is reset.
|
||||||
|
|
||||||
|
It is recommended for new-style daemons to implement the following:
|
||||||
|
|
||||||
|
1. If SIGTERM is received, shut down the daemon and exit cleanly.
|
||||||
|
|
||||||
|
2. If SIGHUP is received, reload the configuration files, if this
|
||||||
|
applies.
|
||||||
|
|
||||||
|
3. Provide a correct exit code from the main daemon process, as this
|
||||||
|
is used by the init system to detect service errors and problems.
|
||||||
|
It is recommended to follow the exit code scheme as defined in the
|
||||||
|
LSB recommendations for SysV init scripts[1].
|
||||||
|
|
||||||
|
4. If possible and applicable, expose the daemon's control interface
|
||||||
|
via the D-Bus IPC system and grab a bus name as last step of
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
5. For integration in systemd, provide a .service unit file that
|
||||||
|
carries information about starting, stopping and otherwise
|
||||||
|
maintaining the daemon. See systemd.service(5) for details.
|
||||||
|
|
||||||
|
6. As much as possible, rely on the init system's functionality to
|
||||||
|
limit the access of the daemon to files, services and other
|
||||||
|
resources, i.e. in the case of systemd, rely on systemd's resource
|
||||||
|
limit control instead of implementing your own, rely on systemd's
|
||||||
|
privilege dropping code instead of implementing it in the daemon,
|
||||||
|
and similar. See systemd.exec(5) for the available controls.
|
||||||
|
|
||||||
|
7. If D-Bus is used, make your daemon bus-activatable by supplying a
|
||||||
|
D-Bus service activation configuration file. This has multiple
|
||||||
|
advantages: your daemon may be started lazily on-demand; it may be
|
||||||
|
started in parallel to other daemons requiring it — which maximizes
|
||||||
|
parallelization and boot-up speed; your daemon can be restarted on
|
||||||
|
failure without losing any bus requests, as the bus queues requests
|
||||||
|
for activatable services. See below for details.
|
||||||
|
|
||||||
|
8. If your daemon provides services to other local processes or remote
|
||||||
|
clients via a socket, it should be made socket-activatable
|
||||||
|
following the scheme pointed out below. Like D-Bus activation, this
|
||||||
|
enables on-demand starting of services as well as it allows
|
||||||
|
improved parallelization of service start-up. Also, for state-less
|
||||||
|
protocols (such as syslog, DNS), a daemon implementing socket-based
|
||||||
|
activation can be restarted without losing a single request. See
|
||||||
|
below for details.
|
||||||
|
|
||||||
|
9. If applicable, a daemon should notify the init system about startup
|
||||||
|
completion or status updates via the sd_notify(3) interface.
|
||||||
|
|
||||||
|
10. Instead of using the syslog() call to log directly to the system
|
||||||
|
syslog service, a new-style daemon may choose to simply log to
|
||||||
|
standard error via fprintf(), which is then forwarded to syslog by
|
||||||
|
the init system. If log levels are necessary, these can be encoded
|
||||||
|
by prefixing individual log lines with strings like "<4>" (for log
|
||||||
|
level 4 "WARNING" in the syslog priority scheme), following a
|
||||||
|
similar style as the Linux kernel's printk() level system. For
|
||||||
|
details, see sd-daemon(3) and systemd.exec(5).
|
||||||
|
|
||||||
|
These recommendations are similar but not identical to the Apple MacOS
|
||||||
|
X Daemon Requirements[2].
|
||||||
|
|
||||||
|
ACTIVATION
|
||||||
|
New-style init systems provide multiple additional mechanisms to
|
||||||
|
activate services, as detailed below. It is common that services are
|
||||||
|
configured to be activated via more than one mechanism at the same
|
||||||
|
time. An example for systemd: bluetoothd.service might get activated
|
||||||
|
either when Bluetooth hardware is plugged in, or when an application
|
||||||
|
accesses its programming interfaces via D-Bus. Or, a print server
|
||||||
|
daemon might get activated when traffic arrives at an IPP port, or when
|
||||||
|
a printer is plugged in, or when a file is queued in the printer spool
|
||||||
|
directory. Even for services that are intended to be started on system
|
||||||
|
bootup unconditionally, it is a good idea to implement some of the
|
||||||
|
various activation schemes outlined below, in order to maximize
|
||||||
|
parallelization. If a daemon implements a D-Bus service or listening
|
||||||
|
socket, implementing the full bus and socket activation scheme allows
|
||||||
|
starting of the daemon with its clients in parallel (which speeds up
|
||||||
|
boot-up), since all its communication channels are established already,
|
||||||
|
and no request is lost because client requests will be queued by the
|
||||||
|
bus system (in case of D-Bus) or the kernel (in case of sockets) until
|
||||||
|
the activation is completed.
|
||||||
|
|
||||||
|
Activation on Boot
|
||||||
|
Old-style daemons are usually activated exclusively on boot (and
|
||||||
|
manually by the administrator) via SysV init scripts, as detailed in
|
||||||
|
the LSB Linux Standard Base Core Specification[1]. This method of
|
||||||
|
activation is supported ubiquitously on Linux init systems, both
|
||||||
|
old-style and new-style systems. Among other issues, SysV init scripts
|
||||||
|
have the disadvantage of involving shell scripts in the boot process.
|
||||||
|
New-style init systems generally employ updated versions of activation,
|
||||||
|
both during boot-up and during runtime and using more minimal service
|
||||||
|
description files.
|
||||||
|
|
||||||
|
In systemd, if the developer or administrator wants to make sure that a
|
||||||
|
service or other unit is activated automatically on boot, it is
|
||||||
|
recommended to place a symlink to the unit file in the .wants/
|
||||||
|
directory of either multi-user.target or graphical.target, which are
|
||||||
|
normally used as boot targets at system startup. See systemd.unit(5)
|
||||||
|
for details about the .wants/ directories, and systemd.special(7) for
|
||||||
|
details about the two boot targets.
|
||||||
|
|
||||||
|
Socket-Based Activation
|
||||||
|
In order to maximize the possible parallelization and robustness and
|
||||||
|
simplify configuration and development, it is recommended for all
|
||||||
|
new-style daemons that communicate via listening sockets to employ
|
||||||
|
socket-based activation. In a socket-based activation scheme, the
|
||||||
|
creation and binding of the listening socket as primary communication
|
||||||
|
channel of daemons to local (and sometimes remote) clients is moved out
|
||||||
|
of the daemon code and into the init system. Based on per-daemon
|
||||||
|
configuration, the init system installs the sockets and then hands them
|
||||||
|
off to the spawned process as soon as the respective daemon is to be
|
||||||
|
started. Optionally, activation of the service can be delayed until the
|
||||||
|
first inbound traffic arrives at the socket to implement on-demand
|
||||||
|
activation of daemons. However, the primary advantage of this scheme is
|
||||||
|
that all providers and all consumers of the sockets can be started in
|
||||||
|
parallel as soon as all sockets are established. In addition to that,
|
||||||
|
daemons can be restarted with losing only a minimal number of client
|
||||||
|
transactions, or even any client request at all (the latter is
|
||||||
|
particularly true for state-less protocols, such as DNS or syslog),
|
||||||
|
because the socket stays bound and accessible during the restart, and
|
||||||
|
all requests are queued while the daemon cannot process them.
|
||||||
|
|
||||||
|
New-style daemons which support socket activation must be able to
|
||||||
|
receive their sockets from the init system instead of creating and
|
||||||
|
binding them themselves. For details about the programming interfaces
|
||||||
|
for this scheme provided by systemd, see sd_listen_fds(3) and sd-
|
||||||
|
daemon(3). For details about porting existing daemons to socket-based
|
||||||
|
activation, see below. With minimal effort, it is possible to implement
|
||||||
|
socket-based activation in addition to traditional internal socket
|
||||||
|
creation in the same codebase in order to support both new-style and
|
||||||
|
old-style init systems from the same daemon binary.
|
||||||
|
|
||||||
|
systemd implements socket-based activation via .socket units, which are
|
||||||
|
described in systemd.socket(5). When configuring socket units for
|
||||||
|
socket-based activation, it is essential that all listening sockets are
|
||||||
|
pulled in by the special target unit sockets.target. It is recommended
|
||||||
|
to place a WantedBy=sockets.target directive in the "[Install]" section
|
||||||
|
to automatically add such a dependency on installation of a socket
|
||||||
|
unit. Unless DefaultDependencies=no is set, the necessary ordering
|
||||||
|
dependencies are implicitly created for all socket units. For more
|
||||||
|
information about sockets.target, see systemd.special(7). It is not
|
||||||
|
necessary or recommended to place any additional dependencies on socket
|
||||||
|
units (for example from multi-user.target or suchlike) when one is
|
||||||
|
installed in sockets.target.
|
||||||
|
|
||||||
|
Bus-Based Activation
|
||||||
|
When the D-Bus IPC system is used for communication with clients,
|
||||||
|
new-style daemons should employ bus activation so that they are
|
||||||
|
automatically activated when a client application accesses their IPC
|
||||||
|
interfaces. This is configured in D-Bus service files (not to be
|
||||||
|
confused with systemd service unit files!). To ensure that D-Bus uses
|
||||||
|
systemd to start-up and maintain the daemon, use the SystemdService=
|
||||||
|
directive in these service files to configure the matching systemd
|
||||||
|
service for a D-Bus service. e.g.: For a D-Bus service whose D-Bus
|
||||||
|
activation file is named org.freedesktop.RealtimeKit.service, make sure
|
||||||
|
to set SystemdService=rtkit-daemon.service in that file to bind it to
|
||||||
|
the systemd service rtkit-daemon.service. This is needed to make sure
|
||||||
|
that the daemon is started in a race-free fashion when activated via
|
||||||
|
multiple mechanisms simultaneously.
|
||||||
|
|
||||||
|
Device-Based Activation
|
||||||
|
Often, daemons that manage a particular type of hardware should be
|
||||||
|
activated only when the hardware of the respective kind is plugged in
|
||||||
|
or otherwise becomes available. In a new-style init system, it is
|
||||||
|
possible to bind activation to hardware plug/unplug events. In systemd,
|
||||||
|
kernel devices appearing in the sysfs/udev device tree can be exposed
|
||||||
|
as units if they are tagged with the string "systemd". Like any other
|
||||||
|
kind of unit, they may then pull in other units when activated (i.e.
|
||||||
|
plugged in) and thus implement device-based activation. systemd
|
||||||
|
dependencies may be encoded in the udev database via the SYSTEMD_WANTS=
|
||||||
|
property. See systemd.device(5) for details. Often, it is nicer to pull
|
||||||
|
in services from devices only indirectly via dedicated targets.
|
||||||
|
Example: Instead of pulling in bluetoothd.service from all the various
|
||||||
|
bluetooth dongles and other hardware available, pull in
|
||||||
|
bluetooth.target from them and bluetoothd.service from that target.
|
||||||
|
This provides for nicer abstraction and gives administrators the option
|
||||||
|
to enable bluetoothd.service via controlling a bluetooth.target.wants/
|
||||||
|
symlink uniformly with a command like enable of systemctl(1) instead of
|
||||||
|
manipulating the udev ruleset.
|
||||||
|
|
||||||
|
Path-Based Activation
|
||||||
|
Often, runtime of daemons processing spool files or directories (such
|
||||||
|
as a printing system) can be delayed until these file system objects
|
||||||
|
change state, or become non-empty. New-style init systems provide a way
|
||||||
|
to bind service activation to file system changes. systemd implements
|
||||||
|
this scheme via path-based activation configured in .path units, as
|
||||||
|
outlined in systemd.path(5).
|
||||||
|
|
||||||
|
Timer-Based Activation
|
||||||
|
Some daemons that implement clean-up jobs that are intended to be
|
||||||
|
executed in regular intervals benefit from timer-based activation. In
|
||||||
|
systemd, this is implemented via .timer units, as described in
|
||||||
|
systemd.timer(5).
|
||||||
|
|
||||||
|
Other Forms of Activation
|
||||||
|
Other forms of activation have been suggested and implemented in some
|
||||||
|
systems. However, there are often simpler or better alternatives, or
|
||||||
|
they can be put together of combinations of the schemes above. Example:
|
||||||
|
Sometimes, it appears useful to start daemons or .socket units when a
|
||||||
|
specific IP address is configured on a network interface, because
|
||||||
|
network sockets shall be bound to the address. However, an alternative
|
||||||
|
to implement this is by utilizing the Linux IP_FREEBIND socket option,
|
||||||
|
as accessible via FreeBind=yes in systemd socket files (see
|
||||||
|
systemd.socket(5) for details). This option, when enabled, allows
|
||||||
|
sockets to be bound to a non-local, not configured IP address, and
|
||||||
|
hence allows bindings to a particular IP address before it actually
|
||||||
|
becomes available, making such an explicit dependency to the configured
|
||||||
|
address redundant. Another often suggested trigger for service
|
||||||
|
activation is low system load. However, here too, a more convincing
|
||||||
|
approach might be to make proper use of features of the operating
|
||||||
|
system, in particular, the CPU or I/O scheduler of Linux. Instead of
|
||||||
|
scheduling jobs from userspace based on monitoring the OS scheduler, it
|
||||||
|
is advisable to leave the scheduling of processes to the OS scheduler
|
||||||
|
itself. systemd provides fine-grained access to the CPU and I/O
|
||||||
|
schedulers. If a process executed by the init system shall not
|
||||||
|
negatively impact the amount of CPU or I/O bandwidth available to other
|
||||||
|
processes, it should be configured with CPUSchedulingPolicy=idle and/or
|
||||||
|
IOSchedulingClass=idle. Optionally, this may be combined with
|
||||||
|
timer-based activation to schedule background jobs during runtime and
|
||||||
|
with minimal impact on the system, and remove it from the boot phase
|
||||||
|
itself.
|
||||||
|
|
||||||
|
INTEGRATION WITH SYSTEMD
|
||||||
|
Writing systemd Unit Files
|
||||||
|
When writing systemd unit files, it is recommended to consider the
|
||||||
|
following suggestions:
|
||||||
|
|
||||||
|
1. If possible, do not use the Type=forking setting in service files.
|
||||||
|
But if you do, make sure to set the PID file path using PIDFile=.
|
||||||
|
See systemd.service(5) for details.
|
||||||
|
|
||||||
|
2. If your daemon registers a D-Bus name on the bus, make sure to use
|
||||||
|
Type=dbus in the service file if possible.
|
||||||
|
|
||||||
|
3. Make sure to set a good human-readable description string with
|
||||||
|
Description=.
|
||||||
|
|
||||||
|
4. Do not disable DefaultDependencies=, unless you really know what
|
||||||
|
you do and your unit is involved in early boot or late system
|
||||||
|
shutdown.
|
||||||
|
|
||||||
|
5. Normally, little if any dependencies should need to be defined
|
||||||
|
explicitly. However, if you do configure explicit dependencies,
|
||||||
|
only refer to unit names listed on systemd.special(7) or names
|
||||||
|
introduced by your own package to keep the unit file operating
|
||||||
|
system-independent.
|
||||||
|
|
||||||
|
6. Make sure to include an "[Install]" section including installation
|
||||||
|
information for the unit file. See systemd.unit(5) for details. To
|
||||||
|
activate your service on boot, make sure to add a
|
||||||
|
WantedBy=multi-user.target or WantedBy=graphical.target directive.
|
||||||
|
To activate your socket on boot, make sure to add
|
||||||
|
WantedBy=sockets.target. Usually, you also want to make sure that
|
||||||
|
when your service is installed, your socket is installed too, hence
|
||||||
|
add Also=foo.socket in your service file foo.service, for a
|
||||||
|
hypothetical program foo.
|
||||||
|
|
||||||
|
Installing systemd Service Files
|
||||||
|
At the build installation time (e.g. make install during package
|
||||||
|
build), packages are recommended to install their systemd unit files in
|
||||||
|
the directory returned by pkg-config systemd
|
||||||
|
--variable=systemdsystemunitdir (for system services) or pkg-config
|
||||||
|
systemd --variable=systemduserunitdir (for user services). This will
|
||||||
|
make the services available in the system on explicit request but not
|
||||||
|
activate them automatically during boot. Optionally, during package
|
||||||
|
installation (e.g. rpm -i by the administrator), symlinks should be
|
||||||
|
created in the systemd configuration directories via the enable command
|
||||||
|
of the systemctl(1) tool to activate them automatically on boot.
|
||||||
|
|
||||||
|
Packages using autoconf(1) are recommended to use a configure script
|
||||||
|
excerpt like the following to determine the unit installation path
|
||||||
|
during source configuration:
|
||||||
|
|
||||||
|
PKG_PROG_PKG_CONFIG
|
||||||
|
AC_ARG_WITH([systemdsystemunitdir],
|
||||||
|
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||||
|
[with_systemdsystemunitdir=auto])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||||
|
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||||
|
|
||||||
|
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||||
|
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||||
|
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||||
|
with_systemdsystemunitdir=no],
|
||||||
|
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||||
|
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||||
|
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||||
|
|
||||||
|
This snippet allows automatic installation of the unit files on systemd
|
||||||
|
machines, and optionally allows their installation even on machines
|
||||||
|
lacking systemd. (Modification of this snippet for the user unit
|
||||||
|
directory is left as an exercise for the reader.)
|
||||||
|
|
||||||
|
Additionally, to ensure that make distcheck continues to work, it is
|
||||||
|
recommended to add the following to the top-level Makefile.am file in
|
||||||
|
automake(1)-based projects:
|
||||||
|
|
||||||
|
DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||||
|
|
||||||
|
Finally, unit files should be installed in the system with an automake
|
||||||
|
excerpt like the following:
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
systemdsystemunit_DATA = \
|
||||||
|
foobar.socket \
|
||||||
|
foobar.service
|
||||||
|
endif
|
||||||
|
|
||||||
|
In the rpm(8) .spec file, use snippets like the following to
|
||||||
|
enable/disable the service during installation/deinstallation. This
|
||||||
|
makes use of the RPM macros shipped along systemd. Consult the
|
||||||
|
packaging guidelines of your distribution for details and the
|
||||||
|
equivalent for other package managers.
|
||||||
|
|
||||||
|
At the top of the file:
|
||||||
|
|
||||||
|
BuildRequires: systemd
|
||||||
|
%{?systemd_requires}
|
||||||
|
|
||||||
|
And as scriptlets, further down:
|
||||||
|
|
||||||
|
%post
|
||||||
|
%systemd_post foobar.service foobar.socket
|
||||||
|
|
||||||
|
%preun
|
||||||
|
%systemd_preun foobar.service foobar.socket
|
||||||
|
|
||||||
|
%postun
|
||||||
|
%systemd_postun
|
||||||
|
|
||||||
|
If the service shall be restarted during upgrades, replace the
|
||||||
|
"%postun" scriptlet above with the following:
|
||||||
|
|
||||||
|
%postun
|
||||||
|
%systemd_postun_with_restart foobar.service
|
||||||
|
|
||||||
|
Note that "%systemd_post" and "%systemd_preun" expect the names of all
|
||||||
|
units that are installed/removed as arguments, separated by spaces.
|
||||||
|
"%systemd_postun" expects no arguments. "%systemd_postun_with_restart"
|
||||||
|
expects the units to restart as arguments.
|
||||||
|
|
||||||
|
To facilitate upgrades from a package version that shipped only SysV
|
||||||
|
init scripts to a package version that ships both a SysV init script
|
||||||
|
and a native systemd service file, use a fragment like the following:
|
||||||
|
|
||||||
|
%triggerun -- foobar < 0.47.11-1
|
||||||
|
if /sbin/chkconfig --level 5 foobar ; then
|
||||||
|
/bin/systemctl --no-reload enable foobar.service foobar.socket >/dev/null 2>&1 || :
|
||||||
|
fi
|
||||||
|
|
||||||
|
Where 0.47.11-1 is the first package version that includes the native
|
||||||
|
unit file. This fragment will ensure that the first time the unit file
|
||||||
|
is installed, it will be enabled if and only if the SysV init script is
|
||||||
|
enabled, thus making sure that the enable status is not changed. Note
|
||||||
|
that chkconfig is a command specific to Fedora which can be used to
|
||||||
|
check whether a SysV init script is enabled. Other operating systems
|
||||||
|
will have to use different commands here.
|
||||||
|
|
||||||
|
PORTING EXISTING DAEMONS
|
||||||
|
Since new-style init systems such as systemd are compatible with
|
||||||
|
traditional SysV init systems, it is not strictly necessary to port
|
||||||
|
existing daemons to the new style. However, doing so offers additional
|
||||||
|
functionality to the daemons as well as simplifying integration into
|
||||||
|
new-style init systems.
|
||||||
|
|
||||||
|
To port an existing SysV compatible daemon, the following steps are
|
||||||
|
recommended:
|
||||||
|
|
||||||
|
1. If not already implemented, add an optional command line switch to
|
||||||
|
the daemon to disable daemonization. This is useful not only for
|
||||||
|
using the daemon in new-style init systems, but also to ease
|
||||||
|
debugging.
|
||||||
|
|
||||||
|
2. If the daemon offers interfaces to other software running on the
|
||||||
|
local system via local AF_UNIX sockets, consider implementing
|
||||||
|
socket-based activation (see above). Usually, a minimal patch is
|
||||||
|
sufficient to implement this: Extend the socket creation in the
|
||||||
|
daemon code so that sd_listen_fds(3) is checked for already passed
|
||||||
|
sockets first. If sockets are passed (i.e. when sd_listen_fds()
|
||||||
|
returns a positive value), skip the socket creation step and use
|
||||||
|
the passed sockets. Secondly, ensure that the file system socket
|
||||||
|
nodes for local AF_UNIX sockets used in the socket-based activation
|
||||||
|
are not removed when the daemon shuts down, if sockets have been
|
||||||
|
passed. Third, if the daemon normally closes all remaining open
|
||||||
|
file descriptors as part of its initialization, the sockets passed
|
||||||
|
from the init system must be spared. Since new-style init systems
|
||||||
|
guarantee that no left-over file descriptors are passed to executed
|
||||||
|
processes, it might be a good choice to simply skip the closing of
|
||||||
|
all remaining open file descriptors if sockets are passed.
|
||||||
|
|
||||||
|
3. Write and install a systemd unit file for the service (and the
|
||||||
|
sockets if socket-based activation is used, as well as a path unit
|
||||||
|
file, if the daemon processes a spool directory), see above for
|
||||||
|
details.
|
||||||
|
|
||||||
|
4. If the daemon exposes interfaces via D-Bus, write and install a
|
||||||
|
D-Bus activation file for the service, see above for details.
|
||||||
|
|
||||||
|
PLACING DAEMON DATA
|
||||||
|
It is recommended to follow the general guidelines for placing package
|
||||||
|
files, as discussed in file-hierarchy(7).
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
systemd(1), sd-daemon(3), sd_listen_fds(3), sd_notify(3), daemon(3),
|
||||||
|
systemd.service(5), file-hierarchy(7)
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
1. LSB recommendations for SysV init scripts
|
||||||
|
http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
|
||||||
|
|
||||||
|
2. Apple MacOS X Daemon Requirements
|
||||||
|
https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
|
||||||
|
|
||||||
|
systemd 236 DAEMON(7)
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* main.c: the daemon itself
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "trie.h"
|
||||||
|
#include "tasks.h"
|
||||||
|
|
||||||
|
void shutdown(int sigval);
|
||||||
|
|
||||||
|
static int sockfd;
|
||||||
|
|
||||||
|
int main (void) {
|
||||||
|
openlog(cfg_log_ident, cfg_log_opt, cfg_log_facility); //I will probably close its fd a moment later, FIXME
|
||||||
|
// No arguments, if you want something, go change source, according to documentation.
|
||||||
|
daemonize(0);
|
||||||
|
|
||||||
|
// Load saved state -- variable values and tasks
|
||||||
|
trie_load();
|
||||||
|
tasks_load();
|
||||||
|
|
||||||
|
// Set basic signal handlers
|
||||||
|
struct sigaction *term_sigh = (struct sigaction *) malloc(sizeof(struct sigaction));
|
||||||
|
term_sigh -> sa_handler = &shutdown;
|
||||||
|
term_sigh -> sa_mask = 0;
|
||||||
|
term_sigh -> sa_flags = 0;
|
||||||
|
sigaction(SIGTERM, term_sigh, NULL);
|
||||||
|
|
||||||
|
struct sigaction *chld_sigh = (struct sigaction *) malloc(sizeof(struct sigaction));
|
||||||
|
chld_sigh -> sa_handler = SIG_IGN; // We don't care for children
|
||||||
|
chld_sigh -> sa_mask = 0;
|
||||||
|
chld_sigh -> sa_flags = SA_NOCLDWAIT;
|
||||||
|
sigaction(SIGCHLD, chld_sigh, NULL);
|
||||||
|
|
||||||
|
// Setup a socket to listen on
|
||||||
|
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
memcpy(&addr.sun_path, cfg_socket, strlen(cfg_socket));
|
||||||
|
bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
||||||
|
listen(sockfd, cfg_sock_maxclients);
|
||||||
|
|
||||||
|
// Everything prepared
|
||||||
|
syslog(cfg_log_facility | LOG_INFO, "Started");
|
||||||
|
|
||||||
|
// Main loop: (everything else is handled by process() and signal handlers).
|
||||||
|
while (true) {
|
||||||
|
int fd = accept(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
||||||
|
process(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(cfg_log_facility | LOG_CRIT, "Escaped infinite loop!");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown (int signum) {
|
||||||
|
trie_save();
|
||||||
|
tasks_save();
|
||||||
|
close(sockfd);
|
||||||
|
unlink(cfg_socket);
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* trie.c: Trie implementation for storing values
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "trie.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
static struct trie *trie_base;
|
||||||
|
|
||||||
|
static int indexof(char c) {
|
||||||
|
// ASCII only!
|
||||||
|
if ('A' <= c && c <= 'Z') {
|
||||||
|
return (c - 'A');
|
||||||
|
} else if ('0' <= c && c <= '9') {
|
||||||
|
return (c - '0' + 26);
|
||||||
|
} else if (c == '_') {
|
||||||
|
return 36;
|
||||||
|
} else {
|
||||||
|
// Something weird happened
|
||||||
|
syslog(cfg_log_facility|LOG_ERR, "trie: weird input character: %c", c);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool trie_set_deep(char string[], int64_t val, struct trie *vertex) {
|
||||||
|
// Recursive function to find and set a value
|
||||||
|
if (string[0] = '\0') {
|
||||||
|
vertex -> val = val;
|
||||||
|
vertex -> defined = true;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
int chld_index = indexof(string[0]);
|
||||||
|
if (chld_index == -1) {
|
||||||
|
return false; //and don't save anything
|
||||||
|
} else if (vertex -> children[chld_index] == NULL) {
|
||||||
|
vertex -> children[chld_index] = (struct trie *) calloc(1, sizeof(struct trie));
|
||||||
|
if (vertex -> children[chld_index] == NULL) {
|
||||||
|
syslog(cfg_log_facility | LOG_ERR, "trie: could not allocate space: %m");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trie_set_deep(string+1, val, vertex->children[chld_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trie_set(char string[], int64_t val) {
|
||||||
|
if (trie_base == NULL) {
|
||||||
|
trie_base = (struct trie *) calloc(1, sizeof(struct trie));
|
||||||
|
if(trie_base == NULL) {
|
||||||
|
syslog(cfg_log_facility | LOG_ERR, "trie: could not allocate any space: %m");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trie_set_deep(string, val, trie_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trie_retval trie_lookup_deep(char string[], struct trie *vertex) {
|
||||||
|
if (string[0] == '\0') {
|
||||||
|
return (struct trie_retval) {vertex -> val, vertex -> def};
|
||||||
|
} else {
|
||||||
|
int chld_index = indexof(string[0]);
|
||||||
|
if (chld_index == -1 || vertex -> children[chld_index] == NULL) {
|
||||||
|
return (struct trie_retval) {0, false};
|
||||||
|
} else {
|
||||||
|
return trie_lookup_deep(string + 1, vertex->children[chld_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct trie_retval trie_lookup(char string[]) {
|
||||||
|
if (trie_base == NULL) {
|
||||||
|
return (struct trie_retval) {0, false};
|
||||||
|
} else return trie_lookup_deep(string, trie_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trie_load(void) {
|
||||||
|
FILE *f = fopen(cfg_trie_file, "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARNING, "trie: could not load trie from file: %m");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME: This is bad.
|
||||||
|
int64_t val;
|
||||||
|
char name[cfg_var_name_max_len+1];
|
||||||
|
int status;
|
||||||
|
while ((status = fscanf(f, "%s %d\n", name, &val)) != EOF) {
|
||||||
|
if (status != 2) {
|
||||||
|
syslog(cfg_log_facility | LOG_ERR, "trie: fscanf matched bad number of items: %d", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(trie_set(name, val) == false) {
|
||||||
|
syslog(cfg_log_facility | LOG_WARN, "trie: setting a variable failed: %s = %d", name, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trie_save(void) {
|
||||||
|
FILE *f = fopen(cfg_trie_file, "w");
|
||||||
|
if (f == NULL) {
|
||||||
|
syslog(cfg_log_facility | LOG_CRIT, "trie: could not open file to save trie in: %m");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DFS, write every found variable into f
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* trie.h: declaration of functions workong with trie
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TRIE_H
|
||||||
|
#define TRIE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
bool trie_load(void);
|
||||||
|
bool trie_save(void);
|
||||||
|
|
||||||
|
struct trie {
|
||||||
|
int64_t val;
|
||||||
|
struct trie *children[37]; //Alphabet: A-Z0-9_
|
||||||
|
bool defined;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum trie_ret_status {OK, UNDEF};
|
||||||
|
|
||||||
|
struct trie_retval {
|
||||||
|
int64_t val;
|
||||||
|
bool def;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trie_retval trie_lookup(char string[]);
|
||||||
|
bool trie_set (char string[], int64_t val);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue