]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cmd/lxc_monitord.c
af_unix: fix return value & cleanups
[mirror_lxc.git] / src / lxc / cmd / lxc_monitord.c
1 /*
2 * lxc: linux Container library
3 *
4 * Copyright © 2012 Oracle.
5 *
6 * Authors:
7 * Dwight Engen <dwight.engen@oracle.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <setjmp.h>
36 #include <sys/epoll.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/un.h>
42
43 #include <lxc/lxccontainer.h>
44
45 #include "af_unix.h"
46 #include "log.h"
47 #include "mainloop.h"
48 #include "monitor.h"
49 #include "utils.h"
50
51 #define CLIENTFDS_CHUNK 64
52
53 lxc_log_define(lxc_monitord, lxc);
54
55 sigjmp_buf mark;
56
57 static void lxc_monitord_cleanup(void);
58
59 /*
60 * Defines the structure to store the monitor information
61 * @lxcpath : the path being monitored
62 * @fifofd : the file descriptor for publishers (containers) to write state
63 * @listenfd : the file descriptor for subscribers (lxc-monitors) to connect
64 * @clientfds : accepted client file descriptors
65 * @clientfds_size : number of file descriptors clientfds can hold
66 * @clientfds_cnt : the count of valid fds in clientfds
67 * @descr : the lxc_mainloop state
68 */
69 struct lxc_monitor {
70 const char *lxcpath;
71 int fifofd;
72 int listenfd;
73 int *clientfds;
74 int clientfds_size;
75 int clientfds_cnt;
76 struct lxc_epoll_descr descr;
77 };
78
79 static struct lxc_monitor mon;
80 static int quit;
81
82 static int lxc_monitord_fifo_create(struct lxc_monitor *mon)
83 {
84 struct flock lk;
85 char fifo_path[PATH_MAX];
86 int ret;
87
88 ret = lxc_monitor_fifo_name(mon->lxcpath, fifo_path, sizeof(fifo_path), 1);
89 if (ret < 0)
90 return ret;
91
92 ret = mknod(fifo_path, S_IFIFO|S_IRUSR|S_IWUSR, 0);
93 if (ret < 0 && errno != EEXIST) {
94 SYSINFO("Failed to mknod monitor fifo %s", fifo_path);
95 return -1;
96 }
97
98 mon->fifofd = open(fifo_path, O_RDWR);
99 if (mon->fifofd < 0) {
100 SYSERROR("Failed to open monitor fifo %s", fifo_path);
101 unlink(fifo_path);
102 return -1;
103 }
104
105 lk.l_type = F_WRLCK;
106 lk.l_whence = SEEK_SET;
107 lk.l_start = 0;
108 lk.l_len = 0;
109
110 if (fcntl(mon->fifofd, F_SETLK, &lk) != 0) {
111 /* another lxc-monitord is already running, don't start up */
112 SYSDEBUG("lxc-monitord already running on lxcpath %s", mon->lxcpath);
113 close(mon->fifofd);
114 return -1;
115 }
116
117 return 0;
118 }
119
120 static int lxc_monitord_fifo_delete(struct lxc_monitor *mon)
121 {
122 char fifo_path[PATH_MAX];
123 int ret;
124
125 ret = lxc_monitor_fifo_name(mon->lxcpath, fifo_path, sizeof(fifo_path), 0);
126 if (ret < 0)
127 return ret;
128
129 unlink(fifo_path);
130 return 0;
131 }
132
133 static void lxc_monitord_sockfd_remove(struct lxc_monitor *mon, int fd) {
134 int i;
135
136 if (lxc_mainloop_del_handler(&mon->descr, fd))
137 CRIT("File descriptor %d not found in mainloop", fd);
138 close(fd);
139
140 for (i = 0; i < mon->clientfds_cnt; i++)
141 if (mon->clientfds[i] == fd)
142 break;
143
144 if (i >= mon->clientfds_cnt) {
145 CRIT("File descriptor %d not found in clients array", fd);
146 lxc_monitord_cleanup();
147 exit(EXIT_FAILURE);
148 }
149
150 memmove(&mon->clientfds[i], &mon->clientfds[i+1],
151 (mon->clientfds_cnt - i - 1) * sizeof(mon->clientfds[0]));
152 mon->clientfds_cnt--;
153 }
154
155 static int lxc_monitord_sock_handler(int fd, uint32_t events, void *data,
156 struct lxc_epoll_descr *descr)
157 {
158 struct lxc_monitor *mon = data;
159
160 if (events & EPOLLIN) {
161 int rc;
162 char buf[4];
163
164 rc = read(fd, buf, sizeof(buf));
165 if (rc > 0 && !strncmp(buf, "quit", 4))
166 quit = LXC_MAINLOOP_CLOSE;
167 }
168
169 if (events & EPOLLHUP)
170 lxc_monitord_sockfd_remove(mon, fd);
171
172 return quit;
173 }
174
175 static int lxc_monitord_sock_accept(int fd, uint32_t events, void *data,
176 struct lxc_epoll_descr *descr)
177 {
178 int ret,clientfd;
179 struct lxc_monitor *mon = data;
180 struct ucred cred;
181 socklen_t credsz = sizeof(cred);
182
183 ret = LXC_MAINLOOP_ERROR;
184 clientfd = accept(fd, NULL, 0);
185 if (clientfd < 0) {
186 SYSERROR("Failed to accept connection for client file descriptor %d", fd);
187 goto out;
188 }
189
190 if (fcntl(clientfd, F_SETFD, FD_CLOEXEC)) {
191 SYSERROR("Failed to set FD_CLOEXEC on client socket connection %d", clientfd);
192 goto err1;
193 }
194
195 if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz)) {
196 SYSERROR("Failed to get credentials on client socket connection %d", clientfd);
197 goto err1;
198 }
199
200 if (cred.uid && cred.uid != geteuid()) {
201 WARN("Monitor denied for uid %d on client socket connection %d", cred.uid, clientfd);
202 goto err1;
203 }
204
205 if (mon->clientfds_cnt + 1 > mon->clientfds_size) {
206 int *clientfds;
207
208 clientfds = realloc(mon->clientfds,
209 (mon->clientfds_size + CLIENTFDS_CHUNK) * sizeof(mon->clientfds[0]));
210 if (clientfds == NULL) {
211 ERROR("Failed to realloc memory for %d client file descriptors",
212 mon->clientfds_size + CLIENTFDS_CHUNK);
213 goto err1;
214 }
215
216 mon->clientfds = clientfds;
217 mon->clientfds_size += CLIENTFDS_CHUNK;
218 }
219
220 ret = lxc_mainloop_add_handler(&mon->descr, clientfd,
221 lxc_monitord_sock_handler, mon);
222 if (ret < 0) {
223 ERROR("Failed to add socket handler");
224 goto err1;
225 }
226
227 mon->clientfds[mon->clientfds_cnt++] = clientfd;
228 INFO("Accepted client file descriptor %d. Number of accepted file descriptors is now %d",
229 clientfd, mon->clientfds_cnt);
230 goto out;
231
232 err1:
233 close(clientfd);
234
235 out:
236 return ret;
237 }
238
239 static int lxc_monitord_sock_create(struct lxc_monitor *mon)
240 {
241 struct sockaddr_un addr;
242 int fd;
243
244 if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0)
245 return -1;
246
247 fd = lxc_abstract_unix_open(addr.sun_path, SOCK_STREAM, O_TRUNC);
248 if (fd < 0) {
249 SYSERROR("Failed to open unix socket");
250 return -1;
251 }
252
253 mon->listenfd = fd;
254 return 0;
255 }
256
257 static int lxc_monitord_sock_delete(struct lxc_monitor *mon)
258 {
259 struct sockaddr_un addr;
260
261 if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0)
262 return -1;
263
264 if (addr.sun_path[0])
265 unlink(addr.sun_path);
266
267 return 0;
268 }
269
270 static int lxc_monitord_create(struct lxc_monitor *mon)
271 {
272 int ret;
273
274 ret = lxc_monitord_fifo_create(mon);
275 if (ret < 0)
276 return ret;
277
278 return lxc_monitord_sock_create(mon);
279 }
280
281 static void lxc_monitord_delete(struct lxc_monitor *mon)
282 {
283 int i;
284
285 lxc_mainloop_del_handler(&mon->descr, mon->listenfd);
286 lxc_abstract_unix_close(mon->listenfd);
287 lxc_monitord_sock_delete(mon);
288
289 lxc_mainloop_del_handler(&mon->descr, mon->fifofd);
290 lxc_monitord_fifo_delete(mon);
291 close(mon->fifofd);
292
293 for (i = 0; i < mon->clientfds_cnt; i++) {
294 lxc_mainloop_del_handler(&mon->descr, mon->clientfds[i]);
295 close(mon->clientfds[i]);
296 }
297
298 mon->clientfds_cnt = 0;
299 }
300
301 static int lxc_monitord_fifo_handler(int fd, uint32_t events, void *data,
302 struct lxc_epoll_descr *descr)
303 {
304 int ret,i;
305 struct lxc_msg msglxc;
306 struct lxc_monitor *mon = data;
307
308 ret = read(fd, &msglxc, sizeof(msglxc));
309 if (ret != sizeof(msglxc)) {
310 SYSERROR("Reading from fifo failed");
311 return LXC_MAINLOOP_CLOSE;
312 }
313
314 for (i = 0; i < mon->clientfds_cnt; i++) {
315 ret = write(mon->clientfds[i], &msglxc, sizeof(msglxc));
316 if (ret < 0)
317 SYSERROR("Failed to send message to client file descriptor %d",
318 mon->clientfds[i]);
319 }
320
321 return LXC_MAINLOOP_CONTINUE;
322 }
323
324 static int lxc_monitord_mainloop_add(struct lxc_monitor *mon)
325 {
326 int ret;
327
328 ret = lxc_mainloop_add_handler(&mon->descr, mon->fifofd,
329 lxc_monitord_fifo_handler, mon);
330 if (ret < 0) {
331 ERROR("Failed to add to mainloop monitor handler for fifo");
332 return -1;
333 }
334
335 ret = lxc_mainloop_add_handler(&mon->descr, mon->listenfd,
336 lxc_monitord_sock_accept, mon);
337 if (ret < 0) {
338 ERROR("Failed to add to mainloop monitor handler for listen socket");
339 return -1;
340 }
341
342 return 0;
343 }
344
345 static void lxc_monitord_cleanup(void)
346 {
347 lxc_monitord_delete(&mon);
348 }
349
350 static void lxc_monitord_sig_handler(int sig)
351 {
352 siglongjmp(mark, 1);
353 }
354
355 int main(int argc, char *argv[])
356 {
357 int ret, pipefd;
358 char logpath[PATH_MAX];
359 sigset_t mask;
360 char *lxcpath = argv[1];
361 bool mainloop_opened = false;
362 bool monitord_created = false;
363 struct lxc_log log;
364
365 if (argc != 3) {
366 fprintf(stderr,
367 "Usage: lxc-monitord lxcpath sync-pipe-fd\n\n"
368 "NOTE: lxc-monitord is intended for use by lxc internally\n"
369 " and does not need to be run by hand\n\n");
370 exit(EXIT_FAILURE);
371 }
372
373 ret = snprintf(logpath, sizeof(logpath), "%s/lxc-monitord.log",
374 (strcmp(LXCPATH, lxcpath) ? lxcpath : LOGPATH ));
375 if (ret < 0 || ret >= sizeof(logpath))
376 exit(EXIT_FAILURE);
377
378 log.name = NULL;
379 log.file = logpath;
380 log.level = "DEBUG";
381 log.prefix = "lxc-monitord";
382 log.quiet = 0;
383 log.lxcpath = lxcpath;
384
385 ret = lxc_log_init(&log);
386 if (ret)
387 INFO("Failed to open log file %s, log will be lost", lxcpath);
388 lxc_log_options_no_override();
389
390 if (lxc_safe_int(argv[2], &pipefd) < 0)
391 exit(EXIT_FAILURE);
392
393 if (sigfillset(&mask) ||
394 sigdelset(&mask, SIGILL) ||
395 sigdelset(&mask, SIGSEGV) ||
396 sigdelset(&mask, SIGBUS) ||
397 sigdelset(&mask, SIGTERM) ||
398 pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
399 SYSERROR("Failed to set signal mask");
400 exit(EXIT_FAILURE);
401 }
402
403 signal(SIGILL, lxc_monitord_sig_handler);
404 signal(SIGSEGV, lxc_monitord_sig_handler);
405 signal(SIGBUS, lxc_monitord_sig_handler);
406 signal(SIGTERM, lxc_monitord_sig_handler);
407
408 if (sigsetjmp(mark, 1) != 0)
409 goto on_signal;
410
411 ret = EXIT_FAILURE;
412
413 memset(&mon, 0, sizeof(mon));
414 mon.lxcpath = lxcpath;
415 if (lxc_mainloop_open(&mon.descr)) {
416 ERROR("Failed to create mainloop");
417 goto on_error;
418 }
419 mainloop_opened = true;
420
421 if (lxc_monitord_create(&mon))
422 goto on_error;
423 monitord_created = true;
424
425 /* sync with parent, we're ignoring the return from write
426 * because regardless if it works or not, the following
427 * close will sync us with the parent process. the
428 * if-empty-statement construct is to quiet the
429 * warn-unused-result warning.
430 */
431 if (write(pipefd, "S", 1))
432 ;
433 close(pipefd);
434
435 if (lxc_monitord_mainloop_add(&mon)) {
436 ERROR("Failed to add mainloop handlers");
437 goto on_error;
438 }
439
440 NOTICE("lxc-monitord with pid %d is now monitoring lxcpath %s",
441 lxc_raw_getpid(), mon.lxcpath);
442
443 for (;;) {
444 ret = lxc_mainloop(&mon.descr, 1000 * 30);
445 if (ret) {
446 ERROR("mainloop returned an error");
447 break;
448 }
449
450 if (mon.clientfds_cnt <= 0) {
451 NOTICE("No remaining clients. lxc-monitord is exiting");
452 break;
453 }
454
455 if (quit == LXC_MAINLOOP_CLOSE) {
456 NOTICE("Got quit command. lxc-monitord is exitting");
457 break;
458 }
459 }
460
461 on_signal:
462 ret = EXIT_SUCCESS;
463
464 on_error:
465 if (monitord_created)
466 lxc_monitord_cleanup();
467
468 if (mainloop_opened)
469 lxc_mainloop_close(&mon.descr);
470
471 exit(ret);
472 }