]>
Commit | Line | Data |
---|---|---|
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. */ |
33 | static 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 | |
45 | static volatile sig_atomic_t _shutdown_requested = 0; | |
46 | ||
47 | static 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 | */ | |
63 | static 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 |
90 | static 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 | ||
125 | out: | |
126 | umask(old_mask); | |
127 | (void) dm_prepare_selinux_context(NULL, 0); | |
128 | return fd; | |
129 | ||
130 | error: | |
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 |
141 | static void remove_lockfile(const char *file) |
142 | { | |
143 | if (unlink(file)) | |
dc85d3fb | 144 | perror("unlink failed"); |
d7448a72 PR |
145 | } |
146 | ||
147 | static 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 | 206 | response 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 |
221 | struct thread_baton { |
222 | daemon_state s; | |
223 | client_handle client; | |
224 | }; | |
225 | ||
6e4e3082 | 226 | static 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 | 233 | static 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 | ||
6e4e3082 | 242 | static void *client_thread(void *baton) |
92658f56 PR |
243 | { |
244 | struct thread_baton *b = baton; | |
245 | request req; | |
6d404585 ZK |
246 | response res; |
247 | ||
92658f56 PR |
248 | while (1) { |
249 | if (!read_buffer(b->client.socket_fd, &req.buffer)) | |
250 | goto fail; | |
251 | ||
c033ea01 | 252 | req.cft = dm_config_from_string(req.buffer); |
372e9b3d PR |
253 | if (!req.cft) |
254 | fprintf(stderr, "error parsing request:\n %s\n", req.buffer); | |
6d404585 | 255 | res = b->s.handler(b->s, b->client, req); |
92658f56 PR |
256 | |
257 | if (!res.buffer) { | |
c033ea01 | 258 | dm_config_write_node(res.cft->root, buffer_line, &res); |
372e9b3d | 259 | buffer_rewrite(&res.buffer, "%s\n\n", NULL); |
615534d3 | 260 | dm_config_destroy(res.cft); |
92658f56 PR |
261 | } |
262 | ||
d5406851 PR |
263 | if (req.cft) |
264 | dm_config_destroy(req.cft); | |
265 | dm_free(req.buffer); | |
266 | ||
92658f56 PR |
267 | write_buffer(b->client.socket_fd, res.buffer, strlen(res.buffer)); |
268 | ||
269 | free(res.buffer); | |
92658f56 PR |
270 | } |
271 | fail: | |
272 | /* TODO what should we really do here? */ | |
d5406851 | 273 | close(b->client.socket_fd); |
a7204204 | 274 | free(baton); |
92658f56 PR |
275 | return NULL; |
276 | } | |
277 | ||
6e4e3082 | 278 | static int handle_connect(daemon_state s) |
92658f56 | 279 | { |
6d404585 | 280 | struct thread_baton *baton; |
92658f56 | 281 | struct sockaddr_un sockaddr; |
6d404585 | 282 | client_handle client = { .thread_id = 0 }; |
92658f56 | 283 | socklen_t sl = sizeof(sockaddr); |
6d404585 ZK |
284 | |
285 | client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl); | |
286 | if (client.socket_fd < 0) | |
92658f56 PR |
287 | return 0; |
288 | ||
6d404585 | 289 | if (!(baton = malloc(sizeof(struct thread_baton)))) |
92658f56 PR |
290 | return 0; |
291 | ||
92658f56 PR |
292 | baton->s = s; |
293 | baton->client = client; | |
294 | ||
295 | if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton)) | |
296 | return 0; | |
6d404585 | 297 | |
d5406851 PR |
298 | pthread_detach(baton->client.thread_id); |
299 | ||
92658f56 PR |
300 | return 1; |
301 | } | |
302 | ||
dc85d3fb | 303 | void daemon_start(daemon_state s) |
d7448a72 | 304 | { |
73ffd6e7 | 305 | int failed = 0; |
d7448a72 PR |
306 | /* |
307 | * Switch to C locale to avoid reading large locale-archive file used by | |
308 | * some glibc (on some distributions it takes over 100MB). Some daemons | |
309 | * need to use mlockall(). | |
310 | */ | |
311 | if (setenv("LANG", "C", 1)) | |
312 | perror("Cannot set LANG to C"); | |
313 | ||
314 | if (!s.foreground) | |
315 | _daemonise(); | |
316 | ||
317 | /* TODO logging interface should be somewhat more elaborate */ | |
318 | openlog(s.name, LOG_PID, LOG_DAEMON); | |
319 | ||
320 | (void) dm_prepare_selinux_context(s.pidfile, S_IFREG); | |
321 | ||
322 | /* | |
73ffd6e7 PR |
323 | * NB. Take care to not keep stale locks around. Best not exit(...) |
324 | * after this point. | |
d7448a72 PR |
325 | */ |
326 | if (dm_create_lockfile(s.pidfile) == 0) | |
327 | exit(1); | |
328 | ||
329 | (void) dm_prepare_selinux_context(NULL, 0); | |
330 | ||
331 | /* Set normal exit signals to request shutdown instead of dying. */ | |
332 | signal(SIGINT, &_exit_handler); | |
333 | signal(SIGHUP, &_exit_handler); | |
334 | signal(SIGQUIT, &_exit_handler); | |
92658f56 | 335 | signal(SIGTERM, &_exit_handler); |
c033ea01 | 336 | signal(SIGALRM, &_exit_handler); |
92658f56 | 337 | signal(SIGPIPE, SIG_IGN); |
d7448a72 PR |
338 | |
339 | #ifdef linux | |
340 | if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) | |
341 | syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); | |
342 | #endif | |
343 | ||
73ffd6e7 PR |
344 | if (s.socket_path) { |
345 | s.socket_fd = _open_socket(s); | |
346 | if (s.socket_fd < 0) | |
347 | failed = 1; | |
348 | } | |
349 | ||
d7448a72 PR |
350 | /* Signal parent, letting them know we are ready to go. */ |
351 | if (!s.foreground) | |
352 | kill(getppid(), SIGTERM); | |
353 | ||
372e9b3d PR |
354 | if (s.daemon_init) |
355 | s.daemon_init(&s); | |
356 | ||
73ffd6e7 | 357 | while (!_shutdown_requested && !failed) { |
92658f56 PR |
358 | fd_set in; |
359 | FD_ZERO(&in); | |
360 | FD_SET(s.socket_fd, &in); | |
361 | if (select(FD_SETSIZE, &in, NULL, NULL, NULL) < 0 && errno != EINTR) | |
362 | perror("select error"); | |
363 | if (FD_ISSET(s.socket_fd, &in)) | |
364 | if (!handle_connect(s)) | |
365 | syslog(LOG_ERR, "Failed to handle a client connection."); | |
d7448a72 PR |
366 | } |
367 | ||
92658f56 | 368 | if (s.socket_fd >= 0) |
6e2761e9 ZK |
369 | if (unlink(s.socket_path)) |
370 | perror("unlink error"); | |
92658f56 | 371 | |
372e9b3d PR |
372 | if (s.daemon_fini) |
373 | s.daemon_fini(&s); | |
374 | ||
d7448a72 PR |
375 | syslog(LOG_NOTICE, "%s shutting down", s.name); |
376 | closelog(); | |
377 | remove_lockfile(s.pidfile); | |
73ffd6e7 PR |
378 | if (failed) |
379 | exit(1); | |
d7448a72 | 380 | } |