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