process.c 4.19 KB
Newer Older
1
/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    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 <http://www.gnu.org/licenses/>.
 */

17 18 19 20 21
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
22 23 24
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
25
#include <sys/stat.h>
26
#include <sys/types.h>
27
#include <unistd.h>
28

Daniel Salzman's avatar
Daniel Salzman committed
29
#include "knot/common/log.h"
30
#include "knot/common/process.h"
31
#include "knot/conf/conf.h"
32
#include "libknot/errcode.h"
Daniel Salzman's avatar
Daniel Salzman committed
33

34
static char* pid_filename(void)
35
{
Daniel Salzman's avatar
Daniel Salzman committed
36 37 38 39
	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);
40
	free(rundir);
41

42
	return pidfile;
43 44
}

45
static pid_t pid_read(const char *filename)
46
{
47 48 49
	if (filename == NULL) {
		return 0;
	}
50

51 52 53
	size_t len = 0;
	char buf[64] = { 0 };

54 55 56 57
	FILE *fp = fopen(filename, "r");
	if (fp == NULL) {
		return 0;
	}
58

59
	/* Read the content of the file. */
60
	len = fread(buf, 1, sizeof(buf) - 1, fp);
61
	fclose(fp);
62
	if (len < 1) {
63 64
		return 0;
	}
65

66 67 68 69 70 71
	/* Convert pid. */
	errno = 0;
	char *end = 0;
	unsigned long pid = strtoul(buf, &end, 10);
	if (end == buf || *end != '\0'|| errno != 0) {
		return 0;
72 73
	}

74
	return (pid_t)pid;
75 76
}

77
static int pid_write(const char *filename)
78
{
79
	if (filename == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
80
		return KNOT_EINVAL;
81
	}
82

83
	/* Convert. */
84
	char buf[64];
85
	int len = 0;
86 87 88 89
	len = snprintf(buf, sizeof(buf), "%lu", (unsigned long)getpid());
	if (len < 0 || len >= sizeof(buf)) {
		return KNOT_ENOMEM;
	}
90

91 92
	/* Create file. */
	int ret = KNOT_EOK;
93
	int fd = open(filename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP);
94
	if (fd >= 0) {
95 96 97
		if (write(fd, buf, len) != len) {
			ret = knot_map_errno();
		}
98 99
		close(fd);
	} else {
Daniel Salzman's avatar
Daniel Salzman committed
100
		ret = knot_map_errno();
101 102
	}

103
	return ret;
104 105
}

106
char *pid_check_and_create(void)
107
{
108 109 110 111 112 113 114 115 116 117 118
	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_error("server PID found, already running");
		free(pidfile);
		return NULL;
	} else if (stat(pidfile, &st) == 0) {
		log_warning("removing stale PID file '%s'", pidfile);
119
		pid_cleanup();
Marek Vavrusa's avatar
Marek Vavrusa committed
120 121
	}

122 123 124 125 126 127 128 129 130 131
	/* Create a PID file. */
	int ret = pid_write(pidfile);
	if (ret != KNOT_EOK) {
		log_error("failed to create a PID file '%s' (%s)", pidfile,
		          knot_strerror(ret));
		free(pidfile);
		return NULL;
	}

	return pidfile;
132 133
}

134
void pid_cleanup(void)
135 136 137 138 139 140 141 142 143
{
	char *pidfile = pid_filename();
	if (pidfile != NULL) {
		(void)unlink(pidfile);
		free(pidfile);
	}
}

bool pid_running(pid_t pid)
144 145 146 147
{
	return kill(pid, 0) == 0;
}

148
int proc_update_privileges(int uid, int gid)
149 150 151
{
#ifdef HAVE_SETGROUPS
	/* Drop supplementary groups. */
152
	if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) {
153
		if (setgroups(0, NULL) < 0) {
Daniel Salzman's avatar
Daniel Salzman committed
154
			log_warning("failed to drop supplementary groups for "
155
				    "UID %d (%s)", getuid(), strerror(errno));
156
		}
157 158 159
# ifdef HAVE_INITGROUPS
		struct passwd *pw;
		if ((pw = getpwuid(uid)) == NULL) {
160
			log_warning("failed to get passwd entry for UID %d (%s)",
161
			            uid, strerror(errno));
162 163
		} else {
			if (initgroups(pw->pw_name, gid) < 0) {
Daniel Salzman's avatar
Daniel Salzman committed
164
				log_warning("failed to set supplementary groups "
165
					    "for UID %d (%s)", uid, strerror(errno));
166 167 168
			}
		}
# endif /* HAVE_INITGROUPS */
169
	}
170
#endif /* HAVE_SETGROUPS */
Jan Včelák's avatar
Jan Včelák committed
171

172
	/* Watch uid/gid. */
173
	if ((gid_t)gid != getgid()) {
174
		log_info("changing GID to %d", gid);
175
		if (setregid(gid, gid) < 0) {
176
			log_error("failed to change GID to %d", gid);
177
			return KNOT_ERROR;
178 179
		}
	}
180
	if ((uid_t)uid != getuid()) {
181
		log_info("changing UID to %d", uid);
182
		if (setreuid(uid, uid) < 0) {
183
			log_error("failed to change UID to %d", uid);
184
			return KNOT_ERROR;
185 186
		}
	}
Jan Včelák's avatar
Jan Včelák committed
187

Daniel Salzman's avatar
Daniel Salzman committed
188
	return KNOT_EOK;
189
}