]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxcutmp.c
Rename utmp.c/utmp.h to lxcutmp.c/lxcutmp.h
[mirror_lxc.git] / src / lxc / lxcutmp.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <dlezcano at fr.ibm.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
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "config.h"
25
26 #ifdef HAVE_UTMPX_H
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <sys/inotify.h>
35 #include <sys/ioctl.h>
36 #include <sys/timerfd.h>
37
38 #include "conf.h"
39 #include "cgroup.h"
40 #include "start.h"
41 #include "mainloop.h"
42 #include "lxc.h"
43 #include "log.h"
44
45 #ifndef __USE_GNU
46 #define __USE_GNU
47 #endif
48 #include <utmpx.h>
49 #undef __USE_GNU
50
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.
59 */
60
61 lxc_log_define(lxc_utmp, lxc);
62
63 struct lxc_utmp {
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
69 char container_state;
70 int timer_fd;
71 int prev_runlevel, curr_runlevel;
72 };
73
74 typedef void (*lxc_mainloop_timer_t) (void *data);
75
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);
84
85 static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
86 {
87 struct inotify_event *ie;
88 int size, ret, length;
89
90 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
91
92 /*
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
96 */
97 char buffer[MAXPATHLEN];
98
99 if (ioctl(fd, FIONREAD, &size) < 0) {
100 SYSERROR("cannot determine the size of this notification");
101 return -1;
102 }
103
104 if (read(fd, buffer, size) < 0) {
105 SYSERROR("failed to read notification");
106 return -1;
107 }
108
109 ie = (struct inotify_event *)buffer;
110
111 if (ie->len <= 0) {
112
113 if (ie->mask & IN_UNMOUNT) {
114 DEBUG("watched directory removed");
115 goto out;
116 }
117
118 SYSERROR("inotify event with no name (mask %d)", ie->mask);
119 return -1;
120 }
121
122 ret = 0;
123
124 DEBUG("got inotify event %d for %s", ie->mask, ie->name);
125
126 length = (4 < ie->len) ? 4 : ie->len;
127
128 /* only care about utmp */
129
130 if (strncmp(ie->name, "utmp", length))
131 return 0;
132
133 if (ie->mask & (IN_MODIFY | IN_CREATE))
134 ret = utmp_get_runlevel(utmp_data);
135
136 if (ret < 0)
137 goto out;
138
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");
147 goto out;
148 }
149
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");
158 goto out;
159 }
160
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");
167 goto out;
168 }
169
170 out:
171 return 0;
172 }
173
174 static int utmp_get_runlevel(struct lxc_utmp *utmp_data)
175 {
176 struct utmpx *utmpx;
177 char path[MAXPATHLEN];
178 struct lxc_handler *handler = utmp_data->handler;
179
180 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp",
181 handler->pid) > MAXPATHLEN) {
182 ERROR("path is too long");
183 return -1;
184 }
185
186 if (!access(path, F_OK) && !utmpxname(path))
187 goto utmp_ok;
188
189 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp",
190 handler->pid) > MAXPATHLEN) {
191 ERROR("path is too long");
192 return -1;
193 }
194
195 if (utmpxname(path)) {
196 SYSERROR("failed to 'utmpxname'");
197 return -1;
198 }
199
200 utmp_ok:
201
202 setutxent();
203
204 while ((utmpx = getutxent())) {
205
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);
212 }
213 }
214
215 endutxent();
216
217 return 0;
218 }
219
220 static int utmp_get_ntasks(struct lxc_handler *handler)
221 {
222 int ntasks;
223
224 ntasks = lxc_cgroup_nrtasks(handler->name);
225
226 if (ntasks < 0) {
227 ERROR("failed to get the number of tasks");
228 return -1;
229 }
230
231 DEBUG("there are %d tasks running", ntasks);
232
233 return ntasks;
234 }
235
236 int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
237 struct lxc_handler *handler)
238 {
239 char path[MAXPATHLEN];
240 char path2[MAXPATHLEN];
241 int fd, wd;
242 struct lxc_utmp *utmp_data;
243
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
246 * events as well.
247 */
248 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run",
249 handler->pid) > MAXPATHLEN) {
250 ERROR("path is too long");
251 return -1;
252 }
253 if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp",
254 handler->pid) > MAXPATHLEN) {
255 ERROR("path is too long");
256 return -1;
257 }
258 if (!access(path2, F_OK))
259 goto run_ok;
260
261 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run",
262 handler->pid) > MAXPATHLEN) {
263 ERROR("path is too long");
264 return -1;
265 }
266
267 if (access(path, F_OK)) {
268 WARN("'%s' not found", path);
269 return 0;
270 }
271
272 run_ok:
273
274 utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp));
275
276 if (NULL == utmp_data) {
277 SYSERROR("failed to malloc handler utmp_data");
278 return -1;
279 }
280
281 memset(utmp_data, 0, sizeof(struct lxc_utmp));
282
283 fd = inotify_init();
284 if (fd < 0) {
285 SYSERROR("failed to inotify_init");
286 goto out;
287 }
288
289 if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
290 SYSERROR("failed to set inotify fd to close-on-exec");
291 goto out_close;
292
293 }
294
295 wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
296 if (wd < 0) {
297 SYSERROR("failed to add watch for '%s'", path);
298 goto out_close;
299 }
300
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';
306
307 if (lxc_mainloop_add_handler
308 (descr, fd, utmp_handler, (void *)utmp_data)) {
309 SYSERROR("failed to add mainloop");
310 goto out_close;
311 }
312
313 DEBUG("Added '%s' to inotifywatch", path);
314
315 return 0;
316 out_close:
317 close(fd);
318 out:
319 free(utmp_data);
320 return -1;
321 }
322
323 static int utmp_shutdown_handler(int fd, void *data,
324 struct lxc_epoll_descr *descr)
325 {
326 int ntasks;
327 ssize_t nread;
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;
332
333 /* read and clear notifications */
334 nread = read(fd, &expirations, sizeof(expirations));
335 if (nread < 0)
336 SYSERROR("Failed to read timer notification");
337
338 ntasks = utmp_get_ntasks(handler);
339
340 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) {
341 INFO("container has shutdown");
342 /* shutdown timer */
343 lxc_utmp_del_timer(descr, utmp_data);
344
345 kill(handler->pid, SIGKILL);
346 }
347
348 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) {
349 INFO("container has rebooted");
350 conf->reboot = 1;
351 /* shutdown timer */
352 lxc_utmp_del_timer(descr, utmp_data);
353 /* this seems a bit rough. */
354 kill(handler->pid, SIGKILL);
355 }
356 return 0;
357
358 }
359
360 int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
361 lxc_mainloop_callback_t callback, void *data)
362 {
363 int fd, result;
364 struct itimerspec timeout;
365 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
366
367 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
368 if (fd < 0) {
369 SYSERROR("failed to create timer");
370 return -1;
371 }
372
373 DEBUG("Setting up utmp shutdown timer");
374
375 /* set a one second timeout. Repeated. */
376 timeout.it_value.tv_sec = 1;
377 timeout.it_value.tv_nsec = 0;
378
379 timeout.it_interval.tv_sec = 1;
380 timeout.it_interval.tv_nsec = 0;
381
382 result = timerfd_settime(fd, 0, &timeout, NULL);
383
384 if (result < 0) {
385 SYSERROR("timerfd_settime:");
386 return -1;
387 }
388
389 if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) {
390 SYSERROR("failed to add utmp timer to mainloop");
391 close(fd);
392 return -1;
393 }
394
395 utmp_data->timer_fd = fd;
396
397 return 0;
398 }
399
400 int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
401 struct lxc_utmp *utmp_data)
402 {
403 int result;
404
405 DEBUG("Clearing utmp shutdown timer");
406
407 result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd);
408 if (result < 0)
409 SYSERROR("failed to del utmp timer from mainloop");
410
411 /* shutdown timer_fd */
412 close(utmp_data->timer_fd);
413 utmp_data->timer_fd = -1;
414
415 if (result < 0)
416 return -1;
417 else
418 return 0;
419 }
420
421 #endif