Commit ff4cd218 authored by Marek Vavrusa's avatar Marek Vavrusa

knotd creates PID only when daemonized, atomic PID write, cleanup

refs #36
parent 9d9ce62c
......@@ -26,6 +26,7 @@
#include <assert.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "knot/knot.h"
#include "knot/ctl/process.h"
......@@ -89,31 +90,28 @@ pid_t pid_read(const char* fn)
int pid_write(const char* fn)
{
if (!fn) {
if (!fn)
return KNOT_EINVAL;
}
// Convert
/* Convert. */
char buf[64];
int wbytes = 0;
wbytes = snprintf(buf, sizeof(buf), "%lu", (unsigned long) getpid());
if (wbytes < 0) {
int len = 0;
len = snprintf(buf, sizeof(buf), "%lu", (unsigned long) getpid());
if (len < 0)
return KNOT_EINVAL;
}
// Write
FILE *fp = fopen(fn, "w");
if (fp) {
int rc = fwrite(buf, wbytes, 1, fp);
fclose(fp);
if (rc < 0) {
return KNOT_ERROR;
}
return 0;
/* Create file. */
int ret = KNOT_EOK;
int fd = open(fn, O_RDWR|O_CREAT, 0644);
if (fd) {
if (write(fd, buf, len) != len)
ret = KNOT_ERROR;
close(fd);
} else {
ret = knot_map_errno(errno);
}
return KNOT_ENOENT;
return ret;
}
int pid_remove(const char* fn)
......@@ -176,3 +174,30 @@ int proc_update_privileges(int uid, int gid)
free(lfile);
return ret;
}
char *pid_check_and_create()
{
struct stat st;
char* pidfile = pid_filename();
pid_t pid = pid_read(pidfile);
/* Check PID for existence and liveness. */
if (pid > 0 && pid_running(pid)) {
log_server_error("Server PID found, already running.\n");
free(pidfile);
return NULL;
} else if (stat(pidfile, &st) == 0) {
log_server_warning("Removing stale PID file '%s'.\n", pidfile);
pid_remove(pidfile);
}
/* Create a PID file. */
int ret = pid_write(pidfile);
if (ret != KNOT_EOK) {
log_server_error("Couldn't create a PID file '%s'.\n", pidfile);
free(pidfile);
return NULL;
}
return pidfile;
}
......@@ -94,6 +94,14 @@ int pid_running(pid_t pid);
*/
int proc_update_privileges(int uid, int gid);
/*!
* \brief Check if PID file exists and create it if possible.
* \retval NULL if failed
* \retval Created PID file path
*/
char *pid_check_and_create();
#endif // _KNOTD_PROCESS_H_
/*! @} */
......@@ -20,6 +20,7 @@
#include <unistd.h>
#include <getopt.h>
#include <sys/stat.h>
#include <limits.h>
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
......@@ -46,7 +47,7 @@ static volatile short sig_req_refresh = 0;
static volatile short sig_stopping = 0;
// Cleanup handler
static void do_cleanup(server_t *server, char *configf, char *pidf);
static int do_cleanup(server_t *server, char *configf, char *pidf);
// SIGINT signal handler
void interrupt_handle(int s)
......@@ -237,64 +238,40 @@ int main(int argc, char **argv)
log_server_info("Reading configuration '%s' ...\n", config_fn);
int conf_ret = conf_open(config_fn);
if (conf_ret != KNOT_EOK) {
if (conf_ret == KNOT_ENOENT)
if (conf_ret == KNOT_ENOENT) {
log_server_error("Couldn't open configuration file "
"'%s'.\n", config_fn);
else
"'%s'.\n", config_fn);
} else {
log_server_error("Failed to load configuration '%s'.\n",
config_fn);
do_cleanup(server, config_fn, NULL);
return 1;
config_fn);
}
return do_cleanup(server, config_fn, NULL);
} else {
log_server_info("Configured %d interfaces and %d zones.\n",
conf()->ifaces_count, conf()->zones_count);
}
log_server_info("\n");
/* Check PID. */
struct stat st;
unsigned do_start = 0;
char* pidfile = pid_filename();
int pid = pid_read(pidfile);
if (pid > 0 && pid_running(pid)) {
log_server_warning("Server PID found, already running.\n");
} else if (stat(pidfile, &st) == 0) {
log_server_warning("PID file '%s' exists, another process "
"is starting or PID file is stale.\n",
pidfile);
} else {
do_start = 1;
}
if (!do_start) {
log_server_error("Server start failed.\n");
do_cleanup(server, config_fn, pidfile);
return 1;
}
/* Create empty PID file. */
FILE *f = fopen(pidfile, "w");
if (f == NULL) {
log_server_warning("PID file '%s' is not writeable.\n",
pidfile);
do_cleanup(server, config_fn, pidfile);
return 1;
}
fclose(f);
/* Alter PID file privileges. */
if (chown(pidfile, conf()->uid, conf()->gid) < 0) {
log_server_warning("Cannot change PID file ownership\n");
pid_remove(pidfile);
do_cleanup(server, config_fn, pidfile);
return 1;
}
/* Alter privileges. */
log_update_privileges(conf()->uid, conf()->gid);
if (proc_update_privileges(conf()->uid, conf()->gid) != KNOT_EOK) {
pid_remove(pidfile);
do_cleanup(server, config_fn, pidfile);
return 1;
if (proc_update_privileges(conf()->uid, conf()->gid) != KNOT_EOK)
return do_cleanup(server, config_fn, NULL);
/* Check and create PID file. */
long pid = (long)getpid();
char *pidf = NULL;
char *cwd = NULL;
if (daemonize) {
if ((pidf = pid_check_and_create()) == NULL)
return do_cleanup(server, config_fn, pidf);
log_server_info("Server started as a daemon, PID = %ld\n", pid);
log_server_info("PID stored in '%s'\n", pidf);
if ((cwd = malloc(PATH_MAX)) != NULL)
cwd = getcwd(cwd, PATH_MAX);
chdir("/");
} else {
log_server_info("Server started in foreground, PID = %ld\n", pid);
log_server_info("Server running without PID file.\n");
}
/* Load zones and add hook. */
......@@ -303,36 +280,8 @@ int main(int argc, char **argv)
// Run server
int res = 0;
int has_pid = 0;
log_server_info("Starting server...\n");
if ((server_start(server)) == KNOT_EOK) {
// Save PID
has_pid = 1;
int rc = pid_write(pidfile);
if (rc < 0) {
has_pid = 0;
log_server_warning("Failed to create "
"PID file '%s' (%s).\n",
pidfile, strerror(errno));
}
// Change directory if daemonized
if (daemonize) {
log_server_info("Server started as a daemon, "
"PID = %ld\n", (long)getpid());
if (chdir("/") != 0) {
res = 1;
}
} else {
log_server_info("Server started in foreground, "
"PID = %ld\n", (long)getpid());
}
if (has_pid) {
log_server_info("PID stored in %s\n", pidfile);
} else {
log_server_warning("Server running without PID file.\n");
}
size_t zcount = server->nameserver->zone_db->zone_count;
if (!zcount) {
log_server_warning("Server started, but no zones served.\n");
......@@ -429,21 +378,25 @@ int main(int argc, char **argv)
res = 1;
}
// Remove PID file
if (has_pid && pid_remove(pidfile) < 0) {
log_server_warning("Failed to remove PID file.\n");
}
log_server_info("Shut down.\n");
log_close();
do_cleanup(server, config_fn, pidfile);
/* Cleanup. */
if (pidf && pid_remove(pidf) < 0)
log_server_warning("Failed to remove PID file.\n");
do_cleanup(server, config_fn, pidf);
if (!daemonize) {
fflush(stdout);
fflush(stderr);
}
/* Return to original working directory. */
if (cwd) {
chdir(cwd);
free(cwd);
}
/* Restart hook. */
if (sig_req_rst)
return execvp(argv[0], argv);
......@@ -451,7 +404,7 @@ int main(int argc, char **argv)
return res;
}
static void do_cleanup(server_t *server, char *configf, char *pidf)
static int do_cleanup(server_t *server, char *configf, char *pidf)
{
/* Free alloc'd variables. */
if (server) {
......@@ -467,4 +420,5 @@ static void do_cleanup(server_t *server, char *configf, char *pidf)
/* Free event loop. */
evqueue_t *q = evqueue();
evqueue_free(&q);
return 1;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment