NAME

libslack(daemon) - daemon module


SYNOPSIS

    #include <slack/daemon.h>

    int daemon_started_by_init(void);
    int daemon_started_by_inetd(void);
    int daemon_prevent_core(void);
    int daemon_revoke_privileges(void);
    char *daemon_absolute_path(const char *path);
    int daemon_path_is_safe(const char *path);
    void *daemon_parse_config(const char *path, void *obj, daemon_config_parser_t *parser);
    int daemon_init(const char *name);
    int daemon_close(void);


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), 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). The return value is cached so any subsequent calls are faster.

int daemon_started_by_inetd(void)

If this process was started by inetd(8), returns 1. If not, returns 0. 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. The return value is cached so any subsequent calls are faster.

int daemon_prevent_core(void)

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

int daemon_revoke_privileges(void)

Revokes suid and sgid 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 upon startup (e.g. binding to a privileged socket). Performs the following: Clears all supplementary groups. Sets the effective gid to the real gid if they 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.

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 mem_release(3) or free(3). On success, returns the absolute path. On error, returns -1 with errno set appropriately.

int daemon_path_is_safe(const char *path)

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. 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 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 errors, 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().

int daemon_init(const char *name)

Initialises a daemon by performing the following tasks:

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.


ERRORS

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

ENAMETOOLONG

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

ENOMEM

daemon_init() failed to allocate memory for the the pid file's path.


EXAMPLE

    #include <stdio.h>

    #include <unistd.h>
    #include <syslog.h>
    #include <signal.h>

    #include <slack/daemon.h>
    #include <slack/prog.h>
    #include <slack/sig.h>

    void hup(int signo)
    {
        // reload config file...
    }

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

    void do_stuff()
    {
        // do stuff...
        kill(getpid(), SIGTERM);
        signal_handle_all();
    }

    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, "%s %s %s %s %d %d", device, mount, fstype, opts, &freq, &passno) != 6)
            fprintf(stderr, "Syntax error in %s (line %d): %s\n", path, lineno, line);
        else
            printf("%s %s %s %s %d %d\n", device, mount, fstype, opts, freq, passno);
    }

    int main(int ac, char **av)
    {
        const char * const config = "/etc/fstab";

        if (daemon_revoke_privileges() == -1 ||
            daemon_prevent_core() == -1 ||
            daemon_parse_config(config, NULL, fstab_parser) == NULL ||
            daemon_init(prog_basename(*av)) == -1 ||
            signal_set_handler(SIGHUP, 0, hup) == -1 ||
            signal_set_handler(SIGTERM, 0, term) == -1 ||
            daemon_path_is_safe(config) != 1)
            return 1;

        do_stuff();
        return 0; // unreached
    }


MT-Level

MT-Safe


BUGS

It is possible to obtain a controlling terminal under BSD (and even under SVR4 if SVR4 was not defined or NO_EXTRA_SVR4_FORK was defined when libslack is compiled). If anything calls open(2) on a terminal device without the O_NOCTTY flag, the process doing so will obtain a controlling terminal.

Because root's pidfiles are created in a different directory (/var/run on Linux, /etc on Solaris) 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 but if it is, 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() ignores ACLs (so does sendmail). It should probably treat a path as unsafe if there are any ACLs (allowing extra access) along the path.

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


SEE ALSO

libslack(3), daemon(1), init(8), inetd(8)


AUTHOR

20010215 raf <raf@raf.org>