]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/lxcutmp.c
ovl_rsync: make sure to umount
[mirror_lxc.git] / src / lxc / lxcutmp.c
CommitLineData
563f2f2c
DL
1/*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
9afe19d6 7 * Daniel Lezcano <daniel.lezcano at free.fr>
563f2f2c
DL
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
563f2f2c
DL
22 */
23
ffbf5815
SG
24#include "config.h"
25
563f2f2c
DL
26#include <stdio.h>
27#include <unistd.h>
28#include <errno.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <fcntl.h>
32#include <sys/inotify.h>
b0badabd 33#include <sys/ioctl.h>
f15b77ca 34#ifdef HAVE_SYS_TIMERFD_H
b0badabd 35#include <sys/timerfd.h>
e316c085
SG
36#else
37#include <sys/syscall.h>
38#ifndef TFD_NONBLOCK
39#define TFD_NONBLOCK O_NONBLOCK
40#endif
41
42#ifndef TFD_CLOEXEC
43#define TFD_CLOEXEC O_CLOEXEC
44#endif
45static int timerfd_create (clockid_t __clock_id, int __flags) {
46 return syscall(__NR_timerfd_create, __clock_id, __flags);
47}
48
49static int timerfd_settime (int __ufd, int __flags,
50 const struct itimerspec *__utmr,
51 struct itimerspec *__otmr) {
52
53 return syscall(__NR_timerfd_settime, __ufd, __flags,
54 __utmr, __otmr);
55}
56
57#endif
563f2f2c
DL
58
59#include "conf.h"
60#include "cgroup.h"
61#include "start.h"
62#include "mainloop.h"
63#include "lxc.h"
64#include "log.h"
ffbf5815
SG
65
66#ifndef __USE_GNU
563f2f2c 67#define __USE_GNU
ffbf5815 68#endif
e316c085 69#ifdef HAVE_UTMPX_H
563f2f2c 70#include <utmpx.h>
8b6d8b71
NC
71#ifndef HAVE_UTMPXNAME
72#include <utmp.h>
73#endif
74
e316c085
SG
75#else
76#include <utmp.h>
77
78#ifndef RUN_LVL
79#define RUN_LVL 1
80#endif
81
e316c085
SG
82static void setutxent(void) {
83 return setutent();
84}
85
86static struct utmp * getutxent (void) {
87 return (struct utmp *) getutent();
88}
89
90static void endutxent (void) {
91#ifdef IS_BIONIC
92 /* bionic isn't exporting endutend */
93 return;
94#else
95 return endutent();
96#endif
97}
98#endif
8b6d8b71
NC
99
100#ifndef HAVE_UTMPXNAME
101static int utmpxname(const char *file) {
102 int result;
103 result = utmpname(file);
104
105#ifdef IS_BIONIC
106 /* Yeah bionic is that weird */
107 result = result - 1;
108#endif
109
110 return result;
111}
112#endif
113
563f2f2c
DL
114#undef __USE_GNU
115
b0badabd
AP
116/* This file watches the /var/run/utmp file in the container
117 * (that should probably be configurable)
118 * We use inotify to put a watch on the /var/run directory for
119 * create and modify events. These can trigger a read of the
120 * utmp file looking for runlevel changes. If a runlevel change
121 * to reboot or halt states is detected, we set up an itimer to
122 * regularly check for the container shutdown, and reboot or halt
123 * as appropriate when we get down to 1 task remaining.
124 */
125
563f2f2c
DL
126lxc_log_define(lxc_utmp, lxc);
127
b0badabd
AP
128struct lxc_utmp {
129 struct lxc_handler *handler;
130#define CONTAINER_STARTING 0
131#define CONTAINER_REBOOTING 1
132#define CONTAINER_HALTING 2
133#define CONTAINER_RUNNING 4
134 char container_state;
135 int timer_fd;
136 int prev_runlevel, curr_runlevel;
137};
138
139typedef void (*lxc_mainloop_timer_t) (void *data);
140
141static int utmp_get_runlevel(struct lxc_utmp *utmp_data);
142static int utmp_get_ntasks(struct lxc_handler *handler);
84c92abd 143static int utmp_shutdown_handler(int fd, uint32_t events, void *data,
b0badabd
AP
144 struct lxc_epoll_descr *descr);
145static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
146 lxc_mainloop_callback_t callback, void *data);
147static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
148 struct lxc_utmp *utmp_data);
149
84c92abd
DE
150static int utmp_handler(int fd, uint32_t events, void *data,
151 struct lxc_epoll_descr *descr)
563f2f2c 152{
b0badabd
AP
153 struct inotify_event *ie;
154 int size, ret, length;
155
156 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
157
59991679
DL
158 /*
159 * we're monitoring a directory. ie->name is not included in
160 * sizeof(struct inotify_event) if we don't read it all at once,
161 * read gives us EINVAL, so we read and cast to struct ie
b0badabd
AP
162 */
163 char buffer[MAXPATHLEN];
164
165 if (ioctl(fd, FIONREAD, &size) < 0) {
166 SYSERROR("cannot determine the size of this notification");
167 return -1;
168 }
169
9de2ebe9 170 if (read(fd, buffer, size) < size) {
b0badabd
AP
171 SYSERROR("failed to read notification");
172 return -1;
173 }
174
175 ie = (struct inotify_event *)buffer;
563f2f2c 176
b0badabd 177 if (ie->len <= 0) {
59991679
DL
178
179 if (ie->mask & IN_UNMOUNT) {
180 DEBUG("watched directory removed");
181 goto out;
182 }
183
184 SYSERROR("inotify event with no name (mask %d)", ie->mask);
563f2f2c
DL
185 return -1;
186 }
187
b0badabd
AP
188 ret = 0;
189
190 DEBUG("got inotify event %d for %s", ie->mask, ie->name);
191
192 length = (4 < ie->len) ? 4 : ie->len;
193
194 /* only care about utmp */
195
196 if (strncmp(ie->name, "utmp", length))
197 return 0;
198
199 if (ie->mask & (IN_MODIFY | IN_CREATE))
200 ret = utmp_get_runlevel(utmp_data);
201
202 if (ret < 0)
203 goto out;
204
205 /* container halting, from running or starting state */
206 if (utmp_data->curr_runlevel == '0'
207 && ((utmp_data->container_state == CONTAINER_RUNNING)
208 || (utmp_data->container_state == CONTAINER_STARTING))) {
209 utmp_data->container_state = CONTAINER_HALTING;
210 if (utmp_data->timer_fd == -1)
211 lxc_utmp_add_timer(descr, utmp_shutdown_handler, data);
212 DEBUG("Container halting");
213 goto out;
214 }
215
216 /* container rebooting, from running or starting state */
217 if (utmp_data->curr_runlevel == '6'
218 && ((utmp_data->container_state == CONTAINER_RUNNING)
219 || (utmp_data->container_state == CONTAINER_STARTING))) {
220 utmp_data->container_state = CONTAINER_REBOOTING;
221 if (utmp_data->timer_fd == -1)
222 lxc_utmp_add_timer(descr, utmp_shutdown_handler, data);
223 DEBUG("Container rebooting");
224 goto out;
225 }
226
227 /* normal operation, running, from starting state. */
228 if (utmp_data->curr_runlevel > '0' && utmp_data->curr_runlevel < '6') {
229 utmp_data->container_state = CONTAINER_RUNNING;
230 if (utmp_data->timer_fd > 0)
231 lxc_utmp_del_timer(descr, utmp_data);
232 DEBUG("Container running");
233 goto out;
234 }
235
236out:
237 return 0;
238}
239
240static int utmp_get_runlevel(struct lxc_utmp *utmp_data)
241{
e316c085 242 #if HAVE_UTMPX_H
b0badabd 243 struct utmpx *utmpx;
e316c085
SG
244 #else
245 struct utmp *utmpx;
246 #endif
b0badabd
AP
247 char path[MAXPATHLEN];
248 struct lxc_handler *handler = utmp_data->handler;
b0badabd 249
4cb05a60
SH
250 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp",
251 handler->pid) > MAXPATHLEN) {
252 ERROR("path is too long");
253 return -1;
254 }
255
256 if (!access(path, F_OK) && !utmpxname(path))
257 goto utmp_ok;
258
59991679
DL
259 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp",
260 handler->pid) > MAXPATHLEN) {
563f2f2c
DL
261 ERROR("path is too long");
262 return -1;
263 }
264
265 if (utmpxname(path)) {
266 SYSERROR("failed to 'utmpxname'");
267 return -1;
268 }
269
4cb05a60
SH
270utmp_ok:
271
563f2f2c
DL
272 setutxent();
273
274 while ((utmpx = getutxent())) {
275
276 if (utmpx->ut_type == RUN_LVL) {
b0badabd
AP
277 utmp_data->prev_runlevel = utmpx->ut_pid / 256;
278 utmp_data->curr_runlevel = utmpx->ut_pid % 256;
279 DEBUG("utmp handler - run level is %c/%c",
280 utmp_data->prev_runlevel,
281 utmp_data->curr_runlevel);
563f2f2c
DL
282 }
283 }
284
b0badabd 285 endutxent();
563f2f2c 286
b0badabd
AP
287 return 0;
288}
563f2f2c 289
b0badabd
AP
290static int utmp_get_ntasks(struct lxc_handler *handler)
291{
292 int ntasks;
563f2f2c 293
4fb3cba5 294 ntasks = cgroup_nrtasks(handler);
563f2f2c 295
b0badabd
AP
296 if (ntasks < 0) {
297 ERROR("failed to get the number of tasks");
298 return -1;
563f2f2c
DL
299 }
300
b0badabd 301 DEBUG("there are %d tasks running", ntasks);
563f2f2c 302
b0badabd 303 return ntasks;
563f2f2c
DL
304}
305
306int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
307 struct lxc_handler *handler)
308{
563f2f2c 309 char path[MAXPATHLEN];
4cb05a60 310 char path2[MAXPATHLEN];
563f2f2c 311 int fd, wd;
b0badabd 312 struct lxc_utmp *utmp_data;
563f2f2c 313
59991679
DL
314 /* We set up a watch for the /var/run directory. We're only interested
315 * in utmp at the moment, but want to watch for delete and create
316 * events as well.
b0badabd 317 */
4cb05a60
SH
318 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run",
319 handler->pid) > MAXPATHLEN) {
320 ERROR("path is too long");
321 return -1;
322 }
323 if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp",
324 handler->pid) > MAXPATHLEN) {
325 ERROR("path is too long");
326 return -1;
327 }
328 if (!access(path2, F_OK))
329 goto run_ok;
330
59991679
DL
331 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run",
332 handler->pid) > MAXPATHLEN) {
563f2f2c
DL
333 ERROR("path is too long");
334 return -1;
335 }
336
337 if (access(path, F_OK)) {
338 WARN("'%s' not found", path);
339 return 0;
340 }
341
4cb05a60
SH
342run_ok:
343
b0badabd
AP
344 utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp));
345
346 if (NULL == utmp_data) {
347 SYSERROR("failed to malloc handler utmp_data");
348 return -1;
349 }
350
351 memset(utmp_data, 0, sizeof(struct lxc_utmp));
352
563f2f2c
DL
353 fd = inotify_init();
354 if (fd < 0) {
355 SYSERROR("failed to inotify_init");
b0badabd 356 goto out;
563f2f2c
DL
357 }
358
359 if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
360 SYSERROR("failed to set inotify fd to close-on-exec");
b0badabd
AP
361 goto out_close;
362
563f2f2c
DL
363 }
364
b0badabd 365 wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
563f2f2c
DL
366 if (wd < 0) {
367 SYSERROR("failed to add watch for '%s'", path);
b0badabd 368 goto out_close;
563f2f2c
DL
369 }
370
b0badabd
AP
371 utmp_data->handler = handler;
372 utmp_data->container_state = CONTAINER_STARTING;
373 utmp_data->timer_fd = -1;
374 utmp_data->prev_runlevel = 'N';
375 utmp_data->curr_runlevel = 'N';
376
377 if (lxc_mainloop_add_handler
378 (descr, fd, utmp_handler, (void *)utmp_data)) {
563f2f2c 379 SYSERROR("failed to add mainloop");
b0badabd
AP
380 goto out_close;
381 }
382
383 DEBUG("Added '%s' to inotifywatch", path);
384
385 return 0;
386out_close:
387 close(fd);
388out:
389 free(utmp_data);
390 return -1;
391}
392
84c92abd 393static int utmp_shutdown_handler(int fd, uint32_t events, void *data,
b0badabd
AP
394 struct lxc_epoll_descr *descr)
395{
396 int ntasks;
6ca5b95e 397 ssize_t nread;
b0badabd
AP
398 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
399 struct lxc_handler *handler = utmp_data->handler;
400 struct lxc_conf *conf = handler->conf;
401 uint64_t expirations;
402
403 /* read and clear notifications */
6ca5b95e
DL
404 nread = read(fd, &expirations, sizeof(expirations));
405 if (nread < 0)
406 SYSERROR("Failed to read timer notification");
b0badabd
AP
407
408 ntasks = utmp_get_ntasks(handler);
409
410 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) {
411 INFO("container has shutdown");
412 /* shutdown timer */
413 lxc_utmp_del_timer(descr, utmp_data);
414
415 kill(handler->pid, SIGKILL);
416 }
417
418 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) {
419 INFO("container has rebooted");
420 conf->reboot = 1;
421 /* shutdown timer */
422 lxc_utmp_del_timer(descr, utmp_data);
423 /* this seems a bit rough. */
424 kill(handler->pid, SIGKILL);
425 }
426 return 0;
427
428}
429
430int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
431 lxc_mainloop_callback_t callback, void *data)
432{
433 int fd, result;
434 struct itimerspec timeout;
435 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
436
437 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
438 if (fd < 0) {
439 SYSERROR("failed to create timer");
440 return -1;
441 }
442
443 DEBUG("Setting up utmp shutdown timer");
444
445 /* set a one second timeout. Repeated. */
446 timeout.it_value.tv_sec = 1;
447 timeout.it_value.tv_nsec = 0;
448
449 timeout.it_interval.tv_sec = 1;
450 timeout.it_interval.tv_nsec = 0;
451
452 result = timerfd_settime(fd, 0, &timeout, NULL);
453
454 if (result < 0) {
455 SYSERROR("timerfd_settime:");
456 return -1;
457 }
458
459 if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) {
460 SYSERROR("failed to add utmp timer to mainloop");
563f2f2c
DL
461 close(fd);
462 return -1;
463 }
464
b0badabd
AP
465 utmp_data->timer_fd = fd;
466
563f2f2c
DL
467 return 0;
468}
b0badabd
AP
469
470int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
471 struct lxc_utmp *utmp_data)
472{
473 int result;
474
475 DEBUG("Clearing utmp shutdown timer");
476
477 result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd);
478 if (result < 0)
479 SYSERROR("failed to del utmp timer from mainloop");
480
481 /* shutdown timer_fd */
482 close(utmp_data->timer_fd);
483 utmp_data->timer_fd = -1;
484
485 if (result < 0)
486 return -1;
487 else
488 return 0;
489}