1 /* SPDX-License-Identifier: LGPL-2.1+ */
9 #include <netinet/in.h>
16 #include <sys/epoll.h>
17 #include <sys/param.h>
18 #include <sys/socket.h>
20 #include <sys/types.h>
24 #include <lxc/lxccontainer.h>
31 #include "process_utils.h"
34 #define CLIENTFDS_CHUNK 64
36 lxc_log_define(lxc_monitord
, lxc
);
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
57 struct lxc_async_descr descr
;
60 static struct lxc_monitor monitor
;
63 static int lxc_monitord_fifo_create(struct lxc_monitor
*mon
)
66 char fifo_path
[PATH_MAX
];
69 ret
= lxc_monitor_fifo_name(mon
->lxcpath
, fifo_path
, sizeof(fifo_path
), 1);
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
);
79 mon
->fifofd
= open(fifo_path
, O_RDWR
);
80 if (mon
->fifofd
< 0) {
81 SYSERROR("Failed to open monitor fifo %s", fifo_path
);
87 lk
.l_whence
= SEEK_SET
;
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
);
101 static int lxc_monitord_fifo_delete(struct lxc_monitor
*mon
)
103 char fifo_path
[PATH_MAX
];
106 ret
= lxc_monitor_fifo_name(mon
->lxcpath
, fifo_path
, sizeof(fifo_path
), 0);
114 static int lxc_monitord_sockfd_remove(struct lxc_monitor
*mon
, int fd
)
118 for (i
= 0; i
< mon
->clientfds_cnt
; i
++)
119 if (mon
->clientfds
[i
] == fd
)
122 if (i
>= mon
->clientfds_cnt
) {
123 CRIT("File descriptor %d not found in clients array", fd
);
124 return LXC_MAINLOOP_ERROR
;
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
;
133 static int lxc_monitord_sock_handler(int fd
, uint32_t events
, void *data
,
134 struct lxc_async_descr
*descr
)
136 struct lxc_monitor
*mon
= data
;
138 if (events
& EPOLLIN
) {
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
;
149 if (events
& EPOLLHUP
)
150 return lxc_monitord_sockfd_remove(mon
, fd
);
155 static int lxc_monitord_sock_accept(int fd
, uint32_t events
, void *data
,
156 struct lxc_async_descr
*descr
)
159 struct lxc_monitor
*mon
= data
;
161 socklen_t credsz
= sizeof(cred
);
163 ret
= LXC_MAINLOOP_ERROR
;
164 clientfd
= accept(fd
, NULL
, 0);
166 SYSERROR("Failed to accept connection for client file descriptor %d", fd
);
170 if (fcntl(clientfd
, F_SETFD
, FD_CLOEXEC
)) {
171 SYSERROR("Failed to set FD_CLOEXEC on client socket connection %d", clientfd
);
175 if (getsockopt(clientfd
, SOL_SOCKET
, SO_PEERCRED
, &cred
, &credsz
)) {
176 SYSERROR("Failed to get credentials on client socket connection %d", clientfd
);
180 if (cred
.uid
&& cred
.uid
!= geteuid()) {
181 WARN("Monitor denied for uid %d on client socket connection %d", cred
.uid
, clientfd
);
185 if (mon
->clientfds_cnt
+ 1 > mon
->clientfds_size
) {
188 clientfds
= realloc(mon
->clientfds
,
189 (mon
->clientfds_size
+ CLIENTFDS_CHUNK
) * sizeof(mon
->clientfds
[0]));
191 ERROR("Failed to realloc memory for %d client file descriptors",
192 mon
->clientfds_size
+ CLIENTFDS_CHUNK
);
196 mon
->clientfds
= clientfds
;
197 mon
->clientfds_size
+= CLIENTFDS_CHUNK
;
200 ret
= lxc_mainloop_add_handler(&mon
->descr
, clientfd
,
201 lxc_monitord_sock_handler
,
202 default_cleanup_handler
,
203 mon
, "lxc_monitord_sock_handler");
205 ERROR("Failed to add socket handler");
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
);
221 static int lxc_monitord_sock_create(struct lxc_monitor
*mon
)
223 struct sockaddr_un addr
;
226 if (lxc_monitor_sock_name(mon
->lxcpath
, &addr
) < 0)
229 fd
= lxc_abstract_unix_open(addr
.sun_path
, SOCK_STREAM
, O_TRUNC
);
231 SYSERROR("Failed to open unix socket");
239 static int lxc_monitord_sock_delete(struct lxc_monitor
*mon
)
241 struct sockaddr_un addr
;
243 if (lxc_monitor_sock_name(mon
->lxcpath
, &addr
) < 0)
246 if (addr
.sun_path
[0])
247 unlink(addr
.sun_path
);
252 static int lxc_monitord_create(struct lxc_monitor
*mon
)
256 ret
= lxc_monitord_fifo_create(mon
);
260 return lxc_monitord_sock_create(mon
);
263 static void lxc_monitord_delete(struct lxc_monitor
*mon
)
265 lxc_abstract_unix_close(mon
->listenfd
);
266 lxc_monitord_sock_delete(mon
);
268 lxc_monitord_fifo_delete(mon
);
271 for (int i
= 0; i
< mon
->clientfds_cnt
; i
++)
272 close(mon
->clientfds
[i
]);
274 mon
->clientfds_cnt
= 0;
277 static int lxc_monitord_fifo_handler(int fd
, uint32_t events
, void *data
,
278 struct lxc_async_descr
*descr
)
281 struct lxc_msg msglxc
;
282 struct lxc_monitor
*mon
= data
;
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
;
290 for (i
= 0; i
< mon
->clientfds_cnt
; i
++) {
291 ret
= lxc_write_nointr(mon
->clientfds
[i
], &msglxc
, sizeof(msglxc
));
293 SYSERROR("Failed to send message to client file descriptor %d",
297 return LXC_MAINLOOP_CONTINUE
;
300 static int lxc_monitord_mainloop_add(struct lxc_monitor
*mon
)
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");
309 ERROR("Failed to add to mainloop monitor handler for fifo");
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");
318 ERROR("Failed to add to mainloop monitor handler for listen socket");
325 static void lxc_monitord_sig_handler(int sig
)
330 int main(int argc
, char *argv
[])
332 int ret
, pipefd
= -1;
334 const char *lxcpath
= NULL
;
335 bool mainloop_opened
= false;
336 bool monitord_created
= false;
337 bool persistent
= false;
339 if (argc
> 1 && !strcmp(argv
[1], "--daemon")) {
350 lxcpath
= lxc_global_config_value("lxc.lxcpath");
352 ERROR("Failed to get default lxcpath");
358 if (lxc_safe_int(argv
[1], &pipefd
) < 0)
364 if (argc
!= 1 || (persistent
!= (pipefd
== -1))) {
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");
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");
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
);
388 if (sigsetjmp(mark
, 1) != 0)
393 memset(&monitor
, 0, sizeof(monitor
));
394 monitor
.lxcpath
= lxcpath
;
395 if (lxc_mainloop_open(&monitor
.descr
)) {
396 ERROR("Failed to create mainloop");
399 mainloop_opened
= true;
401 if (lxc_monitord_create(&monitor
))
403 monitord_created
= true;
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.
412 if (lxc_write_nointr(pipefd
, "S", 1))
417 if (lxc_monitord_mainloop_add(&monitor
)) {
418 ERROR("Failed to add mainloop handlers");
422 NOTICE("lxc-monitord with pid %d is now monitoring lxcpath %s",
423 lxc_raw_getpid(), monitor
.lxcpath
);
426 ret
= lxc_mainloop(&monitor
.descr
, persistent
? -1 : 1000 * 30);
428 ERROR("mainloop returned an error");
432 if (monitor
.clientfds_cnt
<= 0) {
433 NOTICE("No remaining clients. lxc-monitord is exiting");
437 if (quit
== LXC_MAINLOOP_CLOSE
) {
438 NOTICE("Got quit command. lxc-monitord is exiting");
448 lxc_mainloop_close(&monitor
.descr
);
450 if (monitord_created
)
451 lxc_monitord_delete(&monitor
);