2
* lxc: linux Container library
4
* (C) Copyright IBM Corp. 2007, 2008
7
* Daniel Lezcano <dlezcano at fr.ibm.com>
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.
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.
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
21
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32
#include <sys/inotify.h>
33
#include <sys/ioctl.h>
34
#ifdef HAVE_SYS_TIMERFD_H
35
#include <sys/timerfd.h>
37
#include <sys/syscall.h>
39
#define TFD_NONBLOCK O_NONBLOCK
43
#define TFD_CLOEXEC O_CLOEXEC
45
static int timerfd_create (clockid_t __clock_id, int __flags) {
46
return syscall(__NR_timerfd_create, __clock_id, __flags);
49
static int timerfd_settime (int __ufd, int __flags,
50
const struct itimerspec *__utmr,
51
struct itimerspec *__otmr) {
53
return syscall(__NR_timerfd_settime, __ufd, __flags,
78
static int utmpxname(const char *file) {
80
result = utmpname(file);
83
/* Yeah bionic is that weird */
90
static void setutxent(void) {
94
static struct utmp * getutxent (void) {
95
return (struct utmp *) getutent();
98
static void endutxent (void) {
100
/* bionic isn't exporting endutend */
109
/* This file watches the /var/run/utmp file in the container
110
* (that should probably be configurable)
111
* We use inotify to put a watch on the /var/run directory for
112
* create and modify events. These can trigger a read of the
113
* utmp file looking for runlevel changes. If a runlevel change
114
* to reboot or halt states is detected, we set up an itimer to
115
* regularly check for the container shutdown, and reboot or halt
116
* as appropriate when we get down to 1 task remaining.
119
lxc_log_define(lxc_utmp, lxc);
122
struct lxc_handler *handler;
123
#define CONTAINER_STARTING 0
124
#define CONTAINER_REBOOTING 1
125
#define CONTAINER_HALTING 2
126
#define CONTAINER_RUNNING 4
127
char container_state;
129
int prev_runlevel, curr_runlevel;
132
typedef void (*lxc_mainloop_timer_t) (void *data);
134
static int utmp_get_runlevel(struct lxc_utmp *utmp_data);
135
static int utmp_get_ntasks(struct lxc_handler *handler);
136
static int utmp_shutdown_handler(int fd, void *data,
137
struct lxc_epoll_descr *descr);
138
static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
139
lxc_mainloop_callback_t callback, void *data);
140
static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
141
struct lxc_utmp *utmp_data);
143
static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
145
struct inotify_event *ie;
146
int size, ret, length;
148
struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
151
* we're monitoring a directory. ie->name is not included in
152
* sizeof(struct inotify_event) if we don't read it all at once,
153
* read gives us EINVAL, so we read and cast to struct ie
155
char buffer[MAXPATHLEN];
157
if (ioctl(fd, FIONREAD, &size) < 0) {
158
SYSERROR("cannot determine the size of this notification");
162
if (read(fd, buffer, size) < 0) {
163
SYSERROR("failed to read notification");
167
ie = (struct inotify_event *)buffer;
171
if (ie->mask & IN_UNMOUNT) {
172
DEBUG("watched directory removed");
176
SYSERROR("inotify event with no name (mask %d)", ie->mask);
182
DEBUG("got inotify event %d for %s", ie->mask, ie->name);
184
length = (4 < ie->len) ? 4 : ie->len;
186
/* only care about utmp */
188
if (strncmp(ie->name, "utmp", length))
191
if (ie->mask & (IN_MODIFY | IN_CREATE))
192
ret = utmp_get_runlevel(utmp_data);
197
/* container halting, from running or starting state */
198
if (utmp_data->curr_runlevel == '0'
199
&& ((utmp_data->container_state == CONTAINER_RUNNING)
200
|| (utmp_data->container_state == CONTAINER_STARTING))) {
201
utmp_data->container_state = CONTAINER_HALTING;
202
if (utmp_data->timer_fd == -1)
203
lxc_utmp_add_timer(descr, utmp_shutdown_handler, data);
204
DEBUG("Container halting");
208
/* container rebooting, from running or starting state */
209
if (utmp_data->curr_runlevel == '6'
210
&& ((utmp_data->container_state == CONTAINER_RUNNING)
211
|| (utmp_data->container_state == CONTAINER_STARTING))) {
212
utmp_data->container_state = CONTAINER_REBOOTING;
213
if (utmp_data->timer_fd == -1)
214
lxc_utmp_add_timer(descr, utmp_shutdown_handler, data);
215
DEBUG("Container rebooting");
219
/* normal operation, running, from starting state. */
220
if (utmp_data->curr_runlevel > '0' && utmp_data->curr_runlevel < '6') {
221
utmp_data->container_state = CONTAINER_RUNNING;
222
if (utmp_data->timer_fd > 0)
223
lxc_utmp_del_timer(descr, utmp_data);
224
DEBUG("Container running");
232
static int utmp_get_runlevel(struct lxc_utmp *utmp_data)
239
char path[MAXPATHLEN];
240
struct lxc_handler *handler = utmp_data->handler;
242
if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp",
243
handler->pid) > MAXPATHLEN) {
244
ERROR("path is too long");
248
if (!access(path, F_OK) && !utmpxname(path))
251
if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp",
252
handler->pid) > MAXPATHLEN) {
253
ERROR("path is too long");
257
if (utmpxname(path)) {
258
SYSERROR("failed to 'utmpxname'");
266
while ((utmpx = getutxent())) {
268
if (utmpx->ut_type == RUN_LVL) {
269
utmp_data->prev_runlevel = utmpx->ut_pid / 256;
270
utmp_data->curr_runlevel = utmpx->ut_pid % 256;
271
DEBUG("utmp handler - run level is %c/%c",
272
utmp_data->prev_runlevel,
273
utmp_data->curr_runlevel);
282
static int utmp_get_ntasks(struct lxc_handler *handler)
286
ntasks = lxc_cgroup_nrtasks(handler->name);
289
ERROR("failed to get the number of tasks");
293
DEBUG("there are %d tasks running", ntasks);
298
int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
299
struct lxc_handler *handler)
301
char path[MAXPATHLEN];
302
char path2[MAXPATHLEN];
304
struct lxc_utmp *utmp_data;
306
/* We set up a watch for the /var/run directory. We're only interested
307
* in utmp at the moment, but want to watch for delete and create
310
if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run",
311
handler->pid) > MAXPATHLEN) {
312
ERROR("path is too long");
315
if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp",
316
handler->pid) > MAXPATHLEN) {
317
ERROR("path is too long");
320
if (!access(path2, F_OK))
323
if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run",
324
handler->pid) > MAXPATHLEN) {
325
ERROR("path is too long");
329
if (access(path, F_OK)) {
330
WARN("'%s' not found", path);
336
utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp));
338
if (NULL == utmp_data) {
339
SYSERROR("failed to malloc handler utmp_data");
343
memset(utmp_data, 0, sizeof(struct lxc_utmp));
347
SYSERROR("failed to inotify_init");
351
if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
352
SYSERROR("failed to set inotify fd to close-on-exec");
357
wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
359
SYSERROR("failed to add watch for '%s'", path);
363
utmp_data->handler = handler;
364
utmp_data->container_state = CONTAINER_STARTING;
365
utmp_data->timer_fd = -1;
366
utmp_data->prev_runlevel = 'N';
367
utmp_data->curr_runlevel = 'N';
369
if (lxc_mainloop_add_handler
370
(descr, fd, utmp_handler, (void *)utmp_data)) {
371
SYSERROR("failed to add mainloop");
375
DEBUG("Added '%s' to inotifywatch", path);
385
static int utmp_shutdown_handler(int fd, void *data,
386
struct lxc_epoll_descr *descr)
390
struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
391
struct lxc_handler *handler = utmp_data->handler;
392
struct lxc_conf *conf = handler->conf;
393
uint64_t expirations;
395
/* read and clear notifications */
396
nread = read(fd, &expirations, sizeof(expirations));
398
SYSERROR("Failed to read timer notification");
400
ntasks = utmp_get_ntasks(handler);
402
if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) {
403
INFO("container has shutdown");
405
lxc_utmp_del_timer(descr, utmp_data);
407
kill(handler->pid, SIGKILL);
410
if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) {
411
INFO("container has rebooted");
414
lxc_utmp_del_timer(descr, utmp_data);
415
/* this seems a bit rough. */
416
kill(handler->pid, SIGKILL);
422
int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
423
lxc_mainloop_callback_t callback, void *data)
426
struct itimerspec timeout;
427
struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
429
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
431
SYSERROR("failed to create timer");
435
DEBUG("Setting up utmp shutdown timer");
437
/* set a one second timeout. Repeated. */
438
timeout.it_value.tv_sec = 1;
439
timeout.it_value.tv_nsec = 0;
441
timeout.it_interval.tv_sec = 1;
442
timeout.it_interval.tv_nsec = 0;
444
result = timerfd_settime(fd, 0, &timeout, NULL);
447
SYSERROR("timerfd_settime:");
451
if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) {
452
SYSERROR("failed to add utmp timer to mainloop");
457
utmp_data->timer_fd = fd;
462
int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
463
struct lxc_utmp *utmp_data)
467
DEBUG("Clearing utmp shutdown timer");
469
result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd);
471
SYSERROR("failed to del utmp timer from mainloop");
473
/* shutdown timer_fd */
474
close(utmp_data->timer_fd);
475
utmp_data->timer_fd = -1;