~xnox/ubuntu/saucy/lxc/dep8

« back to all changes in this revision

Viewing changes to src/lxc/lxcutmp.c

  • Committer: Stéphane Graber
  • Date: 2013-02-18 15:20:18 UTC
  • mto: This revision was merged to the branch mainline in revision 190.
  • Revision ID: stgraber@ubuntu.com-20130218152018-ls2gi9hkqs2kuhj8
Tags: upstream-0.9.0~alpha3
Import upstream version 0.9.0~alpha3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include <stdio.h>
 
27
#include <unistd.h>
 
28
#include <errno.h>
 
29
#include <signal.h>
 
30
#include <stdlib.h>
 
31
#include <fcntl.h>
 
32
#include <sys/inotify.h>
 
33
#include <sys/ioctl.h>
 
34
#ifdef HAVE_SYS_TIMERFD_H
 
35
#include <sys/timerfd.h>
 
36
#else
 
37
#include <sys/syscall.h>
 
38
#ifndef TFD_NONBLOCK
 
39
#define TFD_NONBLOCK O_NONBLOCK
 
40
#endif
 
41
 
 
42
#ifndef TFD_CLOEXEC
 
43
#define TFD_CLOEXEC O_CLOEXEC
 
44
#endif
 
45
static int timerfd_create (clockid_t __clock_id, int __flags) {
 
46
        return syscall(__NR_timerfd_create, __clock_id, __flags);
 
47
}
 
48
 
 
49
static int timerfd_settime (int __ufd, int __flags,
 
50
                            const struct itimerspec *__utmr,
 
51
                            struct itimerspec *__otmr) {
 
52
 
 
53
        return syscall(__NR_timerfd_settime, __ufd, __flags,
 
54
                            __utmr, __otmr);
 
55
}
 
56
 
 
57
#endif
 
58
 
 
59
#include "conf.h"
 
60
#include "cgroup.h"
 
61
#include "start.h"
 
62
#include "mainloop.h"
 
63
#include "lxc.h"
 
64
#include "log.h"
 
65
 
 
66
#ifndef __USE_GNU
 
67
#define __USE_GNU
 
68
#endif
 
69
#ifdef HAVE_UTMPX_H
 
70
#include <utmpx.h>
 
71
#else
 
72
#include <utmp.h>
 
73
 
 
74
#ifndef RUN_LVL
 
75
#define RUN_LVL 1
 
76
#endif
 
77
 
 
78
static int utmpxname(const char *file) {
 
79
        int result;
 
80
        result = utmpname(file);
 
81
 
 
82
#ifdef IS_BIONIC
 
83
        /* Yeah bionic is that weird */
 
84
        result = result - 1;
 
85
#endif
 
86
 
 
87
        return result;
 
88
}
 
89
 
 
90
static void setutxent(void) {
 
91
        return setutent();
 
92
}
 
93
 
 
94
static struct utmp * getutxent (void) {
 
95
        return (struct utmp *) getutent();
 
96
}
 
97
 
 
98
static void endutxent (void) {
 
99
#ifdef IS_BIONIC
 
100
        /* bionic isn't exporting endutend */
 
101
        return;
 
102
#else
 
103
        return endutent();
 
104
#endif
 
105
}
 
106
#endif
 
107
#undef __USE_GNU
 
108
 
 
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.
 
117
 */
 
118
 
 
119
lxc_log_define(lxc_utmp, lxc);
 
120
 
 
121
struct lxc_utmp {
 
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;
 
128
        int timer_fd;
 
129
        int prev_runlevel, curr_runlevel;
 
130
};
 
131
 
 
132
typedef void (*lxc_mainloop_timer_t) (void *data);
 
133
 
 
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);
 
142
 
 
143
static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
 
