# NAME

libslack(daemon) - daemon module

# SYNOPSIS

        #include <slack/std.h>
        #include <slack/daemon.h>

        typedef void daemon_config_parser_t(void *obj, const char *path, char *line, size_t lineno);

        int daemon_started_by_init(void);
        int daemon_started_by_inetd(void);
        int daemon_prevent_core(void);
        int daemon_revoke_privileges(void);
        int daemon_become_user(uid_t uid, gid_t gid, char *user);
        char *daemon_absolute_path(const char *path);
        int daemon_path_is_safe(const char *path, char *explanation, size_t explanation_size);
        void *daemon_parse_config(const char *path, void *obj, daemon_config_parser_t *parser);
        int daemon_pidfile(const char *name);
        int daemon_init(const char *name);
        int daemon_close(void);
        pid_t daemon_getpid(const char *name);
        int daemon_is_running(const char *name);
        int daemon_stop(const char *name);

# DESCRIPTION

This module provides functions for writing daemons. There are many tasks that need to be performed to correctly set up a daemon process. This can be tedious. These functions perform these tasks for you.

`int daemon_started_by_init(void)`

  If this process was started by init(8) (i.e. if the parent process id is 1), returns 1. If not, returns `0`. If it was, we might be getting respawned so fork(2) and exit(2) would be a big mistake (and unnecessary anyway, since there is no controlling terminal). On error, returns `-1` with `errno` set appropriately.

`int daemon_started_by_inetd(void)`

  If this process was started by inetd(8) (i.e. if stdin is a socket), returns `1`. If not, returns `0`. On error, returns `-1` with `errno` set appropriately. If it was, `stdin`, `stdout` and `stderr` would be opened to a socket. Closing them would be a big mistake. We also would not need to fork(2) and exit(2) because there is no controlling terminal.

`int daemon_prevent_core(void)`

  Prevents core files from being generated. This is used to prevent leaking sensitive information in daemons run by root. On success, returns `0`. On error, returns `-1` with `errno` set appropriately.

`int daemon_revoke_privileges(void)`

  Revokes setuid and setgid privileges. Useful when your program does not require any special privileges, and may become unsafe if incorrectly installed with special privileges. Also useful when your program only requires special privileges initially upon startup (e.g. binding to a privileged socket). Performs the following tasks: Sets the effective gid to the real gid if they differ. Checks that they no longer differ. Sets the effective uid to the real uid if they differ. Checks that they no longer differ. Also closes /etc/passwd and /etc/group in case they were opened by root and give access to user and group passwords. On success, returns `0`. On error, returns `-1` with `errno` set appropriately.

`int daemon_become_user(uid_t uid, gid_t gid, char *user)`

  Changes the owner and group of the process to `uid` and `gid`, respectively. If `user` is not null, the supplementary group list will be initialised with initgroups(3). Otherwise, the supplementary group list will be cleared of all groups. On success, returns `0`. On error, returns `-1` with `errno` set appropriately. Only root can use this function.

`char *daemon_absolute_path(const char *path)`

  Returns `path` converted into an absolute path. Cleans up any `.` and `..` and `//` and trailing `/` found in the returned path. Note that the returned path looks canonical but isn't, because symbolic links are not followed and expanded. It is the caller's responsibility to deallocate the path returned with free(3) or mem_release(3) or mem_destroy(3). It is strongly recommended to use mem_destroy(3), because it also sets the pointer variable to `null`. On success, returns the absolute path. On error, returns `null` with `errno` set appropriately.

`int daemon_path_is_safe(const char *path, char *explanation, size_t explanation_size)`

  Checks that the file referred to by `path` is not group- or world-writable. Also checks that the containing directories are not group- or world-writable, following symbolic links. Useful when you need to know whether or not you can trust a user supplied configuration/command file before reading and acting upon its contents. On success, returns `1` if `path` is safe or `0` if it is not. When the path is not safe, an explanation is written to the `explanation` buffer (if it is not `null`). No more than `explanation_size` bytes including the terminating `nul` byte will be written to the `explanation` buffer. On error, returns `-1` with `errno` set appropriately.

`void *daemon_parse_config(const char *path, void *obj, daemon_config_parser_t *parser)`

  Parses the text configuration file named `path`. Blank lines are ignored. Comments (`'#'` to end of line) are ignored. Lines that end with `'\'` are joined with the following line. There may be whitespace characters, and even a comment, after the `'\'` character, but nothing else. The `parser` function is called with the client supplied `obj`, the file name, the line and the line number as arguments. On success, returns `obj`. On error, returns `null` (i.e. if the configuration file could not be read). Note: Don't parse config files unless they are "safe" as determined by daemon_path_is_safe(3).

