The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/1723
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Closes #1616. Signed-off-by: Christian Brauner <christian.brau...@ubuntu.com>
From 7a1c38d6214728d79b9342e42af9b03c73b7bf03 Mon Sep 17 00:00:00 2001 From: Christian Brauner <christian.brau...@ubuntu.com> Date: Mon, 31 Jul 2017 23:04:54 +0200 Subject: [PATCH] start: remove utmp watch Closes #1616. Signed-off-by: Christian Brauner <christian.brau...@ubuntu.com> --- src/lxc/Makefile.am | 1 - src/lxc/lxcutmp.c | 489 ---------------------------------------------------- src/lxc/lxcutmp.h | 34 ---- src/lxc/start.c | 102 ----------- 4 files changed, 626 deletions(-) delete mode 100644 src/lxc/lxcutmp.c delete mode 100644 src/lxc/lxcutmp.h diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index a55103ec5..1937278b4 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -127,7 +127,6 @@ liblxc_la_SOURCES = \ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ - lxcutmp.c lxcutmp.h \ lxclock.h lxclock.c \ lxccontainer.c lxccontainer.h \ version.h \ diff --git a/src/lxc/lxcutmp.c b/src/lxc/lxcutmp.c deleted file mode 100644 index ba65654ad..000000000 --- a/src/lxc/lxcutmp.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano <daniel.lezcano at free.fr> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <signal.h> -#include <stdlib.h> -#include <fcntl.h> -#include <sys/inotify.h> -#include <sys/ioctl.h> -#ifdef HAVE_SYS_TIMERFD_H -#include <sys/timerfd.h> -#else -#include <sys/syscall.h> -#ifndef TFD_NONBLOCK -#define TFD_NONBLOCK O_NONBLOCK -#endif - -#ifndef TFD_CLOEXEC -#define TFD_CLOEXEC O_CLOEXEC -#endif -static int timerfd_create (clockid_t __clock_id, int __flags) { - return syscall(__NR_timerfd_create, __clock_id, __flags); -} - -static int timerfd_settime (int __ufd, int __flags, - const struct itimerspec *__utmr, - struct itimerspec *__otmr) { - - return syscall(__NR_timerfd_settime, __ufd, __flags, - __utmr, __otmr); -} - -#endif - -#include "conf.h" -#include "cgroup.h" -#include "start.h" -#include "mainloop.h" -#include "lxc.h" -#include "log.h" - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#ifdef HAVE_UTMPX_H -#include <utmpx.h> -#ifndef HAVE_UTMPXNAME -#include <utmp.h> -#endif - -#else -#include <utmp.h> - -#ifndef RUN_LVL -#define RUN_LVL 1 -#endif - -static void setutxent(void) { - return setutent(); -} - -static struct utmp * getutxent (void) { - return (struct utmp *) getutent(); -} - -static void endutxent (void) { -#ifdef IS_BIONIC - /* bionic isn't exporting endutend */ - return; -#else - return endutent(); -#endif -} -#endif - -#ifndef HAVE_UTMPXNAME -static int utmpxname(const char *file) { - int result; - result = utmpname(file); - -#ifdef IS_BIONIC - /* Yeah bionic is that weird */ - result = result - 1; -#endif - - return result; -} -#endif - -#undef __USE_GNU - -/* This file watches the /var/run/utmp file in the container - * (that should probably be configurable) - * We use inotify to put a watch on the /var/run directory for - * create and modify events. These can trigger a read of the - * utmp file looking for runlevel changes. If a runlevel change - * to reboot or halt states is detected, we set up an itimer to - * regularly check for the container shutdown, and reboot or halt - * as appropriate when we get down to 1 task remaining. - */ - -lxc_log_define(lxc_utmp, lxc); - -struct lxc_utmp { - struct lxc_handler *handler; -#define CONTAINER_STARTING 0 -#define CONTAINER_REBOOTING 1 -#define CONTAINER_HALTING 2 -#define CONTAINER_RUNNING 4 - char container_state; - int timer_fd; - int prev_runlevel, curr_runlevel; -}; - -typedef void (*lxc_mainloop_timer_t) (void *data); - -static int utmp_get_runlevel(struct lxc_utmp *utmp_data); -static int utmp_get_ntasks(struct lxc_handler *handler); -static int utmp_shutdown_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr); -static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, - lxc_mainloop_callback_t callback, void *data); -static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, - struct lxc_utmp *utmp_data); - -static int utmp_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr) -{ - struct inotify_event *ie; - int size, ret, length; - - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - - /* - * we're monitoring a directory. ie->name is not included in - * sizeof(struct inotify_event) if we don't read it all at once, - * read gives us EINVAL, so we read and cast to struct ie - */ - char buffer[MAXPATHLEN]; - - if (ioctl(fd, FIONREAD, &size) < 0) { - SYSERROR("cannot determine the size of this notification"); - return -1; - } - - if (read(fd, buffer, size) < size) { - SYSERROR("failed to read notification"); - return -1; - } - - ie = (struct inotify_event *)buffer; - - if (ie->len <= 0) { - - if (ie->mask & IN_UNMOUNT) { - DEBUG("watched directory removed"); - goto out; - } - - SYSERROR("inotify event with no name (mask %d)", ie->mask); - return -1; - } - - ret = 0; - - DEBUG("got inotify event %d for %s", ie->mask, ie->name); - - length = (4 < ie->len) ? 4 : ie->len; - - /* only care about utmp */ - - if (strncmp(ie->name, "utmp", length)) - return 0; - - if (ie->mask & (IN_MODIFY | IN_CREATE)) - ret = utmp_get_runlevel(utmp_data); - - if (ret < 0) - goto out; - - /* container halting, from running or starting state */ - if (utmp_data->curr_runlevel == '0' - && ((utmp_data->container_state == CONTAINER_RUNNING) - || (utmp_data->container_state == CONTAINER_STARTING))) { - utmp_data->container_state = CONTAINER_HALTING; - if (utmp_data->timer_fd == -1) - lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); - DEBUG("Container halting"); - goto out; - } - - /* container rebooting, from running or starting state */ - if (utmp_data->curr_runlevel == '6' - && ((utmp_data->container_state == CONTAINER_RUNNING) - || (utmp_data->container_state == CONTAINER_STARTING))) { - utmp_data->container_state = CONTAINER_REBOOTING; - if (utmp_data->timer_fd == -1) - lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); - DEBUG("Container rebooting"); - goto out; - } - - /* normal operation, running, from starting state. */ - if (utmp_data->curr_runlevel > '0' && utmp_data->curr_runlevel < '6') { - utmp_data->container_state = CONTAINER_RUNNING; - if (utmp_data->timer_fd > 0) - lxc_utmp_del_timer(descr, utmp_data); - DEBUG("Container running"); - goto out; - } - -out: - return 0; -} - -static int utmp_get_runlevel(struct lxc_utmp *utmp_data) -{ - #if HAVE_UTMPX_H - struct utmpx *utmpx; - #else - struct utmp *utmpx; - #endif - char path[MAXPATHLEN]; - struct lxc_handler *handler = utmp_data->handler; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (!access(path, F_OK) && !utmpxname(path)) - goto utmp_ok; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (utmpxname(path)) { - SYSERROR("failed to 'utmpxname'"); - return -1; - } - -utmp_ok: - - setutxent(); - - while ((utmpx = getutxent())) { - - if (utmpx->ut_type == RUN_LVL) { - utmp_data->prev_runlevel = utmpx->ut_pid / 256; - utmp_data->curr_runlevel = utmpx->ut_pid % 256; - DEBUG("utmp handler - run level is %c/%c", - utmp_data->prev_runlevel, - utmp_data->curr_runlevel); - } - } - - endutxent(); - - return 0; -} - -static int utmp_get_ntasks(struct lxc_handler *handler) -{ - int ntasks; - - ntasks = cgroup_nrtasks(handler); - - if (ntasks < 0) { - ERROR("failed to get the number of tasks"); - return -1; - } - - DEBUG("there are %d tasks running", ntasks); - - return ntasks; -} - -int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr, - struct lxc_handler *handler) -{ - char path[MAXPATHLEN]; - char path2[MAXPATHLEN]; - int fd, wd; - struct lxc_utmp *utmp_data; - - /* We set up a watch for the /var/run directory. We're only interested - * in utmp at the moment, but want to watch for delete and create - * events as well. - */ - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - if (!access(path2, F_OK)) - goto run_ok; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (access(path, F_OK)) { - WARN("'%s' not found", path); - return 0; - } - -run_ok: - - utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp)); - - if (NULL == utmp_data) { - SYSERROR("failed to malloc handler utmp_data"); - return -1; - } - - memset(utmp_data, 0, sizeof(struct lxc_utmp)); - - fd = inotify_init(); - if (fd < 0) { - SYSERROR("failed to inotify_init"); - goto out; - } - - if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { - SYSERROR("failed to set inotify fd to close-on-exec"); - goto out_close; - - } - - wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE); - if (wd < 0) { - SYSERROR("failed to add watch for '%s'", path); - goto out_close; - } - - utmp_data->handler = handler; - utmp_data->container_state = CONTAINER_STARTING; - utmp_data->timer_fd = -1; - utmp_data->prev_runlevel = 'N'; - utmp_data->curr_runlevel = 'N'; - - if (lxc_mainloop_add_handler - (descr, fd, utmp_handler, (void *)utmp_data)) { - SYSERROR("failed to add mainloop"); - goto out_close; - } - - DEBUG("Added '%s' to inotifywatch", path); - - return 0; -out_close: - close(fd); -out: - free(utmp_data); - return -1; -} - -static int utmp_shutdown_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr) -{ - int ntasks; - ssize_t nread; - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - struct lxc_handler *handler = utmp_data->handler; - struct lxc_conf *conf = handler->conf; - uint64_t expirations; - - /* read and clear notifications */ - nread = read(fd, &expirations, sizeof(expirations)); - if (nread < 0) - SYSERROR("Failed to read timer notification"); - - ntasks = utmp_get_ntasks(handler); - - if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) { - INFO("container has shutdown"); - /* shutdown timer */ - lxc_utmp_del_timer(descr, utmp_data); - - kill(handler->pid, SIGKILL); - } - - if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) { - INFO("container has rebooted"); - conf->reboot = 1; - /* shutdown timer */ - lxc_utmp_del_timer(descr, utmp_data); - /* this seems a bit rough. */ - kill(handler->pid, SIGKILL); - } - return 0; - -} - -int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, - lxc_mainloop_callback_t callback, void *data) -{ - int fd, result; - struct itimerspec timeout; - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - - fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - if (fd < 0) { - SYSERROR("failed to create timer"); - return -1; - } - - DEBUG("Setting up utmp shutdown timer"); - - /* set a one second timeout. Repeated. */ - timeout.it_value.tv_sec = 1; - timeout.it_value.tv_nsec = 0; - - timeout.it_interval.tv_sec = 1; - timeout.it_interval.tv_nsec = 0; - - result = timerfd_settime(fd, 0, &timeout, NULL); - - if (result < 0) { - SYSERROR("timerfd_settime:"); - return -1; - } - - if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) { - SYSERROR("failed to add utmp timer to mainloop"); - close(fd); - return -1; - } - - utmp_data->timer_fd = fd; - - return 0; -} - -int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, - struct lxc_utmp *utmp_data) -{ - int result; - - DEBUG("Clearing utmp shutdown timer"); - - result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd); - if (result < 0) - SYSERROR("failed to del utmp timer from mainloop"); - - /* shutdown timer_fd */ - close(utmp_data->timer_fd); - utmp_data->timer_fd = -1; - - if (result < 0) - return -1; - else - return 0; -} diff --git a/src/lxc/lxcutmp.h b/src/lxc/lxcutmp.h deleted file mode 100644 index 062ecdf38..000000000 --- a/src/lxc/lxcutmp.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano <daniel.lezcano at free.fr> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_LXCUTMP_H -#define __LXC_LXCUTMP_H - -#include "config.h" - -struct lxc_handler; -struct lxc_epoll_descr; - -int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr, - struct lxc_handler *handler); -#endif diff --git a/src/lxc/start.c b/src/lxc/start.c index 9fa208f2b..390dc56b8 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -75,7 +75,6 @@ #include "lxccontainer.h" #include "lxclock.h" #include "lxcseccomp.h" -#include "lxcutmp.h" #include "mainloop.h" #include "monitor.h" #include "namespace.h" @@ -489,16 +488,6 @@ int lxc_poll(const char *name, struct lxc_handler *handler) goto out_mainloop_open; } - if (handler->conf->need_utmp_watch) { - #if HAVE_LIBCAP - if (lxc_utmp_mainloop_add(&descr, handler)) { - ERROR("Failed to add utmp handler to LXC mainloop."); - goto out_mainloop_open; - } - #else - DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support."); - #endif - } TRACE("lxc mainloop is ready"); return lxc_mainloop(&descr, -1); @@ -791,75 +780,6 @@ void lxc_abort(const char *name, struct lxc_handler *handler) } } -#include <sys/reboot.h> -#include <linux/reboot.h> - -/* reboot(LINUX_REBOOT_CMD_CAD_ON) will return -EINVAL in a child pid namespace - * if container reboot support exists. Otherwise, it will either succeed or - * return -EPERM. - */ -static int container_reboot_supported(void *arg) -{ - int *cmd = arg; - int ret; - - ret = reboot(*cmd); - if (ret == -1 && errno == EINVAL) - return 1; - return 0; -} - -static int must_drop_cap_sys_boot(struct lxc_conf *conf) -{ - FILE *f; - int ret, cmd, v, flags; - long stack_size = 4096; - void *stack = alloca(stack_size); - int status; - pid_t pid; - - f = fopen("/proc/sys/kernel/ctrl-alt-del", "r"); - if (!f) { - DEBUG("failed to open /proc/sys/kernel/ctrl-alt-del"); - return 1; - } - - ret = fscanf(f, "%d", &v); - fclose(f); - if (ret != 1) { - DEBUG("Failed to read /proc/sys/kernel/ctrl-alt-del."); - return 1; - } - cmd = v ? LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF; - - flags = CLONE_NEWPID | SIGCHLD; - if (!lxc_list_empty(&conf->id_map)) - flags |= CLONE_NEWUSER; - -#ifdef __ia64__ - pid = __clone2(container_reboot_supported, stack, stack_size, flags, &cmd); -#else - stack += stack_size; - pid = clone(container_reboot_supported, stack, flags, &cmd); -#endif - if (pid < 0) { - if (flags & CLONE_NEWUSER) - ERROR("Failed to clone (%#x): %s (includes CLONE_NEWUSER).", flags, strerror(errno)); - else - ERROR("Failed to clone (%#x): %s.", flags, strerror(errno)); - return -1; - } - if (wait(&status) < 0) { - SYSERROR("Unexpected wait error: %s.", strerror(errno)); - return -1; - } - - if (WEXITSTATUS(status) != 1) - return 1; - - return 0; -} - /* netpipe is used in the unprivileged case to transfer the ifindexes from * parent to child */ @@ -979,16 +899,6 @@ static int do_start(void *data) goto out_warn_father; } - #if HAVE_LIBCAP - if (handler->conf->need_utmp_watch) { - if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) { - SYSERROR("Failed to remove the CAP_SYS_BOOT capability."); - goto out_warn_father; - } - DEBUG("Dropped the CAP_SYS_BOOT capability."); - } - #endif - ret = snprintf(path, sizeof(path), "%s/dev/null", handler->conf->rootfs.mount); if (ret < 0 || ret >= sizeof(path)) goto out_warn_father; @@ -1563,17 +1473,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler, handler->backgrounded = backgrounded; handler->netnsfd = -1; - if (must_drop_cap_sys_boot(handler->conf)) { - #if HAVE_LIBCAP - DEBUG("Dropping CAP_SYS_BOOT capability."); - #else - DEBUG("Not dropping CAP_SYS_BOOT capability as capabilities aren't supported."); - #endif - } else { - DEBUG("Not dropping CAP_SYS_BOOT or watching utmp."); - handler->conf->need_utmp_watch = 0; - } - if (!attach_block_device(handler->conf)) { ERROR("Failed to attach block device."); goto out_fini_nonet; @@ -1708,7 +1607,6 @@ int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler, .argv = argv, }; - handler->conf->need_utmp_watch = 1; return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded); }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel