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