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