`int daemon_pidfile(const char *name)`

  Creates a pid file for a daemon and locks it. The file has one line containing the process id of the daemon. The well-known locations for the file is defined in `ROOT_PID_DIR` for root (`"/var/run"` by default) and `USER_PID_DIR` for all other users (`"/tmp"` by default). The name of the file is the name of the daemon (given by the name argument) followed by `".pid"` (If name is an absolute file path, it is used as is). The presence of this file will prevent two daemons with the same name from running at the same time. On success, returns `0`. On error, returns `-1` with `errno` set appropriately. Note: This is called by daemon_init(3), so there is usually no need to call this function directly.

`int daemon_init(const char *name)`

  Initialises a daemon by performing the following tasks:

  - If the process was not invoked by init(8) (i.e. parent process id is 1) or inetd(8) (i.e. `stdin` is a socket):

    - Ignore `SIGHUP` signals in case the current process session leader terminates while attached to a controlling terminal causing us to receive a `SIGHUP` signal before we start our own process session below.

      This can happen when the process that calls daemon_init(3) was itself invoked interactively via the shell builtin `exec`. When this initial process terminates below, the terminal emulator that invoked the shell also terminates.

    - Background the process to lose process group leadership.

    - Start a new process session.

    - Background the process again to lose process session leadership. Under SVR4, this prevents the process from ever gaining a controlling terminal. This is only necessary under SVR4, but is always done for simplicity. Note that ignoring `SIGHUP` signals earlier means that when the newly created process session leader terminates, then even if it has a controlling terminal open, the newly backgrounded process won't receive the corresponding `SIGHUP` signal that is sent to all processes in the process session's foreground process group, because it inherited signal dispositions from the initial process.

  - Change the current directory to the root directory so as not to hamper umounts.

  - Clear the umask to enable explicit file creation modes.

  - Close all open file descriptors. If the process was invoked by inetd(8), `stdin`, `stdout` and `stderr` are left open, because they are open to a socket.

  - Open `stdin`, `stdout` and `stderr` to `/dev/null` in case something requires them to be open. Of course, this is not done if the process was invoked by inetd(8).

  - If `name` is non-null, create and lock a file containing the process id of the process. The presence of this locked file prevents two instances of a daemon with the same name from running at the same time. The default location of the pidfile is `/var/run` for root (`/etc` on Solaris, `/opt/local/var/run` on macOS when installed via macports), and `/tmp` for ordinary users.

  On success, returns `0`. On error, returns `-1` with `errno` set appropriately.

`int daemon_close(void)`

  Unlinks the locked pid file, if any. Returns 0.

`pid_t daemon_getpid(const char *name)`

  Returns the process id of the daemon with the given `name`. If the daemon in question is owned by root, then this function must be invoked by root. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by an ordinary user. If `name` is the absolute path to the pidfile (rather than just the daemon name), then any user may call this function. On success, returns the process id of the daemon. On error, returns `-1` with `errno` set appropriately.

`int daemon_is_running(const char *name)`

  Checks whether or not a daemon with the given `name` is running. If the daemon in question is owned by root, then this function must be invoked by root. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by an ordinary user. However, if `name` is the absolute path to the pidfile (rather than just the daemon name), then any user may call this function. On success, returns `1` if the daemon is running or `0` if it is not. On error, returns `-1` with `errno` set appropriately.

`int daemon_stop(const char *name)`

  Stops a daemon process with the given `name` by sending it a `SIGTERM` signal. If the daemon in question is owned by root, then this function must be invoked by root. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by that user. Note that root can't use this function to stop a daemon started by another user just by passing the name of the daemon (because the pidfiles for root daemons and user daemons are stored in different directories). In order for root to stop an ordinary user's daemon process, `name` has to be the absolute path to the daemon's pidfile. On success, returns `0`. On error, returns `-1` with `errno` set appropriately.

# ERRORS

Additional errors may be generated and returned from the underlying system calls. See their manual pages for details.

`EINVAL`

  An argument was invalid (e.g. `null`).

`ENAMETOOLONG`

  The `name` passed to daemon_init(3) or daemon_path_is_safe(3) resulted in a path name that is too long for the intended filesystem.