144
{
 
145
        struct inotify_event *ie;
 
146
        int size, ret, length;
 
147
 
 
148
        struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
 
149
 
 
150
        /*
 
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
 
154
         */
 
155
        char buffer[MAXPATHLEN];
 
156
 
 
157
        if (ioctl(fd, FIONREAD, &size) < 0) {
 
158
                SYSERROR("cannot determine the size of this notification");
 
159
                return -1;
 
160
        }
 
161
 
 
162
        if (read(fd, buffer, size) < 0) {
 
163
                SYSERROR("failed to read notification");
 
164
                return -1;
 
165
        }
 
166
 
 
167
        ie = (struct inotify_event *)buffer;
 
168
 
 
169
        if (ie->len <= 0) {
 
170
 
 
171
                if (ie->mask & IN_UNMOUNT) {
 
172
                        DEBUG("watched directory removed");
 
173
                        goto out;
 
174
                }
 
175
 
 
176
                SYSERROR("inotify event with no name (mask %d)", ie->mask);
 
177
                return -1;
 
178
        }
 
179
 
 
180
        ret = 0;
 
181
 
 
182
        DEBUG("got inotify event %d for %s", ie->mask, ie->name);
 
183
 
 
184
        length = (4 < ie->len) ? 4 : ie->len;
 
185
 
 
186
        /* only care about utmp */
 
187
 
 
188
        if (strncmp(ie->name, "utmp", length))
 
189
                return 0;
 
190
 
 
191
        if (ie->mask & (IN_MODIFY | IN_CREATE))
 
192
                ret = utmp_get_runlevel(utmp_data);
 
193
 
 
194
        if (ret < 0)
 
195
                goto out;
 
196
 
 
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");
 
205
                goto out;
 
206
        }
 
207
 
 
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");
 
216
                goto out;
 
217
        }
 
218
 
 
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");
 
225
                goto out;
 
226
        }
 
227
 
 
228
out:
 
229
        return 0;
 
230
}
 
231
 
 
232
static int utmp_get_runlevel(struct lxc_utmp *utmp_data)
 
233
{
 
234
        #if HAVE_UTMPX_H
 
235
        struct utmpx *utmpx;
 
236
        #else
 
237
        struct utmp *utmpx;
 
238
        #endif
 
239
        char path[MAXPATHLEN];
 
240
        struct lxc_handler *handler = utmp_data->handler;
 
241
 
 
242
        if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp",
 
243
                     handler->pid) > MAXPATHLEN) {
 
244
                ERROR("path is too long");
 
245
                return -1;
 
246
        }
 
247
 
 
248
        if (!access(path, F_OK) && !utmpxname(path))
 
249
                goto utmp_ok;
 
250
 
 
251
        if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp",
 
252
                     handler->pid) > MAXPATHLEN) {
 
253
                ERROR("path is too long");
 
254
                return -1;
 
255
        }
 
256
 
 
257
        if (utmpxname(path)) {
 
258
                SYSERROR("failed to 'utmpxname'");
 
259
                return -1;
 
260
        }
 
261
 
 
262
utmp_ok:
 
263
 
 
264
        setutxent();
 
265
 
 
266
        while ((utmpx = getutxent())) {
 
267
 
 
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);
 
274
                }
 
275
        }
 
276
 
 
277
        endutxent();
 
278
 
 
279
        return 0;
 
280
}
 
281
 
 
282
static int utmp_get_ntasks(struct lxc_handler *handler)
 
283
{
 
284
        int ntasks;
 
285
 
 
286
        ntasks = lxc_cgroup_nrtasks(handler->name);
 
287
 
 
288
        if (ntasks < 0) {
 
289
                ERROR("failed to get the number of tasks");
 
290
                return -1;
 
291
        }
 
292
 
 
293
        DEBUG("there are %d tasks running", ntasks);
 
294
 
 
295
        return ntasks;
 
296
}
 
297
 
 
298
int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
 
299
                          struct lxc_handler *handler)
 
300
{
 
301
        char path[MAXPATHLEN];
 
302
        char path2[MAXPATHLEN];
 
303
        int fd, wd;
 
304
        struct lxc_utmp *utmp_data;
 
305
 
 
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
 
308
         * events as well.
 
309
         */
 
310
        if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run",
 
311
                     handler->pid) > MAXPATHLEN) {
 
312
                ERROR("path is too long");
 
313
                return -1;
 
314
        }
 
315
        if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp",
 
316
                     handler->pid) > MAXPATHLEN) {
 
317
                ERROR("path is too long");
 
318
                return -1;
 
319
        }
 
320
        if (!access(path2, F_OK))
 
321
                goto run_ok;
 
322
 
 
323
        if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run",
 
324
                     handler->pid) > MAXPATHLEN) {
 
325
                ERROR("path is too long");
 
326
                return -1;
 
327
        }
 
328
 
 
329
        if (access(path, F_OK)) {
 
330
                WARN("'%s' not found", path);
 
331
                return 0;
 
332
        }
 
333
 
 
334
run_ok:
 
335
 
 
336
        utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp));
 
337
 
 
338
        if (NULL == utmp_data) {
 
339
                SYSERROR("failed to malloc handler utmp_data");
 
340
                return -1;
 
341
        }
 
