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