]> sourceware.org Git - lvm2.git/blame - daemons/common/daemon-server.c
Test dm_hash_insert() failures mem failures
[lvm2.git] / daemons / common / daemon-server.c
CommitLineData
d7448a72
PR
1/*
2 * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
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
13#include <dlfcn.h>
14#include <errno.h>
15#include <pthread.h>
16#include <sys/file.h>
17#include <sys/stat.h>
18#include <sys/wait.h>
19#include <sys/time.h>
20#include <sys/resource.h>
dc85d3fb
PR
21#include <netinet/in.h>
22#include <sys/un.h>
d7448a72
PR
23#include <unistd.h>
24#include <signal.h>
25
26#include <syslog.h>
dc85d3fb 27#include "daemon-server.h"
aaca7f11 28#include "daemon-shared.h"
dc85d3fb 29#include "libdevmapper.h"
d7448a72 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;
46
47static void _exit_handler(int sig __attribute__((unused)))
48{
49 _shutdown_requested = 1;
50}
51
52#ifdef linux
53# define OOM_ADJ_FILE "/proc/self/oom_adj"
dc85d3fb 54# include <stdio.h>
d7448a72
PR
55
56/* From linux/oom.h */
57# define OOM_DISABLE (-17)
58# define OOM_ADJUST_MIN (-16)
59
60/*
61 * Protection against OOM killer if kernel supports it
62 */
63static int _set_oom_adj(int val)
64{
65 FILE *fp;
66
67 struct stat st;
68
69 if (stat(OOM_ADJ_FILE, &st) == -1) {
70 if (errno == ENOENT)
71 perror(OOM_ADJ_FILE " not found");
72 else
73 perror(OOM_ADJ_FILE ": stat failed");
74 return 1;
75 }
76
77 if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
78 perror(OOM_ADJ_FILE ": fopen failed");
79 return 0;
80 }
81
82 fprintf(fp, "%i", val);
dc85d3fb 83 if (fclose(fp))
d7448a72
PR
84 perror(OOM_ADJ_FILE ": fclose failed");
85
86 return 1;
87}
88#endif
89
73ffd6e7
PR
90static int _open_socket(daemon_state s)
91{
92 int fd = -1;
93 struct sockaddr_un sockaddr;
94 mode_t old_mask;
95
96 (void) dm_prepare_selinux_context(s.socket_path, S_IFSOCK);
97 old_mask = umask(0077);
98
99 /* Open local socket */
100 fd = socket(PF_UNIX, SOCK_STREAM, 0);
101 if (fd < 0) {
dc85d3fb 102 perror("Can't create local socket.");
73ffd6e7
PR
103 goto error;
104 }
105
106 /* Set Close-on-exec & non-blocking */
107 if (fcntl(fd, F_SETFD, 1))
dc85d3fb 108 fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", fd, strerror(errno));
73ffd6e7
PR
109 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
110
55e30071 111 fprintf(stderr, "[D] creating %s\n", s.socket_path);
73ffd6e7 112 memset(&sockaddr, 0, sizeof(sockaddr));
92658f56 113 strcpy(sockaddr.sun_path, s.socket_path);
73ffd6e7
PR
114 sockaddr.sun_family = AF_UNIX;
115
116 if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
dc85d3fb 117 perror("can't bind local socket.");
73ffd6e7
PR
118 goto error;
119 }
120 if (listen(fd, 1) != 0) {
dc85d3fb 121 perror("listen local");
73ffd6e7
PR
122 goto error;
123 }
124
125out:
126 umask(old_mask);
127 (void) dm_prepare_selinux_context(NULL, 0);
128 return fd;
129
130error:
131 if (fd >= 0) {
6e2761e9
ZK
132 if (close(fd))
133 perror("close failed");
134 if (unlink(s.socket_path))
135 perror("unlink failed");
73ffd6e7
PR
136 fd = -1;
137 }
138 goto out;
139}
140
d7448a72
PR
141static void remove_lockfile(const char *file)
142{
143 if (unlink(file))
dc85d3fb 144 perror("unlink failed");
d7448a72
PR
145}
146
147static void _daemonise(void)
148{
149 int child_status;
150 int fd;
151 pid_t pid;
152 struct rlimit rlim;
153 struct timeval tval;
154 sigset_t my_sigset;
155
156 sigemptyset(&my_sigset);
157 if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) {
158 fprintf(stderr, "Unable to restore signals.\n");
159 exit(EXIT_FAILURE);
160 }
161 signal(SIGTERM, &_exit_handler);
162
163 switch (pid = fork()) {
164 case -1:
165 perror("fork failed:");
166 exit(EXIT_FAILURE);
167
168 case 0: /* Child */
169 break;
170
171 default:
172 /* Wait for response from child */
dc85d3fb 173 while (!waitpid(pid, &child_status, WNOHANG) && !_shutdown_requested) {
d7448a72
PR
174 tval.tv_sec = 0;
175 tval.tv_usec = 250000; /* .25 sec */
176 select(0, NULL, NULL, NULL, &tval);
177 }
178
179 if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */
180 exit(0);
181
182 /* Problem with child. Determine what it is by exit code */
dc85d3fb 183 fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
d7448a72
PR
184 exit(WEXITSTATUS(child_status));
185 }
186
187 if (chdir("/"))
188 exit(1);
189
190 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
191 fd = 256; /* just have to guess */
192 else
193 fd = rlim.rlim_cur;
194
195 for (--fd; fd >= 0; fd--)
196 close(fd);
197
198 if ((open("/dev/null", O_RDONLY) < 0) ||
199 (open("/dev/null", O_WRONLY) < 0) ||
200 (open("/dev/null", O_WRONLY) < 0))
201 exit(1);
202
203 setsid();
204}
205
e68a6fbf 206response daemon_reply_simple(const char *id, ...)
aaca7f11
PR
207{
208 va_list ap;
6d404585
ZK
209 response res = { .cft = NULL };
210
aaca7f11 211 va_start(ap, id);
aaca7f11 212
6d404585 213 if (!(res.buffer = format_buffer("response", id, ap)))
aaca7f11
PR
214 res.error = ENOMEM;
215
6d404585
ZK
216 va_end(ap);
217
aaca7f11
PR
218 return res;
219}
220
92658f56
PR
221struct thread_baton {
222 daemon_state s;
223 client_handle client;
224};
225
6e4e3082 226static int buffer_rewrite(char **buf, const char *format, const char *string) {
372e9b3d
PR
227 char *old = *buf;
228 dm_asprintf(buf, format, *buf, string);
229 dm_free(old);
230 return 0;
231}
232
6e4e3082 233static int buffer_line(const char *line, void *baton) {
372e9b3d
PR
234 response *r = baton;
235 if (r->buffer)
236 buffer_rewrite(&r->buffer, "%s\n%s", line);
237 else
238 dm_asprintf(&r->buffer, "%s\n", line);
239 return 0;
240}
241
3f694b12
PR
242static response builtin_handler(daemon_state s, client_handle h, request r)
243{
244 const char *rq = daemon_request_str(r, "request", "NONE");
245
246 if (!strcmp(rq, "hello")) {
247 return daemon_reply_simple("OK", "protocol = %s", s.protocol ?: "default",
248 "version = %d", s.protocol_version, NULL);
249 }
250
251 response res = { .buffer = NULL, .error = EPROTO };
252 return res;
253}
254
6e4e3082 255static void *client_thread(void *baton)
92658f56
PR
256{
257 struct thread_baton *b = baton;
258 request req;
6d404585
ZK
259 response res;
260
92658f56
PR
261 while (1) {
262 if (!read_buffer(b->client.socket_fd, &req.buffer))
263 goto fail;
264
c033ea01 265 req.cft = dm_config_from_string(req.buffer);
372e9b3d
PR
266 if (!req.cft)
267 fprintf(stderr, "error parsing request:\n %s\n", req.buffer);
3f694b12
PR
268
269 res = builtin_handler(b->s, b->client, req);
270
271 if (res.error == EPROTO) /* Not a builtin, delegate to the custom handler. */
272 res = b->s.handler(b->s, b->client, req);
92658f56
PR
273
274 if (!res.buffer) {
c033ea01 275 dm_config_write_node(res.cft->root, buffer_line, &res);
372e9b3d 276 buffer_rewrite(&res.buffer, "%s\n\n", NULL);
615534d3 277 dm_config_destroy(res.cft);
92658f56
PR
278 }
279
d5406851
PR
280 if (req.cft)
281 dm_config_destroy(req.cft);
282 dm_free(req.buffer);
283
92658f56
PR
284 write_buffer(b->client.socket_fd, res.buffer, strlen(res.buffer));
285
286 free(res.buffer);
92658f56
PR
287 }
288fail:
289 /* TODO what should we really do here? */
d5406851 290 close(b->client.socket_fd);
a7204204 291 free(baton);
92658f56
PR
292 return NULL;
293}
294
6e4e3082 295static int handle_connect(daemon_state s)
92658f56 296{
6d404585 297 struct thread_baton *baton;
92658f56 298 struct sockaddr_un sockaddr;
6d404585 299 client_handle client = { .thread_id = 0 };
92658f56 300 socklen_t sl = sizeof(sockaddr);
6d404585
ZK
301
302 client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl);
303 if (client.socket_fd < 0)
92658f56
PR
304 return 0;
305
6d404585 306 if (!(baton = malloc(sizeof(struct thread_baton))))
92658f56
PR
307 return 0;
308
92658f56
PR
309 baton->s = s;
310 baton->client = client;
311
312 if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton))
313 return 0;
6d404585 314
d5406851
PR
315 pthread_detach(baton->client.thread_id);
316
92658f56
PR
317 return 1;
318}
319
dc85d3fb 320void daemon_start(daemon_state s)
d7448a72 321{
73ffd6e7 322 int failed = 0;
d7448a72
PR
323 /*
324 * Switch to C locale to avoid reading large locale-archive file used by
325 * some glibc (on some distributions it takes over 100MB). Some daemons
326 * need to use mlockall().
327 */
328 if (setenv("LANG", "C", 1))
329 perror("Cannot set LANG to C");
330
331 if (!s.foreground)
332 _daemonise();
333
334 /* TODO logging interface should be somewhat more elaborate */
335 openlog(s.name, LOG_PID, LOG_DAEMON);
336
337 (void) dm_prepare_selinux_context(s.pidfile, S_IFREG);
338
339 /*
73ffd6e7
PR
340 * NB. Take care to not keep stale locks around. Best not exit(...)
341 * after this point.
d7448a72
PR
342 */
343 if (dm_create_lockfile(s.pidfile) == 0)
344 exit(1);
345
346 (void) dm_prepare_selinux_context(NULL, 0);
347
348 /* Set normal exit signals to request shutdown instead of dying. */
349 signal(SIGINT, &_exit_handler);
350 signal(SIGHUP, &_exit_handler);
351 signal(SIGQUIT, &_exit_handler);
92658f56 352 signal(SIGTERM, &_exit_handler);
c033ea01 353 signal(SIGALRM, &_exit_handler);
92658f56 354 signal(SIGPIPE, SIG_IGN);
d7448a72
PR
355
356#ifdef linux
357 if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN))
358 syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer");
359#endif
360
73ffd6e7
PR
361 if (s.socket_path) {
362 s.socket_fd = _open_socket(s);
363 if (s.socket_fd < 0)
364 failed = 1;
365 }
366
d7448a72
PR
367 /* Signal parent, letting them know we are ready to go. */
368 if (!s.foreground)
369 kill(getppid(), SIGTERM);
370
372e9b3d
PR
371 if (s.daemon_init)
372 s.daemon_init(&s);
373
73ffd6e7 374 while (!_shutdown_requested && !failed) {
92658f56
PR
375 fd_set in;
376 FD_ZERO(&in);
377 FD_SET(s.socket_fd, &in);
378 if (select(FD_SETSIZE, &in, NULL, NULL, NULL) < 0 && errno != EINTR)
379 perror("select error");
380 if (FD_ISSET(s.socket_fd, &in))
381 if (!handle_connect(s))
382 syslog(LOG_ERR, "Failed to handle a client connection.");
d7448a72
PR
383 }
384
92658f56 385 if (s.socket_fd >= 0)
6e2761e9
ZK
386 if (unlink(s.socket_path))
387 perror("unlink error");
92658f56 388
372e9b3d
PR
389 if (s.daemon_fini)
390 s.daemon_fini(&s);
391
d7448a72
PR
392 syslog(LOG_NOTICE, "%s shutting down", s.name);
393 closelog();
394 remove_lockfile(s.pidfile);
73ffd6e7
PR
395 if (failed)
396 exit(1);
d7448a72 397}
This page took 0.077468 seconds and 5 git commands to generate.