`ELOOP`

  daemon_path_is_safe(3) recursed too deeply (16 levels).

`ESRCH`

  daemon_stop(3) found that there was no daemon running with the given name.

# MT-Level

MT-Safe

# EXAMPLE

This example reads and prints `/etc/fstab` with daemon_parse_config(3), becomes a daemon and then sends a syslog(3) message and then terminates.

        #include <slack/lib.h>

        const char * const config_fname = "/etc/fstab";
        List *config = NULL;

        void fstab_parser(void *obj, const char *path, char *line, size_t lineno)
        {
            char device[64], mount[64], fstype[64], opts[64];
            int freq, passno;

            if (sscanf(line, "%63s %63s %63s %63s %d %d", device, mount, fstype, opts, &freq, &passno) != 6)
                fprintf(stderr, "Syntax Error in %s (line %d): %s\n", path, lineno, line);
            else
            {
                char *copy;

                printf("%s %s %s %s %d %d\n", device, mount, fstype, opts, freq, passno);

                if (!(copy = mem_strdup(line)))
                    fprintf(stderr, "out of memory\n");
                else if (!list_append(config, copy))
                    fprintf(stderr, "failed to add line %d to config\n", lineno);
            }
        }

        void hup(int signo)
        {
            list_remove_range(config, 0, -1);
            daemon_parse_config(config_fname, config, fstab_parser);
        }

        void term(int signo)
        {
            daemon_close();
            exit(EXIT_SUCCESS);
        }

        void do_stuff()
        {
            // do stuff...
            syslog(LOG_DAEMON | LOG_DEBUG, "Here we are");
            kill(getpid(), SIGTERM);
            signal_handle_all();
        }

        int main(int ac, char **av)
        {
            if (daemon_revoke_privileges() == -1 ||
                daemon_prevent_core() == -1 ||
                daemon_path_is_safe(config_fname, NULL, 0) != 1 ||
                (config = list_create(free)) == NULL ||
                daemon_parse_config(config_fname, config, fstab_parser) == NULL ||
                daemon_init(prog_basename(*av)) == -1 ||
                signal_set_handler(SIGHUP, 0, hup) == -1 ||
                signal_set_handler(SIGTERM, 0, term) == -1)
                return EXIT_FAILURE;

            do_stuff();

            return EXIT_SUCCESS; // unreached
        }

# CAVEAT

Because root's pidfiles are created in a different directory (`/var/run`) to those of ordinary users (`/tmp`), it is possible for root and another user to use the same name for a daemon client. This shouldn't be a problem. It's probably desirable. But if it is a problem, recompile libslack and relink daemon so that all pidfiles are created in `/tmp` by defining `ROOT_PID_DIR` and `USER_PID_DIR` to both be `/tmp`.

The exclusive creation and locking of the pidfile doesn't work correctly over NFS on Linux so pidfiles must reside locally.

daemon_path_is_safe(3) ignores ACLs (so does sendmail(8)). It should probably treat a path as unsafe if there are any ACLs (allowing extra access) along the path.

The functions daemon_prevent_core(3), daemon_revoke_privileges(3), daemon_become_user(3), daemon_absolute_path(3), daemon_path_is_safe(3) and daemon_parse_config(3) should probably all have the daemon_ prefix removed from their names. Their use is more general than just in daemons.

If you use "exec daemon" to run a KDE application you may find that the KDE application sometimes doesn't run. This problem has only been seen with konsole(1) but it may happen with other KDE applications as well. Capturing the standard error of the KDE application might show something like:

      unnamed app(9697): KUniqueApplication: Registering failed!
      unnamed app(9697): Communication problem with  "konsole" , it probably crashed.
      Error message was:  "org.freedesktop.DBus.Error.ServiceUnknown" : " "The name
                          org.kde.konsole was not provided by any .service files"

A workaround seems to be to delay the termination of the initial daemon(1) process by at least 0.4 seconds. To make this happen, set the environment variable `DAEMON_INIT_EXIT_DELAY_MSEC` to the number of milliseconds by which to delay. For example: `DAEMON_INIT_EXIT_DELAY_MSEC=400`.

# SEE ALSO

libslack(3), daemon(1), init(8), inetd(8), fork(2), umask(2), setsid(2), chdir(2), setrlimit(2), setgid(2), setuid(2), setgroups(2), initgroups(3), endpwent(3), endgrent(3), kill(2))

# AUTHOR

20210220 raf <raf@raf.org>