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
34 #include <sys/inotify.h>
35 #include <sys/ioctl.h>
36 #include <sys/timerfd.h>
51 /* This file watches the /var/run/utmp file in the container
52 * (that should probably be configurable)
53 * We use inotify to put a watch on the /var/run directory for
54 * create and modify events. These can trigger a read of the
55 * utmp file looking for runlevel changes. If a runlevel change
56 * to reboot or halt states is detected, we set up an itimer to
57 * regularly check for the container shutdown, and reboot or halt
58 * as appropriate when we get down to 1 task remaining.
61 lxc_log_define(lxc_utmp
, lxc
);
64 struct lxc_handler
*handler
;
65 #define CONTAINER_STARTING 0
66 #define CONTAINER_REBOOTING 1
67 #define CONTAINER_HALTING 2
68 #define CONTAINER_RUNNING 4
71 int prev_runlevel
, curr_runlevel
;
74 typedef void (*lxc_mainloop_timer_t
) (void *data
);
76 static int utmp_get_runlevel(struct lxc_utmp
*utmp_data
);
77 static int utmp_get_ntasks(struct lxc_handler
*handler
);
78 static int utmp_shutdown_handler(int fd
, void *data
,
79 struct lxc_epoll_descr
*descr
);
80 static int lxc_utmp_add_timer(struct lxc_epoll_descr
*descr
,
81 lxc_mainloop_callback_t callback
, void *data
);
82 static int lxc_utmp_del_timer(struct lxc_epoll_descr
*descr
,
83 struct lxc_utmp
*utmp_data
);
85 static int utmp_handler(int fd
, void *data
, struct lxc_epoll_descr
*descr
)
87 struct inotify_event
*ie
;
88 int size
, ret
, length
;
90 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
93 * we're monitoring a directory. ie->name is not included in
94 * sizeof(struct inotify_event) if we don't read it all at once,
95 * read gives us EINVAL, so we read and cast to struct ie
97 char buffer
[MAXPATHLEN
];
99 if (ioctl(fd
, FIONREAD
, &size
) < 0) {
100 SYSERROR("cannot determine the size of this notification");
104 if (read(fd
, buffer
, size
) < 0) {
105 SYSERROR("failed to read notification");
109 ie
= (struct inotify_event
*)buffer
;
113 if (ie
->mask
& IN_UNMOUNT
) {
114 DEBUG("watched directory removed");
118 SYSERROR("inotify event with no name (mask %d)", ie
->mask
);
124 DEBUG("got inotify event %d for %s", ie
->mask
, ie
->name
);
126 length
= (4 < ie
->len
) ? 4 : ie
->len
;
128 /* only care about utmp */
130 if (strncmp(ie
->name
, "utmp", length
))
133 if (ie
->mask
& (IN_MODIFY
| IN_CREATE
))
134 ret
= utmp_get_runlevel(utmp_data
);
139 /* container halting, from running or starting state */
140 if (utmp_data
->curr_runlevel
== '0'
141 && ((utmp_data
->container_state
== CONTAINER_RUNNING
)
142 || (utmp_data
->container_state
== CONTAINER_STARTING
))) {
143 utmp_data
->container_state
= CONTAINER_HALTING
;
144 if (utmp_data
->timer_fd
== -1)
145 lxc_utmp_add_timer(descr
, utmp_shutdown_handler
, data
);
146 DEBUG("Container halting");
150 /* container rebooting, from running or starting state */
151 if (utmp_data
->curr_runlevel
== '6'
152 && ((utmp_data
->container_state
== CONTAINER_RUNNING
)
153 || (utmp_data
->container_state
== CONTAINER_STARTING
))) {
154 utmp_data
->container_state
= CONTAINER_REBOOTING
;
155 if (utmp_data
->timer_fd
== -1)
156 lxc_utmp_add_timer(descr
, utmp_shutdown_handler
, data
);
157 DEBUG("Container rebooting");
161 /* normal operation, running, from starting state. */
162 if (utmp_data
->curr_runlevel
> '0' && utmp_data
->curr_runlevel
< '6') {
163 utmp_data
->container_state
= CONTAINER_RUNNING
;
164 if (utmp_data
->timer_fd
> 0)
165 lxc_utmp_del_timer(descr
, utmp_data
);
166 DEBUG("Container running");
174 static int utmp_get_runlevel(struct lxc_utmp
*utmp_data
)
177 char path
[MAXPATHLEN
];
178 struct lxc_handler
*handler
= utmp_data
->handler
;
180 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/run/utmp",
181 handler
->pid
) > MAXPATHLEN
) {
182 ERROR("path is too long");
186 if (!access(path
, F_OK
) && !utmpxname(path
))
189 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/var/run/utmp",
190 handler
->pid
) > MAXPATHLEN
) {
191 ERROR("path is too long");
195 if (utmpxname(path
)) {
196 SYSERROR("failed to 'utmpxname'");
204 while ((utmpx
= getutxent())) {
206 if (utmpx
->ut_type
== RUN_LVL
) {
207 utmp_data
->prev_runlevel
= utmpx
->ut_pid
/ 256;
208 utmp_data
->curr_runlevel
= utmpx
->ut_pid
% 256;
209 DEBUG("utmp handler - run level is %c/%c",
210 utmp_data
->prev_runlevel
,
211 utmp_data
->curr_runlevel
);
220 static int utmp_get_ntasks(struct lxc_handler
*handler
)
224 ntasks
= lxc_cgroup_nrtasks(handler
->name
);
227 ERROR("failed to get the number of tasks");
231 DEBUG("there are %d tasks running", ntasks
);
236 int lxc_utmp_mainloop_add(struct lxc_epoll_descr
*descr
,
237 struct lxc_handler
*handler
)
239 char path
[MAXPATHLEN
];
240 char path2
[MAXPATHLEN
];
242 struct lxc_utmp
*utmp_data
;
244 /* We set up a watch for the /var/run directory. We're only interested
245 * in utmp at the moment, but want to watch for delete and create
248 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/run",
249 handler
->pid
) > MAXPATHLEN
) {
250 ERROR("path is too long");
253 if (snprintf(path2
, MAXPATHLEN
, "/proc/%d/root/run/utmp",
254 handler
->pid
) > MAXPATHLEN
) {
255 ERROR("path is too long");
258 if (!access(path2
, F_OK
))
261 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/var/run",
262 handler
->pid
) > MAXPATHLEN
) {
263 ERROR("path is too long");
267 if (access(path
, F_OK
)) {
268 WARN("'%s' not found", path
);
274 utmp_data
= (struct lxc_utmp
*)malloc(sizeof(struct lxc_utmp
));
276 if (NULL
== utmp_data
) {
277 SYSERROR("failed to malloc handler utmp_data");
281 memset(utmp_data
, 0, sizeof(struct lxc_utmp
));
285 SYSERROR("failed to inotify_init");
289 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
)) {
290 SYSERROR("failed to set inotify fd to close-on-exec");
295 wd
= inotify_add_watch(fd
, path
, IN_MODIFY
| IN_CREATE
);
297 SYSERROR("failed to add watch for '%s'", path
);
301 utmp_data
->handler
= handler
;
302 utmp_data
->container_state
= CONTAINER_STARTING
;
303 utmp_data
->timer_fd
= -1;
304 utmp_data
->prev_runlevel
= 'N';
305 utmp_data
->curr_runlevel
= 'N';
307 if (lxc_mainloop_add_handler
308 (descr
, fd
, utmp_handler
, (void *)utmp_data
)) {
309 SYSERROR("failed to add mainloop");
313 DEBUG("Added '%s' to inotifywatch", path
);
323 static int utmp_shutdown_handler(int fd
, void *data
,
324 struct lxc_epoll_descr
*descr
)
328 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
329 struct lxc_handler
*handler
= utmp_data
->handler
;
330 struct lxc_conf
*conf
= handler
->conf
;
331 uint64_t expirations
;
333 /* read and clear notifications */
334 nread
= read(fd
, &expirations
, sizeof(expirations
));
336 SYSERROR("Failed to read timer notification");
338 ntasks
= utmp_get_ntasks(handler
);
340 if (ntasks
== 1 && (utmp_data
->container_state
== CONTAINER_HALTING
)) {
341 INFO("container has shutdown");
343 lxc_utmp_del_timer(descr
, utmp_data
);
345 kill(handler
->pid
, SIGKILL
);
348 if (ntasks
== 1 && (utmp_data
->container_state
== CONTAINER_REBOOTING
)) {
349 INFO("container has rebooted");
352 lxc_utmp_del_timer(descr
, utmp_data
);
353 /* this seems a bit rough. */
354 kill(handler
->pid
, SIGKILL
);
360 int lxc_utmp_add_timer(struct lxc_epoll_descr
*descr
,
361 lxc_mainloop_callback_t callback
, void *data
)
364 struct itimerspec timeout
;
365 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
367 fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
| TFD_CLOEXEC
);
369 SYSERROR("failed to create timer");
373 DEBUG("Setting up utmp shutdown timer");
375 /* set a one second timeout. Repeated. */
376 timeout
.it_value
.tv_sec
= 1;
377 timeout
.it_value
.tv_nsec
= 0;
379 timeout
.it_interval
.tv_sec
= 1;
380 timeout
.it_interval
.tv_nsec
= 0;
382 result
= timerfd_settime(fd
, 0, &timeout
, NULL
);
385 SYSERROR("timerfd_settime:");
389 if (lxc_mainloop_add_handler(descr
, fd
, callback
, utmp_data
)) {
390 SYSERROR("failed to add utmp timer to mainloop");
395 utmp_data
->timer_fd
= fd
;
400 int lxc_utmp_del_timer(struct lxc_epoll_descr
*descr
,
401 struct lxc_utmp
*utmp_data
)
405 DEBUG("Clearing utmp shutdown timer");
407 result
= lxc_mainloop_del_handler(descr
, utmp_data
->timer_fd
);
409 SYSERROR("failed to del utmp timer from mainloop");
411 /* shutdown timer_fd */
412 close(utmp_data
->timer_fd
);
413 utmp_data
->timer_fd
= -1;