From 3cf8ae170b10aeee0cc5033298e8e2e126e35bf4 Mon Sep 17 00:00:00 2001 From: LEdoian Date: Mon, 12 Feb 2018 04:24:23 +0100 Subject: [PATCH] Need DFS in trie.c, process() in main.c, and the rest --- config.h | 20 ++ daemonize.c | 158 ++++++++++++++++ daemonize.h | 35 ++++ doc/daemon_7 | 525 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 65 +++++++ trie.c | 112 +++++++++++ trie.h | 30 +++ 7 files changed, 945 insertions(+) create mode 100644 config.h create mode 100644 daemonize.c create mode 100644 daemonize.h create mode 100644 doc/daemon_7 create mode 100644 main.c create mode 100644 trie.c create mode 100644 trie.h diff --git a/config.h b/config.h new file mode 100644 index 0000000..fd81573 --- /dev/null +++ b/config.h @@ -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 diff --git a/daemonize.c b/daemonize.c new file mode 100644 index 0000000..ea95c6d --- /dev/null +++ b/daemonize.c @@ -0,0 +1,158 @@ +/* + * daemonize.c -- provides daemonize() function to daemonize a process + * according to daemon(7) and given flags. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/daemonize.h b/daemonize.h new file mode 100644 index 0000000..83d2d9f --- /dev/null +++ b/daemonize.h @@ -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 +#include + +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 diff --git a/doc/daemon_7 b/doc/daemon_7 new file mode 100644 index 0000000..36d5998 --- /dev/null +++ b/doc/daemon_7 @@ -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) diff --git a/main.c b/main.c new file mode 100644 index 0000000..b307b47 --- /dev/null +++ b/main.c @@ -0,0 +1,65 @@ +/* + * main.c: the daemon itself + */ + +#include +#include +#include + +#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); +} diff --git a/trie.c b/trie.c new file mode 100644 index 0000000..c4777e6 --- /dev/null +++ b/trie.c @@ -0,0 +1,112 @@ +/* + * trie.c: Trie implementation for storing values + */ + +#include +#include +#include + +#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 + +} diff --git a/trie.h b/trie.h new file mode 100644 index 0000000..55e4307 --- /dev/null +++ b/trie.h @@ -0,0 +1,30 @@ +/* + * trie.h: declaration of functions workong with trie + */ + +#ifndef TRIE_H +#define TRIE_H + +#include +#include + +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