You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
5.4 KiB
223 lines
5.4 KiB
2 months ago
|
/*
|
||
|
* UCW Library -- Daemon Control
|
||
|
*
|
||
|
* (c) 2012 Martin Mares <mj@ucw.cz>
|
||
|
* (c) 2014 Pavel Charvat <pchar@ucw.cz>
|
||
|
*
|
||
|
* This software may be freely distributed and used according to the terms
|
||
|
* of the GNU Lesser General Public License.
|
||
|
*/
|
||
|
|
||
|
#include <ucw/lib.h>
|
||
|
#include <ucw/daemon.h>
|
||
|
#include <ucw/process.h>
|
||
|
#include <ucw/strtonum.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <signal.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/file.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
static enum daemon_control_status
|
||
|
daemon_control_err(struct daemon_control_params *dc, char *msg, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, msg);
|
||
|
vsnprintf(dc->error_msg, DAEMON_ERR_LEN, msg, args);
|
||
|
va_end(args);
|
||
|
return DAEMON_STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
static enum daemon_control_status
|
||
|
daemon_read_pid(struct daemon_control_params *dc, int will_wait, int *pidp)
|
||
|
{
|
||
|
// We expect successfully locked guard file, so no foreign process
|
||
|
// can be inside daemon_control() and therefore also in daemon_init()
|
||
|
// or daemon_run(). Only these results are then possible:
|
||
|
//
|
||
|
// -- DAEMON_STATUS_ERROR -- some local failure
|
||
|
// -- DAEMON_STATUS_NOT_RUNNING -- no daemon is running
|
||
|
// -- DAEMON_STATUS_STALE -- crashed daemon
|
||
|
// -- DAEMON_STATUS_OK, pid > 0 -- running daemon with known PID
|
||
|
// -- DAEMON_STATUS_OK, pid == 0 -- just exiting daemon, after ftruncate()
|
||
|
|
||
|
enum daemon_control_status st = DAEMON_STATUS_NOT_RUNNING;
|
||
|
*pidp = 0;
|
||
|
|
||
|
int pid_fd = open(dc->pid_file, O_RDONLY);
|
||
|
if (pid_fd < 0)
|
||
|
{
|
||
|
if (errno == ENOENT)
|
||
|
return st;
|
||
|
return daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file);
|
||
|
}
|
||
|
|
||
|
while (flock(pid_fd, LOCK_SH | (will_wait ? 0 : LOCK_NB)) < 0)
|
||
|
{
|
||
|
if (errno == EWOULDBLOCK)
|
||
|
{
|
||
|
st = DAEMON_STATUS_OK;
|
||
|
break;
|
||
|
}
|
||
|
else if (errno != EINTR)
|
||
|
{
|
||
|
daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char buf[16];
|
||
|
int n = read(pid_fd, buf, sizeof(buf));
|
||
|
if (n < 0)
|
||
|
{
|
||
|
daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file);
|
||
|
goto fail;
|
||
|
}
|
||
|
if (n == (int) sizeof(buf))
|
||
|
{
|
||
|
daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file);
|
||
|
goto fail;
|
||
|
}
|
||
|
buf[n] = 0;
|
||
|
|
||
|
if (!n)
|
||
|
{
|
||
|
close(pid_fd);
|
||
|
return st;
|
||
|
}
|
||
|
|
||
|
if (st != DAEMON_STATUS_OK)
|
||
|
{
|
||
|
close(pid_fd);
|
||
|
return DAEMON_STATUS_STALE;
|
||
|
}
|
||
|
|
||
|
int pid;
|
||
|
const char *next;
|
||
|
if (str_to_int(&pid, buf, &next, 10) || strcmp(next, "\n"))
|
||
|
{
|
||
|
daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file);
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
close(pid_fd);
|
||
|
*pidp = pid;
|
||
|
return DAEMON_STATUS_OK;
|
||
|
|
||
|
fail:
|
||
|
close(pid_fd);
|
||
|
return DAEMON_STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
enum daemon_control_status
|
||
|
daemon_control(struct daemon_control_params *dc)
|
||
|
{
|
||
|
int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666);
|
||
|
if (guard_fd < 0)
|
||
|
return daemon_control_err(dc, "Cannot open guard file `%s': %m", dc->guard_file);
|
||
|
if (flock(guard_fd, LOCK_EX) < 0)
|
||
|
return daemon_control_err(dc, "Cannot lock guard file `%s': %m", dc->guard_file);
|
||
|
|
||
|
// Read the PID file
|
||
|
int pid, sig;
|
||
|
enum daemon_control_status st = daemon_read_pid(dc, 0, &pid);
|
||
|
if (st == DAEMON_STATUS_ERROR)
|
||
|
goto done;
|
||
|
|
||
|
switch (dc->action)
|
||
|
{
|
||
|
case DAEMON_CONTROL_CHECK:
|
||
|
break;
|
||
|
case DAEMON_CONTROL_START:
|
||
|
if (st == DAEMON_STATUS_OK)
|
||
|
st = DAEMON_STATUS_ALREADY_DONE;
|
||
|
else
|
||
|
{
|
||
|
pid_t pp = fork();
|
||
|
if (pp < 0)
|
||
|
{
|
||
|
st = daemon_control_err(dc, "Cannot fork: %m");
|
||
|
goto done;
|
||
|
}
|
||
|
if (!pp)
|
||
|
{
|
||
|
close(guard_fd);
|
||
|
execvp(dc->argv[0], dc->argv);
|
||
|
fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]);
|
||
|
exit(DAEMON_STATUS_ERROR);
|
||
|
}
|
||
|
int stat;
|
||
|
int ec = waitpid(pp, &stat, 0);
|
||
|
if (ec < 0)
|
||
|
{
|
||
|
st = daemon_control_err(dc, "Cannot wait: %m");
|
||
|
goto done;
|
||
|
}
|
||
|
if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR)
|
||
|
{
|
||
|
st = daemon_control_err(dc, "Cannot execute the daemon");
|
||
|
goto done;
|
||
|
}
|
||
|
char ecmsg[EXIT_STATUS_MSG_SIZE];
|
||
|
if (format_exit_status(ecmsg, stat))
|
||
|
{
|
||
|
st = daemon_control_err(dc, "Daemon %s %s", dc->argv[0], ecmsg);
|
||
|
goto done;
|
||
|
}
|
||
|
enum daemon_control_status st2 = daemon_read_pid(dc, 0, &pid);
|
||
|
if (st2 != DAEMON_STATUS_OK && st2 != DAEMON_STATUS_NOT_RUNNING)
|
||
|
st = daemon_control_err(dc, "Daemon %s failed to write the PID file `%s'", dc->argv[0], dc->pid_file);
|
||
|
else if (st != DAEMON_STATUS_STALE)
|
||
|
st = DAEMON_STATUS_OK;
|
||
|
}
|
||
|
break;
|
||
|
case DAEMON_CONTROL_STOP:
|
||
|
if (st != DAEMON_STATUS_OK)
|
||
|
{
|
||
|
if (st == DAEMON_STATUS_NOT_RUNNING)
|
||
|
st = DAEMON_STATUS_ALREADY_DONE;
|
||
|
goto done;
|
||
|
}
|
||
|
if (pid)
|
||
|
{
|
||
|
sig = dc->signal ? : SIGTERM;
|
||
|
if (kill(pid, sig) < 0 && errno != ESRCH)
|
||
|
{
|
||
|
st = daemon_control_err(dc, "Cannot send signal %d: %m", sig);
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Just exiting daemon => we can safely wait without sending any signal
|
||
|
}
|
||
|
st = daemon_read_pid(dc, 1, &pid);
|
||
|
if (st != DAEMON_STATUS_ERROR)
|
||
|
st = DAEMON_STATUS_OK;
|
||
|
break;
|
||
|
case DAEMON_CONTROL_SIGNAL:
|
||
|
if (!pid)
|
||
|
return DAEMON_STATUS_NOT_RUNNING;
|
||
|
sig = dc->signal ? : SIGHUP;
|
||
|
if (kill(pid, sig) >= 0)
|
||
|
st = DAEMON_STATUS_OK;
|
||
|
else if (errno == ESRCH)
|
||
|
st = DAEMON_STATUS_NOT_RUNNING;
|
||
|
else
|
||
|
st = daemon_control_err(dc, "Cannot send signal %d: %m", sig);
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
close(guard_fd);
|
||
|
return st;
|
||
|
}
|