libslack(daemon) - daemon module
#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); 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);
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)
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)
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)
0
. On
error, returns -1
with errno
set appropriately.
int daemon_revoke_privileges(void)
0
. On error, returns
-1
with errno
set appropriately.
int daemon_become_user(uid_t uid, gid_t gid, char *user)
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.
Only root can use this function.
char *daemon_absolute_path(const char *path)
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 null
with errno
set appropriately.
int daemon_path_is_safe(const char *path)
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)
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 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)
ROOT_PID_DIR
for root (by default, "/var/run"
on
Linux and "/etc"
on Solaris) 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)
SVR4
is defined and
NO_EXTRA_SVR4_FORK
is not defined when libslack is compiled. Before
doing this, ignore SIGHUP
because when the session leader terminates, all
processes in the foreground process group are sent a SIGHUP
signal
(apparently). Note that this code may not execute (e.g. when started by
init(8) or inetd(8) or when either SVR4
was not defined or
NO_EXTRA_SVR4_FORK
was defined when libslack was compiled). This means
that the client can't make any assumptions about the SIGHUP
handler when
daemon_init(3) returns.
stdin
, stdout
and stderr
are left open since they are open to a
socket.
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).
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
on Linux and /etc
on Solaris for
root or /tmp
for ordinary users.
On success, returns 0
. On error, returns -1
with errno
set
appropriately.
int daemon_close(void)
pid_t daemon_getpid(const char *name)
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)
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. 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)
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.
Additional errors may be generated and returned from the underlying system calls. See their manual pages.
null
).
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.
#include <stdio.h> #include <unistd.h> #include <syslog.h>
#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, "%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 { 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... 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) != 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 1;
do_stuff();
return EXIT_SUCCESS; // unreached }
MT-Safe
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(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.
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)
20020916 raf <raf@raf.org>