]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/utmp.c
Makefile.am: use right .h file name for seccomp
[mirror_lxc.git] / src / lxc / utmp.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 <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <sys/inotify.h>
31 #include <sys/ioctl.h>
32 #include <sys/timerfd.h>
33
34 #include "conf.h"
35 #include "cgroup.h"
36 #include "start.h"
37 #include "mainloop.h"
38 #include "lxc.h"
39 #include "log.h"
40 #define __USE_GNU
41 #include <utmpx.h>
42 #undef __USE_GNU
43
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.
52 */
53
54 lxc_log_define(lxc_utmp, lxc);
55
56 struct lxc_utmp {
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
62 char container_state;
63 int timer_fd;
64 int prev_runlevel, curr_runlevel;
65 };
66
67 typedef void (*lxc_mainloop_timer_t) (void *data);
68
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);
77
78 static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
79 {
80 struct inotify_event *ie;
81 int size, ret, length;
82
83 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
84
85 /*
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
89 */
90 char buffer[MAXPATHLEN];
91
92 if (ioctl(fd, FIONREAD, &size) < 0) {
93 SYSERROR("cannot determine the size of this notification");
94 return -1;
95 }
96
97 if (read(fd, buffer, size) < 0) {
98 SYSERROR("failed to read notification");
99 return -1;
100 }
101
102 ie = (struct inotify_event *)buffer;
103
104 if (ie->len <= 0) {
105
106 if (ie->mask & IN_UNMOUNT) {
107 DEBUG("watched directory removed");
108 goto out;
109 }
110
111 SYSERROR("inotify event with no name (mask %d)", ie->mask);
112 return -1;
113 }
114
115 ret = 0;
116
117 DEBUG("got inotify event %d for %s", ie->mask, ie->name);
118
119 length = (4 < ie->len) ? 4 : ie->len;
120
121 /* only care about utmp */
122
123 if (strncmp(ie->name, "utmp", length))
124 return 0;
125
126 if (ie->mask & (IN_MODIFY | IN_CREATE))
127 ret = utmp_get_runlevel(utmp_data);
128
129 if (ret < 0)
130 goto out;
131
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");
140 goto out;
141 }
142
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");
151 goto out;
152 }
153
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");
160 goto out;
161 }
162
163 out:
164 return 0;
165 }
166
167 static int utmp_get_runlevel(struct lxc_utmp *utmp_data)
168 {
169 struct utmpx *utmpx;
170 char path[MAXPATHLEN];
171 struct lxc_handler *handler = utmp_data->handler;
172
173 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp",
174 handler->pid) > MAXPATHLEN) {
175 ERROR("path is too long");
176 return -1;
177 }
178
179 if (!access(path, F_OK) && !utmpxname(path))
180 goto utmp_ok;
181
182 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp",
183 handler->pid) > MAXPATHLEN) {
184 ERROR("path is too long");
185 return -1;
186 }
187
188 if (utmpxname(path)) {
189 SYSERROR("failed to 'utmpxname'");
190 return -1;
191 }
192
193 utmp_ok:
194
195 setutxent();
196
197 while ((utmpx = getutxent())) {
198
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);
205 }
206 }
207
208 endutxent();
209
210 return 0;
211 }
212
213 static int utmp_get_ntasks(struct lxc_handler *handler)
214 {
215 int ntasks;
216
217 ntasks = lxc_cgroup_nrtasks(handler->name);
218
219 if (ntasks < 0) {
220 ERROR("failed to get the number of tasks");
221 return -1;
222 }
223
224 DEBUG("there are %d tasks running", ntasks);
225
226 return ntasks;
227 }
228
229 int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
230 struct lxc_handler *handler)
231 {
232 char path[MAXPATHLEN];
233 char path2[MAXPATHLEN];
234 int fd, wd;
235 struct lxc_utmp *utmp_data;
236
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
239 * events as well.
240 */
241 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run",
242 handler->pid) > MAXPATHLEN) {
243 ERROR("path is too long");
244 return -1;
245 }
246 if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp",
247 handler->pid) > MAXPATHLEN) {
248 ERROR("path is too long");
249 return -1;
250 }
251 if (!access(path2, F_OK))
252 goto run_ok;
253
254 if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run",
255 handler->pid) > MAXPATHLEN) {
256 ERROR("path is too long");
257 return -1;
258 }
259
260 if (access(path, F_OK)) {
261 WARN("'%s' not found", path);
262 return 0;
263 }
264
265 run_ok:
266
267 utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp));
268
269 if (NULL == utmp_data) {
270 SYSERROR("failed to malloc handler utmp_data");
271 return -1;
272 }
273
274 memset(utmp_data, 0, sizeof(struct lxc_utmp));
275
276 fd = inotify_init();
277 if (fd < 0) {
278 SYSERROR("failed to inotify_init");
279 goto out;
280 }
281
282 if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
283 SYSERROR("failed to set inotify fd to close-on-exec");
284 goto out_close;
285
286 }
287
288 wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
289 if (wd < 0) {
290 SYSERROR("failed to add watch for '%s'", path);
291 goto out_close;
292 }
293
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';
299
300 if (lxc_mainloop_add_handler
301 (descr, fd, utmp_handler, (void *)utmp_data)) {
302 SYSERROR("failed to add mainloop");
303 goto out_close;
304 }
305
306 DEBUG("Added '%s' to inotifywatch", path);
307
308 return 0;
309 out_close:
310 close(fd);
311 out:
312 free(utmp_data);
313 return -1;
314 }
315
316 static int utmp_shutdown_handler(int fd, void *data,
317 struct lxc_epoll_descr *descr)
318 {
319 int ntasks;
320 ssize_t nread;
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;
325
326 /* read and clear notifications */
327 nread = read(fd, &expirations, sizeof(expirations));
328 if (nread < 0)
329 SYSERROR("Failed to read timer notification");
330
331 ntasks = utmp_get_ntasks(handler);
332
333 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) {
334 INFO("container has shutdown");
335 /* shutdown timer */
336 lxc_utmp_del_timer(descr, utmp_data);
337
338 kill(handler->pid, SIGKILL);
339 }
340
341 if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) {
342 INFO("container has rebooted");
343 conf->reboot = 1;
344 /* shutdown timer */
345 lxc_utmp_del_timer(descr, utmp_data);
346 /* this seems a bit rough. */
347 kill(handler->pid, SIGKILL);
348 }
349 return 0;
350
351 }
352
353 int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
354 lxc_mainloop_callback_t callback, void *data)
355 {
356 int fd, result;
357 struct itimerspec timeout;
358 struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
359
360 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
361 if (fd < 0) {
362 SYSERROR("failed to create timer");
363 return -1;
364 }
365
366 DEBUG("Setting up utmp shutdown timer");
367
368 /* set a one second timeout. Repeated. */
369 timeout.it_value.tv_sec = 1;
370 timeout.it_value.tv_nsec = 0;
371
372 timeout.it_interval.tv_sec = 1;
373 timeout.it_interval.tv_nsec = 0;
374
375 result = timerfd_settime(fd, 0, &timeout, NULL);
376
377 if (result < 0) {
378 SYSERROR("timerfd_settime:");
379 return -1;
380 }
381
382 if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) {
383 SYSERROR("failed to add utmp timer to mainloop");
384 close(fd);
385 return -1;
386 }
387
388 utmp_data->timer_fd = fd;
389
390 return 0;
391 }
392
393 int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
394 struct lxc_utmp *utmp_data)
395 {
396 int result;
397
398 DEBUG("Clearing utmp shutdown timer");
399
400 result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd);
401 if (result < 0)
402 SYSERROR("failed to del utmp timer from mainloop");
403
404 /* shutdown timer_fd */
405 close(utmp_data->timer_fd);
406 utmp_data->timer_fd = -1;
407
408 if (result < 0)
409 return -1;
410 else
411 return 0;
412 }