Skip to content

Commit 5f6420b

Browse files
committed
supervise-daemon: poc refactor
1 parent 0ef286f commit 5f6420b

6 files changed

Lines changed: 1124 additions & 1201 deletions

File tree

src/supervise-daemon/daemon.c

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

src/supervise-daemon/daemon.h

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

src/supervise-daemon/meson.build

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
executable('supervise-daemon',
2-
['supervise-daemon.c', rc_exec_c, misc_c, plugin_c, schedules_c, timeutils_c, usage_c, version_h],
2+
[
3+
'supervise-daemon.c', 'supervise.c', 'daemon.c',
4+
rc_exec_c, misc_c, plugin_c, schedules_c,
5+
timeutils_c, usage_c, version_h
6+
],
37
c_args : [cc_branding_flags, cc_pam_flags, cc_selinux_flags],
48
link_with: [libeinfo, librc],
59
dependencies: [dl_dep, pam_dep, cap_dep, util_dep, selinux_dep],
610
include_directories: [incdir, einfo_incdir, rc_incdir],
711
install: true,
8-
install_dir: sbindir)
12+
install_dir: rc_sbindir)
13+
14+
executable('start-stop-daemon',
15+
[ 'start-stop-daemon.c', rc_exec_c, misc_c, plugin_c, schedules_c, timeutils_c, version_h ],
16+
c_args : [cc_branding_flags, cc_pam_flags, cc_selinux_flags],
17+
link_with: [libeinfo, librc],
18+
dependencies: [dl_dep, pam_dep, cap_dep, util_dep, selinux_dep],
19+
include_directories: [incdir, einfo_incdir, rc_incdir],
20+
install: true,
21+
install_dir: rc_sbindir)
922

1023
if get_option('pam')
1124
install_data('supervise-daemon.pam',

0 commit comments

Comments
 (0)