Fix #95: Create PID file if program does not

This patch adds support for creating, and removing, a PID file for
programs (services) that for some reason does not create one.  With
Finit this is very useful since it allows us to use our condition
support even for "broken" daemons.

To enable this for a service, use the following syntax:

    service pid[:[/path/to/]file[.pid]] /path/to/daemon

I.e., `pid` is a required argument if you want to enable the feature,
but the rest is optional:

    pid             --> /run/`basename $CMD`.pid
    pid:file        --> /run/file.pid
    pid:file.dip    --> /run/file.dip
    pid:/mnt/k      --> /mnt/k.pid
    pid:/mnt/k.foo  --> /mnt/k.foo

There is an additional feature with this patch.  For daemons that run
with a different identiy one usually wants to create the PID file with
that identity, e.g.:

    service inadyn -I dyn     -- Dyn.com DDNS client
    service inadyn -I freedns -- FreeDNS DDNS client

Here inadyn will look for /etc/dyn.conf and create /run/dyn.pid, and
/etc/freedns.conf and create /run/freedns.pid, respetively.  While Finit
will in both cases assume the PID file is /run/inadyn.pid ... err fail!

To get out of this mess the `pid` argument can be used to tell Finit
what the real PID file the daemon creates is called:

    service pid:!dyn     inadyn -I dyn     -- Dyn.com DDNS client
    service pid:!freedns inadyn -I freedns -- FreeDNS DDNS client

The same composition rules listed above applies also to the `!` variant.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2018-01-10 23:27:05 +01:00
parent 4011b5a3b8
commit 6a6ae6fa3b
2 changed files with 90 additions and 10 deletions
+89 -10
View File
@@ -114,6 +114,75 @@ static int is_norespawn(void)
fexist("/tmp/norespawn");
}
static char *pid_file(svc_t *svc)
{
static char fn[MAX_ARG_LEN];
if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!')
return &svc->pidfile[1];
return svc->pidfile;
}
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
return fn;
}
static int pid_file_create(svc_t *svc)
{
FILE *fp;
if (!svc->pidfile[0] || svc->pidfile[0] == '!')
return 1;
fp = fopen(svc->pidfile, "w");
if (!fp)
return 1;
fprintf(fp, "%d\n", svc->pid);
return fclose(fp);
}
static int pid_file_parse(svc_t *svc, char *arg)
{
int len, not = 0;
/* Always clear, called with some sort of 'pid[:..]' argument */
svc->pidfile[0] = 0;
/* Sanity check ... */
if (!arg || !arg[0])
return 1;
/* 'pid:' imples argument following*/
if (!strncmp(arg, "pid:", 4)) {
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/') {
strlcpy(svc->pidfile, arg, sizeof(svc->pidfile));
return 0;
}
if (arg[0] == '!') {
arg++;
not++;
}
len = snprintf(svc->pidfile, sizeof(svc->pidfile), "%s%s%s",
not ? "!" : "", _PATH_VARRUN, arg);
if (len > 4 && strcmp(&svc->pidfile[len - 4], ".pid"))
strlcat(svc->pidfile, ".pid", sizeof(svc->pidfile));
return 0;
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid")) {
strlcpy(svc->pidfile, pid_file(svc), sizeof(svc->pidfile));
return 0;
}
return 1;
}
/**
* service_start - Start service
* @svc: Service to start
@@ -323,7 +392,10 @@ static int service_start(svc_t *svc)
svc_set_state(svc, SVC_STOPPING_STATE);
}
}
if (svc_is_daemon(svc))
pid_file_create(svc);
sigprocmask(SIG_SETMASK, &omask, NULL);
print_result(result);
@@ -540,7 +612,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
int forking = 0;
#endif
int log = 0, levels = 0;
char *username = NULL, *line;
char *line;
char *username = NULL, *pid = NULL;
char *service = NULL, *proto = NULL, *ifaces = NULL;
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
svc_t *svc;
@@ -573,7 +646,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
while (cmd) {
if (cmd[0] == '@') /* @username[:group] */
if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */
runlevels = &cmd[0];
@@ -587,10 +660,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else if (!strncasecmp(cmd, "wait", 4))
forking = 0;
#endif
else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else if (!strncasecmp(cmd, "log", 3))
log = 1;
else if (!strncasecmp(cmd, "pid", 3))
pid = cmd;
else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else
break;
@@ -679,6 +754,9 @@ recreate:
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
_e("Invalid 'pid' argument to service: %s", pid);
if (username) {
char *ptr = strchr(username, ':');
@@ -759,7 +837,6 @@ void service_unregister(svc_t *svc)
void service_monitor(pid_t lost)
{
svc_t *svc;
char pidfile[MAX_ARG_LEN];
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
@@ -778,10 +855,12 @@ void service_monitor(pid_t lost)
_d("collected %s(%d)", svc->cmd, lost);
/* Try removing PID file (in case service does not clean up after itself) */
snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
if (remove(pidfile)) {
if (errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), pidfile);
if (svc_is_daemon(svc)) {
char *fn;
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
}
/* No longer running, update books. */
+1
View File
@@ -90,6 +90,7 @@ typedef struct svc {
/* Service details */
pid_t pid;
char pidfile[MAX_ARG_LEN];
long start_time; /* Start time, as seconds since boot, from sysinfo() */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, inetd, ... */