]> sourceware.org Git - lvm2.git/blame - libdaemon/server/daemon-server.c
Reflect new file locations, include file updates etc.
[lvm2.git] / libdaemon / server / daemon-server.c
CommitLineData
d7448a72 1/*
7126d8c2 2 * Copyright (C) 2011-2012 Red Hat, Inc.
d7448a72
PR
3 *
4 * This copyrighted material is made available to anyone wishing to use,
5 * modify, copy, or redistribute it subject to the terms and conditions
6 * of the GNU Lesser General Public License v.2.1.
7 *
8 * You should have received a copy of the GNU Lesser General Public License
9 * along with this program; if not, write to the Free Software Foundation,
10 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
11 */
12
7126d8c2
AK
13#include "daemon-shared.h"
14#include "daemon-server.h"
15
d7448a72
PR
16#include <dlfcn.h>
17#include <errno.h>
18#include <pthread.h>
19#include <sys/file.h>
20#include <sys/stat.h>
21#include <sys/wait.h>
22#include <sys/time.h>
23#include <sys/resource.h>
dc85d3fb
PR
24#include <netinet/in.h>
25#include <sys/un.h>
d7448a72
PR
26#include <unistd.h>
27#include <signal.h>
28
29#include <syslog.h>
30
dc85d3fb 31#if 0
d7448a72
PR
32/* Create a device monitoring thread. */
33static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int stacksize)
34{
35 pthread_attr_t attr;
36 pthread_attr_init(&attr);
37 /*
38 * We use a smaller stack since it gets preallocated in its entirety
39 */
40 pthread_attr_setstacksize(&attr, stacksize);
41 return pthread_create(t, &attr, fun, arg);
42}
dc85d3fb 43#endif
d7448a72
PR
44
45static volatile sig_atomic_t _shutdown_requested = 0;
870353f9 46static int _systemd_activation = 0;
d7448a72
PR
47
48static void _exit_handler(int sig __attribute__((unused)))
49{
50 _shutdown_requested = 1;
51}
52
53#ifdef linux
870353f9
PR
54
55#include <stddef.h>
56
57/*
58 * Kernel version 2.6.36 and higher has
59 * new OOM killer adjustment interface.
60 */
61# define OOM_ADJ_FILE_OLD "/proc/self/oom_adj"
62# define OOM_ADJ_FILE "/proc/self/oom_score_adj"
d7448a72
PR
63
64/* From linux/oom.h */
870353f9 65/* Old interface */
d7448a72
PR
66# define OOM_DISABLE (-17)
67# define OOM_ADJUST_MIN (-16)
870353f9
PR
68/* New interface */
69# define OOM_SCORE_ADJ_MIN (-1000)
70
71/* Systemd on-demand activation support */
72# define SD_LISTEN_PID_ENV_VAR_NAME "LISTEN_PID"
73# define SD_LISTEN_FDS_ENV_VAR_NAME "LISTEN_FDS"
74# define SD_LISTEN_FDS_START 3
75# define SD_FD_SOCKET_SERVER SD_LISTEN_FDS_START
76
77# include <stdio.h>
78
79static int _set_oom_adj(const char *oom_adj_path, int val)
80{
81 FILE *fp;
82
83 if (!(fp = fopen(oom_adj_path, "w"))) {
84 perror("oom_adj: fopen failed");
85 return 0;
86 }
87
88 fprintf(fp, "%i", val);
89
90 if (dm_fclose(fp))
91 perror("oom_adj: fclose failed");
92
93 return 1;
94}
d7448a72
PR
95
96/*
97 * Protection against OOM killer if kernel supports it
98 */
870353f9 99static int _protect_against_oom_killer(void)
d7448a72 100{
d7448a72
PR
101 struct stat st;
102
103 if (stat(OOM_ADJ_FILE, &st) == -1) {
870353f9 104 if (errno != ENOENT)
d7448a72 105 perror(OOM_ADJ_FILE ": stat failed");
870353f9
PR
106
107 /* Try old oom_adj interface as a fallback */
108 if (stat(OOM_ADJ_FILE_OLD, &st) == -1) {
109 if (errno == ENOENT)
110 perror(OOM_ADJ_FILE_OLD " not found");
111 else
112 perror(OOM_ADJ_FILE_OLD ": stat failed");
113 return 1;
114 }
115
116 return _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_DISABLE) ||
117 _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_ADJUST_MIN);
d7448a72
PR
118 }
119
870353f9
PR
120 return _set_oom_adj(OOM_ADJ_FILE, OOM_SCORE_ADJ_MIN);
121}
122
123union sockaddr_union {
124 struct sockaddr sa;
125 struct sockaddr_un un;
126};
127
128static int _handle_preloaded_socket(int fd, const char *path)
129{
130 struct stat st_fd;
131 union sockaddr_union sockaddr;
132 int type = 0;
133 socklen_t len = sizeof(type);
134 size_t path_len = strlen(path);
135
136 if (fd < 0)
d7448a72 137 return 0;
d7448a72 138
870353f9
PR
139 if (fstat(fd, &st_fd) < 0 || !S_ISSOCK(st_fd.st_mode))
140 return 0;
141
142 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0 ||
143 len != sizeof(type) || type != SOCK_STREAM)
144 return 0;
145
146 memset(&sockaddr, 0, sizeof(sockaddr));
147 len = sizeof(sockaddr);
148 if (getsockname(fd, &sockaddr.sa, &len) < 0 ||
149 len < sizeof(sa_family_t) ||
150 sockaddr.sa.sa_family != PF_UNIX)
151 return 0;
152
153 if (!(len >= offsetof(struct sockaddr_un, sun_path) + path_len + 1 &&
154 memcmp(path, sockaddr.un.sun_path, path_len) == 0))
155 return 0;
d7448a72
PR
156
157 return 1;
158}
870353f9
PR
159
160static int _systemd_handover(struct daemon_state *ds)
161{
162 const char *e;
163 char *p;
164 unsigned long env_pid, env_listen_fds;
165 int r = 0;
166
167 /* LISTEN_PID must be equal to our PID! */
168 if (!(e = getenv(SD_LISTEN_PID_ENV_VAR_NAME)))
169 goto out;
170
171 errno = 0;
172 env_pid = strtoul(e, &p, 10);
173 if (errno || !p || *p || env_pid <= 0 ||
174 getpid() != (pid_t) env_pid)
175 ;
176
177 /* LISTEN_FDS must be 1 and the fd must be a socket! */
178 if (!(e = getenv(SD_LISTEN_FDS_ENV_VAR_NAME)))
179 goto out;
180
181 errno = 0;
182 env_listen_fds = strtoul(e, &p, 10);
183 if (errno || !p || *p || env_listen_fds != 1)
184 goto out;
185
186 /* Check and handle the socket passed in */
187 if ((r = _handle_preloaded_socket(SD_FD_SOCKET_SERVER, ds->socket_path)))
188 ds->socket_fd = SD_FD_SOCKET_SERVER;
189
190out:
191 unsetenv(SD_LISTEN_PID_ENV_VAR_NAME);
192 unsetenv(SD_LISTEN_FDS_ENV_VAR_NAME);
193 return r;
194}
195
d7448a72
PR
196#endif
197
73ffd6e7
PR
198static int _open_socket(daemon_state s)
199{
200 int fd = -1;
201 struct sockaddr_un sockaddr;
202 mode_t old_mask;
203
204 (void) dm_prepare_selinux_context(s.socket_path, S_IFSOCK);
205 old_mask = umask(0077);
206
207 /* Open local socket */
208 fd = socket(PF_UNIX, SOCK_STREAM, 0);
209 if (fd < 0) {
dc85d3fb 210 perror("Can't create local socket.");
73ffd6e7
PR
211 goto error;
212 }
213
214 /* Set Close-on-exec & non-blocking */
215 if (fcntl(fd, F_SETFD, 1))
dc85d3fb 216 fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", fd, strerror(errno));
73ffd6e7
PR
217 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
218
55e30071 219 fprintf(stderr, "[D] creating %s\n", s.socket_path);
73ffd6e7 220 memset(&sockaddr, 0, sizeof(sockaddr));
92658f56 221 strcpy(sockaddr.sun_path, s.socket_path);
73ffd6e7
PR
222 sockaddr.sun_family = AF_UNIX;
223
224 if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
dc85d3fb 225 perror("can't bind local socket.");
73ffd6e7
PR
226 goto error;
227 }
228 if (listen(fd, 1) != 0) {
dc85d3fb 229 perror("listen local");
73ffd6e7
PR
230 goto error;
231 }
232
233out:
234 umask(old_mask);
235 (void) dm_prepare_selinux_context(NULL, 0);
236 return fd;
237
238error:
239 if (fd >= 0) {
6e2761e9
ZK
240 if (close(fd))
241 perror("close failed");
242 if (unlink(s.socket_path))
243 perror("unlink failed");
73ffd6e7
PR
244 fd = -1;
245 }
246 goto out;
247}
248
d7448a72
PR
249static void remove_lockfile(const char *file)
250{
251 if (unlink(file))
dc85d3fb 252 perror("unlink failed");
d7448a72
PR
253}
254
255static void _daemonise(void)
256{
257 int child_status;
258 int fd;
259 pid_t pid;
260 struct rlimit rlim;
261 struct timeval tval;
262 sigset_t my_sigset;
263
264 sigemptyset(&my_sigset);
265 if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) {
266 fprintf(stderr, "Unable to restore signals.\n");
267 exit(EXIT_FAILURE);
268 }
269 signal(SIGTERM, &_exit_handler);
270
271 switch (pid = fork()) {
272 case -1:
273 perror("fork failed:");
274 exit(EXIT_FAILURE);
275
276 case 0: /* Child */
277 break;
278
279 default:
280 /* Wait for response from child */
dc85d3fb 281 while (!waitpid(pid, &child_status, WNOHANG) && !_shutdown_requested) {
d7448a72
PR
282 tval.tv_sec = 0;
283 tval.tv_usec = 250000; /* .25 sec */
284 select(0, NULL, NULL, NULL, &tval);
285 }
286
287 if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */
288 exit(0);
289
290 /* Problem with child. Determine what it is by exit code */
dc85d3fb 291 fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
d7448a72
PR
292 exit(WEXITSTATUS(child_status));
293 }
294
295 if (chdir("/"))
296 exit(1);
297
298 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
299 fd = 256; /* just have to guess */
300 else
301 fd = rlim.rlim_cur;
302
870353f9
PR
303 for (--fd; fd >= 0; fd--) {
304#ifdef linux
305 /* Do not close fds preloaded by systemd! */
306 if (_systemd_activation && fd == SD_FD_SOCKET_SERVER)
307 continue;
308#endif
d7448a72 309 close(fd);
870353f9 310 }
d7448a72
PR
311
312 if ((open("/dev/null", O_RDONLY) < 0) ||
313 (open("/dev/null", O_WRONLY) < 0) ||
314 (open("/dev/null", O_WRONLY) < 0))
315 exit(1);
316
317 setsid();
318}
319
e68a6fbf 320response daemon_reply_simple(const char *id, ...)
aaca7f11
PR
321{
322 va_list ap;
6d404585
ZK
323 response res = { .cft = NULL };
324
aaca7f11 325 va_start(ap, id);
aaca7f11 326
6d404585 327 if (!(res.buffer = format_buffer("response", id, ap)))
aaca7f11
PR
328 res.error = ENOMEM;
329
6d404585
ZK
330 va_end(ap);
331
aaca7f11
PR
332 return res;
333}
334
92658f56
PR
335struct thread_baton {
336 daemon_state s;
337 client_handle client;
338};
339
6e4e3082 340static int buffer_rewrite(char **buf, const char *format, const char *string) {
372e9b3d 341 char *old = *buf;
c682e837
ZK
342 int r = dm_asprintf(buf, format, *buf, string);
343
372e9b3d 344 dm_free(old);
c682e837
ZK
345
346 return (r < 0) ? 0 : 1;
372e9b3d
PR
347}
348
6e4e3082 349static int buffer_line(const char *line, void *baton) {
372e9b3d 350 response *r = baton;
c682e837
ZK
351
352 if (r->buffer) {
353 if (!buffer_rewrite(&r->buffer, "%s\n%s", line))
354 return 0;
355 } else if (dm_asprintf(&r->buffer, "%s\n", line) < 0)
356 return 0;
357
358 return 1;
372e9b3d
PR
359}
360
3f694b12
PR
361static response builtin_handler(daemon_state s, client_handle h, request r)
362{
363 const char *rq = daemon_request_str(r, "request", "NONE");
364
365 if (!strcmp(rq, "hello")) {
366 return daemon_reply_simple("OK", "protocol = %s", s.protocol ?: "default",
367 "version = %d", s.protocol_version, NULL);
368 }
369
370 response res = { .buffer = NULL, .error = EPROTO };
371 return res;
372}
373
6e4e3082 374static void *client_thread(void *baton)
92658f56
PR
375{
376 struct thread_baton *b = baton;
377 request req;
6d404585
ZK
378 response res;
379
92658f56
PR
380 while (1) {
381 if (!read_buffer(b->client.socket_fd, &req.buffer))
382 goto fail;
383
c033ea01 384 req.cft = dm_config_from_string(req.buffer);
372e9b3d
PR
385 if (!req.cft)
386 fprintf(stderr, "error parsing request:\n %s\n", req.buffer);
3f694b12
PR
387
388 res = builtin_handler(b->s, b->client, req);
389
390 if (res.error == EPROTO) /* Not a builtin, delegate to the custom handler. */
391 res = b->s.handler(b->s, b->client, req);
92658f56
PR
392
393 if (!res.buffer) {
c033ea01 394 dm_config_write_node(res.cft->root, buffer_line, &res);
c682e837
ZK
395 if (!buffer_rewrite(&res.buffer, "%s\n\n", NULL))
396 goto fail;
615534d3 397 dm_config_destroy(res.cft);
92658f56
PR
398 }
399
d5406851
PR
400 if (req.cft)
401 dm_config_destroy(req.cft);
402 dm_free(req.buffer);
403
92658f56
PR
404 write_buffer(b->client.socket_fd, res.buffer, strlen(res.buffer));
405
406 free(res.buffer);
92658f56
PR
407 }
408fail:
409 /* TODO what should we really do here? */
d5406851 410 close(b->client.socket_fd);
a7204204 411 free(baton);
92658f56
PR
412 return NULL;
413}
414
6e4e3082 415static int handle_connect(daemon_state s)
92658f56 416{
6d404585 417 struct thread_baton *baton;
92658f56 418 struct sockaddr_un sockaddr;
6d404585 419 client_handle client = { .thread_id = 0 };
92658f56 420 socklen_t sl = sizeof(sockaddr);
6d404585
ZK
421
422 client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl);
423 if (client.socket_fd < 0)
92658f56
PR
424 return 0;
425
6d404585 426 if (!(baton = malloc(sizeof(struct thread_baton))))
92658f56
PR
427 return 0;
428
92658f56
PR
429 baton->s = s;
430 baton->client = client;
431
432 if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton))
433 return 0;
6d404585 434
d5406851
PR
435 pthread_detach(baton->client.thread_id);
436
92658f56
PR
437 return 1;
438}
439
dc85d3fb 440void daemon_start(daemon_state s)
d7448a72 441{
73ffd6e7 442 int failed = 0;
d7448a72
PR
443 /*
444 * Switch to C locale to avoid reading large locale-archive file used by
445 * some glibc (on some distributions it takes over 100MB). Some daemons
446 * need to use mlockall().
447 */
448 if (setenv("LANG", "C", 1))
449 perror("Cannot set LANG to C");
450
870353f9
PR
451#ifdef linux
452 _systemd_activation = _systemd_handover(&s);
453#endif
454
d7448a72
PR
455 if (!s.foreground)
456 _daemonise();
457
458 /* TODO logging interface should be somewhat more elaborate */
459 openlog(s.name, LOG_PID, LOG_DAEMON);
460
461 (void) dm_prepare_selinux_context(s.pidfile, S_IFREG);
462
463 /*
73ffd6e7
PR
464 * NB. Take care to not keep stale locks around. Best not exit(...)
465 * after this point.
d7448a72
PR
466 */
467 if (dm_create_lockfile(s.pidfile) == 0)
468 exit(1);
469
470 (void) dm_prepare_selinux_context(NULL, 0);
471
472 /* Set normal exit signals to request shutdown instead of dying. */
473 signal(SIGINT, &_exit_handler);
474 signal(SIGHUP, &_exit_handler);
475 signal(SIGQUIT, &_exit_handler);
92658f56 476 signal(SIGTERM, &_exit_handler);
c033ea01 477 signal(SIGALRM, &_exit_handler);
92658f56 478 signal(SIGPIPE, SIG_IGN);
d7448a72
PR
479
480#ifdef linux
870353f9
PR
481 /* Systemd has adjusted oom killer for us already */
482 if (s.avoid_oom && !_systemd_activation && !_protect_against_oom_killer())
483 syslog(LOG_ERR, "Failed to protect against OOM killer");
d7448a72
PR
484#endif
485
870353f9 486 if (!_systemd_activation && s.socket_path) {
73ffd6e7
PR
487 s.socket_fd = _open_socket(s);
488 if (s.socket_fd < 0)
489 failed = 1;
490 }
491
d7448a72
PR
492 /* Signal parent, letting them know we are ready to go. */
493 if (!s.foreground)
494 kill(getppid(), SIGTERM);
495
372e9b3d
PR
496 if (s.daemon_init)
497 s.daemon_init(&s);
498
73ffd6e7 499 while (!_shutdown_requested && !failed) {
92658f56
PR
500 fd_set in;
501 FD_ZERO(&in);
502 FD_SET(s.socket_fd, &in);
503 if (select(FD_SETSIZE, &in, NULL, NULL, NULL) < 0 && errno != EINTR)
504 perror("select error");
505 if (FD_ISSET(s.socket_fd, &in))
506 if (!handle_connect(s))
507 syslog(LOG_ERR, "Failed to handle a client connection.");
d7448a72
PR
508 }
509
92658f56 510 if (s.socket_fd >= 0)
6e2761e9
ZK
511 if (unlink(s.socket_path))
512 perror("unlink error");
92658f56 513
372e9b3d
PR
514 if (s.daemon_fini)
515 s.daemon_fini(&s);
516
d7448a72
PR
517 syslog(LOG_NOTICE, "%s shutting down", s.name);
518 closelog();
519 remove_lockfile(s.pidfile);
73ffd6e7
PR
520 if (failed)
521 exit(1);
d7448a72 522}
This page took 0.084606 seconds and 5 git commands to generate.