Skip to content

Commit 49d3772

Browse files
committed
supervise-daemon: poc refactor
1 parent 7f74c93 commit 49d3772

6 files changed

Lines changed: 1169 additions & 1201 deletions

File tree

src/supervise-daemon/daemon.c

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
#ifdef __linux__
2+
#include <sys/capability.h>
3+
#include <sys/prctl.h>
4+
#endif
5+
#include <sys/resource.h>
6+
7+
#include <errno.h>
8+
#include <pwd.h>
9+
#include <stdbool.h>
10+
#include <stdio.h>
11+
#include <string.h>
12+
#include <syslog.h>
13+
#include <unistd.h>
14+
15+
#include <einfo.h>
16+
#include <rc.h>
17+
18+
#include "daemon.h"
19+
20+
#include "rc_exec.h"
21+
#include "helpers.h"
22+
#include "misc.h"
23+
24+
#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set)
25+
# define SYS_ioprio_set __NR_ioprio_set
26+
#endif
27+
#if !defined(__DragonFly__)
28+
static inline int ioprio_set(int which RC_UNUSED, int who RC_UNUSED,
29+
int ioprio RC_UNUSED)
30+
{
31+
#ifdef SYS_ioprio_set
32+
return syscall(SYS_ioprio_set, which, who, ioprio);
33+
#else
34+
return 0;
35+
#endif
36+
}
37+
#endif
38+
39+
extern const char *applet;
40+
41+
static bool
42+
set_nicelevel(const char *option)
43+
{
44+
int nicelevel;
45+
FILE *fp;
46+
47+
if (sscanf(option, "%d", &nicelevel) != 1 || setpriority(PRIO_PROCESS, getpid(), nicelevel)) {
48+
syslog(LOG_ERR, "setpriority %d: %s", nicelevel, strerror(errno));
49+
return false;
50+
}
51+
52+
if ((fp = fopen("/proc/self/autogroup", "r+"))) {
53+
fprintf(fp, "%d\n", nicelevel);
54+
fclose(fp);
55+
} else if (errno != ENOENT) {
56+
syslog(LOG_ERR, "autogroup nice %d: %s", nicelevel, strerror(errno));
57+
return false;
58+
}
59+
60+
return true;
61+
}
62+
63+
static bool
64+
set_ionice(const char *option)
65+
{
66+
int ionicec, ioniced;
67+
if (sscanf(option, "%d:%d", &ionicec, &ioniced) == 0) {
68+
syslog(LOG_ERR, "invalid ionice '%s'", optarg);
69+
return false;
70+
}
71+
if (ionicec == 0)
72+
ioniced = 0;
73+
else if (ionicec == 3)
74+
ioniced = 7;
75+
ionicec <<= 13; /* class shift */
76+
77+
if (ioprio_set(1, getpid(), ionicec | ioniced) == -1) {
78+
syslog(LOG_ERR, "ioprio_set %d %d: %s", ionicec, ioniced, strerror(errno));
79+
return false;
80+
}
81+
return true;
82+
}
83+
84+
static bool
85+
set_oom_score(const char *option)
86+
{
87+
int score;
88+
FILE *fp;
89+
90+
if (sscanf(option, "%d", &score) != 1) {
91+
syslog(LOG_ERR, "invalid oom-score-adj '%s'", option);
92+
return false;
93+
}
94+
95+
if (!(fp = fopen("/proc/self/oom_score_adj", "w"))) {
96+
syslog(LOG_ERR, "oom_score_adj %d: %s", score, strerror(errno));
97+
return false;
98+
}
99+
100+
fprintf(fp, "%d\n", score);
101+
fclose(fp);
102+
103+
return true;
104+
}
105+
106+
static bool
107+
set_capabilities(const char *option)
108+
{
109+
#ifdef __linux__
110+
cap_iab_t cap_iab;
111+
112+
if (!(cap_iab = cap_iab_from_text(option)) || cap_iab_set_proc(cap_iab) != 0 || cap_free(cap_iab) != 0) {
113+
syslog(LOG_ERR, "failed to set capabilities");
114+
return false;
115+
}
116+
117+
return true;
118+
#endif
119+
120+
syslog(LOG_ERR, "capabilities are not supported");
121+
errno = ENOSYS;
122+
return false;
123+
}
124+
125+
static bool
126+
set_secbits(const char *option)
127+
{
128+
#ifdef __linux__
129+
unsigned secbits;
130+
char *end;
131+
132+
if (*option == '\0') {
133+
syslog(LOG_ERR, "secbits are empty");
134+
return false;
135+
}
136+
137+
secbits = strtoul(option, &end, 0);
138+
if (option == end) {
139+
syslog(LOG_ERR, "could not parse secbits: %s", option);
140+
return false;
141+
}
142+
143+
if (cap_set_secbits(secbits) < 0) {
144+
syslog(LOG_ERR, "could not set securebits to 0x%x: %s", secbits, strerror(errno));
145+
return false;
146+
}
147+
148+
return true;
149+
#endif
150+
151+
syslog(LOG_ERR, "secbits are not supported");
152+
errno = ENOSYS;
153+
return false;
154+
}
155+
156+
static bool
157+
set_no_new_privs(const char *option)
158+
{
159+
if (!rc_yesno(option))
160+
return true;
161+
#ifdef PR_SET_NO_NEW_PRIVS
162+
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
163+
syslog(LOG_ERR, "prctl: %s", strerror(errno));
164+
return false;
165+
}
166+
167+
return true;
168+
#endif
169+
170+
syslog(LOG_ERR, "no-new-privs are not supported");
171+
errno = ENOSYS;
172+
return false;
173+
}
174+
175+
static bool
176+
set_umask(const char *option)
177+
{
178+
mode_t mask = 022;
179+
if (parse_mode(&mask, option)) {
180+
syslog(LOG_ERR, "%s: invalid mode '%s'", applet, optarg);
181+
return false;
182+
}
183+
184+
umask(mask);
185+
return true;
186+
}
187+
188+
static bool
189+
set_scheduler(RC_UNUSED const char *option)
190+
{
191+
/* TODO */
192+
return true;
193+
}
194+
195+
static char *expand_home(const char *path)
196+
{
197+
char *expanded = NULL;
198+
struct passwd *pw;
199+
const char *home;
200+
size_t size;
201+
FILE *fp;
202+
203+
if (path[0] == '\\' && path[1] == '~')
204+
return xstrdup(path + 1);
205+
else if (path[0] != '~')
206+
return xstrdup(path);
207+
208+
fp = xopen_memstream(&expanded, &size);
209+
path++;
210+
211+
if (path[0] == '/' || path[0] == '\0') {
212+
if ((home = getenv("HOME"))) {
213+
fputs(home, fp);
214+
} else if ((pw = getpwuid(getuid()))) {
215+
fputs(pw->pw_dir, fp);
216+
} else {
217+
syslog(LOG_ERR, "getpwuid: %s", strerror(errno));
218+
goto err;
219+
}
220+
} else {
221+
char *username;
222+
223+
if ((home = strchr(path, '/'))) {
224+
username = xstrndup(path, home - path);
225+
path = home + 1;
226+
} else {
227+
username = xstrdup(path);
228+
path = NULL;
229+
}
230+
231+
if (!(pw = getpwnam(username))) {
232+
syslog(LOG_ERR, "user %s not found", username);
233+
goto err;
234+
}
235+
fputs(pw->pw_dir, fp);
236+
free(username);
237+
}
238+
239+
if (path)
240+
fputs(path, fp);
241+
242+
xclose_memstream(fp);
243+
return expanded;
244+
245+
err:
246+
xclose_memstream(fp);
247+
free(expanded);
248+
return NULL;
249+
}
250+
251+
static bool
252+
set_dir(const char *option)
253+
{
254+
char *expand_path = expand_home(option);
255+
if (!expand_path)
256+
return false;
257+
if (chdir(expand_path) == -1) {
258+
syslog(LOG_ERR, "chdir(%s): %s", expand_path, strerror(errno));
259+
return false;
260+
}
261+
free(expand_path);
262+
return true;
263+
}
264+
265+
static bool
266+
set_root(const char *option)
267+
{
268+
char *expand_path = expand_home(option);
269+
if (!expand_path)
270+
return false;
271+
if (chroot(expand_path) == -1) {
272+
syslog(LOG_ERR, "chroot(%s): %s", expand_path, strerror(errno));
273+
return false;
274+
}
275+
free(expand_path);
276+
return true;
277+
}
278+
279+
enum open_type { READONLY, CREATE, REDIRECT };
280+
281+
static bool
282+
do_open(const char *option, enum open_type type, int target)
283+
{
284+
int flags = type == CREATE ? O_WRONLY | O_CREAT | O_APPEND : O_RDONLY;
285+
int fd;
286+
287+
if (type == REDIRECT) {
288+
if ((fd = rc_pipe_command(option)) == -1) {
289+
syslog(LOG_ERR, "failed to pipe command to %s", option);
290+
return false;
291+
}
292+
} else {
293+
if ((fd = open(option, flags, S_IRUSR | S_IWUSR)) == -1) {
294+
syslog(LOG_ERR, "failed to open %s", option);
295+
return false;
296+
}
297+
}
298+
299+
if (dup2(fd, target) != -1) {
300+
syslog(LOG_ERR, "dup2: %s", strerror(errno));
301+
return false;
302+
}
303+
304+
return true;
305+
}
306+
307+
RC_NORETURN void
308+
child_process(const char *svcname, struct notify *notify, char **argv)
309+
{
310+
/* the order of the options matter, as they're applied
311+
* in order so earlier options affect later ones! */
312+
static const struct {
313+
const char * const name;
314+
bool (*handler)(const char *option);
315+
enum open_type type;
316+
int target;
317+
} options[] = {
318+
{ "umask", set_umask, 0, 0 },
319+
{ "nicelevel", set_nicelevel, 0, 0 },
320+
{ "ionice", set_ionice, 0, 0 },
321+
{ "oom-score-adj", set_oom_score, 0, 0 },
322+
{ "scheduler", set_scheduler, 0, 0 },
323+
{ "scheduler-priority", set_scheduler, 0, 0 },
324+
{ "capabilities", set_capabilities, 0, 0 },
325+
{ "secbits", set_secbits, 0, 0 },
326+
{ "no-new-privs", set_no_new_privs, 0, 0 },
327+
/* chdir must be relative to the chroot */
328+
{ "chroot", set_root, 0, 0 },
329+
{ "chdir", set_dir, 0, 0 },
330+
331+
{ "stdin", NULL, READONLY, STDIN_FILENO },
332+
{ "stdout", NULL, CREATE, STDOUT_FILENO },
333+
{ "stderr", NULL, CREATE, STDERR_FILENO },
334+
{ "stdout-logger", NULL, REDIRECT, STDOUT_FILENO },
335+
{ "stderr-logger", NULL, REDIRECT, STDERR_FILENO },
336+
};
337+
char *exec;
338+
if (!(exec = rc_service_value_get(svcname, "exec")))
339+
exec = argv[0];
340+
341+
setsid();
342+
343+
for (size_t i = 0; i < ARRAY_SIZE(options); i++) {
344+
char *option;
345+
if (!(option = rc_service_value_get(svcname, options[i].name)))
346+
continue;
347+
if (options[i].handler ? !options[i].handler(option)
348+
: do_open(options[i].name, options[i].type, options[i].target))
349+
exit(EXIT_FAILURE);
350+
free(option);
351+
}
352+
353+
cloexec_fds_from(3);
354+
355+
if (notify->type == NOTIFY_FD && dup2(notify->pipe[1], notify->fd) == -1) {
356+
syslog(LOG_ERR, "Failed to initialize ready fd.");
357+
exit(EXIT_FAILURE);
358+
}
359+
360+
execvp(*argv, argv);
361+
362+
syslog(LOG_ERR, "%s: failed to exec '%s': %s", applet, *argv, strerror(errno));
363+
exit(EXIT_FAILURE);
364+
}

src/supervise-daemon/daemon.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef __DAEMON_H__
2+
#define __DAEMON_H__
3+
4+
#include "misc.h"
5+
#include "helpers.h"
6+
7+
RC_NORETURN void supervise(const char *svcname);
8+
RC_NORETURN void child_process(const char *svcname, struct notify *notify, char **argv);
9+
10+
#endif

0 commit comments

Comments
 (0)