diff options
Diffstat (limited to 'src/knot/common/process.c')
-rw-r--r-- | src/knot/common/process.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/knot/common/process.c b/src/knot/common/process.c new file mode 100644 index 0000000..bdec1d4 --- /dev/null +++ b/src/knot/common/process.c @@ -0,0 +1,194 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "knot/common/log.h" +#include "knot/common/process.h" +#include "knot/conf/conf.h" +#include "libknot/errcode.h" + +static char* pid_filename(void) +{ + conf_val_t val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&val, NULL); + val = conf_get(conf(), C_SRV, C_PIDFILE); + char *pidfile = conf_abs_path(&val, rundir); + free(rundir); + + return pidfile; +} + +static pid_t pid_read(const char *filename) +{ + if (filename == NULL) { + return 0; + } + + size_t len = 0; + char buf[64] = { 0 }; + + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + return 0; + } + + /* Read the content of the file. */ + len = fread(buf, 1, sizeof(buf) - 1, fp); + fclose(fp); + if (len < 1) { + return 0; + } + + /* Convert pid. */ + errno = 0; + char *end = 0; + unsigned long pid = strtoul(buf, &end, 10); + if (end == buf || *end != '\0'|| errno != 0) { + return 0; + } + + return (pid_t)pid; +} + +static int pid_write(const char *filename, pid_t pid) +{ + if (filename == NULL) { + return KNOT_EINVAL; + } + + /* Convert. */ + char buf[64]; + int len = 0; + len = snprintf(buf, sizeof(buf), "%lu", (unsigned long)pid); + if (len < 0 || len >= sizeof(buf)) { + return KNOT_ENOMEM; + } + + /* Create file. */ + int ret = KNOT_EOK; + int fd = open(filename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP); + if (fd >= 0) { + if (write(fd, buf, len) != len) { + ret = knot_map_errno(); + } + close(fd); + } else { + ret = knot_map_errno(); + } + + return ret; +} + +unsigned long pid_check_and_create(void) +{ + 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_fatal("server PID found, already running"); + free(pidfile); + return 0; + } else if (stat(pidfile, &st) == 0) { + assert(pidfile); + log_warning("removing stale PID file '%s'", pidfile); + pid_cleanup(); + } + + /* Get current PID. */ + pid = getpid(); + + /* Create a PID file. */ + int ret = pid_write(pidfile, pid); + if (ret != KNOT_EOK) { + log_fatal("failed to create a PID file '%s' (%s)", pidfile, + knot_strerror(ret)); + free(pidfile); + return 0; + } + free(pidfile); + + return (unsigned long)pid; +} + +void pid_cleanup(void) +{ + char *pidfile = pid_filename(); + if (pidfile != NULL) { + (void)unlink(pidfile); + free(pidfile); + } +} + +bool pid_running(pid_t pid) +{ + return kill(pid, 0) == 0; +} + +int proc_update_privileges(int uid, int gid) +{ +#ifdef HAVE_SETGROUPS + /* Drop supplementary groups. */ + if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) { + if (setgroups(0, NULL) < 0) { + log_warning("failed to drop supplementary groups for " + "UID %d (%s)", getuid(), strerror(errno)); + } +# ifdef HAVE_INITGROUPS + struct passwd *pw; + if ((pw = getpwuid(uid)) == NULL) { + log_warning("failed to get passwd entry for UID %d (%s)", + uid, strerror(errno)); + } else { + if (initgroups(pw->pw_name, gid) < 0) { + log_warning("failed to set supplementary groups " + "for UID %d (%s)", uid, strerror(errno)); + } + } +# endif /* HAVE_INITGROUPS */ + } +#endif /* HAVE_SETGROUPS */ + + /* Watch uid/gid. */ + if ((gid_t)gid != getgid()) { + log_info("changing GID to %d", gid); + if (setregid(gid, gid) < 0) { + log_error("failed to change GID to %d", gid); + return KNOT_ERROR; + } + } + if ((uid_t)uid != getuid()) { + log_info("changing UID to %d", uid); + if (setreuid(uid, uid) < 0) { + log_error("failed to change UID to %d", uid); + return KNOT_ERROR; + } + } + + return KNOT_EOK; +} |