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
30 #include <sys/inotify.h>
31 #include <sys/ioctl.h>
32 #include <sys/timerfd.h>
44 /* This file watches the /var/run/utmp file in the container
45 * (that should probably be configurable)
46 * We use inotify to put a watch on the /var/run directory for
47 * create and modify events. These can trigger a read of the
48 * utmp file looking for runlevel changes. If a runlevel change
49 * to reboot or halt states is detected, we set up an itimer to
50 * regularly check for the container shutdown, and reboot or halt
51 * as appropriate when we get down to 1 task remaining.
54 lxc_log_define(lxc_utmp
, lxc
);
57 struct lxc_handler
*handler
;
58 #define CONTAINER_STARTING 0
59 #define CONTAINER_REBOOTING 1
60 #define CONTAINER_HALTING 2
61 #define CONTAINER_RUNNING 4
64 int prev_runlevel
, curr_runlevel
;
67 typedef void (*lxc_mainloop_timer_t
) (void *data
);
69 static int utmp_get_runlevel(struct lxc_utmp
*utmp_data
);
70 static int utmp_get_ntasks(struct lxc_handler
*handler
);
71 static int utmp_shutdown_handler(int fd
, void *data
,
72 struct lxc_epoll_descr
*descr
);
73 static int lxc_utmp_add_timer(struct lxc_epoll_descr
*descr
,
74 lxc_mainloop_callback_t callback
, void *data
);
75 static int lxc_utmp_del_timer(struct lxc_epoll_descr
*descr
,
76 struct lxc_utmp
*utmp_data
);
78 static int utmp_handler(int fd
, void *data
, struct lxc_epoll_descr
*descr
)
80 struct inotify_event
*ie
;
81 int size
, ret
, length
;
83 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
86 * we're monitoring a directory. ie->name is not included in
87 * sizeof(struct inotify_event) if we don't read it all at once,
88 * read gives us EINVAL, so we read and cast to struct ie
90 char buffer
[MAXPATHLEN
];
92 if (ioctl(fd
, FIONREAD
, &size
) < 0) {
93 SYSERROR("cannot determine the size of this notification");
97 if (read(fd
, buffer
, size
) < 0) {
98 SYSERROR("failed to read notification");
102 ie
= (struct inotify_event
*)buffer
;
106 if (ie
->mask
& IN_UNMOUNT
) {
107 DEBUG("watched directory removed");
111 SYSERROR("inotify event with no name (mask %d)", ie
->mask
);
117 DEBUG("got inotify event %d for %s", ie
->mask
, ie
->name
);
119 length
= (4 < ie
->len
) ? 4 : ie
->len
;
121 /* only care about utmp */
123 if (strncmp(ie
->name
, "utmp", length
))
126 if (ie
->mask
& (IN_MODIFY
| IN_CREATE
))
127 ret
= utmp_get_runlevel(utmp_data
);
132 /* container halting, from running or starting state */
133 if (utmp_data
->curr_runlevel
== '0'
134 && ((utmp_data
->container_state
== CONTAINER_RUNNING
)
135 || (utmp_data
->container_state
== CONTAINER_STARTING
))) {
136 utmp_data
->container_state
= CONTAINER_HALTING
;
137 if (utmp_data
->timer_fd
== -1)
138 lxc_utmp_add_timer(descr
, utmp_shutdown_handler
, data
);
139 DEBUG("Container halting");
143 /* container rebooting, from running or starting state */
144 if (utmp_data
->curr_runlevel
== '6'
145 && ((utmp_data
->container_state
== CONTAINER_RUNNING
)
146 || (utmp_data
->container_state
== CONTAINER_STARTING
))) {
147 utmp_data
->container_state
= CONTAINER_REBOOTING
;
148 if (utmp_data
->timer_fd
== -1)
149 lxc_utmp_add_timer(descr
, utmp_shutdown_handler
, data
);
150 DEBUG("Container rebooting");
154 /* normal operation, running, from starting state. */
155 if (utmp_data
->curr_runlevel
> '0' && utmp_data
->curr_runlevel
< '6') {
156 utmp_data
->container_state
= CONTAINER_RUNNING
;
157 if (utmp_data
->timer_fd
> 0)
158 lxc_utmp_del_timer(descr
, utmp_data
);
159 DEBUG("Container running");
167 static int utmp_get_runlevel(struct lxc_utmp
*utmp_data
)
170 char path
[MAXPATHLEN
];
171 struct lxc_handler
*handler
= utmp_data
->handler
;
173 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/run/utmp",
174 handler
->pid
) > MAXPATHLEN
) {
175 ERROR("path is too long");
179 if (!access(path
, F_OK
) && !utmpxname(path
))
182 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/var/run/utmp",
183 handler
->pid
) > MAXPATHLEN
) {
184 ERROR("path is too long");
188 if (utmpxname(path
)) {
189 SYSERROR("failed to 'utmpxname'");
197 while ((utmpx
= getutxent())) {
199 if (utmpx
->ut_type
== RUN_LVL
) {
200 utmp_data
->prev_runlevel
= utmpx
->ut_pid
/ 256;
201 utmp_data
->curr_runlevel
= utmpx
->ut_pid
% 256;
202 DEBUG("utmp handler - run level is %c/%c",
203 utmp_data
->prev_runlevel
,
204 utmp_data
->curr_runlevel
);
213 static int utmp_get_ntasks(struct lxc_handler
*handler
)
217 ntasks
= lxc_cgroup_nrtasks(handler
->name
);
220 ERROR("failed to get the number of tasks");
224 DEBUG("there are %d tasks running", ntasks
);
229 int lxc_utmp_mainloop_add(struct lxc_epoll_descr
*descr
,
230 struct lxc_handler
*handler
)
232 char path
[MAXPATHLEN
];
233 char path2
[MAXPATHLEN
];
235 struct lxc_utmp
*utmp_data
;
237 /* We set up a watch for the /var/run directory. We're only interested
238 * in utmp at the moment, but want to watch for delete and create
241 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/run",
242 handler
->pid
) > MAXPATHLEN
) {
243 ERROR("path is too long");
246 if (snprintf(path2
, MAXPATHLEN
, "/proc/%d/root/run/utmp",
247 handler
->pid
) > MAXPATHLEN
) {
248 ERROR("path is too long");
251 if (!access(path2
, F_OK
))
254 if (snprintf(path
, MAXPATHLEN
, "/proc/%d/root/var/run",
255 handler
->pid
) > MAXPATHLEN
) {
256 ERROR("path is too long");
260 if (access(path
, F_OK
)) {
261 WARN("'%s' not found", path
);
267 utmp_data
= (struct lxc_utmp
*)malloc(sizeof(struct lxc_utmp
));
269 if (NULL
== utmp_data
) {
270 SYSERROR("failed to malloc handler utmp_data");
274 memset(utmp_data
, 0, sizeof(struct lxc_utmp
));
278 SYSERROR("failed to inotify_init");
282 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
)) {
283 SYSERROR("failed to set inotify fd to close-on-exec");
288 wd
= inotify_add_watch(fd
, path
, IN_MODIFY
| IN_CREATE
);
290 SYSERROR("failed to add watch for '%s'", path
);
294 utmp_data
->handler
= handler
;
295 utmp_data
->container_state
= CONTAINER_STARTING
;
296 utmp_data
->timer_fd
= -1;
297 utmp_data
->prev_runlevel
= 'N';
298 utmp_data
->curr_runlevel
= 'N';
300 if (lxc_mainloop_add_handler
301 (descr
, fd
, utmp_handler
, (void *)utmp_data
)) {
302 SYSERROR("failed to add mainloop");
306 DEBUG("Added '%s' to inotifywatch", path
);
316 static int utmp_shutdown_handler(int fd
, void *data
,
317 struct lxc_epoll_descr
*descr
)
321 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
322 struct lxc_handler
*handler
= utmp_data
->handler
;
323 struct lxc_conf
*conf
= handler
->conf
;
324 uint64_t expirations
;
326 /* read and clear notifications */
327 nread
= read(fd
, &expirations
, sizeof(expirations
));
329 SYSERROR("Failed to read timer notification");
331 ntasks
= utmp_get_ntasks(handler
);
333 if (ntasks
== 1 && (utmp_data
->container_state
== CONTAINER_HALTING
)) {
334 INFO("container has shutdown");
336 lxc_utmp_del_timer(descr
, utmp_data
);
338 kill(handler
->pid
, SIGKILL
);
341 if (ntasks
== 1 && (utmp_data
->container_state
== CONTAINER_REBOOTING
)) {
342 INFO("container has rebooted");
345 lxc_utmp_del_timer(descr
, utmp_data
);
346 /* this seems a bit rough. */
347 kill(handler
->pid
, SIGKILL
);
353 int lxc_utmp_add_timer(struct lxc_epoll_descr
*descr
,
354 lxc_mainloop_callback_t callback
, void *data
)
357 struct itimerspec timeout
;
358 struct lxc_utmp
*utmp_data
= (struct lxc_utmp
*)data
;
360 fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
| TFD_CLOEXEC
);
362 SYSERROR("failed to create timer");
366 DEBUG("Setting up utmp shutdown timer");
368 /* set a one second timeout. Repeated. */
369 timeout
.it_value
.tv_sec
= 1;
370 timeout
.it_value
.tv_nsec
= 0;
372 timeout
.it_interval
.tv_sec
= 1;
373 timeout
.it_interval
.tv_nsec
= 0;
375 result
= timerfd_settime(fd
, 0, &timeout
, NULL
);
378 SYSERROR("timerfd_settime:");
382 if (lxc_mainloop_add_handler(descr
, fd
, callback
, utmp_data
)) {
383 SYSERROR("failed to add utmp timer to mainloop");
388 utmp_data
->timer_fd
= fd
;
393 int lxc_utmp_del_timer(struct lxc_epoll_descr
*descr
,
394 struct lxc_utmp
*utmp_data
)
398 DEBUG("Clearing utmp shutdown timer");
400 result
= lxc_mainloop_del_handler(descr
, utmp_data
->timer_fd
);
402 SYSERROR("failed to del utmp timer from mainloop");
404 /* shutdown timer_fd */
405 close(utmp_data
->timer_fd
);
406 utmp_data
->timer_fd
= -1;