342
 
 
343
        memset(utmp_data, 0, sizeof(struct lxc_utmp));
 
344
 
 
345
        fd = inotify_init();
 
346
        if (fd < 0) {
 
347
                SYSERROR("failed to inotify_init");
 
348
                goto out;
 
349
        }
 
350
 
 
351
        if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
 
352
                SYSERROR("failed to set inotify fd to close-on-exec");
 
353
                goto out_close;
 
354
 
 
355
        }
 
356
 
 
357
        wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
 
358
        if (wd < 0) {
 
359
                SYSERROR("failed to add watch for '%s'", path);
 
360
                goto out_close;
 
361
        }
 
362
 
 
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';
 
368
 
 
369
        if (lxc_mainloop_add_handler
 
370
            (descr, fd, utmp_handler, (void *)utmp_data)) {
 
371
                SYSERROR("failed to add mainloop");
 
372
                goto out_close;
 
373
        }
 
374
 
 
375
        DEBUG("Added '%s' to inotifywatch", path);
 
376
 
 
377
        return 0;
 
378
out_close:
 
379
        close(fd);
 
380
out:
 
381
        free(utmp_data);
 
382
        return -1;
 
383
}
 
384
 
 
385
static int utmp_shutdown_handler(int fd, void *data,
 
386
                                 struct lxc_epoll_descr *descr)
 
387
{
 
388
        int ntasks;
 
389
        ssize_t nread;
 
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;
 
394
 
 
395
        /* read and clear notifications */
 
396
        nread = read(fd, &expirations, sizeof(expirations));
 
397
        if (nread < 0)
 
398
                SYSERROR("Failed to read timer notification");
 
399
 
 
400
        ntasks = utmp_get_ntasks(handler);
 
401
 
 
402
        if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) {
 
403
                INFO("container has shutdown");
 
404
                /* shutdown timer */
 
405
                lxc_utmp_del_timer(descr, utmp_data);
 
406
 
 
407
                kill(handler->pid, SIGKILL);
 
408
        }
 
409
 
 
410
        if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) {
 
411
                INFO("container has rebooted");
 
412
                conf->reboot = 1;
 
413
                /* shutdown timer */
 
414
                lxc_utmp_del_timer(descr, utmp_data);
 
415
                /* this seems a bit rough. */
 
416
                kill(handler->pid, SIGKILL);
 
417
        }
 
418
        return 0;
 
419
 
 
420
}
 
421
 
 
422
int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
 
423
                       lxc_mainloop_callback_t callback, void *data)
 
424
{
 
425
        int fd, result;
 
426
        struct itimerspec timeout;
 
427
        struct lxc_utmp *utmp_data = (struct lxc_utmp *)data;
 
428
 
 
429
        fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
 
430
        if (fd < 0) {
 
431
                SYSERROR("failed to create timer");
 
432
                return -1;
 
433
        }
 
434
 
 
435
        DEBUG("Setting up utmp shutdown timer");
 
436
 
 
437
        /* set a one second timeout. Repeated. */
 
438
        timeout.it_value.tv_sec = 1;
 
439
        timeout.it_value.tv_nsec = 0;
 
440
 
 
441
        timeout.it_interval.tv_sec = 1;
 
442
        timeout.it_interval.tv_nsec = 0;
 
443
 
 
444
        result = timerfd_settime(fd, 0, &timeout, NULL);
 
445
 
 
446
        if (result < 0) {
 
447
                SYSERROR("timerfd_settime:");
 
448
                return -1;
 
449
        }
 
450
 
 
451
        if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) {
 
452
                SYSERROR("failed to add utmp timer to mainloop");
 
453
                close(fd);
 
454
                return -1;
 
455
        }
 
456
 
 
457
        utmp_data->timer_fd = fd;
 
458
 
 
459
        return 0;
 
460
}
 
461
 
 
462
int lxc_utmp_del_timer(struct lxc_epoll_descr *descr,
 
463
                       struct lxc_utmp *utmp_data)
 
464
{
 
465
        int result;
 
466
 
 
467
        DEBUG("Clearing utmp shutdown timer");
 
468
 
 
469
        result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd);
 
470
        if (result < 0)
 
471
                SYSERROR("failed to del utmp timer from mainloop");
 
472
 
 
473
        /* shutdown timer_fd */
 
474
        close(utmp_data->timer_fd);
 
475
        utmp_data->timer_fd = -1;
 
476
 
 
477
        if (result < 0)
 
478
                return -1;
 
479
        else
 
480
                return 0;
 
481
}