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