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