[lxc-devel] [lxc/master] 2016 07 31/append to search path
brauner on Github
lxc-bot at linuxcontainers.org
Sun Jul 31 10:50:54 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 412 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160731/7e3575bc/attachment.bin>
-------------- next part --------------
From d8e4899290758cdafb2c33d65b5d319a5fe1ca9b Mon Sep 17 00:00:00 2001
From: Christian Brauner <cbrauner at suse.de>
Date: Sun, 31 Jul 2016 12:04:28 +0200
Subject: [PATCH 1/3] bdev: add subdirectories to search path
This allows us to avoid using relative includes which is cleaner in the long
run when we create subdirectories for other components of liblxc.
Signed-off-by: Christian Brauner <cbrauner at suse.de>
---
src/lxc/Makefile.am | 8 +++++---
src/lxc/cgfs.c | 2 +-
src/lxc/cgfsng.c | 2 +-
src/lxc/cgmanager.c | 2 +-
src/lxc/conf.c | 6 +++---
src/lxc/confile.c | 2 +-
src/lxc/criu.c | 2 +-
src/lxc/lxc_copy.c | 2 +-
src/lxc/lxc_create.c | 2 +-
src/lxc/lxc_snapshot.c | 2 +-
src/lxc/lxccontainer.c | 6 +++---
src/lxc/start.c | 2 +-
12 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index f361c3f..5078b5c 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -138,8 +138,7 @@ liblxc_so_SOURCES += ../include/getline.c ../include/getline.h
endif
endif
-AM_CFLAGS=-I$(top_srcdir)/src \
- -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
+AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DLXCPATH=\"$(LXCPATH)\" \
-DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \
-DLXCINITDIR=\"$(LXCINITDIR)\" \
@@ -152,7 +151,10 @@ AM_CFLAGS=-I$(top_srcdir)/src \
-DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \
-DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \
-DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \
- -DSBINDIR=\"$(SBINDIR)\"
+ -DSBINDIR=\"$(SBINDIR)\" \
+ -I $(top_srcdir)/src \
+ -I $(top_srcdir)/src/lxc \
+ -I $(top_srcdir)/src/lxc/bdev
if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR
diff --git a/src/lxc/cgfs.c b/src/lxc/cgfs.c
index c493d58..6b2ac7e 100644
--- a/src/lxc/cgfs.c
+++ b/src/lxc/cgfs.c
@@ -39,12 +39,12 @@
#include <netinet/in.h>
#include <net/if.h>
+#include "bdev.h"
#include "error.h"
#include "commands.h"
#include "list.h"
#include "conf.h"
#include "utils.h"
-#include "bdev/bdev.h"
#include "log.h"
#include "cgroup.h"
#include "start.h"
diff --git a/src/lxc/cgfsng.c b/src/lxc/cgfsng.c
index 16a9457..27c2721 100644
--- a/src/lxc/cgfsng.c
+++ b/src/lxc/cgfsng.c
@@ -43,11 +43,11 @@
#include <dirent.h>
#include <grp.h>
+#include "bdev.h"
#include "log.h"
#include "cgroup.h"
#include "utils.h"
#include "commands.h"
-#include "bdev/bdev.h"
lxc_log_define(lxc_cgfsng, lxc);
diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
index c387b00..4da891d 100644
--- a/src/lxc/cgmanager.c
+++ b/src/lxc/cgmanager.c
@@ -41,12 +41,12 @@
#include <net/if.h>
#include <poll.h>
+#include "bdev.h"
#include "error.h"
#include "commands.h"
#include "list.h"
#include "conf.h"
#include "utils.h"
-#include "bdev/bdev.h"
#include "log.h"
#include "cgroup.h"
#include "start.h"
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 1e330ac..9fea993 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -66,6 +66,7 @@
#include <net/if.h>
#include <libgen.h>
+#include "bdev.h"
#include "network.h"
#include "error.h"
#include "af_unix.h"
@@ -74,9 +75,8 @@
#include "conf.h"
#include "log.h"
#include "caps.h" /* for lxc_caps_last_cap() */
-#include "bdev/bdev.h"
-#include "bdev/lxcaufs.h"
-#include "bdev/lxcoverlay.h"
+#include "lxcaufs.h"
+#include "lxcoverlay.h"
#include "cgroup.h"
#include "lxclock.h"
#include "namespace.h"
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 645cac0..db89caa 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -40,10 +40,10 @@
#include <dirent.h>
#include <syslog.h>
+#include "bdev.h"
#include "parse.h"
#include "config.h"
#include "confile.h"
-#include "bdev/bdev.h"
#include "utils.h"
#include "log.h"
#include "conf.h"
diff --git a/src/lxc/criu.c b/src/lxc/criu.c
index bf9a87a..65998ed 100644
--- a/src/lxc/criu.c
+++ b/src/lxc/criu.c
@@ -35,7 +35,7 @@
#include "config.h"
-#include "bdev/bdev.h"
+#include "bdev.h"
#include "cgroup.h"
#include "conf.h"
#include "commands.h"
diff --git a/src/lxc/lxc_copy.c b/src/lxc/lxc_copy.c
index 69de7d5..9f653e3 100644
--- a/src/lxc/lxc_copy.c
+++ b/src/lxc/lxc_copy.c
@@ -37,6 +37,7 @@
#include <lxc/lxccontainer.h>
#include "attach.h"
+#include "bdev.h"
#include "log.h"
#include "confile.h"
#include "arguments.h"
@@ -44,7 +45,6 @@
#include "conf.h"
#include "state.h"
#include "utils.h"
-#include "bdev/bdev.h"
#ifndef HAVE_GETSUBOPT
#include <../include/getsubopt.h>
diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c
index 5fd5a29..4b0e9d7 100644
--- a/src/lxc/lxc_create.c
+++ b/src/lxc/lxc_create.c
@@ -27,10 +27,10 @@
#include <sys/types.h>
#include "arguments.h"
+#include "bdev.h"
#include "log.h"
#include "lxc.h"
#include "utils.h"
-#include "bdev/bdev.h"
lxc_log_define(lxc_create_ui, lxc);
diff --git a/src/lxc/lxc_snapshot.c b/src/lxc/lxc_snapshot.c
index 147101e..8f44891 100644
--- a/src/lxc/lxc_snapshot.c
+++ b/src/lxc/lxc_snapshot.c
@@ -27,9 +27,9 @@
#include <lxc/lxccontainer.h>
+#include "bdev.h"
#include "lxc.h"
#include "log.h"
-#include "bdev/bdev.h"
#include "arguments.h"
#include "utils.h"
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 8ea0005..24ded6f 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -39,9 +39,9 @@
#include <sys/wait.h>
#include "attach.h"
-#include "bdev/bdev.h"
-#include "bdev/lxcoverlay.h"
-#include "bdev/lxcbtrfs.h"
+#include "bdev.h"
+#include "lxcoverlay.h"
+#include "lxcbtrfs.h"
#include "cgroup.h"
#include "conf.h"
#include "config.h"
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 1ba0f9b..1ce9903 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -55,6 +55,7 @@
#endif
#include "af_unix.h"
+#include "bdev.h"
#include "caps.h"
#include "cgroup.h"
#include "commands.h"
@@ -71,7 +72,6 @@
#include "start.h"
#include "sync.h"
#include "utils.h"
-#include "bdev/bdev.h"
#include "lsm/lsm.h"
lxc_log_define(lxc_start, lxc);
From 8d3b0a65b2f71e2c8c4261b18890ac7457e5e5fb Mon Sep 17 00:00:00 2001
From: Christian Brauner <cbrauner at suse.de>
Date: Sun, 31 Jul 2016 12:21:58 +0200
Subject: [PATCH 2/3] cgroups: move cgroup files to common subfolder
Signed-off-by: Christian Brauner <cbrauner at suse.de>
---
src/lxc/Makefile.am | 13 +-
src/lxc/cgfs.c | 2655 -------------------------------------------
src/lxc/cgfsng.c | 1691 ---------------------------
src/lxc/cgmanager.c | 1672 ---------------------------
src/lxc/cgroup.c | 245 ----
src/lxc/cgroup.h | 89 --
src/lxc/cgroups/cgfs.c | 2655 +++++++++++++++++++++++++++++++++++++++++++
src/lxc/cgroups/cgfsng.c | 1691 +++++++++++++++++++++++++++
src/lxc/cgroups/cgmanager.c | 1672 +++++++++++++++++++++++++++
src/lxc/cgroups/cgroup.c | 245 ++++
src/lxc/cgroups/cgroup.h | 89 ++
11 files changed, 6359 insertions(+), 6358 deletions(-)
delete mode 100644 src/lxc/cgfs.c
delete mode 100644 src/lxc/cgfsng.c
delete mode 100644 src/lxc/cgmanager.c
delete mode 100644 src/lxc/cgroup.c
delete mode 100644 src/lxc/cgroup.h
create mode 100644 src/lxc/cgroups/cgfs.c
create mode 100644 src/lxc/cgroups/cgfsng.c
create mode 100644 src/lxc/cgroups/cgmanager.c
create mode 100644 src/lxc/cgroups/cgroup.c
create mode 100644 src/lxc/cgroups/cgroup.h
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 5078b5c..9a87c15 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -17,8 +17,8 @@ noinst_HEADERS = \
bdev/lxcrbd.h \
bdev/lxcrsync.h \
bdev/lxczfs.h \
+ cgroups/cgroup.h \
caps.h \
- cgroup.h \
conf.h \
console.h \
error.h \
@@ -80,6 +80,9 @@ liblxc_so_SOURCES = \
bdev/lxcrbd.c bdev/lxcrbd.h \
bdev/lxcrsync.c bdev/lxcrsync.h \
bdev/lxczfs.c bdev/lxczfs.h \
+ cgroups/cgfs.c \
+ cgroups/cgfsng.c \
+ cgroups/cgroup.c cgroups/cgroup.h \
commands.c commands.h \
start.c start.h \
execute.c \
@@ -88,9 +91,6 @@ liblxc_so_SOURCES = \
freezer.c \
error.h error.c \
parse.c parse.h \
- cgfs.c \
- cgfsng.c \
- cgroup.c cgroup.h \
lxc.h \
initutils.c initutils.h \
utils.c utils.h \
@@ -122,7 +122,7 @@ liblxc_so_SOURCES = \
$(LSM_SOURCES)
if ENABLE_CGMANAGER
-liblxc_so_SOURCES += cgmanager.c
+liblxc_so_SOURCES += cgroups/cgmanager.c
endif
if IS_BIONIC
@@ -154,7 +154,8 @@ AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DSBINDIR=\"$(SBINDIR)\" \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/lxc \
- -I $(top_srcdir)/src/lxc/bdev
+ -I $(top_srcdir)/src/lxc/bdev \
+ -I $(top_srcdir)/src/lxc/cgroups
if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR
diff --git a/src/lxc/cgfs.c b/src/lxc/cgfs.c
deleted file mode 100644
index 6b2ac7e..0000000
--- a/src/lxc/cgfs.c
+++ /dev/null
@@ -1,2655 +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 <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <sys/inotify.h>
-#include <sys/mount.h>
-#include <netinet/in.h>
-#include <net/if.h>
-
-#include "bdev.h"
-#include "error.h"
-#include "commands.h"
-#include "list.h"
-#include "conf.h"
-#include "utils.h"
-#include "log.h"
-#include "cgroup.h"
-#include "start.h"
-#include "state.h"
-
-#if IS_BIONIC
-#include <../include/lxcmntent.h>
-#else
-#include <mntent.h>
-#endif
-
-struct cgroup_hierarchy;
-struct cgroup_meta_data;
-struct cgroup_mount_point;
-
-/*
- * cgroup_meta_data: the metadata about the cgroup infrastructure on this
- * host
- */
-struct cgroup_meta_data {
- ptrdiff_t ref; /* simple refcount */
- struct cgroup_hierarchy **hierarchies;
- struct cgroup_mount_point **mount_points;
- int maximum_hierarchy;
-};
-
-/*
- * cgroup_hierarchy: describes a single cgroup hierarchy
- * (may have multiple mount points)
- */
-struct cgroup_hierarchy {
- int index;
- bool used; /* false if the hierarchy should be ignored by lxc */
- char **subsystems;
- struct cgroup_mount_point *rw_absolute_mount_point;
- struct cgroup_mount_point *ro_absolute_mount_point;
- struct cgroup_mount_point **all_mount_points;
- size_t all_mount_point_capacity;
-};
-
-/*
- * cgroup_mount_point: a mount point to where a hierarchy
- * is mounted to
- */
-struct cgroup_mount_point {
- struct cgroup_hierarchy *hierarchy;
- char *mount_point;
- char *mount_prefix;
- bool read_only;
- bool need_cpuset_init;
-};
-
-/*
- * cgroup_process_info: describes the membership of a
- * process to the different cgroup
- * hierarchies
- *
- * Note this is the per-process info tracked by the cgfs_ops.
- * This is not used with cgmanager.
- */
-struct cgroup_process_info {
- struct cgroup_process_info *next;
- struct cgroup_meta_data *meta_ref;
- struct cgroup_hierarchy *hierarchy;
- char *cgroup_path;
- char *cgroup_path_sub;
- char **created_paths;
- size_t created_paths_capacity;
- size_t created_paths_count;
- struct cgroup_mount_point *designated_mount_point;
-};
-
-struct cgfs_data {
- char *name;
- const char *cgroup_pattern;
- struct cgroup_meta_data *meta;
- struct cgroup_process_info *info;
-};
-
-lxc_log_define(lxc_cgfs, lxc);
-
-static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta);
-static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list);
-static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp);
-static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h);
-static bool is_valid_cgroup(const char *name);
-static int create_cgroup(struct cgroup_mount_point *mp, const char *path);
-static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse,
- struct lxc_conf *conf);
-static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *path, const char *suffix);
-static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem);
-static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len);
-static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value);
-static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow);
-static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices);
-static int cgroup_recursive_task_count(const char *cgroup_path);
-static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path);
-static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path);
-
-static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist);
-static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data);
-static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data);
-
-/* free process membership information */
-static void lxc_cgroup_process_info_free(struct cgroup_process_info *info);
-static void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info,
- struct lxc_conf *conf);
-
-static struct cgroup_ops cgfs_ops;
-
-static int cgroup_rmdir(char *dirname)
-{
- struct dirent dirent, *direntp;
- int saved_errno = 0;
- DIR *dir;
- int ret, failed=0;
- char pathname[MAXPATHLEN];
-
- dir = opendir(dirname);
- if (!dir) {
- ERROR("%s: failed to open %s", __func__, dirname);
- return -1;
- }
-
- while (!readdir_r(dir, &dirent, &direntp)) {
- struct stat mystat;
- int rc;
-
- if (!direntp)
- break;
-
- if (!strcmp(direntp->d_name, ".") ||
- !strcmp(direntp->d_name, ".."))
- continue;
-
- rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
- if (rc < 0 || rc >= MAXPATHLEN) {
- ERROR("pathname too long");
- failed=1;
- if (!saved_errno)
- saved_errno = -ENOMEM;
- continue;
- }
- ret = lstat(pathname, &mystat);
- if (ret) {
- SYSERROR("%s: failed to stat %s", __func__, pathname);
- failed=1;
- if (!saved_errno)
- saved_errno = errno;
- continue;
- }
- if (S_ISDIR(mystat.st_mode)) {
- if (cgroup_rmdir(pathname) < 0) {
- if (!saved_errno)
- saved_errno = errno;
- failed=1;
- }
- }
- }
-
- if (rmdir(dirname) < 0) {
- SYSERROR("%s: failed to delete %s", __func__, dirname);
- if (!saved_errno)
- saved_errno = errno;
- failed=1;
- }
-
- ret = closedir(dir);
- if (ret) {
- SYSERROR("%s: failed to close directory %s", __func__, dirname);
- if (!saved_errno)
- saved_errno = errno;
- failed=1;
- }
-
- errno = saved_errno;
- return failed ? -1 : 0;
-}
-
-static int rmdir_wrapper(void *data)
-{
- char *path = data;
-
- if (setresgid(0,0,0) < 0)
- SYSERROR("Failed to setgid to 0");
- if (setresuid(0,0,0) < 0)
- SYSERROR("Failed to setuid to 0");
- if (setgroups(0, NULL) < 0)
- SYSERROR("Failed to clear groups");
-
- return cgroup_rmdir(path);
-}
-
-static struct cgroup_meta_data *lxc_cgroup_load_meta()
-{
- const char *cgroup_use = NULL;
- char **cgroup_use_list = NULL;
- struct cgroup_meta_data *md = NULL;
- int saved_errno;
-
- errno = 0;
- cgroup_use = lxc_global_config_value("lxc.cgroup.use");
- if (!cgroup_use && errno != 0)
- return NULL;
- if (cgroup_use) {
- cgroup_use_list = lxc_string_split_and_trim(cgroup_use, ',');
- if (!cgroup_use_list)
- return NULL;
- }
-
- md = lxc_cgroup_load_meta2((const char **)cgroup_use_list);
- saved_errno = errno;
- lxc_free_array((void **)cgroup_use_list, free);
- errno = saved_errno;
- return md;
-}
-
-/* Step 1: determine all kernel subsystems */
-static bool find_cgroup_subsystems(char ***kernel_subsystems)
-{
- FILE *proc_cgroups;
- bool bret = false;
- char *line = NULL;
- size_t sz = 0;
- size_t kernel_subsystems_count = 0;
- size_t kernel_subsystems_capacity = 0;
- int r;
-
- proc_cgroups = fopen_cloexec("/proc/cgroups", "r");
- if (!proc_cgroups)
- return false;
-
- while (getline(&line, &sz, proc_cgroups) != -1) {
- char *tab1;
- char *tab2;
- int hierarchy_number;
-
- if (line[0] == '#')
- continue;
- if (!line[0])
- continue;
-
- tab1 = strchr(line, '\t');
- if (!tab1)
- continue;
- *tab1++ = '\0';
- tab2 = strchr(tab1, '\t');
- if (!tab2)
- continue;
- *tab2 = '\0';
-
- tab2 = NULL;
- hierarchy_number = strtoul(tab1, &tab2, 10);
- if (!tab2 || *tab2)
- continue;
- (void)hierarchy_number;
-
- r = lxc_grow_array((void ***)kernel_subsystems, &kernel_subsystems_capacity, kernel_subsystems_count + 1, 12);
- if (r < 0)
- goto out;
- (*kernel_subsystems)[kernel_subsystems_count] = strdup(line);
- if (!(*kernel_subsystems)[kernel_subsystems_count])
- goto out;
- kernel_subsystems_count++;
- }
- bret = true;
-
-out:
- fclose(proc_cgroups);
- free(line);
- return bret;
-}
-
-/* Step 2: determine all hierarchies (by reading /proc/self/cgroup),
- * since mount points don't specify hierarchy number and
- * /proc/cgroups does not contain named hierarchies
- */
-static bool find_cgroup_hierarchies(struct cgroup_meta_data *meta_data,
- bool all_kernel_subsystems, bool all_named_subsystems,
- const char **subsystem_whitelist)
-{
- FILE *proc_self_cgroup;
- char *line = NULL;
- size_t sz = 0;
- int r;
- bool bret = false;
- size_t hierarchy_capacity = 0;
-
- proc_self_cgroup = fopen_cloexec("/proc/self/cgroup", "r");
- /* if for some reason (because of setns() and pid namespace for example),
- * /proc/self is not valid, we try /proc/1/cgroup... */
- if (!proc_self_cgroup)
- proc_self_cgroup = fopen_cloexec("/proc/1/cgroup", "r");
- if (!proc_self_cgroup)
- return false;
-
- while (getline(&line, &sz, proc_self_cgroup) != -1) {
- /* file format: hierarchy:subsystems:group,
- * we only extract hierarchy and subsystems
- * here */
- char *colon1;
- char *colon2;
- int hierarchy_number;
- struct cgroup_hierarchy *h = NULL;
- char **p;
-
- if (!line[0])
- continue;
-
- colon1 = strchr(line, ':');
- if (!colon1)
- continue;
- *colon1++ = '\0';
- colon2 = strchr(colon1, ':');
- if (!colon2)
- continue;
- *colon2 = '\0';
-
- colon2 = NULL;
- hierarchy_number = strtoul(line, &colon2, 10);
- if (!colon2 || *colon2)
- continue;
-
- if (hierarchy_number > meta_data->maximum_hierarchy) {
- /* lxc_grow_array will never shrink, so even if we find a lower
- * hierarchy number here, the array will never be smaller
- */
- r = lxc_grow_array((void ***)&meta_data->hierarchies, &hierarchy_capacity, hierarchy_number + 1, 12);
- if (r < 0)
- goto out;
-
- meta_data->maximum_hierarchy = hierarchy_number;
- }
-
- /* this shouldn't happen, we had this already */
- if (meta_data->hierarchies[hierarchy_number])
- goto out;
-
- h = calloc(1, sizeof(struct cgroup_hierarchy));
- if (!h)
- goto out;
-
- meta_data->hierarchies[hierarchy_number] = h;
-
- h->index = hierarchy_number;
- h->subsystems = lxc_string_split_and_trim(colon1, ',');
- if (!h->subsystems)
- goto out;
- /* see if this hierarchy should be considered */
- if (!all_kernel_subsystems || !all_named_subsystems) {
- for (p = h->subsystems; *p; p++) {
- if (!strncmp(*p, "name=", 5)) {
- if (all_named_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
- h->used = true;
- break;
- }
- } else {
- if (all_kernel_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
- h->used = true;
- break;
- }
- }
- }
- } else {
- /* we want all hierarchy anyway */
- h->used = true;
- }
- }
- bret = true;
-
-out:
- fclose(proc_self_cgroup);
- free(line);
- return bret;
-}
-
-/* Step 3: determine all mount points of each hierarchy */
-static bool find_hierarchy_mountpts( struct cgroup_meta_data *meta_data, char **kernel_subsystems)
-{
- bool bret = false;
- FILE *proc_self_mountinfo;
- char *line = NULL;
- size_t sz = 0;
- char **tokens = NULL;
- size_t mount_point_count = 0;
- size_t mount_point_capacity = 0;
- size_t token_capacity = 0;
- int r;
- bool is_cgns = cgns_supported();
-
- proc_self_mountinfo = fopen_cloexec("/proc/self/mountinfo", "r");
- /* if for some reason (because of setns() and pid namespace for example),
- * /proc/self is not valid, we try /proc/1/cgroup... */
- if (!proc_self_mountinfo)
- proc_self_mountinfo = fopen_cloexec("/proc/1/mountinfo", "r");
- if (!proc_self_mountinfo)
- return false;
-
- while (getline(&line, &sz, proc_self_mountinfo) != -1) {
- char *token, *line_tok, *saveptr = NULL;
- size_t i, j, k;
- struct cgroup_mount_point *mount_point;
- struct cgroup_hierarchy *h;
- char **subsystems;
- bool is_lxcfs = false;
-
- if (line[0] && line[strlen(line) - 1] == '\n')
- line[strlen(line) - 1] = '\0';
-
- for (i = 0, line_tok = line; (token = strtok_r(line_tok, " ", &saveptr)); line_tok = NULL) {
- r = lxc_grow_array((void ***)&tokens, &token_capacity, i + 1, 64);
- if (r < 0)
- goto out;
- tokens[i++] = token;
- }
-
- /* layout of /proc/self/mountinfo:
- * 0: id
- * 1: parent id
- * 2: device major:minor
- * 3: mount prefix
- * 4: mount point
- * 5: per-mount options
- * [optional X]: additional data
- * X+7: "-"
- * X+8: type
- * X+9: source
- * X+10: per-superblock options
- */
- for (j = 6; j < i && tokens[j]; j++)
- if (!strcmp(tokens[j], "-"))
- break;
-
- /* could not find separator */
- if (j >= i || !tokens[j])
- continue;
- /* there should be exactly three fields after
- * the separator
- */
- if (i != j + 4)
- continue;
-
- /* not a cgroup filesystem */
- if (strcmp(tokens[j + 1], "cgroup") != 0) {
- if (strcmp(tokens[j + 1], "fuse.lxcfs") != 0)
- continue;
- if (strncmp(tokens[4], "/sys/fs/cgroup/", 15) != 0)
- continue;
- is_lxcfs = true;
- char *curtok = tokens[4] + 15;
- subsystems = subsystems_from_mount_options(curtok,
- kernel_subsystems);
- } else
- subsystems = subsystems_from_mount_options(tokens[j + 3],
- kernel_subsystems);
- if (!subsystems)
- goto out;
-
- h = NULL;
- for (k = 1; k <= meta_data->maximum_hierarchy; k++) {
- if (meta_data->hierarchies[k] &&
- meta_data->hierarchies[k]->subsystems[0] &&
- lxc_string_in_array(meta_data->hierarchies[k]->subsystems[0], (const char **)subsystems)) {
- /* TODO: we could also check if the lists really match completely,
- * just to have an additional sanity check */
- h = meta_data->hierarchies[k];
- break;
- }
- }
- lxc_free_array((void **)subsystems, free);
-
- r = lxc_grow_array((void ***)&meta_data->mount_points, &mount_point_capacity, mount_point_count + 1, 12);
- if (r < 0)
- goto out;
-
- /* create mount point object */
- mount_point = calloc(1, sizeof(*mount_point));
- if (!mount_point)
- goto out;
-
- meta_data->mount_points[mount_point_count++] = mount_point;
-
- mount_point->hierarchy = h;
- if (is_lxcfs || is_cgns)
- mount_point->mount_prefix = strdup("/");
- else
- mount_point->mount_prefix = strdup(tokens[3]);
- mount_point->mount_point = strdup(tokens[4]);
- if (!mount_point->mount_point || !mount_point->mount_prefix)
- goto out;
- mount_point->read_only = !lxc_string_in_list("rw", tokens[5], ',');
-
- if (!strcmp(mount_point->mount_prefix, "/")) {
- if (mount_point->read_only) {
- if (!h->ro_absolute_mount_point)
- h->ro_absolute_mount_point = mount_point;
- } else {
- if (!h->rw_absolute_mount_point)
- h->rw_absolute_mount_point = mount_point;
- }
- }
-
- k = lxc_array_len((void **)h->all_mount_points);
- r = lxc_grow_array((void ***)&h->all_mount_points, &h->all_mount_point_capacity, k + 1, 4);
- if (r < 0)
- goto out;
- h->all_mount_points[k] = mount_point;
- }
- bret = true;
-
-out:
- fclose(proc_self_mountinfo);
- free(tokens);
- free(line);
- return bret;
-}
-
-static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist)
-{
- bool all_kernel_subsystems = true;
- bool all_named_subsystems = false;
- struct cgroup_meta_data *meta_data = NULL;
- char **kernel_subsystems = NULL;
- int saved_errno = 0;
-
- /* if the subsystem whitelist is not specified, include all
- * hierarchies that contain kernel subsystems by default but
- * no hierarchies that only contain named subsystems
- *
- * if it is specified, the specifier @all will select all
- * hierarchies, @kernel will select all hierarchies with
- * kernel subsystems and @named will select all named
- * hierarchies
- */
- all_kernel_subsystems = subsystem_whitelist ?
- (lxc_string_in_array("@kernel", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
- true;
- all_named_subsystems = subsystem_whitelist ?
- (lxc_string_in_array("@named", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
- true;
-
- meta_data = calloc(1, sizeof(struct cgroup_meta_data));
- if (!meta_data)
- return NULL;
- meta_data->ref = 1;
-
- if (!find_cgroup_subsystems(&kernel_subsystems))
- goto out_error;
-
- if (!find_cgroup_hierarchies(meta_data, all_kernel_subsystems,
- all_named_subsystems, subsystem_whitelist))
- goto out_error;
-
- if (!find_hierarchy_mountpts(meta_data, kernel_subsystems))
- goto out_error;
-
- /* oops, we couldn't find anything */
- if (!meta_data->hierarchies || !meta_data->mount_points) {
- errno = EINVAL;
- goto out_error;
- }
-
- lxc_free_array((void **)kernel_subsystems, free);
- return meta_data;
-
-out_error:
- saved_errno = errno;
- lxc_free_array((void **)kernel_subsystems, free);
- lxc_cgroup_put_meta(meta_data);
- errno = saved_errno;
- return NULL;
-}
-
-static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data)
-{
- meta_data->ref++;
- return meta_data;
-}
-
-static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data)
-{
- size_t i;
- if (!meta_data)
- return NULL;
- if (--meta_data->ref > 0)
- return meta_data;
- lxc_free_array((void **)meta_data->mount_points, (lxc_free_fn)lxc_cgroup_mount_point_free);
- if (meta_data->hierarchies) {
- for (i = 0; i <= meta_data->maximum_hierarchy; i++)
- lxc_cgroup_hierarchy_free(meta_data->hierarchies[i]);
- }
- free(meta_data->hierarchies);
- free(meta_data);
- return NULL;
-}
-
-static struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem)
-{
- size_t i;
- for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
- struct cgroup_hierarchy *h = meta_data->hierarchies[i];
- if (h && lxc_string_in_array(subsystem, (const char **)h->subsystems))
- return h;
- }
- return NULL;
-}
-
-static bool mountpoint_is_accessible(struct cgroup_mount_point *mp)
-{
- return mp && access(mp->mount_point, F_OK) == 0;
-}
-
-static struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable)
-{
- struct cgroup_mount_point **mps;
- struct cgroup_mount_point *current_result = NULL;
- ssize_t quality = -1;
-
- /* trivial case */
- if (mountpoint_is_accessible(hierarchy->rw_absolute_mount_point))
- return hierarchy->rw_absolute_mount_point;
- if (!should_be_writable && mountpoint_is_accessible(hierarchy->ro_absolute_mount_point))
- return hierarchy->ro_absolute_mount_point;
-
- for (mps = hierarchy->all_mount_points; mps && *mps; mps++) {
- struct cgroup_mount_point *mp = *mps;
- size_t prefix_len = mp->mount_prefix ? strlen(mp->mount_prefix) : 0;
-
- if (prefix_len == 1 && mp->mount_prefix[0] == '/')
- prefix_len = 0;
-
- if (!mountpoint_is_accessible(mp))
- continue;
-
- if (should_be_writable && mp->read_only)
- continue;
-
- if (!prefix_len ||
- (strncmp(group, mp->mount_prefix, prefix_len) == 0 &&
- (group[prefix_len] == '\0' || group[prefix_len] == '/'))) {
- /* search for the best quality match, i.e. the match with the
- * shortest prefix where this group is still contained
- */
- if (quality == -1 || prefix_len < quality) {
- current_result = mp;
- quality = prefix_len;
- }
- }
- }
-
- if (!current_result)
- errno = ENOENT;
- return current_result;
-}
-
-static char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix)
-{
- struct cgroup_meta_data *meta_data;
- struct cgroup_hierarchy *h;
- struct cgroup_mount_point *mp;
- char *result;
- int saved_errno;
-
- meta_data = lxc_cgroup_load_meta();
- if (!meta_data)
- return NULL;
-
- h = lxc_cgroup_find_hierarchy(meta_data, subsystem);
- if (!h)
- goto out_error;
-
- mp = lxc_cgroup_find_mount_point(h, group, should_be_writable);
- if (!mp)
- goto out_error;
-
- result = cgroup_to_absolute_path(mp, group, suffix);
- if (!result)
- goto out_error;
-
- lxc_cgroup_put_meta(meta_data);
- return result;
-
-out_error:
- saved_errno = errno;
- lxc_cgroup_put_meta(meta_data);
- errno = saved_errno;
- return NULL;
-}
-
-static struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta)
-{
- char pid_buf[32];
- snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid);
- return lxc_cgroup_process_info_getx(pid_buf, meta);
-}
-
-static struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta)
-{
- return lxc_cgroup_process_info_get(1, meta);
-}
-
-static struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta)
-{
- struct cgroup_process_info *i;
- i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta);
- if (!i)
- i = lxc_cgroup_process_info_get(getpid(), meta);
- return i;
-}
-
-/*
- * If a controller has ns cgroup mounted, then in that cgroup the handler->pid
- * is already in a new cgroup named after the pid. 'mnt' is passed in as
- * the full current cgroup. Say that is /sys/fs/cgroup/lxc/2975 and the container
- * name is c1. . We want to rename the cgroup directory to /sys/fs/cgroup/lxc/c1,
- * and return the string /sys/fs/cgroup/lxc/c1.
- */
-static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, pid_t pid, const char *name)
-{
- char *dir, *fulloldpath;
- char *newname, *fullnewpath;
- int len, newlen, ret;
-
- /*
- * if cgroup is mounted at /cgroup and task is in cgroup /ab/, pid 2375 and
- * name is c1,
- * dir: /ab
- * fulloldpath = /cgroup/ab/2375
- * fullnewpath = /cgroup/ab/c1
- * newname = /ab/c1
- */
- dir = alloca(strlen(oldname) + 1);
- strcpy(dir, oldname);
-
- len = strlen(oldname) + strlen(mountpath) + 22;
- fulloldpath = alloca(len);
- ret = snprintf(fulloldpath, len, "%s/%s/%ld", mountpath, oldname, (unsigned long)pid);
- if (ret < 0 || ret >= len)
- return NULL;
-
- len = strlen(dir) + strlen(name) + 2;
- newname = malloc(len);
- if (!newname) {
- SYSERROR("Out of memory");
- return NULL;
- }
- ret = snprintf(newname, len, "%s/%s", dir, name);
- if (ret < 0 || ret >= len) {
- free(newname);
- return NULL;
- }
-
- newlen = strlen(mountpath) + len + 2;
- fullnewpath = alloca(newlen);
- ret = snprintf(fullnewpath, newlen, "%s/%s", mountpath, newname);
- if (ret < 0 || ret >= newlen) {
- free(newname);
- return NULL;
- }
-
- if (access(fullnewpath, F_OK) == 0) {
- if (rmdir(fullnewpath) != 0) {
- SYSERROR("container cgroup %s already exists.", fullnewpath);
- free(newname);
- return NULL;
- }
- }
- if (rename(fulloldpath, fullnewpath)) {
- SYSERROR("failed to rename cgroup %s->%s", fulloldpath, fullnewpath);
- free(newname);
- return NULL;
- }
-
- DEBUG("'%s' renamed to '%s'", oldname, newname);
-
- return newname;
-}
-
-static bool is_crucial_hierarchy(struct cgroup_hierarchy *h)
-{
- char **p;
-
- for (p = h->subsystems; *p; p++) {
- if (is_crucial_cgroup_subsystem(*p))
- return true;
- }
- return false;
-}
-
-/* create a new cgroup */
-static struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern)
-{
- char **cgroup_path_components = NULL;
- char **p = NULL;
- char *path_so_far = NULL;
- char **new_cgroup_paths = NULL;
- char **new_cgroup_paths_sub = NULL;
- struct cgroup_mount_point *mp;
- struct cgroup_hierarchy *h;
- struct cgroup_process_info *base_info = NULL;
- struct cgroup_process_info *info_ptr;
- int saved_errno;
- int r;
- unsigned suffix = 0;
- bool had_sub_pattern = false;
- size_t i;
-
- if (!is_valid_cgroup(name)) {
- ERROR("Invalid cgroup name: '%s'", name);
- errno = EINVAL;
- return NULL;
- }
-
- if (!strstr(path_pattern, "%n")) {
- ERROR("Invalid cgroup path pattern: '%s'; contains no %%n for specifying container name", path_pattern);
- errno = EINVAL;
- return NULL;
- }
-
- /* we will modify the result of this operation directly,
- * so we don't have to copy the data structure
- */
- base_info = (path_pattern[0] == '/') ?
- lxc_cgroup_process_info_get_init(meta_data) :
- lxc_cgroup_process_info_get_self(meta_data);
- if (!base_info)
- return NULL;
-
- new_cgroup_paths = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
- if (!new_cgroup_paths)
- goto out_initial_error;
-
- new_cgroup_paths_sub = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
- if (!new_cgroup_paths_sub)
- goto out_initial_error;
-
- /* find mount points we can use */
- for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
- h = info_ptr->hierarchy;
- mp = lxc_cgroup_find_mount_point(h, info_ptr->cgroup_path, true);
- if (!mp) {
- ERROR("Could not find writable mount point for cgroup hierarchy %d while trying to create cgroup.", h->index);
- goto out_initial_error;
- }
- info_ptr->designated_mount_point = mp;
-
- if (lxc_string_in_array("ns", (const char **)h->subsystems))
- continue;
- if (handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) {
- ERROR("Could not set clone_children to 1 for cpuset hierarchy in parent cgroup.");
- goto out_initial_error;
- }
- }
-
- /* normalize the path */
- cgroup_path_components = lxc_normalize_path(path_pattern);
- if (!cgroup_path_components)
- goto out_initial_error;
-
- /* go through the path components to see if we can create them */
- for (p = cgroup_path_components; *p || (sub_pattern && !had_sub_pattern); p++) {
- /* we only want to create the same component with -1, -2, etc.
- * if the component contains the container name itself, otherwise
- * it's not an error if it already exists
- */
- char *p_eff = *p ? *p : (char *)sub_pattern;
- bool contains_name = strstr(p_eff, "%n");
- char *current_component = NULL;
- char *current_subpath = NULL;
- char *current_entire_path = NULL;
- char *parts[3];
- size_t j = 0;
- i = 0;
-
- /* if we are processing the subpattern, we want to make sure
- * loop is ended the next time around
- */
- if (!*p) {
- had_sub_pattern = true;
- p--;
- }
-
- goto find_name_on_this_level;
-
- cleanup_name_on_this_level:
- /* This is reached if we found a name clash.
- * In that case, remove the cgroup from all previous hierarchies
- */
- for (j = 0, info_ptr = base_info; j < i && info_ptr; info_ptr = info_ptr->next, j++) {
- if (info_ptr->created_paths_count < 1)
- continue;
- r = remove_cgroup(info_ptr->designated_mount_point, info_ptr->created_paths[info_ptr->created_paths_count - 1], false, NULL);
- if (r < 0)
- WARN("could not clean up cgroup we created when trying to create container");
- free(info_ptr->created_paths[info_ptr->created_paths_count - 1]);
- info_ptr->created_paths[--info_ptr->created_paths_count] = NULL;
- }
- if (current_component != current_subpath)
- free(current_subpath);
- if (current_component != p_eff)
- free(current_component);
- current_component = current_subpath = NULL;
- /* try again with another suffix */
- ++suffix;
-
- find_name_on_this_level:
- /* determine name of the path component we should create */
- if (contains_name && suffix > 0) {
- char *buf = calloc(strlen(name) + 32, 1);
- if (!buf)
- goto out_initial_error;
- snprintf(buf, strlen(name) + 32, "%s-%u", name, suffix);
- current_component = lxc_string_replace("%n", buf, p_eff);
- free(buf);
- } else {
- current_component = contains_name ? lxc_string_replace("%n", name, p_eff) : p_eff;
- }
- parts[0] = path_so_far;
- parts[1] = current_component;
- parts[2] = NULL;
- current_subpath = path_so_far ? lxc_string_join("/", (const char **)parts, false) : current_component;
-
- /* Now go through each hierarchy and try to create the
- * corresponding cgroup
- */
- for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
- char *parts2[3];
-
- if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
- continue;
- current_entire_path = NULL;
-
- parts2[0] = !strcmp(info_ptr->cgroup_path, "/") ? "" : info_ptr->cgroup_path;
- parts2[1] = current_subpath;
- parts2[2] = NULL;
- current_entire_path = lxc_string_join("/", (const char **)parts2, false);
-
- if (!*p) {
- /* we are processing the subpath, so only update that one */
- free(new_cgroup_paths_sub[i]);
- new_cgroup_paths_sub[i] = strdup(current_entire_path);
- if (!new_cgroup_paths_sub[i])
- goto cleanup_from_error;
- } else {
- /* remember which path was used on this controller */
- free(new_cgroup_paths[i]);
- new_cgroup_paths[i] = strdup(current_entire_path);
- if (!new_cgroup_paths[i])
- goto cleanup_from_error;
- }
-
- r = create_cgroup(info_ptr->designated_mount_point, current_entire_path);
- if (r < 0 && errno == EEXIST && contains_name) {
- /* name clash => try new name with new suffix */
- free(current_entire_path);
- current_entire_path = NULL;
- goto cleanup_name_on_this_level;
- } else if (r < 0 && errno != EEXIST) {
- if (is_crucial_hierarchy(info_ptr->hierarchy)) {
- SYSERROR("Could not create cgroup '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point);
- goto cleanup_from_error;
- }
- goto skip;
- } else if (r == 0) {
- /* successfully created */
- r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
- if (r < 0)
- goto cleanup_from_error;
- if (!init_cpuset_if_needed(info_ptr->designated_mount_point, current_entire_path)) {
- ERROR("Failed to initialize cpuset for '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point);
- goto cleanup_from_error;
- }
- info_ptr->created_paths[info_ptr->created_paths_count++] = current_entire_path;
- } else {
- /* if we didn't create the cgroup, then we have to make sure that
- * further cgroups will be created properly
- */
- if (handle_cgroup_settings(info_ptr->designated_mount_point, info_ptr->cgroup_path) < 0) {
- ERROR("Could not set clone_children to 1 for cpuset hierarchy in pre-existing cgroup.");
- goto cleanup_from_error;
- }
- if (!init_cpuset_if_needed(info_ptr->designated_mount_point, info_ptr->cgroup_path)) {
- ERROR("Failed to initialize cpuset in pre-existing '%s'.", info_ptr->cgroup_path);
- goto cleanup_from_error;
- }
-
-skip:
- /* already existed but path component of pattern didn't contain '%n',
- * so this is not an error; but then we don't need current_entire_path
- * anymore...
- */
- free(current_entire_path);
- current_entire_path = NULL;
- }
- }
-
- /* save path so far */
- free(path_so_far);
- path_so_far = strdup(current_subpath);
- if (!path_so_far)
- goto cleanup_from_error;
-
- /* cleanup */
- if (current_component != current_subpath)
- free(current_subpath);
- if (current_component != p_eff)
- free(current_component);
- current_component = current_subpath = NULL;
- continue;
-
- cleanup_from_error:
- /* called if an error occurred in the loop, so we
- * do some additional cleanup here
- */
- saved_errno = errno;
- if (current_component != current_subpath)
- free(current_subpath);
- if (current_component != p_eff)
- free(current_component);
- free(current_entire_path);
- errno = saved_errno;
- goto out_initial_error;
- }
-
- /* we're done, now update the paths */
- for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
- /* ignore legacy 'ns' subsystem here, lxc_cgroup_create_legacy
- * will take care of it
- * Since we do a continue in above loop, new_cgroup_paths[i] is
- * unset anyway, as is new_cgroup_paths_sub[i]
- */
- if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
- continue;
- free(info_ptr->cgroup_path);
- info_ptr->cgroup_path = new_cgroup_paths[i];
- info_ptr->cgroup_path_sub = new_cgroup_paths_sub[i];
- }
- /* don't use lxc_free_array since we used the array members
- * to store them in our result...
- */
- free(new_cgroup_paths);
- free(new_cgroup_paths_sub);
- free(path_so_far);
- lxc_free_array((void **)cgroup_path_components, free);
- return base_info;
-
-out_initial_error:
- saved_errno = errno;
- free(path_so_far);
- lxc_cgroup_process_info_free_and_remove(base_info, NULL);
- lxc_free_array((void **)new_cgroup_paths, free);
- lxc_free_array((void **)new_cgroup_paths_sub, free);
- lxc_free_array((void **)cgroup_path_components, free);
- errno = saved_errno;
- return NULL;
-}
-
-static int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid)
-{
- struct cgroup_process_info *info_ptr;
- int r;
-
- for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
- if (!lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
- continue;
- /*
- * For any path which has ns cgroup mounted, handler->pid is already
- * moved into a container called '%d % (handler->pid)'. Rename it to
- * the cgroup name and record that.
- */
- char *tmp = cgroup_rename_nsgroup((const char *)info_ptr->designated_mount_point->mount_point,
- info_ptr->cgroup_path, pid, name);
- if (!tmp)
- return -1;
- free(info_ptr->cgroup_path);
- info_ptr->cgroup_path = tmp;
- r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
- if (r < 0)
- return -1;
- tmp = strdup(tmp);
- if (!tmp)
- return -1;
- info_ptr->created_paths[info_ptr->created_paths_count++] = tmp;
- }
- return 0;
-}
-
-/* get the cgroup membership of a given container */
-static struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data)
-{
- struct cgroup_process_info *result = NULL;
- int saved_errno = 0;
- size_t i;
- struct cgroup_process_info **cptr = &result;
- struct cgroup_process_info *entry = NULL;
- char *path = NULL;
-
- for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
- struct cgroup_hierarchy *h = meta_data->hierarchies[i];
- if (!h || !h->used)
- continue;
-
- /* use the command interface to look for the cgroup */
- path = lxc_cmd_get_cgroup_path(name, lxcpath, h->subsystems[0]);
- if (!path) {
- h->used = false;
- continue;
- }
-
- entry = calloc(1, sizeof(struct cgroup_process_info));
- if (!entry)
- goto out_error;
- entry->meta_ref = lxc_cgroup_get_meta(meta_data);
- entry->hierarchy = h;
- entry->cgroup_path = path;
- path = NULL;
-
- /* it is not an error if we don't find anything here,
- * it is up to the caller to decide what to do in that
- * case */
- entry->designated_mount_point = lxc_cgroup_find_mount_point(h, entry->cgroup_path, true);
-
- *cptr = entry;
- cptr = &entry->next;
- entry = NULL;
- }
-
- return result;
-out_error:
- saved_errno = errno;
- free(path);
- lxc_cgroup_process_info_free(result);
- lxc_cgroup_process_info_free(entry);
- errno = saved_errno;
- return NULL;
-}
-
-/* move a processs to the cgroups specified by the membership */
-static int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub)
-{
- char pid_buf[32];
- char *cgroup_tasks_fn;
- int r;
- struct cgroup_process_info *info_ptr;
-
- snprintf(pid_buf, 32, "%lu", (unsigned long)pid);
- for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
- char *cgroup_path = (enter_sub && info_ptr->cgroup_path_sub) ?
- info_ptr->cgroup_path_sub :
- info_ptr->cgroup_path;
-
- if (!info_ptr->designated_mount_point) {
- info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, cgroup_path, true);
- if (!info_ptr->designated_mount_point) {
- SYSERROR("Could not add pid %lu to cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", (unsigned long)pid, cgroup_path);
- return -1;
- }
- }
-
- cgroup_tasks_fn = cgroup_to_absolute_path(info_ptr->designated_mount_point, cgroup_path, "/tasks");
- if (!cgroup_tasks_fn) {
- SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
- return -1;
- }
-
- r = lxc_write_to_file(cgroup_tasks_fn, pid_buf, strlen(pid_buf), false);
- free(cgroup_tasks_fn);
- if (r < 0 && is_crucial_hierarchy(info_ptr->hierarchy)) {
- SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
- return -1;
- }
- }
-
- return 0;
-}
-
-/* free process membership information */
-void lxc_cgroup_process_info_free(struct cgroup_process_info *info)
-{
- struct cgroup_process_info *next;
- if (!info)
- return;
- next = info->next;
- lxc_cgroup_put_meta(info->meta_ref);
- free(info->cgroup_path);
- free(info->cgroup_path_sub);
- lxc_free_array((void **)info->created_paths, free);
- free(info);
- lxc_cgroup_process_info_free(next);
-}
-
-/* free process membership information and remove cgroups that were created */
-void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info, struct lxc_conf *conf)
-{
- struct cgroup_process_info *next;
- char **pp;
- if (!info)
- return;
- next = info->next;
- {
- struct cgroup_mount_point *mp = info->designated_mount_point;
- if (!mp)
- mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
- if (mp)
- /* ignore return value here, perhaps we created the
- * '/lxc' cgroup in this container but another container
- * is still running (for example)
- */
- (void)remove_cgroup(mp, info->cgroup_path, true, conf);
- }
- for (pp = info->created_paths; pp && *pp; pp++);
- for ((void)(pp && --pp); info->created_paths && pp >= info->created_paths; --pp) {
- free(*pp);
- }
- free(info->created_paths);
- lxc_cgroup_put_meta(info->meta_ref);
- free(info->cgroup_path);
- free(info->cgroup_path_sub);
- free(info);
- lxc_cgroup_process_info_free_and_remove(next, conf);
-}
-
-static char *lxc_cgroup_get_hierarchy_path_data(const char *subsystem, struct cgfs_data *d)
-{
- struct cgroup_process_info *info = d->info;
- info = find_info_for_subsystem(info, subsystem);
- if (!info)
- return NULL;
- prune_init_scope(info->cgroup_path);
- return info->cgroup_path;
-}
-
-static char *lxc_cgroup_get_hierarchy_abs_path_data(const char *subsystem, struct cgfs_data *d)
-{
- struct cgroup_process_info *info = d->info;
- struct cgroup_mount_point *mp = NULL;
-
- info = find_info_for_subsystem(info, subsystem);
- if (!info)
- return NULL;
- if (info->designated_mount_point) {
- mp = info->designated_mount_point;
- } else {
- mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
- if (!mp)
- return NULL;
- }
- return cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
-}
-
-static char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath)
-{
- struct cgroup_meta_data *meta;
- struct cgroup_process_info *base_info, *info;
- struct cgroup_mount_point *mp;
- char *result = NULL;
-
- meta = lxc_cgroup_load_meta();
- if (!meta)
- return NULL;
- base_info = lxc_cgroup_get_container_info(name, lxcpath, meta);
- if (!base_info)
- goto out1;
- info = find_info_for_subsystem(base_info, subsystem);
- if (!info)
- goto out2;
- if (info->designated_mount_point) {
- mp = info->designated_mount_point;
- } else {
- mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
- if (!mp)
- goto out3;
- }
- result = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
-out3:
-out2:
- lxc_cgroup_process_info_free(base_info);
-out1:
- lxc_cgroup_put_meta(meta);
- return result;
-}
-
-static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfs_data *d)
-{
- char *subsystem = NULL, *p, *path;
- int ret = -1;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- errno = ENOENT;
- path = lxc_cgroup_get_hierarchy_abs_path_data(subsystem, d);
- if (path) {
- ret = do_cgroup_set(path, filename, value);
- int saved_errno = errno;
- free(path);
- errno = saved_errno;
- }
- return ret;
-}
-
-static int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath)
-{
- char *subsystem = NULL, *p, *path;
- int ret = -1;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
- if (path) {
- ret = do_cgroup_set(path, filename, value);
- free(path);
- }
- return ret;
-}
-
-static int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
-{
- char *subsystem = NULL, *p, *path;
- int ret = -1;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
- if (path) {
- ret = do_cgroup_get(path, filename, value, len);
- free(path);
- }
- return ret;
-}
-
-static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type)
-{
- size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup");
- char *path = NULL;
- char **parts = NULL;
- char *dirname = NULL;
- char *abs_path = NULL;
- char *abs_path2 = NULL;
- struct cgfs_data *cgfs_d;
- struct cgroup_process_info *info, *base_info;
- int r, saved_errno = 0;
-
- if (cgns_supported())
- return true;
-
- cgfs_d = hdata;
- if (!cgfs_d)
- return false;
- base_info = cgfs_d->info;
-
- /* If we get passed the _NOSPEC types, we default to _MIXED, since we don't
- * have access to the lxc_conf object at this point. It really should be up
- * to the caller to fix this, but this doesn't really hurt.
- */
- if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
- type = LXC_AUTO_CGROUP_FULL_MIXED;
- else if (type == LXC_AUTO_CGROUP_NOSPEC)
- type = LXC_AUTO_CGROUP_MIXED;
-
- if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) {
- ERROR("could not mount cgroups into container: invalid type specified internally");
- errno = EINVAL;
- return false;
- }
-
- path = calloc(1, bufsz);
- if (!path)
- return false;
- snprintf(path, bufsz, "%s/sys/fs/cgroup", root);
- r = safe_mount("cgroup_root", path, "tmpfs",
- MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
- "size=10240k,mode=755",
- root);
- if (r < 0) {
- SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container");
- return false;
- }
-
- /* now mount all the hierarchies we care about */
- for (info = base_info; info; info = info->next) {
- size_t subsystem_count, i;
- struct cgroup_mount_point *mp = info->designated_mount_point;
- if (!mountpoint_is_accessible(mp))
- mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
-
- if (!mp) {
- SYSERROR("could not find original mount point for cgroup hierarchy while trying to mount cgroup filesystem");
- goto out_error;
- }
-
- subsystem_count = lxc_array_len((void **)info->hierarchy->subsystems);
- parts = calloc(subsystem_count + 1, sizeof(char *));
- if (!parts)
- goto out_error;
-
- for (i = 0; i < subsystem_count; i++) {
- if (!strncmp(info->hierarchy->subsystems[i], "name=", 5))
- parts[i] = info->hierarchy->subsystems[i] + 5;
- else
- parts[i] = info->hierarchy->subsystems[i];
- }
- dirname = lxc_string_join(",", (const char **)parts, false);
- if (!dirname)
- goto out_error;
-
- /* create subsystem directory */
- abs_path = lxc_append_paths(path, dirname);
- if (!abs_path)
- goto out_error;
- r = mkdir_p(abs_path, 0755);
- if (r < 0 && errno != EEXIST) {
- SYSERROR("could not create cgroup subsystem directory /sys/fs/cgroup/%s", dirname);
- goto out_error;
- }
-
- abs_path2 = lxc_append_paths(abs_path, info->cgroup_path);
- if (!abs_path2)
- goto out_error;
-
- if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_RW || type == LXC_AUTO_CGROUP_FULL_MIXED) {
- /* bind-mount the cgroup entire filesystem there */
- if (strcmp(mp->mount_prefix, "/") != 0) {
- /* FIXME: maybe we should just try to remount the entire hierarchy
- * with a regular mount command? may that works? */
- ERROR("could not automatically mount cgroup-full to /sys/fs/cgroup/%s: host has no mount point for this cgroup filesystem that has access to the root cgroup", dirname);
- goto out_error;
- }
- r = mount(mp->mount_point, abs_path, "none", MS_BIND, 0);
- if (r < 0) {
- SYSERROR("error bind-mounting %s to %s", mp->mount_point, abs_path);
- goto out_error;
- }
- /* main cgroup path should be read-only */
- if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_MIXED) {
- r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
- if (r < 0) {
- SYSERROR("error re-mounting %s readonly", abs_path);
- goto out_error;
- }
- }
- /* own cgroup should be read-write */
- if (type == LXC_AUTO_CGROUP_FULL_MIXED) {
- r = mount(abs_path2, abs_path2, NULL, MS_BIND, NULL);
- if (r < 0) {
- SYSERROR("error bind-mounting %s onto itself", abs_path2);
- goto out_error;
- }
- r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND, NULL);
- if (r < 0) {
- SYSERROR("error re-mounting %s readwrite", abs_path2);
- goto out_error;
- }
- }
- } else {
- /* create path for container's cgroup */
- r = mkdir_p(abs_path2, 0755);
- if (r < 0 && errno != EEXIST) {
- SYSERROR("could not create cgroup directory /sys/fs/cgroup/%s%s", dirname, info->cgroup_path);
- goto out_error;
- }
-
- /* for read-only and mixed cases, we have to bind-mount the tmpfs directory
- * that points to the hierarchy itself (i.e. /sys/fs/cgroup/cpu etc.) onto
- * itself and then bind-mount it read-only, since we keep the tmpfs itself
- * read-write (see comment below)
- */
- if (type == LXC_AUTO_CGROUP_MIXED || type == LXC_AUTO_CGROUP_RO) {
- r = mount(abs_path, abs_path, NULL, MS_BIND, NULL);
- if (r < 0) {
- SYSERROR("error bind-mounting %s onto itself", abs_path);
- goto out_error;
- }
- r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
- if (r < 0) {
- SYSERROR("error re-mounting %s readonly", abs_path);
- goto out_error;
- }
- }
-
- free(abs_path);
- abs_path = NULL;
-
- /* bind-mount container's cgroup to that directory */
- abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
- if (!abs_path)
- goto out_error;
- r = mount(abs_path, abs_path2, "none", MS_BIND, 0);
- if (r < 0 && is_crucial_hierarchy(info->hierarchy)) {
- SYSERROR("error bind-mounting %s to %s", abs_path, abs_path2);
- goto out_error;
- }
- if (type == LXC_AUTO_CGROUP_RO) {
- r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
- if (r < 0) {
- SYSERROR("error re-mounting %s readonly", abs_path2);
- goto out_error;
- }
- }
- }
-
- free(abs_path);
- free(abs_path2);
- abs_path = NULL;
- abs_path2 = NULL;
-
- /* add symlinks for every single subsystem */
- if (subsystem_count > 1) {
- for (i = 0; i < subsystem_count; i++) {
- abs_path = lxc_append_paths(path, parts[i]);
- if (!abs_path)
- goto out_error;
- r = symlink(dirname, abs_path);
- if (r < 0)
- WARN("could not create symlink %s -> %s in /sys/fs/cgroup of container", parts[i], dirname);
- free(abs_path);
- abs_path = NULL;
- }
- }
- free(dirname);
- free(parts);
- dirname = NULL;
- parts = NULL;
- }
-
- /* We used to remount the entire tmpfs readonly if any :ro or
- * :mixed mode was specified. However, Ubuntu's mountall has the
- * unfortunate behavior to block bootup if /sys/fs/cgroup is
- * mounted read-only and cannot be remounted read-write.
- * (mountall reads /lib/init/fstab and tries to (re-)mount all of
- * these if they are not already mounted with the right options;
- * it contains an entry for /sys/fs/cgroup. In case it can't do
- * that, it prompts for the user to either manually fix it or
- * boot anyway. But without user input, booting of the container
- * hangs.)
- *
- * Instead of remounting the entire tmpfs readonly, we only
- * remount the paths readonly that are part of the cgroup
- * hierarchy.
- */
-
- free(path);
-
- return true;
-
-out_error:
- saved_errno = errno;
- free(path);
- free(dirname);
- free(parts);
- free(abs_path);
- free(abs_path2);
- errno = saved_errno;
- return false;
-}
-
-static int cgfs_nrtasks(void *hdata)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *info;
- struct cgroup_mount_point *mp = NULL;
- char *abs_path = NULL;
- int ret;
-
- if (!d) {
- errno = ENOENT;
- return -1;
- }
-
- info = d->info;
- if (!info) {
- errno = ENOENT;
- return -1;
- }
-
- if (info->designated_mount_point) {
- mp = info->designated_mount_point;
- } else {
- mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, false);
- if (!mp)
- return -1;
- }
-
- abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
- if (!abs_path)
- return -1;
-
- ret = cgroup_recursive_task_count(abs_path);
- free(abs_path);
- return ret;
-}
-
-static struct cgroup_process_info *
-lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str,
- struct cgroup_meta_data *meta)
-{
- struct cgroup_process_info *result = NULL;
- FILE *proc_pid_cgroup = NULL;
- char *line = NULL;
- size_t sz = 0;
- int saved_errno = 0;
- struct cgroup_process_info **cptr = &result;
- struct cgroup_process_info *entry = NULL;
-
- proc_pid_cgroup = fopen_cloexec(proc_pid_cgroup_str, "r");
- if (!proc_pid_cgroup)
- return NULL;
-
- while (getline(&line, &sz, proc_pid_cgroup) != -1) {
- /* file format: hierarchy:subsystems:group */
- char *colon1;
- char *colon2;
- char *endptr;
- int hierarchy_number;
- struct cgroup_hierarchy *h = NULL;
-
- if (!line[0])
- continue;
-
- if (line[strlen(line) - 1] == '\n')
- line[strlen(line) - 1] = '\0';
-
- colon1 = strchr(line, ':');
- if (!colon1)
- continue;
- *colon1++ = '\0';
- colon2 = strchr(colon1, ':');
- if (!colon2)
- continue;
- *colon2++ = '\0';
-
- endptr = NULL;
- hierarchy_number = strtoul(line, &endptr, 10);
- if (!endptr || *endptr)
- continue;
-
- if (hierarchy_number > meta->maximum_hierarchy) {
- /* we encountered a hierarchy we didn't have before,
- * so probably somebody remounted some stuff in the
- * mean time...
- */
- errno = EAGAIN;
- goto out_error;
- }
-
- h = meta->hierarchies[hierarchy_number];
- if (!h) {
- /* we encountered a hierarchy that was thought to be
- * dead before, so probably somebody remounted some
- * stuff in the mean time...
- */
- errno = EAGAIN;
- goto out_error;
- }
-
- /* we are told that we should ignore this hierarchy */
- if (!h->used)
- continue;
-
- entry = calloc(1, sizeof(struct cgroup_process_info));
- if (!entry)
- goto out_error;
-
- entry->meta_ref = lxc_cgroup_get_meta(meta);
- entry->hierarchy = h;
- entry->cgroup_path = strdup(colon2);
- if (!entry->cgroup_path)
- goto out_error;
- prune_init_scope(entry->cgroup_path);
-
- *cptr = entry;
- cptr = &entry->next;
- entry = NULL;
- }
-
- fclose(proc_pid_cgroup);
- free(line);
- return result;
-
-out_error:
- saved_errno = errno;
- if (proc_pid_cgroup)
- fclose(proc_pid_cgroup);
- lxc_cgroup_process_info_free(result);
- lxc_cgroup_process_info_free(entry);
- free(line);
- errno = saved_errno;
- return NULL;
-}
-
-static char **subsystems_from_mount_options(const char *mount_options,
- char **kernel_list)
-{
- char *token, *str, *saveptr = NULL;
- char **result = NULL;
- size_t result_capacity = 0;
- size_t result_count = 0;
- int saved_errno;
- int r;
-
- str = alloca(strlen(mount_options)+1);
- strcpy(str, mount_options);
- for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) {
- /* we have a subsystem if it's either in the list of
- * subsystems provided by the kernel OR if it starts
- * with name= for named hierarchies
- */
- r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 12);
- if (r < 0)
- goto out_free;
- result[result_count + 1] = NULL;
- if (strncmp(token, "name=", 5) && !lxc_string_in_array(token, (const char **)kernel_list)) {
- // this is eg 'systemd' but the mount will be 'name=systemd'
- result[result_count] = malloc(strlen(token) + 6);
- if (result[result_count])
- sprintf(result[result_count], "name=%s", token);
- } else
- result[result_count] = strdup(token);
- if (!result[result_count])
- goto out_free;
- result_count++;
- }
-
- return result;
-
-out_free:
- saved_errno = errno;
- lxc_free_array((void**)result, free);
- errno = saved_errno;
- return NULL;
-}
-
-static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp)
-{
- if (!mp)
- return;
- free(mp->mount_point);
- free(mp->mount_prefix);
- free(mp);
-}
-
-static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h)
-{
- if (!h)
- return;
- lxc_free_array((void **)h->subsystems, free);
- free(h->all_mount_points);
- free(h);
-}
-
-static bool is_valid_cgroup(const char *name)
-{
- const char *p;
- for (p = name; *p; p++) {
- /* Use the ASCII printable characters range(32 - 127)
- * is reasonable, we kick out 32(SPACE) because it'll
- * break legacy lxc-ls
- */
- if (*p <= 32 || *p >= 127 || *p == '/')
- return false;
- }
- return strcmp(name, ".") != 0 && strcmp(name, "..") != 0;
-}
-
-static int create_or_remove_cgroup(bool do_remove,
- struct cgroup_mount_point *mp, const char *path, int recurse,
- struct lxc_conf *conf)
-{
- int r, saved_errno = 0;
- char *buf = cgroup_to_absolute_path(mp, path, NULL);
- if (!buf)
- return -1;
-
- /* create or remove directory */
- if (do_remove) {
- if (!dir_exists(buf))
- return 0;
- if (recurse) {
- if (conf && !lxc_list_empty(&conf->id_map))
- r = userns_exec_1(conf, rmdir_wrapper, buf);
- else
- r = cgroup_rmdir(buf);
- } else
- r = rmdir(buf);
- } else
- r = mkdir(buf, 0777);
- saved_errno = errno;
- free(buf);
- errno = saved_errno;
- return r;
-}
-
-static int create_cgroup(struct cgroup_mount_point *mp, const char *path)
-{
- return create_or_remove_cgroup(false, mp, path, false, NULL);
-}
-
-static int remove_cgroup(struct cgroup_mount_point *mp,
- const char *path, bool recurse, struct lxc_conf *conf)
-{
- return create_or_remove_cgroup(true, mp, path, recurse, conf);
-}
-
-static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp,
- const char *path, const char *suffix)
-{
- /* first we have to make sure we subtract the mount point's prefix */
- char *prefix = mp->mount_prefix;
- char *buf;
- ssize_t len, rv;
-
- /* we want to make sure only absolute paths to cgroups are passed to us */
- if (path[0] != '/') {
- errno = EINVAL;
- return NULL;
- }
-
- if (prefix && !strcmp(prefix, "/"))
- prefix = NULL;
-
- /* prefix doesn't match */
- if (prefix && strncmp(prefix, path, strlen(prefix)) != 0) {
- errno = EINVAL;
- return NULL;
- }
- /* if prefix is /foo and path is /foobar */
- if (prefix && path[strlen(prefix)] != '/' && path[strlen(prefix)] != '\0') {
- errno = EINVAL;
- return NULL;
- }
-
- /* remove prefix from path */
- path += prefix ? strlen(prefix) : 0;
-
- len = strlen(mp->mount_point) + strlen(path) + (suffix ? strlen(suffix) : 0);
- buf = calloc(len + 1, 1);
- if (!buf)
- return NULL;
- rv = snprintf(buf, len + 1, "%s%s%s", mp->mount_point, path, suffix ? suffix : "");
- if (rv > len) {
- free(buf);
- errno = ENOMEM;
- return NULL;
- }
-
- return buf;
-}
-
-static struct cgroup_process_info *
-find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem)
-{
- struct cgroup_process_info *info_ptr;
- for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
- struct cgroup_hierarchy *h = info_ptr->hierarchy;
- if (lxc_string_in_array(subsystem, (const char **)h->subsystems))
- return info_ptr;
- }
- errno = ENOENT;
- return NULL;
-}
-
-static int do_cgroup_get(const char *cgroup_path, const char *sub_filename,
- char *value, size_t len)
-{
- const char *parts[3] = {
- cgroup_path,
- sub_filename,
- NULL
- };
- char *filename;
- int ret, saved_errno;
-
- filename = lxc_string_join("/", parts, false);
- if (!filename)
- return -1;
-
- ret = lxc_read_from_file(filename, value, len);
- saved_errno = errno;
- free(filename);
- errno = saved_errno;
- return ret;
-}
-
-static int do_cgroup_set(const char *cgroup_path, const char *sub_filename,
- const char *value)
-{
- const char *parts[3] = {
- cgroup_path,
- sub_filename,
- NULL
- };
- char *filename;
- int ret, saved_errno;
-
- filename = lxc_string_join("/", parts, false);
- if (!filename)
- return -1;
-
- ret = lxc_write_to_file(filename, value, strlen(value), false);
- saved_errno = errno;
- free(filename);
- errno = saved_errno;
- return ret;
-}
-
-static int do_setup_cgroup_limits(struct cgfs_data *d,
- struct lxc_list *cgroup_settings, bool do_devices)
-{
- struct lxc_list *iterator, *sorted_cgroup_settings, *next;
- struct lxc_cgroup *cg;
- int ret = -1;
-
- if (lxc_list_empty(cgroup_settings))
- return 0;
-
- sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
- if (!sorted_cgroup_settings) {
- return -1;
- }
-
- lxc_list_for_each(iterator, sorted_cgroup_settings) {
- cg = iterator->elem;
-
- if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
- if (strcmp(cg->subsystem, "devices.deny") == 0 &&
- cgroup_devices_has_allow_or_deny(d, cg->value, false))
- continue;
- if (strcmp(cg->subsystem, "devices.allow") == 0 &&
- cgroup_devices_has_allow_or_deny(d, cg->value, true))
- continue;
- if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
- if (do_devices && (errno == EACCES || errno == EPERM)) {
- WARN("Error setting %s to %s for %s",
- cg->subsystem, cg->value, d->name);
- continue;
- }
- SYSERROR("Error setting %s to %s for %s",
- cg->subsystem, cg->value, d->name);
- goto out;
- }
- }
-
- DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
- }
-
- ret = 0;
- INFO("cgroup has been setup");
-out:
- lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
- lxc_list_del(iterator);
- free(iterator);
- }
- free(sorted_cgroup_settings);
- return ret;
-}
-
-static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d,
- char *v, bool for_allow)
-{
- char *path;
- FILE *devices_list;
- char *line = NULL;
- size_t sz = 0;
- bool ret = !for_allow;
- const char *parts[3] = {
- NULL,
- "devices.list",
- NULL
- };
-
- // XXX FIXME if users could use something other than 'lxc.devices.deny = a'.
- // not sure they ever do, but they *could*
- // right now, I'm assuming they do NOT
- if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0)
- return false;
-
- parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_data("devices", d);
- if (!parts[0])
- return false;
- path = lxc_string_join("/", parts, false);
- if (!path) {
- free((void *)parts[0]);
- return false;
- }
-
- devices_list = fopen_cloexec(path, "r");
- if (!devices_list) {
- free(path);
- return false;
- }
-
- while (getline(&line, &sz, devices_list) != -1) {
- size_t len = strlen(line);
- if (len > 0 && line[len-1] == '\n')
- line[len-1] = '\0';
- if (strcmp(line, "a *:* rwm") == 0) {
- ret = for_allow;
- goto out;
- } else if (for_allow && strcmp(line, v) == 0) {
- ret = true;
- goto out;
- }
- }
-
-out:
- fclose(devices_list);
- free(line);
- free(path);
- return ret;
-}
-
-static int cgroup_recursive_task_count(const char *cgroup_path)
-{
- DIR *d;
- struct dirent *dent_buf;
- struct dirent *dent;
- ssize_t name_max;
- int n = 0, r;
-
- /* see man readdir_r(3) */
- name_max = pathconf(cgroup_path, _PC_NAME_MAX);
- if (name_max <= 0)
- name_max = 255;
- dent_buf = malloc(offsetof(struct dirent, d_name) + name_max + 1);
- if (!dent_buf)
- return -1;
-
- d = opendir(cgroup_path);
- if (!d) {
- free(dent_buf);
- return 0;
- }
-
- while (readdir_r(d, dent_buf, &dent) == 0 && dent) {
- const char *parts[3] = {
- cgroup_path,
- dent->d_name,
- NULL
- };
- char *sub_path;
- struct stat st;
-
- if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
- continue;
- sub_path = lxc_string_join("/", parts, false);
- if (!sub_path) {
- closedir(d);
- free(dent_buf);
- return -1;
- }
- r = stat(sub_path, &st);
- if (r < 0) {
- closedir(d);
- free(dent_buf);
- free(sub_path);
- return -1;
- }
- if (S_ISDIR(st.st_mode)) {
- r = cgroup_recursive_task_count(sub_path);
- if (r >= 0)
- n += r;
- } else if (!strcmp(dent->d_name, "tasks")) {
- r = lxc_count_file_lines(sub_path);
- if (r >= 0)
- n += r;
- }
- free(sub_path);
- }
- closedir(d);
- free(dent_buf);
-
- return n;
-}
-
-static int handle_cgroup_settings(struct cgroup_mount_point *mp,
- char *cgroup_path)
-{
- int r, saved_errno = 0;
- char buf[2];
-
- mp->need_cpuset_init = false;
-
- /* If this is the memory cgroup, we want to enforce hierarchy.
- * But don't fail if for some reason we can't.
- */
- if (lxc_string_in_array("memory", (const char **)mp->hierarchy->subsystems)) {
- char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/memory.use_hierarchy");
- if (cc_path) {
- r = lxc_read_from_file(cc_path, buf, 1);
- if (r < 1 || buf[0] != '1') {
- r = lxc_write_to_file(cc_path, "1", 1, false);
- if (r < 0)
- SYSERROR("failed to set memory.use_hierarchy to 1; continuing");
- }
- free(cc_path);
- }
- }
-
- /* if this is a cpuset hierarchy, we have to set cgroup.clone_children in
- * the base cgroup, otherwise containers will start with an empty cpuset.mems
- * and cpuset.cpus and then
- */
- if (lxc_string_in_array("cpuset", (const char **)mp->hierarchy->subsystems)) {
- char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/cgroup.clone_children");
- struct stat sb;
-
- if (!cc_path)
- return -1;
- /* cgroup.clone_children is not available when running under
- * older kernel versions; in this case, we'll initialize
- * cpuset.cpus and cpuset.mems later, after the new cgroup
- * was created
- */
- if (stat(cc_path, &sb) != 0 && errno == ENOENT) {
- mp->need_cpuset_init = true;
- free(cc_path);
- return 0;
- }
- r = lxc_read_from_file(cc_path, buf, 1);
- if (r == 1 && buf[0] == '1') {
- free(cc_path);
- return 0;
- }
- r = lxc_write_to_file(cc_path, "1", 1, false);
- saved_errno = errno;
- free(cc_path);
- errno = saved_errno;
- return r < 0 ? -1 : 0;
- }
- return 0;
-}
-
-static int cgroup_read_from_file(const char *fn, char buf[], size_t bufsize)
-{
- int ret = lxc_read_from_file(fn, buf, bufsize);
- if (ret < 0) {
- SYSERROR("failed to read %s", fn);
- return ret;
- }
- if (ret == bufsize) {
- if (bufsize > 0) {
- /* obviously this wasn't empty */
- buf[bufsize-1] = '\0';
- return ret;
- }
- /* Callers don't do this, but regression/sanity check */
- ERROR("%s: was not expecting 0 bufsize", __func__);
- return -1;
- }
- buf[ret] = '\0';
- return ret;
-}
-
-static bool do_init_cpuset_file(struct cgroup_mount_point *mp,
- const char *path, const char *name)
-{
- char value[1024];
- char *childfile, *parentfile = NULL, *tmp;
- int ret;
- bool ok = false;
-
- childfile = cgroup_to_absolute_path(mp, path, name);
- if (!childfile)
- return false;
-
- /* don't overwrite a non-empty value in the file */
- ret = cgroup_read_from_file(childfile, value, sizeof(value));
- if (ret < 0)
- goto out;
- if (value[0] != '\0' && value[0] != '\n') {
- ok = true;
- goto out;
- }
-
- /* path to the same name in the parent cgroup */
- parentfile = strdup(path);
- if (!parentfile)
- goto out;
-
- tmp = strrchr(parentfile, '/');
- if (!tmp)
- goto out;
- if (tmp == parentfile)
- tmp++; /* keep the '/' at the start */
- *tmp = '\0';
- tmp = parentfile;
- parentfile = cgroup_to_absolute_path(mp, tmp, name);
- free(tmp);
- if (!parentfile)
- goto out;
-
- /* copy from parent to child cgroup */
- ret = cgroup_read_from_file(parentfile, value, sizeof(value));
- if (ret < 0)
- goto out;
- if (ret == sizeof(value)) {
- /* If anyone actually sees this error, we can address it */
- ERROR("parent cpuset value too long");
- goto out;
- }
- ok = (lxc_write_to_file(childfile, value, strlen(value), false) >= 0);
- if (!ok)
- SYSERROR("failed writing %s", childfile);
-
-out:
- free(parentfile);
- free(childfile);
- return ok;
-}
-
-static bool init_cpuset_if_needed(struct cgroup_mount_point *mp,
- const char *path)
-{
- /* the files we have to handle here are only in cpuset hierarchies */
- if (!lxc_string_in_array("cpuset",
- (const char **)mp->hierarchy->subsystems))
- return true;
-
- if (!mp->need_cpuset_init)
- return true;
-
- return (do_init_cpuset_file(mp, path, "/cpuset.cpus") &&
- do_init_cpuset_file(mp, path, "/cpuset.mems") );
-}
-
-struct cgroup_ops *cgfs_ops_init(void)
-{
- return &cgfs_ops;
-}
-
-static void *cgfs_init(const char *name)
-{
- struct cgfs_data *d;
-
- d = malloc(sizeof(*d));
- if (!d)
- return NULL;
-
- memset(d, 0, sizeof(*d));
- d->name = strdup(name);
- if (!d->name)
- goto err1;
-
- d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
-
- d->meta = lxc_cgroup_load_meta();
- if (!d->meta) {
- ERROR("cgroupfs failed to detect cgroup metadata");
- goto err2;
- }
- return d;
-
-err2:
- free(d->name);
-err1:
- free(d);
- return NULL;
-}
-
-static void cgfs_destroy(void *hdata, struct lxc_conf *conf)
-{
- struct cgfs_data *d = hdata;
-
- if (!d)
- return;
- free(d->name);
- lxc_cgroup_process_info_free_and_remove(d->info, conf);
- lxc_cgroup_put_meta(d->meta);
- free(d);
-}
-
-static inline bool cgfs_create(void *hdata)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *i;
- struct cgroup_meta_data *md;
-
- if (!d)
- return false;
- md = d->meta;
- i = lxc_cgroupfs_create(d->name, d->cgroup_pattern, md, NULL);
- if (!i)
- return false;
- d->info = i;
- return true;
-}
-
-static inline bool cgfs_enter(void *hdata, pid_t pid)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *i;
- int ret;
-
- if (!d)
- return false;
- i = d->info;
- ret = lxc_cgroupfs_enter(i, pid, false);
-
- return ret == 0;
-}
-
-static inline bool cgfs_create_legacy(void *hdata, pid_t pid)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *i;
-
- if (!d)
- return false;
- i = d->info;
- if (lxc_cgroup_create_legacy(i, d->name, pid) < 0) {
- ERROR("failed to create legacy ns cgroups for '%s'", d->name);
- return false;
- }
- return true;
-}
-
-static const char *cgfs_get_cgroup(void *hdata, const char *subsystem)
-{
- struct cgfs_data *d = hdata;
-
- if (!d)
- return NULL;
- return lxc_cgroup_get_hierarchy_path_data(subsystem, d);
-}
-
-static const char *cgfs_canonical_path(void *hdata)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *info_ptr;
- char *path = NULL;
-
- if (!d)
- return NULL;
-
- for (info_ptr = d->info; info_ptr; info_ptr = info_ptr->next) {
- if (!path)
- path = info_ptr->cgroup_path;
- else if (strcmp(path, info_ptr->cgroup_path) != 0) {
- ERROR("not all paths match %s, %s has path %s", path,
- info_ptr->hierarchy->subsystems[0], info_ptr->cgroup_path);
- return NULL;
- }
- }
-
- return path;
-}
-
-static bool cgfs_escape(void *hdata)
-{
- struct cgroup_meta_data *md;
- int i;
- bool ret = false;
-
- md = lxc_cgroup_load_meta();
- if (!md)
- return false;
-
- for (i = 1; i <= md->maximum_hierarchy; i++) {
- struct cgroup_hierarchy *h = md->hierarchies[i];
- struct cgroup_mount_point *mp;
- char *tasks;
- FILE *f;
- int written;
-
- if (!h) {
- WARN("not escaping hierarchy %d", i);
- continue;
- }
-
- mp = lxc_cgroup_find_mount_point(h, "/", true);
- if (!mp)
- goto out;
-
- tasks = cgroup_to_absolute_path(mp, "/", "tasks");
- if (!tasks)
- goto out;
-
- f = fopen(tasks, "a");
- free(tasks);
- if (!f)
- goto out;
-
- written = fprintf(f, "%d\n", getpid());
- fclose(f);
- if (written < 0) {
- SYSERROR("writing tasks failed\n");
- goto out;
- }
- }
-
- ret = true;
-out:
- lxc_cgroup_put_meta(md);
- return ret;
-}
-
-static bool cgfs_unfreeze(void *hdata)
-{
- struct cgfs_data *d = hdata;
- char *cgabspath, *cgrelpath;
- int ret;
-
- if (!d)
- return false;
-
- cgrelpath = lxc_cgroup_get_hierarchy_path_data("freezer", d);
- cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL);
- if (!cgabspath)
- return false;
-
- ret = do_cgroup_set(cgabspath, "freezer.state", "THAWED");
- free(cgabspath);
- return ret == 0;
-}
-
-static bool cgroupfs_setup_limits(void *hdata, struct lxc_list *cgroup_conf,
- bool with_devices)
-{
- struct cgfs_data *d = hdata;
-
- if (!d)
- return false;
- return do_setup_cgroup_limits(d, cgroup_conf, with_devices) == 0;
-}
-
-static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)
-{
- struct cgroup_meta_data *meta_data;
- struct cgroup_process_info *container_info;
- int ret;
-
- meta_data = lxc_cgroup_load_meta();
- if (!meta_data) {
- ERROR("could not move attached process %d to cgroup of container", pid);
- return false;
- }
-
- container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
- lxc_cgroup_put_meta(meta_data);
- if (!container_info) {
- ERROR("could not move attached process %d to cgroup of container", pid);
- return false;
- }
-
- ret = lxc_cgroupfs_enter(container_info, pid, false);
- lxc_cgroup_process_info_free(container_info);
- if (ret < 0) {
- ERROR("could not move attached process %d to cgroup of container", pid);
- return false;
- }
- return true;
-}
-
-struct chown_data {
- const char *cgroup_path;
- uid_t origuid;
-};
-
-/*
- * TODO - someone should refactor this to unshare once passing all the paths
- * to be chowned in one go
- */
-static int chown_cgroup_wrapper(void *data)
-{
- struct chown_data *arg = data;
- uid_t destuid;
- char *fpath;
-
- if (setresgid(0,0,0) < 0)
- SYSERROR("Failed to setgid to 0");
- if (setresuid(0,0,0) < 0)
- SYSERROR("Failed to setuid to 0");
- if (setgroups(0, NULL) < 0)
- SYSERROR("Failed to clear groups");
- destuid = get_ns_uid(arg->origuid);
-
- if (chown(arg->cgroup_path, destuid, 0) < 0)
- SYSERROR("Failed chowning %s to %d", arg->cgroup_path, (int)destuid);
-
- fpath = lxc_append_paths(arg->cgroup_path, "tasks");
- if (!fpath)
- return -1;
- if (chown(fpath, destuid, 0) < 0)
- SYSERROR("Error chowning %s\n", fpath);
- free(fpath);
-
- fpath = lxc_append_paths(arg->cgroup_path, "cgroup.procs");
- if (!fpath)
- return -1;
- if (chown(fpath, destuid, 0) < 0)
- SYSERROR("Error chowning %s", fpath);
- free(fpath);
-
- return 0;
-}
-
-static bool do_cgfs_chown(char *cgroup_path, struct lxc_conf *conf)
-{
- struct chown_data data;
- char *fpath;
-
- if (!dir_exists(cgroup_path))
- return true;
-
- if (lxc_list_empty(&conf->id_map))
- /* If there's no mapping then we don't need to chown */
- return true;
-
- data.cgroup_path = cgroup_path;
- data.origuid = geteuid();
-
- /* Unpriv users can't chown it themselves, so chown from
- * a child namespace mapping both our own and the target uid
- */
- if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
- ERROR("Error requesting cgroup chown in new namespace");
- return false;
- }
-
- /*
- * Now chmod 775 the directory else the container cannot create cgroups.
- * This can't be done in the child namespace because it only group-owns
- * the cgroup
- */
- if (chmod(cgroup_path, 0775) < 0) {
- SYSERROR("Error chmoding %s\n", cgroup_path);
- return false;
- }
- fpath = lxc_append_paths(cgroup_path, "tasks");
- if (!fpath)
- return false;
- if (chmod(fpath, 0664) < 0)
- SYSERROR("Error chmoding %s\n", fpath);
- free(fpath);
- fpath = lxc_append_paths(cgroup_path, "cgroup.procs");
- if (!fpath)
- return false;
- if (chmod(fpath, 0664) < 0)
- SYSERROR("Error chmoding %s\n", fpath);
- free(fpath);
-
- return true;
-}
-
-static bool cgfs_chown(void *hdata, struct lxc_conf *conf)
-{
- struct cgfs_data *d = hdata;
- struct cgroup_process_info *info_ptr;
- char *cgpath;
- bool r = true;
-
- if (!d)
- return false;
-
- for (info_ptr = d->info; info_ptr; info_ptr = info_ptr->next) {
- if (!info_ptr->designated_mount_point) {
- info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, info_ptr->cgroup_path, true);
- if (!info_ptr->designated_mount_point) {
- SYSERROR("Could not chown cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", info_ptr->cgroup_path);
- return false;
- }
- }
-
- cgpath = cgroup_to_absolute_path(info_ptr->designated_mount_point, info_ptr->cgroup_path, NULL);
- if (!cgpath) {
- SYSERROR("Could not chown cgroup %s: internal error", info_ptr->cgroup_path);
- continue;
- }
- r = do_cgfs_chown(cgpath, conf);
- if (!r && is_crucial_hierarchy(info_ptr->hierarchy)) {
- ERROR("Failed chowning %s\n", cgpath);
- free(cgpath);
- return false;
- }
- free(cgpath);
- }
-
- return true;
-}
-
-static struct cgroup_ops cgfs_ops = {
- .init = cgfs_init,
- .destroy = cgfs_destroy,
- .create = cgfs_create,
- .enter = cgfs_enter,
- .create_legacy = cgfs_create_legacy,
- .get_cgroup = cgfs_get_cgroup,
- .canonical_path = cgfs_canonical_path,
- .escape = cgfs_escape,
- .get = lxc_cgroupfs_get,
- .set = lxc_cgroupfs_set,
- .unfreeze = cgfs_unfreeze,
- .setup_limits = cgroupfs_setup_limits,
- .name = "cgroupfs",
- .attach = lxc_cgroupfs_attach,
- .chown = cgfs_chown,
- .mount_cgroup = cgroupfs_mount_cgroup,
- .nrtasks = cgfs_nrtasks,
- .driver = CGFS,
-};
diff --git a/src/lxc/cgfsng.c b/src/lxc/cgfsng.c
deleted file mode 100644
index 27c2721..0000000
--- a/src/lxc/cgfsng.c
+++ /dev/null
@@ -1,1691 +0,0 @@
-/*
- * lxc: linux Container library
- *
- * Copyright © 2016 Canonical Ltd.
- *
- * Authors:
- * Serge Hallyn <serge.hallyn at ubuntu.com>
- *
- * 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
- */
-
-/*
- * cgfs-ng.c: this is a new, simplified implementation of a filesystem
- * cgroup backend. The original cgfs.c was designed to be as flexible
- * as possible. It would try to find cgroup filesystems no matter where
- * or how you had them mounted, and deduce the most usable mount for
- * each controller. It also was not designed for unprivileged use, as
- * that was reserved for cgmanager.
- *
- * This new implementation assumes that cgroup filesystems are mounted
- * under /sys/fs/cgroup/clist where clist is either the controller, or
- * a comman-separated list of controllers.
- */
-#include "config.h"
-#include <stdio.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <grp.h>
-
-#include "bdev.h"
-#include "log.h"
-#include "cgroup.h"
-#include "utils.h"
-#include "commands.h"
-
-lxc_log_define(lxc_cgfsng, lxc);
-
-static struct cgroup_ops cgfsng_ops;
-
-/*
- * A descriptor for a mounted hierarchy
- * @controllers: either NULL, or a null-terminated list of all
- * the co-mounted controllers
- * @mountpoint: the mountpoint we will use. It will be either
- * /sys/fs/cgroup/controller or /sys/fs/cgroup/controllerlist
- * @base_cgroup: the cgroup under which the container cgroup path
- is created. This will be either the caller's cgroup (if not
- root), or init's cgroup (if root).
- */
-struct hierarchy {
- char **controllers;
- char *mountpoint;
- char *base_cgroup;
- char *fullcgpath;
-};
-
-/*
- * The cgroup data which is attached to the lxc_handler.
- * @cgroup_pattern - a copy of the lxc.cgroup.pattern
- * @container_cgroup - if not null, the cgroup which was created for
- * the container. For each hierarchy, it is created under the
- * @hierarchy->base_cgroup directory. Relative to the base_cgroup
- * it is the same for all hierarchies.
- * @name - the container name
- */
-struct cgfsng_handler_data {
- char *cgroup_pattern;
- char *container_cgroup; // cgroup we created for the container
- char *name; // container name
-};
-
-/*
- * @hierarchies - a NULL-terminated array of struct hierarchy, one per
- * hierarchy. No duplicates. First sufficient, writeable mounted
- * hierarchy wins
- */
-struct hierarchy **hierarchies;
-
-/*
- * @cgroup_use - a copy of the lxc.cgroup.use
- */
-char *cgroup_use;
-
-static void free_string_list(char **clist)
-{
- if (clist) {
- int i;
-
- for (i = 0; clist[i]; i++)
- free(clist[i]);
- free(clist);
- }
-}
-
-/* Re-alllocate a pointer, do not fail */
-static void *must_realloc(void *orig, size_t sz)
-{
- void *ret;
-
- do {
- ret = realloc(orig, sz);
- } while (!ret);
- return ret;
-}
-
-/* Allocate a pointer, do not fail */
-static void *must_alloc(size_t sz)
-{
- return must_realloc(NULL, sz);
-}
-
-/* return copy of string @entry; do not fail. */
-static char *must_copy_string(const char *entry)
-{
- char *ret;
-
- if (!entry)
- return NULL;
- do {
- ret = strdup(entry);
- } while (!ret);
- return ret;
-}
-
-/*
- * This is a special case - return a copy of @entry
- * prepending 'name='. I.e. turn systemd into name=systemd.
- * Do not fail.
- */
-static char *must_prefix_named(char *entry)
-{
- char *ret;
- size_t len = strlen(entry);
-
- ret = must_alloc(len + 6);
- snprintf(ret, len + 6, "name=%s", entry);
- return ret;
-}
-
-/*
- * Given a pointer to a null-terminated array of pointers, realloc to
- * add one entry, and point the new entry to NULL. Do not fail. Return
- * the index to the second-to-last entry - that is, the one which is
- * now available for use (keeping the list null-terminated).
- */
-static int append_null_to_list(void ***list)
-{
- int newentry = 0;
-
- if (*list)
- for (; (*list)[newentry]; newentry++);
-
- *list = must_realloc(*list, (newentry + 2) * sizeof(void **));
- (*list)[newentry + 1] = NULL;
- return newentry;
-}
-
-/*
- * Given a null-terminated array of strings, check whether @entry
- * is one of the strings
- */
-static bool string_in_list(char **list, const char *entry)
-{
- int i;
-
- if (!list)
- return false;
- for (i = 0; list[i]; i++)
- if (strcmp(list[i], entry) == 0)
- return true;
-
- return false;
-}
-
-/*
- * append an entry to the clist. Do not fail.
- * *clist must be NULL the first time we are called.
- *
- * We also handle named subsystems here. Any controller which is not a
- * kernel subsystem, we prefix 'name='. Any which is both a kernel and
- * named subsystem, we refuse to use because we're not sure which we
- * have here. (TODO - we could work around this in some cases by just
- * remounting to be unambiguous, or by comparing mountpoint contents
- * with current cgroup)
- *
- * The last entry will always be NULL.
- */
-static void must_append_controller(char **klist, char **nlist, char ***clist, char *entry)
-{
- int newentry;
- char *copy;
-
- if (string_in_list(klist, entry) && string_in_list(nlist, entry)) {
- ERROR("Refusing to use ambiguous controller '%s'", entry);
- ERROR("It is both a named and kernel subsystem");
- return;
- }
-
- newentry = append_null_to_list((void ***)clist);
-
- if (strncmp(entry, "name=", 5) == 0)
- copy = must_copy_string(entry);
- else if (string_in_list(klist, entry))
- copy = must_copy_string(entry);
- else
- copy = must_prefix_named(entry);
-
- (*clist)[newentry] = copy;
-}
-
-static void free_handler_data(struct cgfsng_handler_data *d)
-{
- free(d->cgroup_pattern);
- free(d->container_cgroup);
- free(d->name);
- free(d);
-}
-
-/*
- * Given a handler's cgroup data, return the struct hierarchy for the
- * controller @c, or NULL if there is none.
- */
-struct hierarchy *get_hierarchy(const char *c)
-{
- int i;
-
- if (!hierarchies)
- return NULL;
- for (i = 0; hierarchies[i]; i++) {
- if (string_in_list(hierarchies[i]->controllers, c))
- return hierarchies[i];
- }
- return NULL;
-}
-
-static char *must_make_path(const char *first, ...) __attribute__((sentinel));
-
-/* Copy contents of parent(@path)/@file to @path/@file */
-static bool copy_parent_file(char *path, char *file)
-{
- char *lastslash, *value = NULL, *fpath, oldv;
- int len = 0;
- int ret;
-
- lastslash = strrchr(path, '/');
- if (!lastslash) { // bug... this shouldn't be possible
- ERROR("cgfsng:copy_parent_file: bad path %s", path);
- return false;
- }
- oldv = *lastslash;
- *lastslash = '\0';
- fpath = must_make_path(path, file, NULL);
- len = lxc_read_from_file(fpath, NULL, 0);
- if (len <= 0)
- goto bad;
- value = must_alloc(len + 1);
- if (lxc_read_from_file(fpath, value, len) != len)
- goto bad;
- free(fpath);
- *lastslash = oldv;
- fpath = must_make_path(path, file, NULL);
- ret = lxc_write_to_file(fpath, value, len, false);
- if (ret < 0)
- SYSERROR("Unable to write %s to %s", value, fpath);
- free(fpath);
- free(value);
- return ret >= 0;
-
-bad:
- SYSERROR("Error reading '%s'", fpath);
- free(fpath);
- free(value);
- return false;
-}
-
-/*
- * Initialize the cpuset hierarchy in first directory of @gname and
- * set cgroup.clone_children so that children inherit settings.
- * Since the h->base_path is populated by init or ourselves, we know
- * it is already initialized.
- */
-bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname)
-{
- char *cgpath, *clonechildrenpath, v, *slash;
-
- if (!string_in_list(h->controllers, "cpuset"))
- return true;
-
- if (*cgname == '/')
- cgname++;
- slash = strchr(cgname, '/');
- if (slash)
- *slash = '\0';
-
- cgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
- if (slash)
- *slash = '/';
- if (mkdir(cgpath, 0755) < 0 && errno != EEXIST) {
- SYSERROR("Failed to create '%s'", cgpath);
- free(cgpath);
- return false;
- }
- clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
- if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */
- free(clonechildrenpath);
- free(cgpath);
- return true;
- }
- if (lxc_read_from_file(clonechildrenpath, &v, 1) < 0) {
- SYSERROR("Failed to read '%s'", clonechildrenpath);
- free(clonechildrenpath);
- free(cgpath);
- return false;
- }
-
- if (v == '1') { /* already set for us by someone else */
- free(clonechildrenpath);
- free(cgpath);
- return true;
- }
-
- /* copy parent's settings */
- if (!copy_parent_file(cgpath, "cpuset.cpus") ||
- !copy_parent_file(cgpath, "cpuset.mems")) {
- free(cgpath);
- free(clonechildrenpath);
- return false;
- }
- free(cgpath);
-
- if (lxc_write_to_file(clonechildrenpath, "1", 1, false) < 0) {
- /* Set clone_children so children inherit our settings */
- SYSERROR("Failed to write 1 to %s", clonechildrenpath);
- free(clonechildrenpath);
- return false;
- }
- free(clonechildrenpath);
- return true;
-}
-
-/*
- * Given two null-terminated lists of strings, return true if any string
- * is in both.
- */
-static bool controller_lists_intersect(char **l1, char **l2)
-{
- int i;
-
- if (!l1 || !l2)
- return false;
-
- for (i = 0; l1[i]; i++) {
- if (string_in_list(l2, l1[i]))
- return true;
- }
- return false;
-}
-
-/*
- * For a null-terminated list of controllers @clist, return true if any of
- * those controllers is already listed the null-terminated list of
- * hierarchies @hlist. Realistically, if one is present, all must be present.
- */
-static bool controller_list_is_dup(struct hierarchy **hlist, char **clist)
-{
- int i;
-
- if (!hlist)
- return false;
- for (i = 0; hlist[i]; i++)
- if (controller_lists_intersect(hlist[i]->controllers, clist))
- return true;
- return false;
-
-}
-
-/*
- * Return true if the controller @entry is found in the null-terminated
- * list of hierarchies @hlist
- */
-static bool controller_found(struct hierarchy **hlist, char *entry)
-{
- int i;
- if (!hlist)
- return false;
-
- for (i = 0; hlist[i]; i++)
- if (string_in_list(hlist[i]->controllers, entry))
- return true;
- return false;
-}
-
-/*
- * Return true if all of the controllers which we require have been found.
- * The required list is freezer and anything in * lxc.cgroup.use.
- */
-static bool all_controllers_found(void)
-{
- char *p, *saveptr = NULL;
- struct hierarchy ** hlist = hierarchies;
-
- if (!controller_found(hlist, "freezer")) {
- ERROR("no freezer controller mountpoint found");
- return false;
- }
-
- if (!cgroup_use)
- return true;
- for (p = strtok_r(cgroup_use, ",", &saveptr); p;
- p = strtok_r(NULL, ",", &saveptr)) {
- if (!controller_found(hlist, p)) {
- ERROR("no %s controller mountpoint found", p);
- return false;
- }
- }
- return true;
-}
-
-/* Return true if the fs type is fuse.lxcfs */
-static bool is_lxcfs(const char *line)
-{
- char *p = strstr(line, " - ");
- if (!p)
- return false;
- return strncmp(p, " - fuse.lxcfs ", 14) == 0;
-}
-
-/*
- * Get the controllers from a mountinfo line
- * There are other ways we could get this info. For lxcfs, field 3
- * is /cgroup/controller-list. For cgroupfs, we could parse the mount
- * options. But we simply assume that the mountpoint must be
- * /sys/fs/cgroup/controller-list
- */
-static char **get_controllers(char **klist, char **nlist, char *line)
-{
- // the fourth field is /sys/fs/cgroup/comma-delimited-controller-list
- int i;
- char *p = line, *p2, *tok, *saveptr = NULL;
- char **aret = NULL;
-
- for (i = 0; i < 4; i++) {
- p = strchr(p, ' ');
- if (!p)
- return NULL;
- p++;
- }
- if (!p)
- return NULL;
- /* note - if we change how mountinfo works, then our caller
- * will need to verify /sys/fs/cgroup/ in this field */
- if (strncmp(p, "/sys/fs/cgroup/", 15) != 0)
- return NULL;
- p += 15;
- p2 = strchr(p, ' ');
- if (!p2) {
- ERROR("corrupt mountinfo");
- return NULL;
- }
- *p2 = '\0';
- for (tok = strtok_r(p, ",", &saveptr); tok;
- tok = strtok_r(NULL, ",", &saveptr)) {
- must_append_controller(klist, nlist, &aret, tok);
- }
-
- return aret;
-}
-
-/* return true if the fstype is cgroup */
-static bool is_cgroupfs(char *line)
-{
- char *p = strstr(line, " - ");
- if (!p)
- return false;
- return strncmp(p, " - cgroup ", 10) == 0;
-}
-
-/* Add a controller to our list of hierarchies */
-static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
-{
- struct hierarchy *new;
- int newentry;
-
- new = must_alloc(sizeof(*new));
- new->controllers = clist;
- new->mountpoint = mountpoint;
- new->base_cgroup = base_cgroup;
- new->fullcgpath = NULL;
-
- newentry = append_null_to_list((void ***)&hierarchies);
- hierarchies[newentry] = new;
-}
-
-/*
- * Get a copy of the mountpoint from @line, which is a line from
- * /proc/self/mountinfo
- */
-static char *get_mountpoint(char *line)
-{
- int i;
- char *p = line, *sret;
- size_t len;
-
- for (i = 0; i < 4; i++) {
- p = strchr(p, ' ');
- if (!p)
- return NULL;
- p++;
- }
- /* we've already stuck a \0 after the mountpoint */
- len = strlen(p);
- sret = must_alloc(len + 1);
- memcpy(sret, p, len);
- sret[len] = '\0';
- return sret;
-}
-
-/*
- * Given a multi-line string, return a null-terminated copy of the
- * current line.
- */
-static char *copy_to_eol(char *p)
-{
- char *p2 = strchr(p, '\n'), *sret;
- size_t len;
-
- if (!p2)
- return NULL;
-
- len = p2 - p;
- sret = must_alloc(len + 1);
- memcpy(sret, p, len);
- sret[len] = '\0';
- return sret;
-}
-
-/*
- * cgline: pointer to character after the first ':' in a line in a
- * \n-terminated /proc/self/cgroup file. Check whether * controller c is
- * present.
- */
-static bool controller_in_clist(char *cgline, char *c)
-{
- char *tok, *saveptr = NULL, *eol, *tmp;
- size_t len;
-
- eol = strchr(cgline, ':');
- if (!eol)
- return false;
-
- len = eol - cgline;
- tmp = alloca(len + 1);
- memcpy(tmp, cgline, len);
- tmp[len] = '\0';
-
- for (tok = strtok_r(tmp, ",", &saveptr); tok;
- tok = strtok_r(NULL, ",", &saveptr)) {
- if (strcmp(tok, c) == 0)
- return true;
- }
- return false;
-}
-
-/*
- * @basecginfo is a copy of /proc/$$/cgroup. Return the current
- * cgroup for @controller
- */
-static char *get_current_cgroup(char *basecginfo, char *controller)
-{
- char *p = basecginfo;
-
- while (1) {
- p = strchr(p, ':');
- if (!p)
- return NULL;
- p++;
- if (controller_in_clist(p, controller)) {
- p = strchr(p, ':');
- if (!p)
- return NULL;
- p++;
- return copy_to_eol(p);
- }
-
- p = strchr(p, '\n');
- if (!p)
- return NULL;
- p++;
- }
-}
-
-#define BATCH_SIZE 50
-static void batch_realloc(char **mem, size_t oldlen, size_t newlen)
-{
- int newbatches = (newlen / BATCH_SIZE) + 1;
- int oldbatches = (oldlen / BATCH_SIZE) + 1;
-
- if (!*mem || newbatches > oldbatches) {
- *mem = must_realloc(*mem, newbatches * BATCH_SIZE);
- }
-}
-
-static void append_line(char **dest, size_t oldlen, char *new, size_t newlen)
-{
- size_t full = oldlen + newlen;
-
- batch_realloc(dest, oldlen, full + 1);
-
- memcpy(*dest + oldlen, new, newlen + 1);
-}
-
-/* Slurp in a whole file */
-static char *read_file(char *fnam)
-{
- FILE *f;
- char *line = NULL, *buf = NULL;
- size_t len = 0, fulllen = 0;
- int linelen;
-
- f = fopen(fnam, "r");
- if (!f)
- return NULL;
- while ((linelen = getline(&line, &len, f)) != -1) {
- append_line(&buf, fulllen, line, linelen);
- fulllen += linelen;
- }
- fclose(f);
- free(line);
- return buf;
-}
-
-/*
- * Given a hierarchy @mountpoint and base @path, verify that we can create
- * directories underneath it.
- */
-static bool test_writeable(char *mountpoint, char *path)
-{
- char *fullpath = must_make_path(mountpoint, path, NULL);
- int ret;
-
- ret = access(fullpath, W_OK);
- free(fullpath);
- return ret == 0;
-}
-
-static void must_append_string(char ***list, char *entry)
-{
- int newentry = append_null_to_list((void ***)list);
- char *copy;
-
- copy = must_copy_string(entry);
- (*list)[newentry] = copy;
-}
-
-static void get_existing_subsystems(char ***klist, char ***nlist)
-{
- FILE *f;
- char *line = NULL;
- size_t len = 0;
-
- if ((f = fopen("/proc/self/cgroup", "r")) == NULL)
- return;
- while (getline(&line, &len, f) != -1) {
- char *p, *p2, *tok, *saveptr = NULL;
- p = strchr(line, ':');
- if (!p)
- continue;
- p++;
- p2 = strchr(p, ':');
- if (!p2)
- continue;
- *p2 = '\0';
- for (tok = strtok_r(p, ",", &saveptr); tok;
- tok = strtok_r(NULL, ",", &saveptr)) {
- if (strncmp(tok, "name=", 5) == 0)
- must_append_string(nlist, tok);
- else
- must_append_string(klist, tok);
- }
- }
-
- free(line);
- fclose(f);
-}
-
-static void trim(char *s)
-{
- size_t len = strlen(s);
- while (s[len-1] == '\n')
- s[--len] = '\0';
-}
-
-static void print_init_debuginfo(struct cgfsng_handler_data *d)
-{
- int i;
-
- if (!getenv("LXC_DEBUG_CGFSNG"))
- return;
-
- printf("Cgroup information:\n");
- printf(" container name: %s\n", d->name);
- printf(" lxc.cgroup.use: %s\n", cgroup_use ? cgroup_use : "(none)");
- printf(" lxc.cgroup.pattern: %s\n", d->cgroup_pattern);
- printf(" cgroup: %s\n", d->container_cgroup ? d->container_cgroup : "(none)");
- if (!hierarchies) {
- printf(" No hierarchies found.\n");
- return;
- }
- printf(" Hierarchies:\n");
- for (i = 0; hierarchies[i]; i++) {
- struct hierarchy *h = hierarchies[i];
- int j;
- printf(" %d: base_cgroup %s\n", i, h->base_cgroup);
- printf(" mountpoint %s\n", h->mountpoint);
- printf(" controllers:\n");
- for (j = 0; h->controllers[j]; j++)
- printf(" %d: %s\n", j, h->controllers[j]);
- }
-}
-
-static void print_basecg_debuginfo(char *basecginfo, char **klist, char **nlist)
-{
- int k;
- if (!getenv("LXC_DEBUG_CGFSNG"))
- return;
-
- printf("basecginfo is %s\n", basecginfo);
-
- for (k = 0; klist[k]; k++)
- printf("kernel subsystem %d: %s\n", k, klist[k]);
- for (k = 0; nlist[k]; k++)
- printf("named subsystem %d: %s\n", k, nlist[k]);
-}
-
-/*
- * At startup, parse_hierarchies finds all the info we need about
- * cgroup mountpoints and current cgroups, and stores it in @d.
- */
-static bool parse_hierarchies(void)
-{
- FILE *f;
- char * line = NULL, *basecginfo;
- char **klist = NULL, **nlist = NULL;
- size_t len = 0;
-
- /*
- * Root spawned containers escape the current cgroup, so use init's
- * cgroups as our base in that case.
- */
- if (geteuid())
- basecginfo = read_file("/proc/self/cgroup");
- else
- basecginfo = read_file("/proc/1/cgroup");
- if (!basecginfo)
- return false;
-
- if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) {
- SYSERROR("Failed opening /proc/self/mountinfo");
- return false;
- }
-
- get_existing_subsystems(&klist, &nlist);
-
- print_basecg_debuginfo(basecginfo, klist, nlist);
-
- /* we support simple cgroup mounts and lxcfs mounts */
- while (getline(&line, &len, f) != -1) {
- char **controller_list = NULL;
- char *mountpoint, *base_cgroup;
-
- if (!is_lxcfs(line) && !is_cgroupfs(line))
- continue;
-
- controller_list = get_controllers(klist, nlist, line);
- if (!controller_list)
- continue;
-
- if (controller_list_is_dup(hierarchies, controller_list)) {
- free(controller_list);
- continue;
- }
-
- mountpoint = get_mountpoint(line);
- if (!mountpoint) {
- ERROR("Error reading mountinfo: bad line '%s'", line);
- free_string_list(controller_list);
- continue;
- }
-
- base_cgroup = get_current_cgroup(basecginfo, controller_list[0]);
- if (!base_cgroup) {
- ERROR("Failed to find current cgroup for controller '%s'", controller_list[0]);
- free_string_list(controller_list);
- free(mountpoint);
- continue;
- }
- trim(base_cgroup);
- prune_init_scope(base_cgroup);
- if (!test_writeable(mountpoint, base_cgroup)) {
- free_string_list(controller_list);
- free(mountpoint);
- free(base_cgroup);
- continue;
- }
- add_controller(controller_list, mountpoint, base_cgroup);
- }
-
- free_string_list(klist);
- free_string_list(nlist);
-
- free(basecginfo);
-
- fclose(f);
- free(line);
-
- /* verify that all controllers in cgroup.use and all crucial
- * controllers are accounted for
- */
- if (!all_controllers_found())
- return false;
-
- return true;
-}
-
-static bool collect_hierarchy_info(void)
-{
- const char *tmp;
- errno = 0;
- tmp = lxc_global_config_value("lxc.cgroup.use");
- if (!cgroup_use && errno != 0) { // lxc.cgroup.use can be NULL
- SYSERROR("cgfsng: error reading list of cgroups to use");
- return false;
- }
- cgroup_use = must_copy_string(tmp);
-
- return parse_hierarchies();
-}
-
-static void *cgfsng_init(const char *name)
-{
- struct cgfsng_handler_data *d;
- const char *cgroup_pattern;
-
- d = must_alloc(sizeof(*d));
- memset(d, 0, sizeof(*d));
-
- d->name = must_copy_string(name);
-
- cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
- if (!cgroup_pattern) { // lxc.cgroup.pattern is only NULL on error
- ERROR("Error getting cgroup pattern");
- goto out_free;
- }
- d->cgroup_pattern = must_copy_string(cgroup_pattern);
-
- print_init_debuginfo(d);
-
- return d;
-
-out_free:
- free_handler_data(d);
- return NULL;
-}
-
-/*
- * Concatenate all passed-in strings into one path. Do not fail. If any piece is
- * not prefixed with '/', add a '/'.
- */
-static char *must_make_path(const char *first, ...)
-{
- va_list args;
- char *cur, *dest;
- size_t full_len = strlen(first);
-
- dest = must_copy_string(first);
-
- va_start(args, first);
- while ((cur = va_arg(args, char *)) != NULL) {
- full_len += strlen(cur);
- if (cur[0] != '/')
- full_len++;
- dest = must_realloc(dest, full_len + 1);
- if (cur[0] != '/')
- strcat(dest, "/");
- strcat(dest, cur);
- }
- va_end(args);
-
- return dest;
-}
-
-static int cgroup_rmdir(char *dirname)
-{
- struct dirent dirent, *direntp;
- DIR *dir;
- int r = 0;
-
- dir = opendir(dirname);
- if (!dir)
- return -1;
-
- while (!readdir_r(dir, &dirent, &direntp)) {
- struct stat mystat;
- char *pathname;
-
- if (!direntp)
- break;
-
- if (!strcmp(direntp->d_name, ".") ||
- !strcmp(direntp->d_name, ".."))
- continue;
-
- pathname = must_make_path(dirname, direntp->d_name, NULL);
-
- if (lstat(pathname, &mystat)) {
- if (!r)
- WARN("failed to stat %s", pathname);
- r = -1;
- goto next;
- }
-
- if (!S_ISDIR(mystat.st_mode))
- goto next;
- if (cgroup_rmdir(pathname) < 0)
- r = -1;
-next:
- free(pathname);
- }
-
- if (rmdir(dirname) < 0) {
- if (!r)
- WARN("%s: failed to delete %s: %m", __func__, dirname);
- r = -1;
- }
-
- if (closedir(dir) < 0) {
- if (!r)
- WARN("%s: failed to delete %s: %m", __func__, dirname);
- r = -1;
- }
- return r;
-}
-
-static int rmdir_wrapper(void *data)
-{
- char *path = data;
-
- if (setresgid(0,0,0) < 0)
- SYSERROR("Failed to setgid to 0");
- if (setresuid(0,0,0) < 0)
- SYSERROR("Failed to setuid to 0");
- if (setgroups(0, NULL) < 0)
- SYSERROR("Failed to clear groups");
-
- return cgroup_rmdir(path);
-}
-
-void recursive_destroy(char *path, struct lxc_conf *conf)
-{
- int r;
- if (conf && !lxc_list_empty(&conf->id_map))
- r = userns_exec_1(conf, rmdir_wrapper, path);
- else
- r = cgroup_rmdir(path);
-
- if (r < 0)
- ERROR("Error destroying %s", path);
-}
-
-static void cgfsng_destroy(void *hdata, struct lxc_conf *conf)
-{
- struct cgfsng_handler_data *d = hdata;
-
- if (!d)
- return;
-
- if (d->container_cgroup && hierarchies) {
- int i;
- for (i = 0; hierarchies[i]; i++) {
- struct hierarchy *h = hierarchies[i];
- if (h->fullcgpath) {
- recursive_destroy(h->fullcgpath, conf);
- free(h->fullcgpath);
- h->fullcgpath = NULL;
- }
- }
- }
-
- free_handler_data(d);
-}
-
-struct cgroup_ops *cgfsng_ops_init(void)
-{
- if (!collect_hierarchy_info())
- return NULL;
- return &cgfsng_ops;
-}
-
-static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname)
-{
- h->fullcgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
- if (dir_exists(h->fullcgpath)) // it must not already exist
- return false;
- if (!handle_cpuset_hierarchy(h, cgname))
- return false;
- return mkdir_p(h->fullcgpath, 0755) == 0;
-}
-
-static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname)
-{
- if (rmdir(h->fullcgpath) < 0)
- SYSERROR("Failed to clean up cgroup %s from failed creation attempt", h->fullcgpath);
- free(h->fullcgpath);
- h->fullcgpath = NULL;
-}
-
-/*
- * Try to create the same cgroup in all hierarchies.
- * Start with cgroup_pattern; next cgroup_pattern-1, -2, ..., -999
- */
-static inline bool cgfsng_create(void *hdata)
-{
- struct cgfsng_handler_data *d = hdata;
- char *tmp, *cgname, *offset;
- int i, idx = 0;
- size_t len;
-
- if (!d)
- return false;
- if (d->container_cgroup) {
- WARN("cgfsng_create called a second time");
- return false;
- }
-
- tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
- if (!tmp) {
- ERROR("Failed expanding cgroup name pattern");
- return false;
- }
- len = strlen(tmp) + 5; // leave room for -NNN\0
- cgname = must_alloc(len);
- strcpy(cgname, tmp);
- free(tmp);
- offset = cgname + len - 5;
-
-again:
- if (idx == 1000) {
- ERROR("Too many conflicting cgroup names");
- goto out_free;
- }
- if (idx)
- snprintf(offset, 5, "-%d", idx);
- for (i = 0; hierarchies[i]; i++) {
- if (!create_path_for_hierarchy(hierarchies[i], cgname)) {
- int j;
- SYSERROR("Failed to create %s: %s", hierarchies[i]->fullcgpath, strerror(errno));
- free(hierarchies[i]->fullcgpath);
- hierarchies[i]->fullcgpath = NULL;
- for (j = 0; j < i; j++)
- remove_path_for_hierarchy(hierarchies[j], cgname);
- idx++;
- goto again;
- }
- }
- /* Done */
- d->container_cgroup = cgname;
- return true;
-
-out_free:
- free(cgname);
- return false;
-}
-
-static const char *cgfsng_canonical_path(void *hdata)
-{
- struct cgfsng_handler_data *d = hdata;
-
- return d->container_cgroup;
-}
-
-static bool cgfsng_enter(void *hdata, pid_t pid)
-{
- char pidstr[25];
- int i, len;
-
- len = snprintf(pidstr, 25, "%d", pid);
- if (len < 0 || len > 25)
- return false;
-
- for (i = 0; hierarchies[i]; i++) {
- char *fullpath = must_make_path(hierarchies[i]->fullcgpath,
- "cgroup.procs", NULL);
- if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
- SYSERROR("Failed to enter %s", fullpath);
- free(fullpath);
- return false;
- }
- free(fullpath);
- }
-
- return true;
-}
-
-struct chown_data {
- struct cgfsng_handler_data *d;
- uid_t origuid; // target uid in parent namespace
-};
-
-/*
- * chgrp the container cgroups to container group. We leave
- * the container owner as cgroup owner. So we must make the
- * directories 775 so that the container can create sub-cgroups.
- *
- * Also chown the tasks and cgroup.procs files. Those may not
- * exist depending on kernel version.
- */
-static int chown_cgroup_wrapper(void *data)
-{
- struct chown_data *arg = data;
- uid_t destuid;
- int i;
-
- if (setresgid(0,0,0) < 0)
- SYSERROR("Failed to setgid to 0");
- if (setresuid(0,0,0) < 0)
- SYSERROR("Failed to setuid to 0");
- if (setgroups(0, NULL) < 0)
- SYSERROR("Failed to clear groups");
-
- destuid = get_ns_uid(arg->origuid);
-
- for (i = 0; hierarchies[i]; i++) {
- char *fullpath, *path = hierarchies[i]->fullcgpath;
-
- if (chown(path, destuid, 0) < 0) {
- SYSERROR("Error chowning %s to %d", path, (int) destuid);
- return -1;
- }
-
- if (chmod(path, 0775) < 0) {
- SYSERROR("Error chmoding %s", path);
- return -1;
- }
-
- /*
- * Failures to chown these are inconvenient but not detrimental
- * We leave these owned by the container launcher, so that container
- * root can write to the files to attach. We chmod them 664 so that
- * container systemd can write to the files (which systemd in wily
- * insists on doing)
- */
- fullpath = must_make_path(path, "tasks", NULL);
- if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
- WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
- if (chmod(fullpath, 0664) < 0)
- WARN("Error chmoding %s: %m", path);
- free(fullpath);
-
- fullpath = must_make_path(path, "cgroup.procs", NULL);
- if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
- WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
- if (chmod(fullpath, 0664) < 0)
- WARN("Error chmoding %s: %m", path);
- free(fullpath);
- }
-
- return 0;
-}
-
-static bool cgfsns_chown(void *hdata, struct lxc_conf *conf)
-{
- struct cgfsng_handler_data *d = hdata;
- struct chown_data wrap;
-
- if (!d)
- return false;
-
- if (lxc_list_empty(&conf->id_map))
- return true;
-
- wrap.d = d;
- wrap.origuid = geteuid();
-
- if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap) < 0) {
- ERROR("Error requesting cgroup chown in new namespace");
- return false;
- }
-
- return true;
-}
-
-/*
- * We've safe-mounted a tmpfs as parent, so we don't need to protect against
- * symlinks any more - just use mount
- */
-
-/* mount cgroup-full if requested */
-static int mount_cgroup_full(int type, struct hierarchy *h, char *dest,
- char *container_cgroup)
-{
- if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED)
- return 0;
- if (mount(h->mountpoint, dest, "cgroup", MS_BIND, NULL) < 0) {
- SYSERROR("Error bind-mounting %s cgroup onto %s", h->mountpoint,
- dest);
- return -1;
- }
- if (type != LXC_AUTO_CGROUP_FULL_RW) {
- unsigned long flags = MS_BIND | MS_NOSUID | MS_NOEXEC | MS_NODEV |
- MS_REMOUNT | MS_RDONLY;
- if (mount(NULL, dest, "cgroup", flags, NULL) < 0) {
- SYSERROR("Error remounting %s readonly", dest);
- return -1;
- }
- }
-
- INFO("Bind mounted %s onto %s", h->mountpoint, dest);
- if (type != LXC_AUTO_CGROUP_FULL_MIXED)
- return 0;
-
- /* mount just the container path rw */
- char *source = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL);
- char *rwpath = must_make_path(dest, h->base_cgroup, container_cgroup, NULL);
- if (mount(source, rwpath, "cgroup", MS_BIND, NULL) < 0)
- WARN("Failed to mount %s read-write: %m", rwpath);
- INFO("Made %s read-write", rwpath);
- free(rwpath);
- free(source);
- return 0;
-}
-
-/* cgroup-full:* is done, no need to create subdirs */
-static bool cg_mount_needs_subdirs(int type)
-{
- if (type >= LXC_AUTO_CGROUP_FULL_RO)
- return false;
- return true;
-}
-
-/*
- * After $rootfs/sys/fs/container/controller/the/cg/path has been
- * created, remount controller ro if needed and bindmount the
- * cgroupfs onto controll/the/cg/path
- */
-static int
-do_secondstage_mounts_if_needed(int type, struct hierarchy *h,
- char *controllerpath, char *cgpath,
- const char *container_cgroup)
-{
- if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_MIXED) {
- if (mount(controllerpath, controllerpath, "cgroup", MS_BIND, NULL) < 0) {
- SYSERROR("Error bind-mounting %s", controllerpath);
- return -1;
- }
- if (mount(controllerpath, controllerpath, "cgroup",
- MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) < 0) {
- SYSERROR("Error remounting %s read-only", controllerpath);
- return -1;
- }
- INFO("Remounted %s read-only", controllerpath);
- }
- char *sourcepath = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL);
- int flags = MS_BIND;
- if (type == LXC_AUTO_CGROUP_RO)
- flags |= MS_RDONLY;
- INFO("Mounting %s onto %s", sourcepath, cgpath);
- if (mount(sourcepath, cgpath, "cgroup", flags, NULL) < 0) {
- free(sourcepath);
- SYSERROR("Error mounting cgroup %s onto %s", h->controllers[0],
- cgpath);
- return -1;
- }
- free(sourcepath);
- INFO("Completed second stage cgroup automounts for %s", cgpath);
- return 0;
-}
-
-static bool cgfsng_mount(void *hdata, const char *root, int type)
-{
- struct cgfsng_handler_data *d = hdata;
- char *tmpfspath = NULL;
- bool retval = false;
- int i;
-
- if ((type & LXC_AUTO_CGROUP_MASK) == 0)
- return true;
-
- if (cgns_supported())
- return true;
-
- tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL);
-
- if (type == LXC_AUTO_CGROUP_NOSPEC)
- type = LXC_AUTO_CGROUP_MIXED;
- else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
- type = LXC_AUTO_CGROUP_FULL_MIXED;
-
- /* Mount tmpfs */
- if (safe_mount("cgroup_root", tmpfspath, "tmpfs",
- MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
- "size=10240k,mode=755",
- root) < 0)
- goto bad;
-
- for (i = 0; hierarchies[i]; i++) {
- char *controllerpath, *path2;
- struct hierarchy *h = hierarchies[i];
- char *controller = strrchr(h->mountpoint, '/');
- int r;
-
- if (!controller)
- continue;
- controller++;
- controllerpath = must_make_path(tmpfspath, controller, NULL);
- if (dir_exists(controllerpath)) {
- free(controllerpath);
- continue;
- }
- if (mkdir(controllerpath, 0755) < 0) {
- SYSERROR("Error creating cgroup path: %s", controllerpath);
- free(controllerpath);
- goto bad;
- }
- if (mount_cgroup_full(type, h, controllerpath, d->container_cgroup) < 0) {
- free(controllerpath);
- goto bad;
- }
- if (!cg_mount_needs_subdirs(type)) {
- free(controllerpath);
- continue;
- }
- path2 = must_make_path(controllerpath, h->base_cgroup, d->container_cgroup, NULL);
- if (mkdir_p(path2, 0755) < 0) {
- free(controllerpath);
- goto bad;
- }
-
- r = do_secondstage_mounts_if_needed(type, h, controllerpath, path2,
- d->container_cgroup);
- free(controllerpath);
- free(path2);
- if (r < 0)
- goto bad;
- }
- retval = true;
-
-bad:
- free(tmpfspath);
- return retval;
-}
-
-static int recursive_count_nrtasks(char *dirname)
-{
- struct dirent dirent, *direntp;
- DIR *dir;
- int count = 0, ret;
- char *path;
-
- dir = opendir(dirname);
- if (!dir)
- return 0;
-
- while (!readdir_r(dir, &dirent, &direntp)) {
- struct stat mystat;
-
- if (!direntp)
- break;
-
- if (!strcmp(direntp->d_name, ".") ||
- !strcmp(direntp->d_name, ".."))
- continue;
-
- path = must_make_path(dirname, direntp->d_name, NULL);
-
- if (lstat(path, &mystat))
- goto next;
-
- if (!S_ISDIR(mystat.st_mode))
- goto next;
-
- count += recursive_count_nrtasks(path);
-next:
- free(path);
- }
-
- path = must_make_path(dirname, "cgroup.procs", NULL);
- ret = lxc_count_file_lines(path);
- if (ret != -1)
- count += ret;
- free(path);
-
- (void) closedir(dir);
-
- return count;
-}
-
-static int cgfsng_nrtasks(void *hdata) {
- struct cgfsng_handler_data *d = hdata;
- char *path;
- int count;
-
- if (!d || !d->container_cgroup || !hierarchies)
- return -1;
- path = must_make_path(hierarchies[0]->fullcgpath, NULL);
- count = recursive_count_nrtasks(path);
- free(path);
- return count;
-}
-
-/* Only root needs to escape to the cgroup of its init */
-static bool cgfsng_escape()
-{
- struct cgfsng_handler_data *d;
- int i;
- bool ret = false;
-
- if (geteuid())
- return true;
-
- d = cgfsng_init("criu-temp-cgfsng");
- if (!d) {
- ERROR("cgfsng_init failed");
- return false;
- }
-
- for (i = 0; hierarchies[i]; i++) {
- char *fullpath = must_make_path(hierarchies[i]->mountpoint,
- hierarchies[i]->base_cgroup,
- "cgroup.procs", NULL);
- if (lxc_write_to_file(fullpath, "0", 2, false) != 0) {
- SYSERROR("Failed to escape to %s", fullpath);
- free(fullpath);
- goto out;
- }
- free(fullpath);
- }
-
- ret = true;
-out:
- free_handler_data(d);
- return ret;
-}
-
-#define THAWED "THAWED"
-#define THAWED_LEN (strlen(THAWED))
-
-static bool cgfsng_unfreeze(void *hdata)
-{
- char *fullpath;
- struct hierarchy *h = get_hierarchy("freezer");
-
- if (!h)
- return false;
- fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL);
- if (lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false) != 0) {
- free(fullpath);
- return false;
- }
- free(fullpath);
- return true;
-}
-
-static const char *cgfsng_get_cgroup(void *hdata, const char *subsystem)
-{
- struct hierarchy *h = get_hierarchy(subsystem);
- if (!h)
- return NULL;
-
- return h->fullcgpath ? h->fullcgpath + strlen(h->mountpoint) : NULL;
-}
-
-/*
- * Given a cgroup path returned from lxc_cmd_get_cgroup_path, build a
- * full path, which must be freed by the caller.
- */
-static char *build_full_cgpath_from_monitorpath(struct hierarchy *h,
- const char *inpath,
- const char *filename)
-{
- /*
- * XXX Remove this case after 2.0 release. It's for dealing with
- * containers spawned under the old buggy cgfsng which wasn't around
- * for long.
- */
- if (strncmp(inpath, "/sys/fs/cgroup/", 15) == 0)
- return must_make_path(inpath, filename, NULL);
- return must_make_path(h->mountpoint, inpath, filename, NULL);
-}
-
-static bool cgfsng_attach(const char *name, const char *lxcpath, pid_t pid)
-{
- char pidstr[25];
- int i, len;
-
- len = snprintf(pidstr, 25, "%d", pid);
- if (len < 0 || len > 25)
- return false;
-
- for (i = 0; hierarchies[i]; i++) {
- char *path, *fullpath;
- struct hierarchy *h = hierarchies[i];
-
- path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]);
- if (!path) // not running
- continue;
-
- fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs");
- free(path);
- if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
- SYSERROR("Failed to attach %d to %s", (int)pid, fullpath);
- free(fullpath);
- return false;
- }
- free(fullpath);
- }
-
- return true;
-}
-
-/*
- * Called externally (i.e. from 'lxc-cgroup') to query cgroup limits.
- * Here we don't have a cgroup_data set up, so we ask the running
- * container through the commands API for the cgroup path
- */
-static int cgfsng_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
-{
- char *subsystem, *p, *path;
- struct hierarchy *h;
- int ret = -1;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
- if (!path) // not running
- return -1;
-
- h = get_hierarchy(subsystem);
- if (h) {
- char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
- ret = lxc_read_from_file(fullpath, value, len);
- free(fullpath);
- }
-
- free(path);
-
- return ret;
-}
-
-/*
- * Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits.
- * Here we don't have a cgroup_data set up, so we ask the running
- * container through the commands API for the cgroup path
- */
-static int cgfsng_set(const char *filename, const char *value, const char *name, const char *lxcpath)
-{
- char *subsystem, *p, *path;
- struct hierarchy *h;
- int ret = -1;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
- if (!path) // not running
- return -1;
-
- h = get_hierarchy(subsystem);
- if (h) {
- char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
- ret = lxc_write_to_file(fullpath, value, strlen(value), false);
- free(fullpath);
- }
-
- free(path);
-
- return ret;
-}
-
-/*
- * Called from setup_limits - here we have the container's cgroup_data because
- * we created the cgroups
- */
-static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfsng_handler_data *d)
-{
- char *subsystem = NULL, *p;
- int ret = -1;
- struct hierarchy *h;
-
- subsystem = alloca(strlen(filename) + 1);
- strcpy(subsystem, filename);
- if ((p = strchr(subsystem, '.')) != NULL)
- *p = '\0';
-
- h = get_hierarchy(subsystem);
- if (h) {
- char *fullpath = must_make_path(h->fullcgpath, filename, NULL);
- ret = lxc_write_to_file(fullpath, value, strlen(value), false);
- free(fullpath);
- }
- return ret;
-}
-
-static bool cgfsng_setup_limits(void *hdata, struct lxc_list *cgroup_settings,
- bool do_devices)
-{
- struct cgfsng_handler_data *d = hdata;
- struct lxc_list *iterator, *sorted_cgroup_settings, *next;
- struct lxc_cgroup *cg;
- bool ret = false;
-
- if (lxc_list_empty(cgroup_settings))
- return true;
-
- sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
- if (!sorted_cgroup_settings) {
- return false;
- }
-
- lxc_list_for_each(iterator, sorted_cgroup_settings) {
- cg = iterator->elem;
-
- if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
- if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
- if (do_devices && (errno == EACCES || errno == EPERM)) {
- WARN("Error setting %s to %s for %s",
- cg->subsystem, cg->value, d->name);
- continue;
- }
- SYSERROR("Error setting %s to %s for %s",
- cg->subsystem, cg->value, d->name);
- goto out;
- }
- }
-
- DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
- }
-
- ret = true;
- INFO("cgroup has been setup");
-out:
- lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
- lxc_list_del(iterator);
- free(iterator);
- }
- free(sorted_cgroup_settings);
- return ret;
-}
-
-static struct cgroup_ops cgfsng_ops = {
- .init = cgfsng_init,
- .destroy = cgfsng_destroy,
- .create = cgfsng_create,
- .enter = cgfsng_enter,
- .canonical_path = cgfsng_canonical_path,
- .escape = cgfsng_escape,
- .get_cgroup = cgfsng_get_cgroup,
- .get = cgfsng_get,
- .set = cgfsng_set,
- .unfreeze = cgfsng_unfreeze,
- .setup_limits = cgfsng_setup_limits,
- .name = "cgroupfs-ng",
- .attach = cgfsng_attach,
- .chown = cgfsns_chown,
- .mount_cgroup = cgfsng_mount,
- .nrtasks = cgfsng_nrtasks,
- .driver = CGFSNG,
-
- /* unsupported */
- .create_legacy = NULL,
-};
diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
deleted file mode 100644
index 4da891d..0000000
--- a/src/lxc/cgmanager.c
+++ /dev/null
@@ -1,1672 +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 <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pthread.h>
-#include <grp.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <sys/inotify.h>
-#include <sys/mount.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <poll.h>
-
-#include "bdev.h"
-#include "error.h"
-#include "commands.h"
-#include "list.h"
-#include "conf.h"
-#include "utils.h"
-#include "log.h"
-#include "cgroup.h"
-#include "start.h"
-#include "state.h"
-
-#define CGM_SUPPORTS_GET_ABS 3
-#define CGM_SUPPORTS_NAMED 4
-#define CGM_SUPPORTS_MULT_CONTROLLERS 10
-
-#ifdef HAVE_CGMANAGER
-lxc_log_define(lxc_cgmanager, lxc);
-
-#include <nih-dbus/dbus_connection.h>
-#include <cgmanager/cgmanager-client.h>
-#include <nih/alloc.h>
-#include <nih/error.h>
-#include <nih/string.h>
-
-struct cgm_data {
- char *name;
- char *cgroup_path;
- const char *cgroup_pattern;
-};
-
-static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void lock_mutex(pthread_mutex_t *l)
-{
- int ret;
-
- if ((ret = pthread_mutex_lock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
- exit(1);
- }
-}
-
-static void unlock_mutex(pthread_mutex_t *l)
-{
- int ret;
-
- if ((ret = pthread_mutex_unlock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
- exit(1);
- }
-}
-
-void cgm_lock(void)
-{
- lock_mutex(&cgm_mutex);
-}
-
-void cgm_unlock(void)
-{
- unlock_mutex(&cgm_mutex);
-}
-
-#ifdef HAVE_PTHREAD_ATFORK
-__attribute__((constructor))
-static void process_lock_setup_atfork(void)
-{
- pthread_atfork(cgm_lock, cgm_unlock, cgm_unlock);
-}
-#endif
-
-static NihDBusProxy *cgroup_manager = NULL;
-static int32_t api_version;
-
-static struct cgroup_ops cgmanager_ops;
-static int nr_subsystems;
-static char **subsystems, **subsystems_inone;
-static bool dbus_threads_initialized = false;
-static void cull_user_controllers(void);
-
-static void cgm_dbus_disconnect(void)
-{
- if (cgroup_manager) {
- dbus_connection_flush(cgroup_manager->connection);
- dbus_connection_close(cgroup_manager->connection);
- nih_free(cgroup_manager);
- }
- cgroup_manager = NULL;
- cgm_unlock();
-}
-
-#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
-static bool cgm_dbus_connect(void)
-{
- DBusError dbus_error;
- static DBusConnection *connection;
-
- cgm_lock();
- if (!dbus_threads_initialized) {
- // tell dbus to do struct locking for thread safety
- dbus_threads_init_default();
- dbus_threads_initialized = true;
- }
-
- dbus_error_init(&dbus_error);
-
- connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error);
- if (!connection) {
- DEBUG("Failed opening dbus connection: %s: %s",
- dbus_error.name, dbus_error.message);
- dbus_error_free(&dbus_error);
- cgm_unlock();
- return false;
- }
- dbus_connection_set_exit_on_disconnect(connection, FALSE);
- dbus_error_free(&dbus_error);
- cgroup_manager = nih_dbus_proxy_new(NULL, connection,
- NULL /* p2p */,
- "/org/linuxcontainers/cgmanager", NULL, NULL);
- dbus_connection_unref(connection);
- if (!cgroup_manager) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("Error opening cgmanager proxy: %s", nerr->message);
- nih_free(nerr);
- cgm_dbus_disconnect();
- return false;
- }
-
- // get the api version
- if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("Error cgroup manager api version: %s", nerr->message);
- nih_free(nerr);
- cgm_dbus_disconnect();
- return false;
- }
- if (api_version < CGM_SUPPORTS_NAMED)
- cull_user_controllers();
- return true;
-}
-
-static bool cgm_all_controllers_same;
-
-/*
- * Check whether we can use "all" when talking to cgmanager.
- * We check two things:
- * 1. whether cgmanager is new enough to support this.
- * 2. whether the task we are interested in is in the same
- * cgroup for all controllers.
- * In cgm_init (before an lxc-start) we care about our own
- * cgroup. In cgm_attach, we care about the target task's
- * cgroup.
- */
-static void check_supports_multiple_controllers(pid_t pid)
-{
- FILE *f;
- char *line = NULL, *prevpath = NULL;
- size_t sz = 0;
- char path[100];
-
- cgm_all_controllers_same = false;
-
- if (pid == -1)
- sprintf(path, "/proc/self/cgroup");
- else
- sprintf(path, "/proc/%d/cgroup", pid);
- f = fopen(path, "r");
- if (!f)
- return;
-
- cgm_all_controllers_same = true;
-
- while (getline(&line, &sz, f) != -1) {
- /* file format: hierarchy:subsystems:group */
- char *colon;
- if (!line[0])
- continue;
-
- colon = strchr(line, ':');
- if (!colon)
- continue;
- colon = strchr(colon+1, ':');
- if (!colon)
- continue;
- colon++;
- if (!prevpath) {
- prevpath = alloca(strlen(colon)+1);
- strcpy(prevpath, colon);
- continue;
- }
- if (strcmp(prevpath, colon) != 0) {
- cgm_all_controllers_same = false;
- break;
- }
- }
-
- fclose(f);
- free(line);
-}
-
-static int send_creds(int sock, int rpid, int ruid, int rgid)
-{
- struct msghdr msg = { 0 };
- struct iovec iov;
- struct cmsghdr *cmsg;
- struct ucred cred = {
- .pid = rpid,
- .uid = ruid,
- .gid = rgid,
- };
- char cmsgbuf[CMSG_SPACE(sizeof(cred))];
- char buf[1];
- buf[0] = 'p';
-
- msg.msg_control = cmsgbuf;
- msg.msg_controllen = sizeof(cmsgbuf);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
- memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
-
- iov.iov_base = buf;
- iov.iov_len = sizeof(buf);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- if (sendmsg(sock, &msg, 0) < 0)
- return -1;
- return 0;
-}
-
-static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
-{
- bool ret = true;
- if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
- cgroup_path, existed) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
- nih_free(nerr);
- ERROR("Failed to create %s:%s", controller, cgroup_path);
- ret = false;
- }
-
- return ret;
-}
-
-/*
- * Escape to the root cgroup if we are root, so that the container will
- * be in "/lxc/c1" rather than "/user/..../c1"
- * called internally with connection already open
- */
-static bool cgm_escape(void *hdata)
-{
- bool ret = true, cgm_needs_disconnect = false;
- pid_t me = getpid();
- char **slist = subsystems;
- int i;
-
- if (!cgroup_manager) {
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
- cgm_needs_disconnect = true;
- }
-
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
-
- for (i = 0; slist[i]; i++) {
- if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
- slist[i], "/", me) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
- slist[i], nerr->message);
- nih_free(nerr);
- ret = false;
- break;
- }
- }
-
- if (cgm_needs_disconnect)
- cgm_dbus_disconnect();
-
- return ret;
-}
-
-struct chown_data {
- const char *cgroup_path;
- uid_t origuid;
-};
-
-static int do_chown_cgroup(const char *controller, const char *cgroup_path,
- uid_t newuid)
-{
- int sv[2] = {-1, -1}, optval = 1, ret = -1;
- char buf[1];
- struct pollfd fds;
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
- SYSERROR("Error creating socketpair");
- goto out;
- }
- if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
- SYSERROR("setsockopt failed");
- goto out;
- }
- if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
- SYSERROR("setsockopt failed");
- goto out;
- }
- if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
- cgroup_path, sv[1]) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
- nih_free(nerr);
- goto out;
- }
- /* now send credentials */
-
- fds.fd = sv[0];
- fds.events = POLLIN;
- fds.revents = 0;
- if (poll(&fds, 1, -1) <= 0) {
- ERROR("Error getting go-ahead from server: %s", strerror(errno));
- goto out;
- }
- if (read(sv[0], &buf, 1) != 1) {
- ERROR("Error getting reply from server over socketpair");
- goto out;
- }
- if (send_creds(sv[0], getpid(), getuid(), getgid())) {
- SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
- goto out;
- }
- fds.fd = sv[0];
- fds.events = POLLIN;
- fds.revents = 0;
- if (poll(&fds, 1, -1) <= 0) {
- ERROR("Error getting go-ahead from server: %s", strerror(errno));
- goto out;
- }
- if (read(sv[0], &buf, 1) != 1) {
- ERROR("Error getting reply from server over socketpair");
- goto out;
- }
- if (send_creds(sv[0], getpid(), newuid, 0)) {
- SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
- goto out;
- }
- fds.fd = sv[0];
- fds.events = POLLIN;
- fds.revents = 0;
- if (poll(&fds, 1, -1) <= 0) {
- ERROR("Error getting go-ahead from server: %s", strerror(errno));
- goto out;
- }
- ret = read(sv[0], buf, 1);
-out:
- close(sv[0]);
- close(sv[1]);
- if (ret == 1 && *buf == '1')
- return 0;
- return -1;
-}
-
-static int chown_cgroup_wrapper(void *data)
-{
- struct chown_data *arg = data;
- char **slist = subsystems;
- int i, ret = -1;
- uid_t destuid;
-
- if (setresgid(0,0,0) < 0)
- SYSERROR("Failed to setgid to 0");
- if (setresuid(0,0,0) < 0)
- SYSERROR("Failed to setuid to 0");
- if (setgroups(0, NULL) < 0)
- SYSERROR("Failed to clear groups");
- cgm_dbus_disconnect();
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return -1;
- }
- destuid = get_ns_uid(arg->origuid);
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
-
- for (i = 0; slist[i]; i++) {
- if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) {
- ERROR("Failed to chown %s:%s to container root",
- slist[i], arg->cgroup_path);
- goto fail;
- }
- }
- ret = 0;
-fail:
- cgm_dbus_disconnect();
- return ret;
-}
-
-/* Internal helper. Must be called with the cgmanager dbus socket open */
-static bool lxc_cgmanager_chmod(const char *controller,
- const char *cgroup_path, const char *file, int mode)
-{
- if (cgmanager_chmod_sync(NULL, cgroup_manager, controller,
- cgroup_path, file, mode) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message);
- nih_free(nerr);
- return false;
- }
- return true;
-}
-
-/* Internal helper. Must be called with the cgmanager dbus socket open */
-static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf)
-{
- struct chown_data data;
- char **slist = subsystems;
- int i;
-
- if (lxc_list_empty(&conf->id_map))
- /* If there's no mapping then we don't need to chown */
- return true;
-
- data.cgroup_path = cgroup_path;
- data.origuid = geteuid();
-
- /* Unpriv users can't chown it themselves, so chown from
- * a child namespace mapping both our own and the target uid
- */
- if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
- ERROR("Error requesting cgroup chown in new namespace");
- return false;
- }
-
- /*
- * Now chmod 775 the directory else the container cannot create cgroups.
- * This can't be done in the child namespace because it only group-owns
- * the cgroup
- */
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
-
- for (i = 0; slist[i]; i++) {
- if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775))
- return false;
- if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0664))
- return false;
- if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0664))
- return false;
- }
-
- return true;
-}
-
-#define CG_REMOVE_RECURSIVE 1
-/* Internal helper. Must be called with the cgmanager dbus socket open */
-static void cgm_remove_cgroup(const char *controller, const char *path)
-{
- int existed;
- if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
- path, CG_REMOVE_RECURSIVE, &existed) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
- nih_free(nerr);
- ERROR("Error removing %s:%s", controller, path);
- }
- if (existed == -1)
- INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
-}
-
-static void *cgm_init(const char *name)
-{
- struct cgm_data *d;
-
- d = malloc(sizeof(*d));
- if (!d)
- return NULL;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- goto err1;
- }
-
- memset(d, 0, sizeof(*d));
- d->name = strdup(name);
- if (!d->name) {
- cgm_dbus_disconnect();
- goto err1;
- }
-
- d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
-
- // cgm_create immediately gets called so keep the connection open
- return d;
-
-err1:
- free(d);
- return NULL;
-}
-
-/* Called after a failed container startup */
-static void cgm_destroy(void *hdata, struct lxc_conf *conf)
-{
- struct cgm_data *d = hdata;
- char **slist = subsystems;
- int i;
-
- if (!d || !d->cgroup_path)
- return;
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return;
- }
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
- for (i = 0; slist[i]; i++)
- cgm_remove_cgroup(slist[i], d->cgroup_path);
-
- free(d->name);
- free(d->cgroup_path);
- free(d);
- cgm_dbus_disconnect();
-}
-
-/*
- * remove all the cgroups created
- * called internally with dbus connection open
- */
-static inline void cleanup_cgroups(char *path)
-{
- int i;
- char **slist = subsystems;
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
- for (i = 0; slist[i]; i++)
- cgm_remove_cgroup(slist[i], path);
-}
-
-static inline bool cgm_create(void *hdata)
-{
- struct cgm_data *d = hdata;
- char **slist = subsystems;
- int i, index=0, baselen, ret;
- int32_t existed;
- char result[MAXPATHLEN], *tmp, *cgroup_path;
-
- if (!d)
- return false;
-// XXX we should send a hint to the cgmanager that when these
-// cgroups become empty they should be deleted. Requires a cgmanager
-// extension
-
- memset(result, 0, MAXPATHLEN);
- tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
- if (!tmp)
- goto bad;
- if (strlen(tmp) >= MAXPATHLEN) {
- free(tmp);
- goto bad;
- }
- strcpy(result, tmp);
- baselen = strlen(result);
- free(tmp);
- tmp = result;
- while (*tmp == '/')
- tmp++;
-again:
- if (index == 100) { // turn this into a warn later
- ERROR("cgroup error? 100 cgroups with this name already running");
- goto bad;
- }
- if (index) {
- ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
- if (ret < 0 || ret >= MAXPATHLEN-baselen)
- goto bad;
- }
- existed = 0;
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
-
- for (i = 0; slist[i]; i++) {
- if (!lxc_cgmanager_create(slist[i], tmp, &existed)) {
- ERROR("Error creating cgroup %s:%s", slist[i], result);
- cleanup_cgroups(tmp);
- goto bad;
- }
- if (existed == 1)
- goto next;
- }
- // success
- cgroup_path = strdup(tmp);
- if (!cgroup_path) {
- cleanup_cgroups(tmp);
- goto bad;
- }
- d->cgroup_path = cgroup_path;
- cgm_dbus_disconnect();
- return true;
-
-next:
- index++;
- goto again;
-bad:
- cgm_dbus_disconnect();
- return false;
-}
-
-/*
- * Use the cgmanager to move a task into a cgroup for a particular
- * hierarchy.
- * All the subsystems in this hierarchy are co-mounted, so we only
- * need to transition the task into one of the cgroups
- *
- * Internal helper, must be called with cgmanager dbus socket open
- */
-static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
- const char *cgroup_path, bool abs)
-{
- int ret;
-
- if (abs)
- ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
- controller, cgroup_path, pid);
- else
- ret = cgmanager_move_pid_sync(NULL, cgroup_manager,
- controller, cgroup_path, pid);
- if (ret != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- WARN("call to cgmanager_move_pid_%ssync failed: %s",
- abs ? "abs_" : "", nerr->message);
- nih_free(nerr);
- return false;
- }
- return true;
-}
-
-static inline bool cgm_enter(void *hdata, pid_t pid)
-{
- struct cgm_data *d = hdata;
- char **slist = subsystems;
- bool ret = false;
- int i;
-
- if (!d || !d->cgroup_path)
- return false;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
-
- if (cgm_all_controllers_same)
- slist = subsystems_inone;
-
- for (i = 0; slist[i]; i++) {
- if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false))
- goto out;
- }
- ret = true;
-out:
- cgm_dbus_disconnect();
- return ret;
-}
-
-static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
-{
- struct cgm_data *d = hdata;
-
- if (!d || !d->cgroup_path)
- return NULL;
- return d->cgroup_path;
-}
-
-static const char *cgm_canonical_path(void *hdata)
-{
- struct cgm_data *d = hdata;
-
- if (!d || !d->cgroup_path)
- return NULL;
- return d->cgroup_path;
-}
-
-#if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC
-static inline bool abs_cgroup_supported(void) {
- return api_version >= CGM_SUPPORTS_GET_ABS;
-}
-#else
-static inline bool abs_cgroup_supported(void) {
- return false;
-}
-#define cgmanager_get_pid_cgroup_abs_sync(...) -1
-#endif
-
-static char *try_get_abs_cgroup(const char *name, const char *lxcpath,
- const char *controller)
-{
- char *cgroup = NULL;
-
- if (abs_cgroup_supported()) {
- /* get the container init pid and ask for its abs cgroup */
- pid_t pid = lxc_cmd_get_init_pid(name, lxcpath);
- if (pid < 0)
- return NULL;
- if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager,
- controller, pid, &cgroup) != 0) {
- cgroup = NULL;
- NihError *nerr;
- nerr = nih_error_get();
- nih_free(nerr);
- } else
- prune_init_scope(cgroup);
- return cgroup;
- }
-
- /* use the command interface to look for the cgroup */
- return lxc_cmd_get_cgroup_path(name, lxcpath, controller);
-}
-
-/*
- * nrtasks is called by the utmp helper by the container monitor.
- * cgmanager socket was closed after cgroup setup was complete, so we need
- * to reopen here.
- *
- * Return -1 on error.
- */
-static int cgm_get_nrtasks(void *hdata)
-{
- struct cgm_data *d = hdata;
- int32_t *pids;
- size_t pids_len;
-
- if (!d || !d->cgroup_path)
- return -1;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return -1;
- }
- if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0],
- d->cgroup_path, &pids, &pids_len) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message);
- nih_free(nerr);
- pids_len = -1;
- goto out;
- }
- nih_free(pids);
-out:
- cgm_dbus_disconnect();
- return pids_len;
-}
-
-#if HAVE_CGMANAGER_LIST_CONTROLLERS
-static bool lxc_list_controllers(char ***list)
-{
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
- if (cgmanager_list_controllers_sync(NULL, cgroup_manager, list) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_list_controllers_sync failed: %s", nerr->message);
- nih_free(nerr);
- cgm_dbus_disconnect();
- return false;
- }
-
- cgm_dbus_disconnect();
- return true;
-}
-#else
-static bool lxc_list_controllers(char ***list)
-{
- return false;
-}
-#endif
-
-static inline void free_abs_cgroup(char *cgroup)
-{
- if (!cgroup)
- return;
- if (abs_cgroup_supported())
- nih_free(cgroup);
- else
- free(cgroup);
-}
-
-static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue)
-{
- char *controller, *key, *cgroup = NULL, *cglast;
- int len = -1;
- int ret;
- nih_local char *result = NULL;
-
- controller = alloca(strlen(filename)+1);
- strcpy(controller, filename);
- key = strchr(controller, '.');
- if (!key) {
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- exit(1);
- }
- *key = '\0';
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- exit(1);
- }
- cgroup = try_get_abs_cgroup(name, lxcpath, controller);
- if (!cgroup) {
- cgm_dbus_disconnect();
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- exit(1);
- }
- cglast = strrchr(cgroup, '/');
- if (!cglast) {
- cgm_dbus_disconnect();
- free_abs_cgroup(cgroup);
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- exit(1);
- }
- *cglast = '\0';
- if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
- WARN("Failed to enter container cgroup %s:%s", controller, cgroup);
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- cgm_dbus_disconnect();
- free_abs_cgroup(cgroup);
- exit(1);
- }
- if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- nih_free(nerr);
- free_abs_cgroup(cgroup);
- cgm_dbus_disconnect();
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len))
- WARN("Failed to warn cgm_get of error; parent may hang");
- exit(1);
- }
- free_abs_cgroup(cgroup);
- cgm_dbus_disconnect();
- len = strlen(result);
- ret = write(outp, &len, sizeof(len));
- if (ret != sizeof(len)) {
- WARN("Failed to send length to parent");
- exit(1);
- }
- if (!len || !sendvalue) {
- exit(0);
- }
- ret = write(outp, result, len);
- if (ret < 0)
- exit(1);
- exit(0);
-}
-
-/* cgm_get is called to get container cgroup settings, not during startup */
-static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
-{
- pid_t pid;
- int p[2], ret, newlen, readlen;
-
- if (pipe(p) < 0)
- return -1;
- if ((pid = fork()) < 0) {
- close(p[0]);
- close(p[1]);
- return -1;
- }
- if (!pid) // do_cgm_get exits
- do_cgm_get(name, lxcpath, filename, p[1], len && value);
- close(p[1]);
- ret = read(p[0], &newlen, sizeof(newlen));
- if (ret != sizeof(newlen)) {
- close(p[0]);
- ret = -1;
- goto out;
- }
- if (!len || !value) {
- close(p[0]);
- ret = newlen;
- goto out;
- }
- memset(value, 0, len);
- if (newlen < 0) { // child is reporting an error
- close(p[0]);
- ret = -1;
- goto out;
- }
- if (newlen == 0) { // empty read
- close(p[0]);
- ret = 0;
- goto out;
- }
- readlen = newlen > len ? len : newlen;
- ret = read(p[0], value, readlen);
- close(p[0]);
- if (ret != readlen) {
- ret = -1;
- goto out;
- }
- if (newlen >= len) {
- value[len-1] = '\0';
- newlen = len-1;
- } else if (newlen+1 < len) {
- // cgmanager doesn't add eol to last entry
- value[newlen++] = '\n';
- value[newlen] = '\0';
- }
- ret = newlen;
-out:
- if (wait_for_pid(pid))
- WARN("do_cgm_get exited with error");
- return ret;
-}
-
-static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp)
-{
- char *controller, *key, *cgroup = NULL;
- int retval = 0; // value we are sending to the parent over outp
- int ret;
- char *cglast;
-
- controller = alloca(strlen(filename)+1);
- strcpy(controller, filename);
- key = strchr(controller, '.');
- if (!key) {
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- exit(1);
- }
- *key = '\0';
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- exit(1);
- }
- cgroup = try_get_abs_cgroup(name, lxcpath, controller);
- if (!cgroup) {
- cgm_dbus_disconnect();
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- exit(1);
- }
- cglast = strrchr(cgroup, '/');
- if (!cglast) {
- cgm_dbus_disconnect();
- free_abs_cgroup(cgroup);
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- exit(1);
- }
- *cglast = '\0';
- if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
- ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- cgm_dbus_disconnect();
- free_abs_cgroup(cgroup);
- exit(1);
- }
- if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup);
- ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
- nih_free(nerr);
- free_abs_cgroup(cgroup);
- cgm_dbus_disconnect();
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval))
- WARN("Failed to warn cgm_set of error; parent may hang");
- exit(1);
- }
- free_abs_cgroup(cgroup);
- cgm_dbus_disconnect();
- /* tell parent that we are done */
- retval = 1;
- ret = write(outp, &retval, sizeof(retval));
- if (ret != sizeof(retval)) {
- exit(1);
- }
- exit(0);
-}
-
-/* cgm_set is called to change cgroup settings, not during startup */
-static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
-{
- pid_t pid;
- int p[2], ret, v;
-
- if (pipe(p) < 0)
- return -1;
- if ((pid = fork()) < 0) {
- close(p[1]);
- close(p[0]);
- return -1;
- }
- if (!pid) // do_cgm_set exits
- do_cgm_set(name, lxcpath, filename, value, p[1]);
- close(p[1]);
- ret = read(p[0], &v, sizeof(v));
- close(p[0]);
- if (wait_for_pid(pid))
- WARN("do_cgm_set exited with error");
- if (ret != sizeof(v) || !v)
- return -1;
- return 0;
-}
-
-static void free_subsystems(void)
-{
- int i;
-
- for (i = 0; i < nr_subsystems; i++)
- free(subsystems[i]);
- free(subsystems);
- subsystems = NULL;
- nr_subsystems = 0;
-}
-
-static void cull_user_controllers(void)
-{
- int i, j;
-
- for (i = 0; i < nr_subsystems; i++) {
- if (strncmp(subsystems[i], "name=", 5) != 0)
- continue;
- for (j = i; j < nr_subsystems-1; j++)
- subsystems[j] = subsystems[j+1];
- nr_subsystems--;
- }
-}
-
-/*
- * return true if inword is in the comma-delimited list cgroup_use
- */
-static bool in_comma_list(const char *inword, const char *cgroup_use)
-{
- char *e;
- size_t inlen = strlen(inword), len;
-
- do {
- e = strchr(cgroup_use, ',');
- len = e ? e - cgroup_use : strlen(cgroup_use);
- if (len == inlen && strncmp(inword, cgroup_use, len) == 0)
- return true;
- cgroup_use = e + 1;
- } while (e);
-
- return false;
-}
-
-/*
- * inlist is a comma-delimited list of cgroups; so is checklist. Return
- * true if any member of inlist is in checklist.
- */
-static bool any_in_comma_list(const char *inlist, const char *checklist)
-{
- char *tmp = alloca(strlen(inlist) + 1), *tok, *saveptr = NULL;
-
- strcpy(tmp, inlist);
- for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) {
- if (in_comma_list(tok, checklist))
- return true;
- }
-
- return false;
-}
-
-static bool in_subsystem_list(const char *c)
-{
- int i;
-
- for (i = 0; i < nr_subsystems; i++) {
- if (strcmp(c, subsystems[i]) == 0)
- return true;
- }
-
- return false;
-}
-
-/*
- * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory",
- * then clear out any other subsystems, and make sure that freezer
- * and memory are both enabled
- */
-static bool verify_and_prune(const char *cgroup_use)
-{
- const char *p;
- char *e;
- int i, j;
-
- for (p = cgroup_use; p && *p; p = e + 1) {
- e = strchr(p, ',');
- if (e)
- *e = '\0';
-
- if (!in_subsystem_list(p)) {
- ERROR("Controller %s required by lxc.cgroup.use but not available\n", p);
- return false;
- }
-
- if (e)
- *e = ',';
- if (!e)
- break;
- }
-
- for (i = 0; i < nr_subsystems;) {
- if (in_comma_list(subsystems[i], cgroup_use)) {
- i++;
- continue;
- }
- free(subsystems[i]);
- for (j = i; j < nr_subsystems-1; j++)
- subsystems[j] = subsystems[j+1];
- subsystems[nr_subsystems-1] = NULL;
- nr_subsystems--;
- }
-
- return true;
-}
-
-static void drop_subsystem(int which)
-{
- int i;
-
- if (which < 0 || which >= nr_subsystems) {
- ERROR("code error: dropping invalid subsystem index\n");
- exit(1);
- }
-
- free(subsystems[which]);
- /* note - we have nr_subsystems+1 entries, last one a NULL */
- for (i = which; i < nr_subsystems; i++)
- subsystems[i] = subsystems[i+1];
- nr_subsystems -= 1;
-}
-
-/*
- * Check whether we can create the cgroups we would want
- */
-static bool subsys_is_writeable(const char *controller, const char *probe)
-{
- int32_t existed;
- bool ret = true;
-
- if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
- probe, &existed) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
- nih_free(nerr);
- ERROR("Failed to create %s:%s", controller, probe);
- ret = false;
- }
-
- return ret;
-}
-
-static char *get_last_controller_in_list(char *list)
-{
- char *p;
-
- while ((p = strchr(list, ',')) != NULL)
- list = p + 1;
-
- return list;
-}
-
-/*
- * Make sure that all the controllers are writeable.
- * If any are not, then
- * - if they are listed in lxc.cgroup.use, refuse to start
- * - else if they are crucial subsystems, refuse to start
- * - else warn and do not use them
- */
-static bool verify_final_subsystems(const char *cgroup_use)
-{
- int i;
- bool dropped_any = false;
- bool bret = false;
- const char *cgroup_pattern;
- char tmpnam[50], *probe;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
-
- cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
- i = snprintf(tmpnam, 50, "lxcprobe-%d", getpid());
- if (i < 0 || i >= 50) {
- ERROR("Attack - format string modified?");
- return false;
- }
- probe = lxc_string_replace("%n", tmpnam, cgroup_pattern);
- if (!probe)
- goto out;
-
- i = 0;
- while (i < nr_subsystems) {
- char *p = get_last_controller_in_list(subsystems[i]);
-
- if (!subsys_is_writeable(p, probe)) {
- if (is_crucial_cgroup_subsystem(p)) {
- ERROR("Cannot write to crucial subsystem %s\n",
- subsystems[i]);
- goto out;
- }
- if (cgroup_use && any_in_comma_list(subsystems[i], cgroup_use)) {
- ERROR("Cannot write to subsystem %s which is requested in lxc.cgroup.use\n",
- subsystems[i]);
- goto out;
- }
- WARN("Cannot write to subsystem %s, continuing with out it\n",
- subsystems[i]);
- dropped_any = true;
- drop_subsystem(i);
- } else {
- cgm_remove_cgroup(subsystems[i], probe);
- i++;
- }
- }
-
- if (dropped_any)
- cgm_all_controllers_same = false;
- bret = true;
-
-out:
- free(probe);
- cgm_dbus_disconnect();
- return bret;
-}
-
-static bool collect_subsystems(void)
-{
- char *line = NULL;
- nih_local char **cgm_subsys_list = NULL;
- size_t sz = 0;
- FILE *f = NULL;
-
- if (subsystems) // already initialized
- return true;
-
- subsystems_inone = malloc(2 * sizeof(char *));
- if (!subsystems_inone)
- return false;
- subsystems_inone[0] = "all";
- subsystems_inone[1] = NULL;
-
- if (lxc_list_controllers(&cgm_subsys_list)) {
- while (cgm_subsys_list[nr_subsystems]) {
- char **tmp = NIH_MUST( realloc(subsystems,
- (nr_subsystems+2)*sizeof(char *)) );
- tmp[nr_subsystems] = NIH_MUST(
- strdup(cgm_subsys_list[nr_subsystems++]) );
- subsystems = tmp;
- }
- if (nr_subsystems)
- subsystems[nr_subsystems] = NULL;
- goto collected;
- }
-
- INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups");
- f = fopen_cloexec("/proc/self/cgroup", "r");
- if (!f) {
- f = fopen_cloexec("/proc/1/cgroup", "r");
- if (!f)
- return false;
- }
- while (getline(&line, &sz, f) != -1) {
- /* file format: hierarchy:subsystems:group,
- * with multiple subsystems being ,-separated */
- char *slist, *end, *p, *saveptr = NULL, **tmp;
-
- if (!line[0])
- continue;
-
- slist = strchr(line, ':');
- if (!slist)
- continue;
- slist++;
- end = strchr(slist, ':');
- if (!end)
- continue;
- *end = '\0';
-
- for (p = strtok_r(slist, ",", &saveptr);
- p;
- p = strtok_r(NULL, ",", &saveptr)) {
- tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *));
- if (!tmp)
- goto out_free;
-
- subsystems = tmp;
- tmp[nr_subsystems] = strdup(p);
- tmp[nr_subsystems+1] = NULL;
- if (!tmp[nr_subsystems])
- goto out_free;
- nr_subsystems++;
- }
- }
- fclose(f);
- f = NULL;
-
- free(line);
- line = NULL;
-
-collected:
- if (!nr_subsystems) {
- ERROR("No cgroup subsystems found");
- return false;
- }
-
- /* make sure that cgroup.use can be and is honored */
- const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use");
- if (!cgroup_use && errno != 0)
- goto final_verify;
- if (cgroup_use) {
- if (!verify_and_prune(cgroup_use)) {
- free_subsystems();
- return false;
- }
- subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) );
- cgm_all_controllers_same = false;
- }
-
-final_verify:
- return verify_final_subsystems(cgroup_use);
-
-out_free:
- free(line);
- if (f)
- fclose(f);
- free_subsystems();
- return false;
-}
-
-/*
- * called during cgroup.c:cgroup_ops_init(), at startup. No threads.
- * We check whether we can talk to cgmanager, escape to root cgroup if
- * we are root, then close the connection.
- */
-struct cgroup_ops *cgm_ops_init(void)
-{
- check_supports_multiple_controllers(-1);
- if (!collect_subsystems())
- return NULL;
-
- if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS)
- cgm_all_controllers_same = false;
-
- // if root, try to escape to root cgroup
- if (geteuid() == 0 && !cgm_escape(NULL)) {
- free_subsystems();
- return NULL;
- }
-
- return &cgmanager_ops;
-}
-
-/* unfreeze is called by the command api after killing a container. */
-static bool cgm_unfreeze(void *hdata)
-{
- struct cgm_data *d = hdata;
- bool ret = true;
-
- if (!d || !d->cgroup_path)
- return false;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
- if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
- "freezer.state", "THAWED") != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
- nih_free(nerr);
- ERROR("Error unfreezing %s", d->cgroup_path);
- ret = false;
- }
- cgm_dbus_disconnect();
- return ret;
-}
-
-static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
-{
- struct cgm_data *d = hdata;
- struct lxc_list *iterator, *sorted_cgroup_settings, *next;
- struct lxc_cgroup *cg;
- bool ret = false;
-
- if (lxc_list_empty(cgroup_settings))
- return true;
-
- if (!d || !d->cgroup_path)
- return false;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
-
- sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
- if (!sorted_cgroup_settings) {
- return false;
- }
-
- lxc_list_for_each(iterator, sorted_cgroup_settings) {
- char controller[100], *p;
- cg = iterator->elem;
- if (do_devices != !strncmp("devices", cg->subsystem, 7))
- continue;
- if (strlen(cg->subsystem) > 100) // i smell a rat
- goto out;
- strcpy(controller, cg->subsystem);
- p = strchr(controller, '.');
- if (p)
- *p = '\0';
- if (cgmanager_set_value_sync(NULL, cgroup_manager, controller,
- d->cgroup_path, cg->subsystem, cg->value) != 0) {
- NihError *nerr;
- nerr = nih_error_get();
- if (do_devices) {
- WARN("call to cgmanager_set_value_sync failed: %s", nerr->message);
- nih_free(nerr);
- WARN("Error setting cgroup %s:%s limit type %s", controller,
- d->cgroup_path, cg->subsystem);
- continue;
- }
-
- ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
- nih_free(nerr);
- ERROR("Error setting cgroup %s:%s limit type %s", controller,
- d->cgroup_path, cg->subsystem);
- goto out;
- }
-
- DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
- }
-
- ret = true;
- INFO("cgroup limits have been setup");
-out:
- lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
- lxc_list_del(iterator);
- free(iterator);
- }
- free(sorted_cgroup_settings);
- cgm_dbus_disconnect();
- return ret;
-}
-
-static bool cgm_chown(void *hdata, struct lxc_conf *conf)
-{
- struct cgm_data *d = hdata;
-
- if (!d || !d->cgroup_path)
- return false;
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
- if (!chown_cgroup(d->cgroup_path, conf))
- WARN("Failed to chown %s to container root", d->cgroup_path);
- cgm_dbus_disconnect();
- return true;
-}
-
-/*
- * TODO: this should be re-written to use the get_config_item("lxc.id_map")
- * cmd api instead of getting the idmap from c->lxc_conf. The reason is
- * that the id_maps may be different if the container was started with a
- * -f or -s argument.
- * The reason I'm punting on that is because we'll need to parse the
- * idmap results.
- */
-static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
-{
- bool pass = true;
- char *cgroup = NULL;
- char **slist = subsystems;
- int i;
-
- if (!cgm_dbus_connect()) {
- ERROR("Error connecting to cgroup manager");
- return false;
- }
-
- for (i = 0; slist[i]; i++) {
- cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]);
- if (!cgroup) {
- ERROR("Failed to get cgroup for controller %s", slist[i]);
- cgm_dbus_disconnect();
- return false;
- }
-
- if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) {
- pass = false;
- break;
- }
-
- }
- cgm_dbus_disconnect();
- if (!pass)
- ERROR("Failed to enter group %s", cgroup);
-
- free_abs_cgroup(cgroup);
- return pass;
-}
-
-static bool cgm_bind_dir(const char *root, const char *dirname)
-{
- nih_local char *cgpath = NULL;
-
- /* /sys should have been mounted by now */
- cgpath = NIH_MUST( nih_strdup(NULL, root) );
- NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
-
- if (!dir_exists(cgpath)) {
- ERROR("%s does not exist", cgpath);
- return false;
- }
-
- /* mount a tmpfs there so we can create subdirs */
- if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) {
- SYSERROR("Failed to mount tmpfs at %s", cgpath);
- return false;
- }
- NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
-
- if (mkdir(cgpath, 0755) < 0) {
- SYSERROR("Failed to create %s", cgpath);
- return false;
- }
-
- if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) {
- SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
- return false;
- }
-
- return true;
-}
-
-/*
- * cgm_mount_cgroup:
- * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
- * /sys/fs/cgroup/cgmanager/ in the container.
- * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
- * Else do nothing
- */
-#define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
-#define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
-static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
-{
- if (dir_exists(CGMANAGER_LOWER_SOCK))
- return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
- if (dir_exists(CGMANAGER_UPPER_SOCK))
- return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
- // Host doesn't have cgmanager running? Then how did we get here?
- return false;
-}
-
-static struct cgroup_ops cgmanager_ops = {
- .init = cgm_init,
- .destroy = cgm_destroy,
- .create = cgm_create,
- .enter = cgm_enter,
- .create_legacy = NULL,
- .get_cgroup = cgm_get_cgroup,
- .canonical_path = cgm_canonical_path,
- .escape = cgm_escape,
- .get = cgm_get,
- .set = cgm_set,
- .unfreeze = cgm_unfreeze,
- .setup_limits = cgm_setup_limits,
- .name = "cgmanager",
- .chown = cgm_chown,
- .attach = cgm_attach,
- .mount_cgroup = cgm_mount_cgroup,
- .nrtasks = cgm_get_nrtasks,
- .disconnect = NULL,
- .driver = CGMANAGER,
-};
-#endif
diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
deleted file mode 100644
index 91ef359..0000000
--- a/src/lxc/cgroup.c
+++ /dev/null
@@ -1,245 +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 <unistd.h>
-#include <sys/types.h>
-
-#include "cgroup.h"
-#include "conf.h"
-#include "log.h"
-#include "start.h"
-
-lxc_log_define(lxc_cgroup, lxc);
-
-static struct cgroup_ops *ops = NULL;
-
-extern struct cgroup_ops *cgfs_ops_init(void);
-extern struct cgroup_ops *cgfsng_ops_init(void);
-extern struct cgroup_ops *cgm_ops_init(void);
-
-__attribute__((constructor))
-void cgroup_ops_init(void)
-{
- if (ops) {
- INFO("cgroup driver %s", ops->name);
- return;
- }
-
- DEBUG("cgroup_init");
- #if HAVE_CGMANAGER
- ops = cgm_ops_init();
- #endif
- if (!ops)
- ops = cgfsng_ops_init();
- if (!ops)
- ops = cgfs_ops_init();
- if (ops)
- INFO("Initialized cgroup driver %s", ops->name);
-}
-
-bool cgroup_init(struct lxc_handler *handler)
-{
- if (handler->cgroup_data) {
- ERROR("cgroup_init called on already inited handler");
- return true;
- }
-
- if (ops) {
- INFO("cgroup driver %s initing for %s", ops->name, handler->name);
- handler->cgroup_data = ops->init(handler->name);
- }
- return handler->cgroup_data != NULL;
-}
-
-void cgroup_destroy(struct lxc_handler *handler)
-{
- if (ops) {
- ops->destroy(handler->cgroup_data, handler->conf);
- handler->cgroup_data = NULL;
- }
-}
-
-/* Create the container cgroups for all requested controllers */
-bool cgroup_create(struct lxc_handler *handler)
-{
- if (ops)
- return ops->create(handler->cgroup_data);
- return false;
-}
-
-/*
- * Enter the container init into its new cgroups for all
- * requested controllers
- */
-bool cgroup_enter(struct lxc_handler *handler)
-{
- if (ops)
- return ops->enter(handler->cgroup_data, handler->pid);
- return false;
-}
-
-bool cgroup_create_legacy(struct lxc_handler *handler)
-{
- if (ops && ops->create_legacy)
- return ops->create_legacy(handler->cgroup_data, handler->pid);
- return true;
-}
-
-const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem)
-{
- if (ops)
- return ops->get_cgroup(handler->cgroup_data, subsystem);
- return NULL;
-}
-
-bool cgroup_escape(struct lxc_handler *handler)
-{
- if (ops)
- return ops->escape(handler->cgroup_data);
- return false;
-}
-
-const char *cgroup_canonical_path(struct lxc_handler *handler)
-{
- if (geteuid()) {
- WARN("cgroup_canonical_path only makes sense for privileged containers.\n");
- return NULL;
- }
-
- if (ops)
- return ops->canonical_path(handler->cgroup_data);
-
- return NULL;
-}
-
-bool cgroup_unfreeze(struct lxc_handler *handler)
-{
- if (ops)
- return ops->unfreeze(handler->cgroup_data);
- return false;
-}
-
-bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
-{
- if (ops)
- return ops->setup_limits(handler->cgroup_data,
- &handler->conf->cgroup, with_devices);
- return false;
-}
-
-bool cgroup_chown(struct lxc_handler *handler)
-{
- if (ops && ops->chown)
- return ops->chown(handler->cgroup_data, handler->conf);
- return true;
-}
-
-bool cgroup_mount(const char *root, struct lxc_handler *handler, int type)
-{
- if (ops) {
- return ops->mount_cgroup(handler->cgroup_data, root, type);
- }
- return false;
-}
-
-int cgroup_nrtasks(struct lxc_handler *handler)
-{
- if (ops) {
- if (ops->nrtasks)
- return ops->nrtasks(handler->cgroup_data);
- else
- WARN("CGROUP driver %s doesn't implement nrtasks", ops->name);
- }
- return -1;
-}
-
-bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid)
-{
- if (ops)
- return ops->attach(name, lxcpath, pid);
- return false;
-}
-
-int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath)
-{
- if (ops)
- return ops->set(filename, value, name, lxcpath);
- return -1;
-}
-
-int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
-{
- if (ops)
- return ops->get(filename, value, len, name, lxcpath);
- return -1;
-}
-
-void cgroup_disconnect(void)
-{
- if (ops && ops->disconnect)
- ops->disconnect();
-}
-
-cgroup_driver_t cgroup_driver(void)
-{
- return ops->driver;
-}
-
-#define INIT_SCOPE "/init.scope"
-void prune_init_scope(char *cg)
-{
- char *point;
-
- if (!cg)
- return;
-
- point = cg + strlen(cg) - strlen(INIT_SCOPE);
- if (point < cg)
- return;
- if (strcmp(point, INIT_SCOPE) == 0) {
- if (point == cg)
- *(point+1) = '\0';
- else
- *point = '\0';
- }
-}
-
-/*
- * Return true if this is a subsystem which we cannot do
- * without.
- *
- * systemd is questionable here. The way callers currently
- * use this, if systemd is not mounted then it will be ignored.
- * But if systemd is mounted, then it must be setup so that lxc
- * can create cgroups in it, else containers will fail.
- */
-bool is_crucial_cgroup_subsystem(const char *s)
-{
- if (strcmp(s, "systemd") == 0)
- return true;
- if (strcmp(s, "name=systemd") == 0)
- return true;
- if (strcmp(s, "freezer") == 0)
- return true;
- return false;
-}
diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h
deleted file mode 100644
index e56a115..0000000
--- a/src/lxc/cgroup.h
+++ /dev/null
@@ -1,89 +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_CGROUP_H
-#define __LXC_CGROUP_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-struct lxc_handler;
-struct lxc_conf;
-struct lxc_list;
-
-typedef enum {
- CGFS,
- CGMANAGER,
- CGFSNG,
-} cgroup_driver_t;
-
-struct cgroup_ops {
- const char *name;
-
- void *(*init)(const char *name);
- void (*destroy)(void *hdata, struct lxc_conf *conf);
- bool (*create)(void *hdata);
- bool (*enter)(void *hdata, pid_t pid);
- bool (*create_legacy)(void *hdata, pid_t pid);
- const char *(*get_cgroup)(void *hdata, const char *subsystem);
- const char *(*canonical_path)(void *hdata);
- bool (*escape)();
- int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath);
- int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
- bool (*unfreeze)(void *hdata);
- bool (*setup_limits)(void *hdata, struct lxc_list *cgroup_conf, bool with_devices);
- bool (*chown)(void *hdata, struct lxc_conf *conf);
- bool (*attach)(const char *name, const char *lxcpath, pid_t pid);
- bool (*mount_cgroup)(void *hdata, const char *root, int type);
- int (*nrtasks)(void *hdata);
- void (*disconnect)(void);
- cgroup_driver_t driver;
-};
-
-extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid);
-extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type);
-extern void cgroup_destroy(struct lxc_handler *handler);
-extern bool cgroup_init(struct lxc_handler *handler);
-extern bool cgroup_create(struct lxc_handler *handler);
-extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices);
-extern bool cgroup_chown(struct lxc_handler *handler);
-extern bool cgroup_enter(struct lxc_handler *handler);
-extern void cgroup_cleanup(struct lxc_handler *handler);
-extern bool cgroup_create_legacy(struct lxc_handler *handler);
-extern int cgroup_nrtasks(struct lxc_handler *handler);
-extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem);
-extern bool cgroup_escape();
-
-/*
- * Currently, this call only makes sense for privileged containers.
- */
-extern const char *cgroup_canonical_path(struct lxc_handler *handler);
-extern bool cgroup_unfreeze(struct lxc_handler *handler);
-extern void cgroup_disconnect(void);
-extern cgroup_driver_t cgroup_driver(void);
-
-extern void prune_init_scope(char *cg);
-extern bool is_crucial_cgroup_subsystem(const char *s);
-
-#endif
diff --git a/src/lxc/cgroups/cgfs.c b/src/lxc/cgroups/cgfs.c
new file mode 100644
index 0000000..6b2ac7e
--- /dev/null
+++ b/src/lxc/cgroups/cgfs.c
@@ -0,0 +1,2655 @@
+/*
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include "bdev.h"
+#include "error.h"
+#include "commands.h"
+#include "list.h"
+#include "conf.h"
+#include "utils.h"
+#include "log.h"
+#include "cgroup.h"
+#include "start.h"
+#include "state.h"
+
+#if IS_BIONIC
+#include <../include/lxcmntent.h>
+#else
+#include <mntent.h>
+#endif
+
+struct cgroup_hierarchy;
+struct cgroup_meta_data;
+struct cgroup_mount_point;
+
+/*
+ * cgroup_meta_data: the metadata about the cgroup infrastructure on this
+ * host
+ */
+struct cgroup_meta_data {
+ ptrdiff_t ref; /* simple refcount */
+ struct cgroup_hierarchy **hierarchies;
+ struct cgroup_mount_point **mount_points;
+ int maximum_hierarchy;
+};
+
+/*
+ * cgroup_hierarchy: describes a single cgroup hierarchy
+ * (may have multiple mount points)
+ */
+struct cgroup_hierarchy {
+ int index;
+ bool used; /* false if the hierarchy should be ignored by lxc */
+ char **subsystems;
+ struct cgroup_mount_point *rw_absolute_mount_point;
+ struct cgroup_mount_point *ro_absolute_mount_point;
+ struct cgroup_mount_point **all_mount_points;
+ size_t all_mount_point_capacity;
+};
+
+/*
+ * cgroup_mount_point: a mount point to where a hierarchy
+ * is mounted to
+ */
+struct cgroup_mount_point {
+ struct cgroup_hierarchy *hierarchy;
+ char *mount_point;
+ char *mount_prefix;
+ bool read_only;
+ bool need_cpuset_init;
+};
+
+/*
+ * cgroup_process_info: describes the membership of a
+ * process to the different cgroup
+ * hierarchies
+ *
+ * Note this is the per-process info tracked by the cgfs_ops.
+ * This is not used with cgmanager.
+ */
+struct cgroup_process_info {
+ struct cgroup_process_info *next;
+ struct cgroup_meta_data *meta_ref;
+ struct cgroup_hierarchy *hierarchy;
+ char *cgroup_path;
+ char *cgroup_path_sub;
+ char **created_paths;
+ size_t created_paths_capacity;
+ size_t created_paths_count;
+ struct cgroup_mount_point *designated_mount_point;
+};
+
+struct cgfs_data {
+ char *name;
+ const char *cgroup_pattern;
+ struct cgroup_meta_data *meta;
+ struct cgroup_process_info *info;
+};
+
+lxc_log_define(lxc_cgfs, lxc);
+
+static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta);
+static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list);
+static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp);
+static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h);
+static bool is_valid_cgroup(const char *name);
+static int create_cgroup(struct cgroup_mount_point *mp, const char *path);
+static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse,
+ struct lxc_conf *conf);
+static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *path, const char *suffix);
+static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem);
+static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len);
+static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value);
+static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow);
+static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices);
+static int cgroup_recursive_task_count(const char *cgroup_path);
+static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path);
+static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path);
+
+static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist);
+static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data);
+static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data);
+
+/* free process membership information */
+static void lxc_cgroup_process_info_free(struct cgroup_process_info *info);
+static void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info,
+ struct lxc_conf *conf);
+
+static struct cgroup_ops cgfs_ops;
+
+static int cgroup_rmdir(char *dirname)
+{
+ struct dirent dirent, *direntp;
+ int saved_errno = 0;
+ DIR *dir;
+ int ret, failed=0;
+ char pathname[MAXPATHLEN];
+
+ dir = opendir(dirname);
+ if (!dir) {
+ ERROR("%s: failed to open %s", __func__, dirname);
+ return -1;
+ }
+
+ while (!readdir_r(dir, &dirent, &direntp)) {
+ struct stat mystat;
+ int rc;
+
+ if (!direntp)
+ break;
+
+ if (!strcmp(direntp->d_name, ".") ||
+ !strcmp(direntp->d_name, ".."))
+ continue;
+
+ rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
+ if (rc < 0 || rc >= MAXPATHLEN) {
+ ERROR("pathname too long");
+ failed=1;
+ if (!saved_errno)
+ saved_errno = -ENOMEM;
+ continue;
+ }
+ ret = lstat(pathname, &mystat);
+ if (ret) {
+ SYSERROR("%s: failed to stat %s", __func__, pathname);
+ failed=1;
+ if (!saved_errno)
+ saved_errno = errno;
+ continue;
+ }
+ if (S_ISDIR(mystat.st_mode)) {
+ if (cgroup_rmdir(pathname) < 0) {
+ if (!saved_errno)
+ saved_errno = errno;
+ failed=1;
+ }
+ }
+ }
+
+ if (rmdir(dirname) < 0) {
+ SYSERROR("%s: failed to delete %s", __func__, dirname);
+ if (!saved_errno)
+ saved_errno = errno;
+ failed=1;
+ }
+
+ ret = closedir(dir);
+ if (ret) {
+ SYSERROR("%s: failed to close directory %s", __func__, dirname);
+ if (!saved_errno)
+ saved_errno = errno;
+ failed=1;
+ }
+
+ errno = saved_errno;
+ return failed ? -1 : 0;
+}
+
+static int rmdir_wrapper(void *data)
+{
+ char *path = data;
+
+ if (setresgid(0,0,0) < 0)
+ SYSERROR("Failed to setgid to 0");
+ if (setresuid(0,0,0) < 0)
+ SYSERROR("Failed to setuid to 0");
+ if (setgroups(0, NULL) < 0)
+ SYSERROR("Failed to clear groups");
+
+ return cgroup_rmdir(path);
+}
+
+static struct cgroup_meta_data *lxc_cgroup_load_meta()
+{
+ const char *cgroup_use = NULL;
+ char **cgroup_use_list = NULL;
+ struct cgroup_meta_data *md = NULL;
+ int saved_errno;
+
+ errno = 0;
+ cgroup_use = lxc_global_config_value("lxc.cgroup.use");
+ if (!cgroup_use && errno != 0)
+ return NULL;
+ if (cgroup_use) {
+ cgroup_use_list = lxc_string_split_and_trim(cgroup_use, ',');
+ if (!cgroup_use_list)
+ return NULL;
+ }
+
+ md = lxc_cgroup_load_meta2((const char **)cgroup_use_list);
+ saved_errno = errno;
+ lxc_free_array((void **)cgroup_use_list, free);
+ errno = saved_errno;
+ return md;
+}
+
+/* Step 1: determine all kernel subsystems */
+static bool find_cgroup_subsystems(char ***kernel_subsystems)
+{
+ FILE *proc_cgroups;
+ bool bret = false;
+ char *line = NULL;
+ size_t sz = 0;
+ size_t kernel_subsystems_count = 0;
+ size_t kernel_subsystems_capacity = 0;
+ int r;
+
+ proc_cgroups = fopen_cloexec("/proc/cgroups", "r");
+ if (!proc_cgroups)
+ return false;
+
+ while (getline(&line, &sz, proc_cgroups) != -1) {
+ char *tab1;
+ char *tab2;
+ int hierarchy_number;
+
+ if (line[0] == '#')
+ continue;
+ if (!line[0])
+ continue;
+
+ tab1 = strchr(line, '\t');
+ if (!tab1)
+ continue;
+ *tab1++ = '\0';
+ tab2 = strchr(tab1, '\t');
+ if (!tab2)
+ continue;
+ *tab2 = '\0';
+
+ tab2 = NULL;
+ hierarchy_number = strtoul(tab1, &tab2, 10);
+ if (!tab2 || *tab2)
+ continue;
+ (void)hierarchy_number;
+
+ r = lxc_grow_array((void ***)kernel_subsystems, &kernel_subsystems_capacity, kernel_subsystems_count + 1, 12);
+ if (r < 0)
+ goto out;
+ (*kernel_subsystems)[kernel_subsystems_count] = strdup(line);
+ if (!(*kernel_subsystems)[kernel_subsystems_count])
+ goto out;
+ kernel_subsystems_count++;
+ }
+ bret = true;
+
+out:
+ fclose(proc_cgroups);
+ free(line);
+ return bret;
+}
+
+/* Step 2: determine all hierarchies (by reading /proc/self/cgroup),
+ * since mount points don't specify hierarchy number and
+ * /proc/cgroups does not contain named hierarchies
+ */
+static bool find_cgroup_hierarchies(struct cgroup_meta_data *meta_data,
+ bool all_kernel_subsystems, bool all_named_subsystems,
+ const char **subsystem_whitelist)
+{
+ FILE *proc_self_cgroup;
+ char *line = NULL;
+ size_t sz = 0;
+ int r;
+ bool bret = false;
+ size_t hierarchy_capacity = 0;
+
+ proc_self_cgroup = fopen_cloexec("/proc/self/cgroup", "r");
+ /* if for some reason (because of setns() and pid namespace for example),
+ * /proc/self is not valid, we try /proc/1/cgroup... */
+ if (!proc_self_cgroup)
+ proc_self_cgroup = fopen_cloexec("/proc/1/cgroup", "r");
+ if (!proc_self_cgroup)
+ return false;
+
+ while (getline(&line, &sz, proc_self_cgroup) != -1) {
+ /* file format: hierarchy:subsystems:group,
+ * we only extract hierarchy and subsystems
+ * here */
+ char *colon1;
+ char *colon2;
+ int hierarchy_number;
+ struct cgroup_hierarchy *h = NULL;
+ char **p;
+
+ if (!line[0])
+ continue;
+
+ colon1 = strchr(line, ':');
+ if (!colon1)
+ continue;
+ *colon1++ = '\0';
+ colon2 = strchr(colon1, ':');
+ if (!colon2)
+ continue;
+ *colon2 = '\0';
+
+ colon2 = NULL;
+ hierarchy_number = strtoul(line, &colon2, 10);
+ if (!colon2 || *colon2)
+ continue;
+
+ if (hierarchy_number > meta_data->maximum_hierarchy) {
+ /* lxc_grow_array will never shrink, so even if we find a lower
+ * hierarchy number here, the array will never be smaller
+ */
+ r = lxc_grow_array((void ***)&meta_data->hierarchies, &hierarchy_capacity, hierarchy_number + 1, 12);
+ if (r < 0)
+ goto out;
+
+ meta_data->maximum_hierarchy = hierarchy_number;
+ }
+
+ /* this shouldn't happen, we had this already */
+ if (meta_data->hierarchies[hierarchy_number])
+ goto out;
+
+ h = calloc(1, sizeof(struct cgroup_hierarchy));
+ if (!h)
+ goto out;
+
+ meta_data->hierarchies[hierarchy_number] = h;
+
+ h->index = hierarchy_number;
+ h->subsystems = lxc_string_split_and_trim(colon1, ',');
+ if (!h->subsystems)
+ goto out;
+ /* see if this hierarchy should be considered */
+ if (!all_kernel_subsystems || !all_named_subsystems) {
+ for (p = h->subsystems; *p; p++) {
+ if (!strncmp(*p, "name=", 5)) {
+ if (all_named_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
+ h->used = true;
+ break;
+ }
+ } else {
+ if (all_kernel_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
+ h->used = true;
+ break;
+ }
+ }
+ }
+ } else {
+ /* we want all hierarchy anyway */
+ h->used = true;
+ }
+ }
+ bret = true;
+
+out:
+ fclose(proc_self_cgroup);
+ free(line);
+ return bret;
+}
+
+/* Step 3: determine all mount points of each hierarchy */
+static bool find_hierarchy_mountpts( struct cgroup_meta_data *meta_data, char **kernel_subsystems)
+{
+ bool bret = false;
+ FILE *proc_self_mountinfo;
+ char *line = NULL;
+ size_t sz = 0;
+ char **tokens = NULL;
+ size_t mount_point_count = 0;
+ size_t mount_point_capacity = 0;
+ size_t token_capacity = 0;
+ int r;
+ bool is_cgns = cgns_supported();
+
+ proc_self_mountinfo = fopen_cloexec("/proc/self/mountinfo", "r");
+ /* if for some reason (because of setns() and pid namespace for example),
+ * /proc/self is not valid, we try /proc/1/cgroup... */
+ if (!proc_self_mountinfo)
+ proc_self_mountinfo = fopen_cloexec("/proc/1/mountinfo", "r");
+ if (!proc_self_mountinfo)
+ return false;
+
+ while (getline(&line, &sz, proc_self_mountinfo) != -1) {
+ char *token, *line_tok, *saveptr = NULL;
+ size_t i, j, k;
+ struct cgroup_mount_point *mount_point;
+ struct cgroup_hierarchy *h;
+ char **subsystems;
+ bool is_lxcfs = false;
+
+ if (line[0] && line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ for (i = 0, line_tok = line; (token = strtok_r(line_tok, " ", &saveptr)); line_tok = NULL) {
+ r = lxc_grow_array((void ***)&tokens, &token_capacity, i + 1, 64);
+ if (r < 0)
+ goto out;
+ tokens[i++] = token;
+ }
+
+ /* layout of /proc/self/mountinfo:
+ * 0: id
+ * 1: parent id
+ * 2: device major:minor
+ * 3: mount prefix
+ * 4: mount point
+ * 5: per-mount options
+ * [optional X]: additional data
+ * X+7: "-"
+ * X+8: type
+ * X+9: source
+ * X+10: per-superblock options
+ */
+ for (j = 6; j < i && tokens[j]; j++)
+ if (!strcmp(tokens[j], "-"))
+ break;
+
+ /* could not find separator */
+ if (j >= i || !tokens[j])
+ continue;
+ /* there should be exactly three fields after
+ * the separator
+ */
+ if (i != j + 4)
+ continue;
+
+ /* not a cgroup filesystem */
+ if (strcmp(tokens[j + 1], "cgroup") != 0) {
+ if (strcmp(tokens[j + 1], "fuse.lxcfs") != 0)
+ continue;
+ if (strncmp(tokens[4], "/sys/fs/cgroup/", 15) != 0)
+ continue;
+ is_lxcfs = true;
+ char *curtok = tokens[4] + 15;
+ subsystems = subsystems_from_mount_options(curtok,
+ kernel_subsystems);
+ } else
+ subsystems = subsystems_from_mount_options(tokens[j + 3],
+ kernel_subsystems);
+ if (!subsystems)
+ goto out;
+
+ h = NULL;
+ for (k = 1; k <= meta_data->maximum_hierarchy; k++) {
+ if (meta_data->hierarchies[k] &&
+ meta_data->hierarchies[k]->subsystems[0] &&
+ lxc_string_in_array(meta_data->hierarchies[k]->subsystems[0], (const char **)subsystems)) {
+ /* TODO: we could also check if the lists really match completely,
+ * just to have an additional sanity check */
+ h = meta_data->hierarchies[k];
+ break;
+ }
+ }
+ lxc_free_array((void **)subsystems, free);
+
+ r = lxc_grow_array((void ***)&meta_data->mount_points, &mount_point_capacity, mount_point_count + 1, 12);
+ if (r < 0)
+ goto out;
+
+ /* create mount point object */
+ mount_point = calloc(1, sizeof(*mount_point));
+ if (!mount_point)
+ goto out;
+
+ meta_data->mount_points[mount_point_count++] = mount_point;
+
+ mount_point->hierarchy = h;
+ if (is_lxcfs || is_cgns)
+ mount_point->mount_prefix = strdup("/");
+ else
+ mount_point->mount_prefix = strdup(tokens[3]);
+ mount_point->mount_point = strdup(tokens[4]);
+ if (!mount_point->mount_point || !mount_point->mount_prefix)
+ goto out;
+ mount_point->read_only = !lxc_string_in_list("rw", tokens[5], ',');
+
+ if (!strcmp(mount_point->mount_prefix, "/")) {
+ if (mount_point->read_only) {
+ if (!h->ro_absolute_mount_point)
+ h->ro_absolute_mount_point = mount_point;
+ } else {
+ if (!h->rw_absolute_mount_point)
+ h->rw_absolute_mount_point = mount_point;
+ }
+ }
+
+ k = lxc_array_len((void **)h->all_mount_points);
+ r = lxc_grow_array((void ***)&h->all_mount_points, &h->all_mount_point_capacity, k + 1, 4);
+ if (r < 0)
+ goto out;
+ h->all_mount_points[k] = mount_point;
+ }
+ bret = true;
+
+out:
+ fclose(proc_self_mountinfo);
+ free(tokens);
+ free(line);
+ return bret;
+}
+
+static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist)
+{
+ bool all_kernel_subsystems = true;
+ bool all_named_subsystems = false;
+ struct cgroup_meta_data *meta_data = NULL;
+ char **kernel_subsystems = NULL;
+ int saved_errno = 0;
+
+ /* if the subsystem whitelist is not specified, include all
+ * hierarchies that contain kernel subsystems by default but
+ * no hierarchies that only contain named subsystems
+ *
+ * if it is specified, the specifier @all will select all
+ * hierarchies, @kernel will select all hierarchies with
+ * kernel subsystems and @named will select all named
+ * hierarchies
+ */
+ all_kernel_subsystems = subsystem_whitelist ?
+ (lxc_string_in_array("@kernel", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
+ true;
+ all_named_subsystems = subsystem_whitelist ?
+ (lxc_string_in_array("@named", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
+ true;
+
+ meta_data = calloc(1, sizeof(struct cgroup_meta_data));
+ if (!meta_data)
+ return NULL;
+ meta_data->ref = 1;
+
+ if (!find_cgroup_subsystems(&kernel_subsystems))
+ goto out_error;
+
+ if (!find_cgroup_hierarchies(meta_data, all_kernel_subsystems,
+ all_named_subsystems, subsystem_whitelist))
+ goto out_error;
+
+ if (!find_hierarchy_mountpts(meta_data, kernel_subsystems))
+ goto out_error;
+
+ /* oops, we couldn't find anything */
+ if (!meta_data->hierarchies || !meta_data->mount_points) {
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ lxc_free_array((void **)kernel_subsystems, free);
+ return meta_data;
+
+out_error:
+ saved_errno = errno;
+ lxc_free_array((void **)kernel_subsystems, free);
+ lxc_cgroup_put_meta(meta_data);
+ errno = saved_errno;
+ return NULL;
+}
+
+static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data)
+{
+ meta_data->ref++;
+ return meta_data;
+}
+
+static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data)
+{
+ size_t i;
+ if (!meta_data)
+ return NULL;
+ if (--meta_data->ref > 0)
+ return meta_data;
+ lxc_free_array((void **)meta_data->mount_points, (lxc_free_fn)lxc_cgroup_mount_point_free);
+ if (meta_data->hierarchies) {
+ for (i = 0; i <= meta_data->maximum_hierarchy; i++)
+ lxc_cgroup_hierarchy_free(meta_data->hierarchies[i]);
+ }
+ free(meta_data->hierarchies);
+ free(meta_data);
+ return NULL;
+}
+
+static struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem)
+{
+ size_t i;
+ for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
+ struct cgroup_hierarchy *h = meta_data->hierarchies[i];
+ if (h && lxc_string_in_array(subsystem, (const char **)h->subsystems))
+ return h;
+ }
+ return NULL;
+}
+
+static bool mountpoint_is_accessible(struct cgroup_mount_point *mp)
+{
+ return mp && access(mp->mount_point, F_OK) == 0;
+}
+
+static struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable)
+{
+ struct cgroup_mount_point **mps;
+ struct cgroup_mount_point *current_result = NULL;
+ ssize_t quality = -1;
+
+ /* trivial case */
+ if (mountpoint_is_accessible(hierarchy->rw_absolute_mount_point))
+ return hierarchy->rw_absolute_mount_point;
+ if (!should_be_writable && mountpoint_is_accessible(hierarchy->ro_absolute_mount_point))
+ return hierarchy->ro_absolute_mount_point;
+
+ for (mps = hierarchy->all_mount_points; mps && *mps; mps++) {
+ struct cgroup_mount_point *mp = *mps;
+ size_t prefix_len = mp->mount_prefix ? strlen(mp->mount_prefix) : 0;
+
+ if (prefix_len == 1 && mp->mount_prefix[0] == '/')
+ prefix_len = 0;
+
+ if (!mountpoint_is_accessible(mp))
+ continue;
+
+ if (should_be_writable && mp->read_only)
+ continue;
+
+ if (!prefix_len ||
+ (strncmp(group, mp->mount_prefix, prefix_len) == 0 &&
+ (group[prefix_len] == '\0' || group[prefix_len] == '/'))) {
+ /* search for the best quality match, i.e. the match with the
+ * shortest prefix where this group is still contained
+ */
+ if (quality == -1 || prefix_len < quality) {
+ current_result = mp;
+ quality = prefix_len;
+ }
+ }
+ }
+
+ if (!current_result)
+ errno = ENOENT;
+ return current_result;
+}
+
+static char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix)
+{
+ struct cgroup_meta_data *meta_data;
+ struct cgroup_hierarchy *h;
+ struct cgroup_mount_point *mp;
+ char *result;
+ int saved_errno;
+
+ meta_data = lxc_cgroup_load_meta();
+ if (!meta_data)
+ return NULL;
+
+ h = lxc_cgroup_find_hierarchy(meta_data, subsystem);
+ if (!h)
+ goto out_error;
+
+ mp = lxc_cgroup_find_mount_point(h, group, should_be_writable);
+ if (!mp)
+ goto out_error;
+
+ result = cgroup_to_absolute_path(mp, group, suffix);
+ if (!result)
+ goto out_error;
+
+ lxc_cgroup_put_meta(meta_data);
+ return result;
+
+out_error:
+ saved_errno = errno;
+ lxc_cgroup_put_meta(meta_data);
+ errno = saved_errno;
+ return NULL;
+}
+
+static struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta)
+{
+ char pid_buf[32];
+ snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid);
+ return lxc_cgroup_process_info_getx(pid_buf, meta);
+}
+
+static struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta)
+{
+ return lxc_cgroup_process_info_get(1, meta);
+}
+
+static struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta)
+{
+ struct cgroup_process_info *i;
+ i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta);
+ if (!i)
+ i = lxc_cgroup_process_info_get(getpid(), meta);
+ return i;
+}
+
+/*
+ * If a controller has ns cgroup mounted, then in that cgroup the handler->pid
+ * is already in a new cgroup named after the pid. 'mnt' is passed in as
+ * the full current cgroup. Say that is /sys/fs/cgroup/lxc/2975 and the container
+ * name is c1. . We want to rename the cgroup directory to /sys/fs/cgroup/lxc/c1,
+ * and return the string /sys/fs/cgroup/lxc/c1.
+ */
+static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, pid_t pid, const char *name)
+{
+ char *dir, *fulloldpath;
+ char *newname, *fullnewpath;
+ int len, newlen, ret;
+
+ /*
+ * if cgroup is mounted at /cgroup and task is in cgroup /ab/, pid 2375 and
+ * name is c1,
+ * dir: /ab
+ * fulloldpath = /cgroup/ab/2375
+ * fullnewpath = /cgroup/ab/c1
+ * newname = /ab/c1
+ */
+ dir = alloca(strlen(oldname) + 1);
+ strcpy(dir, oldname);
+
+ len = strlen(oldname) + strlen(mountpath) + 22;
+ fulloldpath = alloca(len);
+ ret = snprintf(fulloldpath, len, "%s/%s/%ld", mountpath, oldname, (unsigned long)pid);
+ if (ret < 0 || ret >= len)
+ return NULL;
+
+ len = strlen(dir) + strlen(name) + 2;
+ newname = malloc(len);
+ if (!newname) {
+ SYSERROR("Out of memory");
+ return NULL;
+ }
+ ret = snprintf(newname, len, "%s/%s", dir, name);
+ if (ret < 0 || ret >= len) {
+ free(newname);
+ return NULL;
+ }
+
+ newlen = strlen(mountpath) + len + 2;
+ fullnewpath = alloca(newlen);
+ ret = snprintf(fullnewpath, newlen, "%s/%s", mountpath, newname);
+ if (ret < 0 || ret >= newlen) {
+ free(newname);
+ return NULL;
+ }
+
+ if (access(fullnewpath, F_OK) == 0) {
+ if (rmdir(fullnewpath) != 0) {
+ SYSERROR("container cgroup %s already exists.", fullnewpath);
+ free(newname);
+ return NULL;
+ }
+ }
+ if (rename(fulloldpath, fullnewpath)) {
+ SYSERROR("failed to rename cgroup %s->%s", fulloldpath, fullnewpath);
+ free(newname);
+ return NULL;
+ }
+
+ DEBUG("'%s' renamed to '%s'", oldname, newname);
+
+ return newname;
+}
+
+static bool is_crucial_hierarchy(struct cgroup_hierarchy *h)
+{
+ char **p;
+
+ for (p = h->subsystems; *p; p++) {
+ if (is_crucial_cgroup_subsystem(*p))
+ return true;
+ }
+ return false;
+}
+
+/* create a new cgroup */
+static struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern)
+{
+ char **cgroup_path_components = NULL;
+ char **p = NULL;
+ char *path_so_far = NULL;
+ char **new_cgroup_paths = NULL;
+ char **new_cgroup_paths_sub = NULL;
+ struct cgroup_mount_point *mp;
+ struct cgroup_hierarchy *h;
+ struct cgroup_process_info *base_info = NULL;
+ struct cgroup_process_info *info_ptr;
+ int saved_errno;
+ int r;
+ unsigned suffix = 0;
+ bool had_sub_pattern = false;
+ size_t i;
+
+ if (!is_valid_cgroup(name)) {
+ ERROR("Invalid cgroup name: '%s'", name);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!strstr(path_pattern, "%n")) {
+ ERROR("Invalid cgroup path pattern: '%s'; contains no %%n for specifying container name", path_pattern);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* we will modify the result of this operation directly,
+ * so we don't have to copy the data structure
+ */
+ base_info = (path_pattern[0] == '/') ?
+ lxc_cgroup_process_info_get_init(meta_data) :
+ lxc_cgroup_process_info_get_self(meta_data);
+ if (!base_info)
+ return NULL;
+
+ new_cgroup_paths = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
+ if (!new_cgroup_paths)
+ goto out_initial_error;
+
+ new_cgroup_paths_sub = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
+ if (!new_cgroup_paths_sub)
+ goto out_initial_error;
+
+ /* find mount points we can use */
+ for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
+ h = info_ptr->hierarchy;
+ mp = lxc_cgroup_find_mount_point(h, info_ptr->cgroup_path, true);
+ if (!mp) {
+ ERROR("Could not find writable mount point for cgroup hierarchy %d while trying to create cgroup.", h->index);
+ goto out_initial_error;
+ }
+ info_ptr->designated_mount_point = mp;
+
+ if (lxc_string_in_array("ns", (const char **)h->subsystems))
+ continue;
+ if (handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) {
+ ERROR("Could not set clone_children to 1 for cpuset hierarchy in parent cgroup.");
+ goto out_initial_error;
+ }
+ }
+
+ /* normalize the path */
+ cgroup_path_components = lxc_normalize_path(path_pattern);
+ if (!cgroup_path_components)
+ goto out_initial_error;
+
+ /* go through the path components to see if we can create them */
+ for (p = cgroup_path_components; *p || (sub_pattern && !had_sub_pattern); p++) {
+ /* we only want to create the same component with -1, -2, etc.
+ * if the component contains the container name itself, otherwise
+ * it's not an error if it already exists
+ */
+ char *p_eff = *p ? *p : (char *)sub_pattern;
+ bool contains_name = strstr(p_eff, "%n");
+ char *current_component = NULL;
+ char *current_subpath = NULL;
+ char *current_entire_path = NULL;
+ char *parts[3];
+ size_t j = 0;
+ i = 0;
+
+ /* if we are processing the subpattern, we want to make sure
+ * loop is ended the next time around
+ */
+ if (!*p) {
+ had_sub_pattern = true;
+ p--;
+ }
+
+ goto find_name_on_this_level;
+
+ cleanup_name_on_this_level:
+ /* This is reached if we found a name clash.
+ * In that case, remove the cgroup from all previous hierarchies
+ */
+ for (j = 0, info_ptr = base_info; j < i && info_ptr; info_ptr = info_ptr->next, j++) {
+ if (info_ptr->created_paths_count < 1)
+ continue;
+ r = remove_cgroup(info_ptr->designated_mount_point, info_ptr->created_paths[info_ptr->created_paths_count - 1], false, NULL);
+ if (r < 0)
+ WARN("could not clean up cgroup we created when trying to create container");
+ free(info_ptr->created_paths[info_ptr->created_paths_count - 1]);
+ info_ptr->created_paths[--info_ptr->created_paths_count] = NULL;
+ }
+ if (current_component != current_subpath)
+ free(current_subpath);
+ if (current_component != p_eff)
+ free(current_component);
+ current_component = current_subpath = NULL;
+ /* try again with another suffix */
+ ++suffix;
+
+ find_name_on_this_level:
+ /* determine name of the path component we should create */
+ if (contains_name && suffix > 0) {
+ char *buf = calloc(strlen(name) + 32, 1);
+ if (!buf)
+ goto out_initial_error;
+ snprintf(buf, strlen(name) + 32, "%s-%u", name, suffix);
+ current_component = lxc_string_replace("%n", buf, p_eff);
+ free(buf);
+ } else {
+ current_component = contains_name ? lxc_string_replace("%n", name, p_eff) : p_eff;
+ }
+ parts[0] = path_so_far;
+ parts[1] = current_component;
+ parts[2] = NULL;
+ current_subpath = path_so_far ? lxc_string_join("/", (const char **)parts, false) : current_component;
+
+ /* Now go through each hierarchy and try to create the
+ * corresponding cgroup
+ */
+ for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
+ char *parts2[3];
+
+ if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
+ continue;
+ current_entire_path = NULL;
+
+ parts2[0] = !strcmp(info_ptr->cgroup_path, "/") ? "" : info_ptr->cgroup_path;
+ parts2[1] = current_subpath;
+ parts2[2] = NULL;
+ current_entire_path = lxc_string_join("/", (const char **)parts2, false);
+
+ if (!*p) {
+ /* we are processing the subpath, so only update that one */
+ free(new_cgroup_paths_sub[i]);
+ new_cgroup_paths_sub[i] = strdup(current_entire_path);
+ if (!new_cgroup_paths_sub[i])
+ goto cleanup_from_error;
+ } else {
+ /* remember which path was used on this controller */
+ free(new_cgroup_paths[i]);
+ new_cgroup_paths[i] = strdup(current_entire_path);
+ if (!new_cgroup_paths[i])
+ goto cleanup_from_error;
+ }
+
+ r = create_cgroup(info_ptr->designated_mount_point, current_entire_path);
+ if (r < 0 && errno == EEXIST && contains_name) {
+ /* name clash => try new name with new suffix */
+ free(current_entire_path);
+ current_entire_path = NULL;
+ goto cleanup_name_on_this_level;
+ } else if (r < 0 && errno != EEXIST) {
+ if (is_crucial_hierarchy(info_ptr->hierarchy)) {
+ SYSERROR("Could not create cgroup '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point);
+ goto cleanup_from_error;
+ }
+ goto skip;
+ } else if (r == 0) {
+ /* successfully created */
+ r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
+ if (r < 0)
+ goto cleanup_from_error;
+ if (!init_cpuset_if_needed(info_ptr->designated_mount_point, current_entire_path)) {
+ ERROR("Failed to initialize cpuset for '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point);
+ goto cleanup_from_error;
+ }
+ info_ptr->created_paths[info_ptr->created_paths_count++] = current_entire_path;
+ } else {
+ /* if we didn't create the cgroup, then we have to make sure that
+ * further cgroups will be created properly
+ */
+ if (handle_cgroup_settings(info_ptr->designated_mount_point, info_ptr->cgroup_path) < 0) {
+ ERROR("Could not set clone_children to 1 for cpuset hierarchy in pre-existing cgroup.");
+ goto cleanup_from_error;
+ }
+ if (!init_cpuset_if_needed(info_ptr->designated_mount_point, info_ptr->cgroup_path)) {
+ ERROR("Failed to initialize cpuset in pre-existing '%s'.", info_ptr->cgroup_path);
+ goto cleanup_from_error;
+ }
+
+skip:
+ /* already existed but path component of pattern didn't contain '%n',
+ * so this is not an error; but then we don't need current_entire_path
+ * anymore...
+ */
+ free(current_entire_path);
+ current_entire_path = NULL;
+ }
+ }
+
+ /* save path so far */
+ free(path_so_far);
+ path_so_far = strdup(current_subpath);
+ if (!path_so_far)
+ goto cleanup_from_error;
+
+ /* cleanup */
+ if (current_component != current_subpath)
+ free(current_subpath);
+ if (current_component != p_eff)
+ free(current_component);
+ current_component = current_subpath = NULL;
+ continue;
+
+ cleanup_from_error:
+ /* called if an error occurred in the loop, so we
+ * do some additional cleanup here
+ */
+ saved_errno = errno;
+ if (current_component != current_subpath)
+ free(current_subpath);
+ if (current_component != p_eff)
+ free(current_component);
+ free(current_entire_path);
+ errno = saved_errno;
+ goto out_initial_error;
+ }
+
+ /* we're done, now update the paths */
+ for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
+ /* ignore legacy 'ns' subsystem here, lxc_cgroup_create_legacy
+ * will take care of it
+ * Since we do a continue in above loop, new_cgroup_paths[i] is
+ * unset anyway, as is new_cgroup_paths_sub[i]
+ */
+ if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
+ continue;
+ free(info_ptr->cgroup_path);
+ info_ptr->cgroup_path = new_cgroup_paths[i];
+ info_ptr->cgroup_path_sub = new_cgroup_paths_sub[i];
+ }
+ /* don't use lxc_free_array since we used the array members
+ * to store them in our result...
+ */
+ free(new_cgroup_paths);
+ free(new_cgroup_paths_sub);
+ free(path_so_far);
+ lxc_free_array((void **)cgroup_path_components, free);
+ return base_info;
+
+out_initial_error:
+ saved_errno = errno;
+ free(path_so_far);
+ lxc_cgroup_process_info_free_and_remove(base_info, NULL);
+ lxc_free_array((void **)new_cgroup_paths, free);
+ lxc_free_array((void **)new_cgroup_paths_sub, free);
+ lxc_free_array((void **)cgroup_path_components, free);
+ errno = saved_errno;
+ return NULL;
+}
+
+static int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid)
+{
+ struct cgroup_process_info *info_ptr;
+ int r;
+
+ for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
+ if (!lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
+ continue;
+ /*
+ * For any path which has ns cgroup mounted, handler->pid is already
+ * moved into a container called '%d % (handler->pid)'. Rename it to
+ * the cgroup name and record that.
+ */
+ char *tmp = cgroup_rename_nsgroup((const char *)info_ptr->designated_mount_point->mount_point,
+ info_ptr->cgroup_path, pid, name);
+ if (!tmp)
+ return -1;
+ free(info_ptr->cgroup_path);
+ info_ptr->cgroup_path = tmp;
+ r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
+ if (r < 0)
+ return -1;
+ tmp = strdup(tmp);
+ if (!tmp)
+ return -1;
+ info_ptr->created_paths[info_ptr->created_paths_count++] = tmp;
+ }
+ return 0;
+}
+
+/* get the cgroup membership of a given container */
+static struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data)
+{
+ struct cgroup_process_info *result = NULL;
+ int saved_errno = 0;
+ size_t i;
+ struct cgroup_process_info **cptr = &result;
+ struct cgroup_process_info *entry = NULL;
+ char *path = NULL;
+
+ for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
+ struct cgroup_hierarchy *h = meta_data->hierarchies[i];
+ if (!h || !h->used)
+ continue;
+
+ /* use the command interface to look for the cgroup */
+ path = lxc_cmd_get_cgroup_path(name, lxcpath, h->subsystems[0]);
+ if (!path) {
+ h->used = false;
+ continue;
+ }
+
+ entry = calloc(1, sizeof(struct cgroup_process_info));
+ if (!entry)
+ goto out_error;
+ entry->meta_ref = lxc_cgroup_get_meta(meta_data);
+ entry->hierarchy = h;
+ entry->cgroup_path = path;
+ path = NULL;
+
+ /* it is not an error if we don't find anything here,
+ * it is up to the caller to decide what to do in that
+ * case */
+ entry->designated_mount_point = lxc_cgroup_find_mount_point(h, entry->cgroup_path, true);
+
+ *cptr = entry;
+ cptr = &entry->next;
+ entry = NULL;
+ }
+
+ return result;
+out_error:
+ saved_errno = errno;
+ free(path);
+ lxc_cgroup_process_info_free(result);
+ lxc_cgroup_process_info_free(entry);
+ errno = saved_errno;
+ return NULL;
+}
+
+/* move a processs to the cgroups specified by the membership */
+static int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub)
+{
+ char pid_buf[32];
+ char *cgroup_tasks_fn;
+ int r;
+ struct cgroup_process_info *info_ptr;
+
+ snprintf(pid_buf, 32, "%lu", (unsigned long)pid);
+ for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
+ char *cgroup_path = (enter_sub && info_ptr->cgroup_path_sub) ?
+ info_ptr->cgroup_path_sub :
+ info_ptr->cgroup_path;
+
+ if (!info_ptr->designated_mount_point) {
+ info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, cgroup_path, true);
+ if (!info_ptr->designated_mount_point) {
+ SYSERROR("Could not add pid %lu to cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", (unsigned long)pid, cgroup_path);
+ return -1;
+ }
+ }
+
+ cgroup_tasks_fn = cgroup_to_absolute_path(info_ptr->designated_mount_point, cgroup_path, "/tasks");
+ if (!cgroup_tasks_fn) {
+ SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
+ return -1;
+ }
+
+ r = lxc_write_to_file(cgroup_tasks_fn, pid_buf, strlen(pid_buf), false);
+ free(cgroup_tasks_fn);
+ if (r < 0 && is_crucial_hierarchy(info_ptr->hierarchy)) {
+ SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* free process membership information */
+void lxc_cgroup_process_info_free(struct cgroup_process_info *info)
+{
+ struct cgroup_process_info *next;
+ if (!info)
+ return;
+ next = info->next;
+ lxc_cgroup_put_meta(info->meta_ref);
+ free(info->cgroup_path);
+ free(info->cgroup_path_sub);
+ lxc_free_array((void **)info->created_paths, free);
+ free(info);
+ lxc_cgroup_process_info_free(next);
+}
+
+/* free process membership information and remove cgroups that were created */
+void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info, struct lxc_conf *conf)
+{
+ struct cgroup_process_info *next;
+ char **pp;
+ if (!info)
+ return;
+ next = info->next;
+ {
+ struct cgroup_mount_point *mp = info->designated_mount_point;
+ if (!mp)
+ mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
+ if (mp)
+ /* ignore return value here, perhaps we created the
+ * '/lxc' cgroup in this container but another container
+ * is still running (for example)
+ */
+ (void)remove_cgroup(mp, info->cgroup_path, true, conf);
+ }
+ for (pp = info->created_paths; pp && *pp; pp++);
+ for ((void)(pp && --pp); info->created_paths && pp >= info->created_paths; --pp) {
+ free(*pp);
+ }
+ free(info->created_paths);
+ lxc_cgroup_put_meta(info->meta_ref);
+ free(info->cgroup_path);
+ free(info->cgroup_path_sub);
+ free(info);
+ lxc_cgroup_process_info_free_and_remove(next, conf);
+}
+
+static char *lxc_cgroup_get_hierarchy_path_data(const char *subsystem, struct cgfs_data *d)
+{
+ struct cgroup_process_info *info = d->info;
+ info = find_info_for_subsystem(info, subsystem);
+ if (!info)
+ return NULL;
+ prune_init_scope(info->cgroup_path);
+ return info->cgroup_path;
+}
+
+static char *lxc_cgroup_get_hierarchy_abs_path_data(const char *subsystem, struct cgfs_data *d)
+{
+ struct cgroup_process_info *info = d->info;
+ struct cgroup_mount_point *mp = NULL;
+
+ info = find_info_for_subsystem(info, subsystem);
+ if (!info)
+ return NULL;
+ if (info->designated_mount_point) {
+ mp = info->designated_mount_point;
+ } else {
+ mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
+ if (!mp)
+ return NULL;
+ }
+ return cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
+}
+
+static char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath)
+{
+ struct cgroup_meta_data *meta;
+ struct cgroup_process_info *base_info, *info;
+ struct cgroup_mount_point *mp;
+ char *result = NULL;
+
+ meta = lxc_cgroup_load_meta();
+ if (!meta)
+ return NULL;
+ base_info = lxc_cgroup_get_container_info(name, lxcpath, meta);
+ if (!base_info)
+ goto out1;
+ info = find_info_for_subsystem(base_info, subsystem);
+ if (!info)
+ goto out2;
+ if (info->designated_mount_point) {
+ mp = info->designated_mount_point;
+ } else {
+ mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
+ if (!mp)
+ goto out3;
+ }
+ result = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
+out3:
+out2:
+ lxc_cgroup_process_info_free(base_info);
+out1:
+ lxc_cgroup_put_meta(meta);
+ return result;
+}
+
+static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfs_data *d)
+{
+ char *subsystem = NULL, *p, *path;
+ int ret = -1;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ errno = ENOENT;
+ path = lxc_cgroup_get_hierarchy_abs_path_data(subsystem, d);
+ if (path) {
+ ret = do_cgroup_set(path, filename, value);
+ int saved_errno = errno;
+ free(path);
+ errno = saved_errno;
+ }
+ return ret;
+}
+
+static int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath)
+{
+ char *subsystem = NULL, *p, *path;
+ int ret = -1;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
+ if (path) {
+ ret = do_cgroup_set(path, filename, value);
+ free(path);
+ }
+ return ret;
+}
+
+static int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
+{
+ char *subsystem = NULL, *p, *path;
+ int ret = -1;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
+ if (path) {
+ ret = do_cgroup_get(path, filename, value, len);
+ free(path);
+ }
+ return ret;
+}
+
+static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type)
+{
+ size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup");
+ char *path = NULL;
+ char **parts = NULL;
+ char *dirname = NULL;
+ char *abs_path = NULL;
+ char *abs_path2 = NULL;
+ struct cgfs_data *cgfs_d;
+ struct cgroup_process_info *info, *base_info;
+ int r, saved_errno = 0;
+
+ if (cgns_supported())
+ return true;
+
+ cgfs_d = hdata;
+ if (!cgfs_d)
+ return false;
+ base_info = cgfs_d->info;
+
+ /* If we get passed the _NOSPEC types, we default to _MIXED, since we don't
+ * have access to the lxc_conf object at this point. It really should be up
+ * to the caller to fix this, but this doesn't really hurt.
+ */
+ if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
+ type = LXC_AUTO_CGROUP_FULL_MIXED;
+ else if (type == LXC_AUTO_CGROUP_NOSPEC)
+ type = LXC_AUTO_CGROUP_MIXED;
+
+ if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) {
+ ERROR("could not mount cgroups into container: invalid type specified internally");
+ errno = EINVAL;
+ return false;
+ }
+
+ path = calloc(1, bufsz);
+ if (!path)
+ return false;
+ snprintf(path, bufsz, "%s/sys/fs/cgroup", root);
+ r = safe_mount("cgroup_root", path, "tmpfs",
+ MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
+ "size=10240k,mode=755",
+ root);
+ if (r < 0) {
+ SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container");
+ return false;
+ }
+
+ /* now mount all the hierarchies we care about */
+ for (info = base_info; info; info = info->next) {
+ size_t subsystem_count, i;
+ struct cgroup_mount_point *mp = info->designated_mount_point;
+ if (!mountpoint_is_accessible(mp))
+ mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
+
+ if (!mp) {
+ SYSERROR("could not find original mount point for cgroup hierarchy while trying to mount cgroup filesystem");
+ goto out_error;
+ }
+
+ subsystem_count = lxc_array_len((void **)info->hierarchy->subsystems);
+ parts = calloc(subsystem_count + 1, sizeof(char *));
+ if (!parts)
+ goto out_error;
+
+ for (i = 0; i < subsystem_count; i++) {
+ if (!strncmp(info->hierarchy->subsystems[i], "name=", 5))
+ parts[i] = info->hierarchy->subsystems[i] + 5;
+ else
+ parts[i] = info->hierarchy->subsystems[i];
+ }
+ dirname = lxc_string_join(",", (const char **)parts, false);
+ if (!dirname)
+ goto out_error;
+
+ /* create subsystem directory */
+ abs_path = lxc_append_paths(path, dirname);
+ if (!abs_path)
+ goto out_error;
+ r = mkdir_p(abs_path, 0755);
+ if (r < 0 && errno != EEXIST) {
+ SYSERROR("could not create cgroup subsystem directory /sys/fs/cgroup/%s", dirname);
+ goto out_error;
+ }
+
+ abs_path2 = lxc_append_paths(abs_path, info->cgroup_path);
+ if (!abs_path2)
+ goto out_error;
+
+ if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_RW || type == LXC_AUTO_CGROUP_FULL_MIXED) {
+ /* bind-mount the cgroup entire filesystem there */
+ if (strcmp(mp->mount_prefix, "/") != 0) {
+ /* FIXME: maybe we should just try to remount the entire hierarchy
+ * with a regular mount command? may that works? */
+ ERROR("could not automatically mount cgroup-full to /sys/fs/cgroup/%s: host has no mount point for this cgroup filesystem that has access to the root cgroup", dirname);
+ goto out_error;
+ }
+ r = mount(mp->mount_point, abs_path, "none", MS_BIND, 0);
+ if (r < 0) {
+ SYSERROR("error bind-mounting %s to %s", mp->mount_point, abs_path);
+ goto out_error;
+ }
+ /* main cgroup path should be read-only */
+ if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_MIXED) {
+ r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
+ if (r < 0) {
+ SYSERROR("error re-mounting %s readonly", abs_path);
+ goto out_error;
+ }
+ }
+ /* own cgroup should be read-write */
+ if (type == LXC_AUTO_CGROUP_FULL_MIXED) {
+ r = mount(abs_path2, abs_path2, NULL, MS_BIND, NULL);
+ if (r < 0) {
+ SYSERROR("error bind-mounting %s onto itself", abs_path2);
+ goto out_error;
+ }
+ r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND, NULL);
+ if (r < 0) {
+ SYSERROR("error re-mounting %s readwrite", abs_path2);
+ goto out_error;
+ }
+ }
+ } else {
+ /* create path for container's cgroup */
+ r = mkdir_p(abs_path2, 0755);
+ if (r < 0 && errno != EEXIST) {
+ SYSERROR("could not create cgroup directory /sys/fs/cgroup/%s%s", dirname, info->cgroup_path);
+ goto out_error;
+ }
+
+ /* for read-only and mixed cases, we have to bind-mount the tmpfs directory
+ * that points to the hierarchy itself (i.e. /sys/fs/cgroup/cpu etc.) onto
+ * itself and then bind-mount it read-only, since we keep the tmpfs itself
+ * read-write (see comment below)
+ */
+ if (type == LXC_AUTO_CGROUP_MIXED || type == LXC_AUTO_CGROUP_RO) {
+ r = mount(abs_path, abs_path, NULL, MS_BIND, NULL);
+ if (r < 0) {
+ SYSERROR("error bind-mounting %s onto itself", abs_path);
+ goto out_error;
+ }
+ r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
+ if (r < 0) {
+ SYSERROR("error re-mounting %s readonly", abs_path);
+ goto out_error;
+ }
+ }
+
+ free(abs_path);
+ abs_path = NULL;
+
+ /* bind-mount container's cgroup to that directory */
+ abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
+ if (!abs_path)
+ goto out_error;
+ r = mount(abs_path, abs_path2, "none", MS_BIND, 0);
+ if (r < 0 && is_crucial_hierarchy(info->hierarchy)) {
+ SYSERROR("error bind-mounting %s to %s", abs_path, abs_path2);
+ goto out_error;
+ }
+ if (type == LXC_AUTO_CGROUP_RO) {
+ r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
+ if (r < 0) {
+ SYSERROR("error re-mounting %s readonly", abs_path2);
+ goto out_error;
+ }
+ }
+ }
+
+ free(abs_path);
+ free(abs_path2);
+ abs_path = NULL;
+ abs_path2 = NULL;
+
+ /* add symlinks for every single subsystem */
+ if (subsystem_count > 1) {
+ for (i = 0; i < subsystem_count; i++) {
+ abs_path = lxc_append_paths(path, parts[i]);
+ if (!abs_path)
+ goto out_error;
+ r = symlink(dirname, abs_path);
+ if (r < 0)
+ WARN("could not create symlink %s -> %s in /sys/fs/cgroup of container", parts[i], dirname);
+ free(abs_path);
+ abs_path = NULL;
+ }
+ }
+ free(dirname);
+ free(parts);
+ dirname = NULL;
+ parts = NULL;
+ }
+
+ /* We used to remount the entire tmpfs readonly if any :ro or
+ * :mixed mode was specified. However, Ubuntu's mountall has the
+ * unfortunate behavior to block bootup if /sys/fs/cgroup is
+ * mounted read-only and cannot be remounted read-write.
+ * (mountall reads /lib/init/fstab and tries to (re-)mount all of
+ * these if they are not already mounted with the right options;
+ * it contains an entry for /sys/fs/cgroup. In case it can't do
+ * that, it prompts for the user to either manually fix it or
+ * boot anyway. But without user input, booting of the container
+ * hangs.)
+ *
+ * Instead of remounting the entire tmpfs readonly, we only
+ * remount the paths readonly that are part of the cgroup
+ * hierarchy.
+ */
+
+ free(path);
+
+ return true;
+
+out_error:
+ saved_errno = errno;
+ free(path);
+ free(dirname);
+ free(parts);
+ free(abs_path);
+ free(abs_path2);
+ errno = saved_errno;
+ return false;
+}
+
+static int cgfs_nrtasks(void *hdata)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *info;
+ struct cgroup_mount_point *mp = NULL;
+ char *abs_path = NULL;
+ int ret;
+
+ if (!d) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ info = d->info;
+ if (!info) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (info->designated_mount_point) {
+ mp = info->designated_mount_point;
+ } else {
+ mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, false);
+ if (!mp)
+ return -1;
+ }
+
+ abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
+ if (!abs_path)
+ return -1;
+
+ ret = cgroup_recursive_task_count(abs_path);
+ free(abs_path);
+ return ret;
+}
+
+static struct cgroup_process_info *
+lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str,
+ struct cgroup_meta_data *meta)
+{
+ struct cgroup_process_info *result = NULL;
+ FILE *proc_pid_cgroup = NULL;
+ char *line = NULL;
+ size_t sz = 0;
+ int saved_errno = 0;
+ struct cgroup_process_info **cptr = &result;
+ struct cgroup_process_info *entry = NULL;
+
+ proc_pid_cgroup = fopen_cloexec(proc_pid_cgroup_str, "r");
+ if (!proc_pid_cgroup)
+ return NULL;
+
+ while (getline(&line, &sz, proc_pid_cgroup) != -1) {
+ /* file format: hierarchy:subsystems:group */
+ char *colon1;
+ char *colon2;
+ char *endptr;
+ int hierarchy_number;
+ struct cgroup_hierarchy *h = NULL;
+
+ if (!line[0])
+ continue;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ colon1 = strchr(line, ':');
+ if (!colon1)
+ continue;
+ *colon1++ = '\0';
+ colon2 = strchr(colon1, ':');
+ if (!colon2)
+ continue;
+ *colon2++ = '\0';
+
+ endptr = NULL;
+ hierarchy_number = strtoul(line, &endptr, 10);
+ if (!endptr || *endptr)
+ continue;
+
+ if (hierarchy_number > meta->maximum_hierarchy) {
+ /* we encountered a hierarchy we didn't have before,
+ * so probably somebody remounted some stuff in the
+ * mean time...
+ */
+ errno = EAGAIN;
+ goto out_error;
+ }
+
+ h = meta->hierarchies[hierarchy_number];
+ if (!h) {
+ /* we encountered a hierarchy that was thought to be
+ * dead before, so probably somebody remounted some
+ * stuff in the mean time...
+ */
+ errno = EAGAIN;
+ goto out_error;
+ }
+
+ /* we are told that we should ignore this hierarchy */
+ if (!h->used)
+ continue;
+
+ entry = calloc(1, sizeof(struct cgroup_process_info));
+ if (!entry)
+ goto out_error;
+
+ entry->meta_ref = lxc_cgroup_get_meta(meta);
+ entry->hierarchy = h;
+ entry->cgroup_path = strdup(colon2);
+ if (!entry->cgroup_path)
+ goto out_error;
+ prune_init_scope(entry->cgroup_path);
+
+ *cptr = entry;
+ cptr = &entry->next;
+ entry = NULL;
+ }
+
+ fclose(proc_pid_cgroup);
+ free(line);
+ return result;
+
+out_error:
+ saved_errno = errno;
+ if (proc_pid_cgroup)
+ fclose(proc_pid_cgroup);
+ lxc_cgroup_process_info_free(result);
+ lxc_cgroup_process_info_free(entry);
+ free(line);
+ errno = saved_errno;
+ return NULL;
+}
+
+static char **subsystems_from_mount_options(const char *mount_options,
+ char **kernel_list)
+{
+ char *token, *str, *saveptr = NULL;
+ char **result = NULL;
+ size_t result_capacity = 0;
+ size_t result_count = 0;
+ int saved_errno;
+ int r;
+
+ str = alloca(strlen(mount_options)+1);
+ strcpy(str, mount_options);
+ for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) {
+ /* we have a subsystem if it's either in the list of
+ * subsystems provided by the kernel OR if it starts
+ * with name= for named hierarchies
+ */
+ r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 12);
+ if (r < 0)
+ goto out_free;
+ result[result_count + 1] = NULL;
+ if (strncmp(token, "name=", 5) && !lxc_string_in_array(token, (const char **)kernel_list)) {
+ // this is eg 'systemd' but the mount will be 'name=systemd'
+ result[result_count] = malloc(strlen(token) + 6);
+ if (result[result_count])
+ sprintf(result[result_count], "name=%s", token);
+ } else
+ result[result_count] = strdup(token);
+ if (!result[result_count])
+ goto out_free;
+ result_count++;
+ }
+
+ return result;
+
+out_free:
+ saved_errno = errno;
+ lxc_free_array((void**)result, free);
+ errno = saved_errno;
+ return NULL;
+}
+
+static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp)
+{
+ if (!mp)
+ return;
+ free(mp->mount_point);
+ free(mp->mount_prefix);
+ free(mp);
+}
+
+static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h)
+{
+ if (!h)
+ return;
+ lxc_free_array((void **)h->subsystems, free);
+ free(h->all_mount_points);
+ free(h);
+}
+
+static bool is_valid_cgroup(const char *name)
+{
+ const char *p;
+ for (p = name; *p; p++) {
+ /* Use the ASCII printable characters range(32 - 127)
+ * is reasonable, we kick out 32(SPACE) because it'll
+ * break legacy lxc-ls
+ */
+ if (*p <= 32 || *p >= 127 || *p == '/')
+ return false;
+ }
+ return strcmp(name, ".") != 0 && strcmp(name, "..") != 0;
+}
+
+static int create_or_remove_cgroup(bool do_remove,
+ struct cgroup_mount_point *mp, const char *path, int recurse,
+ struct lxc_conf *conf)
+{
+ int r, saved_errno = 0;
+ char *buf = cgroup_to_absolute_path(mp, path, NULL);
+ if (!buf)
+ return -1;
+
+ /* create or remove directory */
+ if (do_remove) {
+ if (!dir_exists(buf))
+ return 0;
+ if (recurse) {
+ if (conf && !lxc_list_empty(&conf->id_map))
+ r = userns_exec_1(conf, rmdir_wrapper, buf);
+ else
+ r = cgroup_rmdir(buf);
+ } else
+ r = rmdir(buf);
+ } else
+ r = mkdir(buf, 0777);
+ saved_errno = errno;
+ free(buf);
+ errno = saved_errno;
+ return r;
+}
+
+static int create_cgroup(struct cgroup_mount_point *mp, const char *path)
+{
+ return create_or_remove_cgroup(false, mp, path, false, NULL);
+}
+
+static int remove_cgroup(struct cgroup_mount_point *mp,
+ const char *path, bool recurse, struct lxc_conf *conf)
+{
+ return create_or_remove_cgroup(true, mp, path, recurse, conf);
+}
+
+static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp,
+ const char *path, const char *suffix)
+{
+ /* first we have to make sure we subtract the mount point's prefix */
+ char *prefix = mp->mount_prefix;
+ char *buf;
+ ssize_t len, rv;
+
+ /* we want to make sure only absolute paths to cgroups are passed to us */
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (prefix && !strcmp(prefix, "/"))
+ prefix = NULL;
+
+ /* prefix doesn't match */
+ if (prefix && strncmp(prefix, path, strlen(prefix)) != 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* if prefix is /foo and path is /foobar */
+ if (prefix && path[strlen(prefix)] != '/' && path[strlen(prefix)] != '\0') {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* remove prefix from path */
+ path += prefix ? strlen(prefix) : 0;
+
+ len = strlen(mp->mount_point) + strlen(path) + (suffix ? strlen(suffix) : 0);
+ buf = calloc(len + 1, 1);
+ if (!buf)
+ return NULL;
+ rv = snprintf(buf, len + 1, "%s%s%s", mp->mount_point, path, suffix ? suffix : "");
+ if (rv > len) {
+ free(buf);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return buf;
+}
+
+static struct cgroup_process_info *
+find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem)
+{
+ struct cgroup_process_info *info_ptr;
+ for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
+ struct cgroup_hierarchy *h = info_ptr->hierarchy;
+ if (lxc_string_in_array(subsystem, (const char **)h->subsystems))
+ return info_ptr;
+ }
+ errno = ENOENT;
+ return NULL;
+}
+
+static int do_cgroup_get(const char *cgroup_path, const char *sub_filename,
+ char *value, size_t len)
+{
+ const char *parts[3] = {
+ cgroup_path,
+ sub_filename,
+ NULL
+ };
+ char *filename;
+ int ret, saved_errno;
+
+ filename = lxc_string_join("/", parts, false);
+ if (!filename)
+ return -1;
+
+ ret = lxc_read_from_file(filename, value, len);
+ saved_errno = errno;
+ free(filename);
+ errno = saved_errno;
+ return ret;
+}
+
+static int do_cgroup_set(const char *cgroup_path, const char *sub_filename,
+ const char *value)
+{
+ const char *parts[3] = {
+ cgroup_path,
+ sub_filename,
+ NULL
+ };
+ char *filename;
+ int ret, saved_errno;
+
+ filename = lxc_string_join("/", parts, false);
+ if (!filename)
+ return -1;
+
+ ret = lxc_write_to_file(filename, value, strlen(value), false);
+ saved_errno = errno;
+ free(filename);
+ errno = saved_errno;
+ return ret;
+}
+
+static int do_setup_cgroup_limits(struct cgfs_data *d,
+ struct lxc_list *cgroup_settings, bool do_devices)
+{
+ struct lxc_list *iterator, *sorted_cgroup_settings, *next;
+ struct lxc_cgroup *cg;
+ int ret = -1;
+
+ if (lxc_list_empty(cgroup_settings))
+ return 0;
+
+ sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
+ if (!sorted_cgroup_settings) {
+ return -1;
+ }
+
+ lxc_list_for_each(iterator, sorted_cgroup_settings) {
+ cg = iterator->elem;
+
+ if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
+ if (strcmp(cg->subsystem, "devices.deny") == 0 &&
+ cgroup_devices_has_allow_or_deny(d, cg->value, false))
+ continue;
+ if (strcmp(cg->subsystem, "devices.allow") == 0 &&
+ cgroup_devices_has_allow_or_deny(d, cg->value, true))
+ continue;
+ if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
+ if (do_devices && (errno == EACCES || errno == EPERM)) {
+ WARN("Error setting %s to %s for %s",
+ cg->subsystem, cg->value, d->name);
+ continue;
+ }
+ SYSERROR("Error setting %s to %s for %s",
+ cg->subsystem, cg->value, d->name);
+ goto out;
+ }
+ }
+
+ DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
+ }
+
+ ret = 0;
+ INFO("cgroup has been setup");
+out:
+ lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
+ lxc_list_del(iterator);
+ free(iterator);
+ }
+ free(sorted_cgroup_settings);
+ return ret;
+}
+
+static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d,
+ char *v, bool for_allow)
+{
+ char *path;
+ FILE *devices_list;
+ char *line = NULL;
+ size_t sz = 0;
+ bool ret = !for_allow;
+ const char *parts[3] = {
+ NULL,
+ "devices.list",
+ NULL
+ };
+
+ // XXX FIXME if users could use something other than 'lxc.devices.deny = a'.
+ // not sure they ever do, but they *could*
+ // right now, I'm assuming they do NOT
+ if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0)
+ return false;
+
+ parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_data("devices", d);
+ if (!parts[0])
+ return false;
+ path = lxc_string_join("/", parts, false);
+ if (!path) {
+ free((void *)parts[0]);
+ return false;
+ }
+
+ devices_list = fopen_cloexec(path, "r");
+ if (!devices_list) {
+ free(path);
+ return false;
+ }
+
+ while (getline(&line, &sz, devices_list) != -1) {
+ size_t len = strlen(line);
+ if (len > 0 && line[len-1] == '\n')
+ line[len-1] = '\0';
+ if (strcmp(line, "a *:* rwm") == 0) {
+ ret = for_allow;
+ goto out;
+ } else if (for_allow && strcmp(line, v) == 0) {
+ ret = true;
+ goto out;
+ }
+ }
+
+out:
+ fclose(devices_list);
+ free(line);
+ free(path);
+ return ret;
+}
+
+static int cgroup_recursive_task_count(const char *cgroup_path)
+{
+ DIR *d;
+ struct dirent *dent_buf;
+ struct dirent *dent;
+ ssize_t name_max;
+ int n = 0, r;
+
+ /* see man readdir_r(3) */
+ name_max = pathconf(cgroup_path, _PC_NAME_MAX);
+ if (name_max <= 0)
+ name_max = 255;
+ dent_buf = malloc(offsetof(struct dirent, d_name) + name_max + 1);
+ if (!dent_buf)
+ return -1;
+
+ d = opendir(cgroup_path);
+ if (!d) {
+ free(dent_buf);
+ return 0;
+ }
+
+ while (readdir_r(d, dent_buf, &dent) == 0 && dent) {
+ const char *parts[3] = {
+ cgroup_path,
+ dent->d_name,
+ NULL
+ };
+ char *sub_path;
+ struct stat st;
+
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+ sub_path = lxc_string_join("/", parts, false);
+ if (!sub_path) {
+ closedir(d);
+ free(dent_buf);
+ return -1;
+ }
+ r = stat(sub_path, &st);
+ if (r < 0) {
+ closedir(d);
+ free(dent_buf);
+ free(sub_path);
+ return -1;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ r = cgroup_recursive_task_count(sub_path);
+ if (r >= 0)
+ n += r;
+ } else if (!strcmp(dent->d_name, "tasks")) {
+ r = lxc_count_file_lines(sub_path);
+ if (r >= 0)
+ n += r;
+ }
+ free(sub_path);
+ }
+ closedir(d);
+ free(dent_buf);
+
+ return n;
+}
+
+static int handle_cgroup_settings(struct cgroup_mount_point *mp,
+ char *cgroup_path)
+{
+ int r, saved_errno = 0;
+ char buf[2];
+
+ mp->need_cpuset_init = false;
+
+ /* If this is the memory cgroup, we want to enforce hierarchy.
+ * But don't fail if for some reason we can't.
+ */
+ if (lxc_string_in_array("memory", (const char **)mp->hierarchy->subsystems)) {
+ char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/memory.use_hierarchy");
+ if (cc_path) {
+ r = lxc_read_from_file(cc_path, buf, 1);
+ if (r < 1 || buf[0] != '1') {
+ r = lxc_write_to_file(cc_path, "1", 1, false);
+ if (r < 0)
+ SYSERROR("failed to set memory.use_hierarchy to 1; continuing");
+ }
+ free(cc_path);
+ }
+ }
+
+ /* if this is a cpuset hierarchy, we have to set cgroup.clone_children in
+ * the base cgroup, otherwise containers will start with an empty cpuset.mems
+ * and cpuset.cpus and then
+ */
+ if (lxc_string_in_array("cpuset", (const char **)mp->hierarchy->subsystems)) {
+ char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/cgroup.clone_children");
+ struct stat sb;
+
+ if (!cc_path)
+ return -1;
+ /* cgroup.clone_children is not available when running under
+ * older kernel versions; in this case, we'll initialize
+ * cpuset.cpus and cpuset.mems later, after the new cgroup
+ * was created
+ */
+ if (stat(cc_path, &sb) != 0 && errno == ENOENT) {
+ mp->need_cpuset_init = true;
+ free(cc_path);
+ return 0;
+ }
+ r = lxc_read_from_file(cc_path, buf, 1);
+ if (r == 1 && buf[0] == '1') {
+ free(cc_path);
+ return 0;
+ }
+ r = lxc_write_to_file(cc_path, "1", 1, false);
+ saved_errno = errno;
+ free(cc_path);
+ errno = saved_errno;
+ return r < 0 ? -1 : 0;
+ }
+ return 0;
+}
+
+static int cgroup_read_from_file(const char *fn, char buf[], size_t bufsize)
+{
+ int ret = lxc_read_from_file(fn, buf, bufsize);
+ if (ret < 0) {
+ SYSERROR("failed to read %s", fn);
+ return ret;
+ }
+ if (ret == bufsize) {
+ if (bufsize > 0) {
+ /* obviously this wasn't empty */
+ buf[bufsize-1] = '\0';
+ return ret;
+ }
+ /* Callers don't do this, but regression/sanity check */
+ ERROR("%s: was not expecting 0 bufsize", __func__);
+ return -1;
+ }
+ buf[ret] = '\0';
+ return ret;
+}
+
+static bool do_init_cpuset_file(struct cgroup_mount_point *mp,
+ const char *path, const char *name)
+{
+ char value[1024];
+ char *childfile, *parentfile = NULL, *tmp;
+ int ret;
+ bool ok = false;
+
+ childfile = cgroup_to_absolute_path(mp, path, name);
+ if (!childfile)
+ return false;
+
+ /* don't overwrite a non-empty value in the file */
+ ret = cgroup_read_from_file(childfile, value, sizeof(value));
+ if (ret < 0)
+ goto out;
+ if (value[0] != '\0' && value[0] != '\n') {
+ ok = true;
+ goto out;
+ }
+
+ /* path to the same name in the parent cgroup */
+ parentfile = strdup(path);
+ if (!parentfile)
+ goto out;
+
+ tmp = strrchr(parentfile, '/');
+ if (!tmp)
+ goto out;
+ if (tmp == parentfile)
+ tmp++; /* keep the '/' at the start */
+ *tmp = '\0';
+ tmp = parentfile;
+ parentfile = cgroup_to_absolute_path(mp, tmp, name);
+ free(tmp);
+ if (!parentfile)
+ goto out;
+
+ /* copy from parent to child cgroup */
+ ret = cgroup_read_from_file(parentfile, value, sizeof(value));
+ if (ret < 0)
+ goto out;
+ if (ret == sizeof(value)) {
+ /* If anyone actually sees this error, we can address it */
+ ERROR("parent cpuset value too long");
+ goto out;
+ }
+ ok = (lxc_write_to_file(childfile, value, strlen(value), false) >= 0);
+ if (!ok)
+ SYSERROR("failed writing %s", childfile);
+
+out:
+ free(parentfile);
+ free(childfile);
+ return ok;
+}
+
+static bool init_cpuset_if_needed(struct cgroup_mount_point *mp,
+ const char *path)
+{
+ /* the files we have to handle here are only in cpuset hierarchies */
+ if (!lxc_string_in_array("cpuset",
+ (const char **)mp->hierarchy->subsystems))
+ return true;
+
+ if (!mp->need_cpuset_init)
+ return true;
+
+ return (do_init_cpuset_file(mp, path, "/cpuset.cpus") &&
+ do_init_cpuset_file(mp, path, "/cpuset.mems") );
+}
+
+struct cgroup_ops *cgfs_ops_init(void)
+{
+ return &cgfs_ops;
+}
+
+static void *cgfs_init(const char *name)
+{
+ struct cgfs_data *d;
+
+ d = malloc(sizeof(*d));
+ if (!d)
+ return NULL;
+
+ memset(d, 0, sizeof(*d));
+ d->name = strdup(name);
+ if (!d->name)
+ goto err1;
+
+ d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
+
+ d->meta = lxc_cgroup_load_meta();
+ if (!d->meta) {
+ ERROR("cgroupfs failed to detect cgroup metadata");
+ goto err2;
+ }
+ return d;
+
+err2:
+ free(d->name);
+err1:
+ free(d);
+ return NULL;
+}
+
+static void cgfs_destroy(void *hdata, struct lxc_conf *conf)
+{
+ struct cgfs_data *d = hdata;
+
+ if (!d)
+ return;
+ free(d->name);
+ lxc_cgroup_process_info_free_and_remove(d->info, conf);
+ lxc_cgroup_put_meta(d->meta);
+ free(d);
+}
+
+static inline bool cgfs_create(void *hdata)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *i;
+ struct cgroup_meta_data *md;
+
+ if (!d)
+ return false;
+ md = d->meta;
+ i = lxc_cgroupfs_create(d->name, d->cgroup_pattern, md, NULL);
+ if (!i)
+ return false;
+ d->info = i;
+ return true;
+}
+
+static inline bool cgfs_enter(void *hdata, pid_t pid)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *i;
+ int ret;
+
+ if (!d)
+ return false;
+ i = d->info;
+ ret = lxc_cgroupfs_enter(i, pid, false);
+
+ return ret == 0;
+}
+
+static inline bool cgfs_create_legacy(void *hdata, pid_t pid)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *i;
+
+ if (!d)
+ return false;
+ i = d->info;
+ if (lxc_cgroup_create_legacy(i, d->name, pid) < 0) {
+ ERROR("failed to create legacy ns cgroups for '%s'", d->name);
+ return false;
+ }
+ return true;
+}
+
+static const char *cgfs_get_cgroup(void *hdata, const char *subsystem)
+{
+ struct cgfs_data *d = hdata;
+
+ if (!d)
+ return NULL;
+ return lxc_cgroup_get_hierarchy_path_data(subsystem, d);
+}
+
+static const char *cgfs_canonical_path(void *hdata)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *info_ptr;
+ char *path = NULL;
+
+ if (!d)
+ return NULL;
+
+ for (info_ptr = d->info; info_ptr; info_ptr = info_ptr->next) {
+ if (!path)
+ path = info_ptr->cgroup_path;
+ else if (strcmp(path, info_ptr->cgroup_path) != 0) {
+ ERROR("not all paths match %s, %s has path %s", path,
+ info_ptr->hierarchy->subsystems[0], info_ptr->cgroup_path);
+ return NULL;
+ }
+ }
+
+ return path;
+}
+
+static bool cgfs_escape(void *hdata)
+{
+ struct cgroup_meta_data *md;
+ int i;
+ bool ret = false;
+
+ md = lxc_cgroup_load_meta();
+ if (!md)
+ return false;
+
+ for (i = 1; i <= md->maximum_hierarchy; i++) {
+ struct cgroup_hierarchy *h = md->hierarchies[i];
+ struct cgroup_mount_point *mp;
+ char *tasks;
+ FILE *f;
+ int written;
+
+ if (!h) {
+ WARN("not escaping hierarchy %d", i);
+ continue;
+ }
+
+ mp = lxc_cgroup_find_mount_point(h, "/", true);
+ if (!mp)
+ goto out;
+
+ tasks = cgroup_to_absolute_path(mp, "/", "tasks");
+ if (!tasks)
+ goto out;
+
+ f = fopen(tasks, "a");
+ free(tasks);
+ if (!f)
+ goto out;
+
+ written = fprintf(f, "%d\n", getpid());
+ fclose(f);
+ if (written < 0) {
+ SYSERROR("writing tasks failed\n");
+ goto out;
+ }
+ }
+
+ ret = true;
+out:
+ lxc_cgroup_put_meta(md);
+ return ret;
+}
+
+static bool cgfs_unfreeze(void *hdata)
+{
+ struct cgfs_data *d = hdata;
+ char *cgabspath, *cgrelpath;
+ int ret;
+
+ if (!d)
+ return false;
+
+ cgrelpath = lxc_cgroup_get_hierarchy_path_data("freezer", d);
+ cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL);
+ if (!cgabspath)
+ return false;
+
+ ret = do_cgroup_set(cgabspath, "freezer.state", "THAWED");
+ free(cgabspath);
+ return ret == 0;
+}
+
+static bool cgroupfs_setup_limits(void *hdata, struct lxc_list *cgroup_conf,
+ bool with_devices)
+{
+ struct cgfs_data *d = hdata;
+
+ if (!d)
+ return false;
+ return do_setup_cgroup_limits(d, cgroup_conf, with_devices) == 0;
+}
+
+static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)
+{
+ struct cgroup_meta_data *meta_data;
+ struct cgroup_process_info *container_info;
+ int ret;
+
+ meta_data = lxc_cgroup_load_meta();
+ if (!meta_data) {
+ ERROR("could not move attached process %d to cgroup of container", pid);
+ return false;
+ }
+
+ container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
+ lxc_cgroup_put_meta(meta_data);
+ if (!container_info) {
+ ERROR("could not move attached process %d to cgroup of container", pid);
+ return false;
+ }
+
+ ret = lxc_cgroupfs_enter(container_info, pid, false);
+ lxc_cgroup_process_info_free(container_info);
+ if (ret < 0) {
+ ERROR("could not move attached process %d to cgroup of container", pid);
+ return false;
+ }
+ return true;
+}
+
+struct chown_data {
+ const char *cgroup_path;
+ uid_t origuid;
+};
+
+/*
+ * TODO - someone should refactor this to unshare once passing all the paths
+ * to be chowned in one go
+ */
+static int chown_cgroup_wrapper(void *data)
+{
+ struct chown_data *arg = data;
+ uid_t destuid;
+ char *fpath;
+
+ if (setresgid(0,0,0) < 0)
+ SYSERROR("Failed to setgid to 0");
+ if (setresuid(0,0,0) < 0)
+ SYSERROR("Failed to setuid to 0");
+ if (setgroups(0, NULL) < 0)
+ SYSERROR("Failed to clear groups");
+ destuid = get_ns_uid(arg->origuid);
+
+ if (chown(arg->cgroup_path, destuid, 0) < 0)
+ SYSERROR("Failed chowning %s to %d", arg->cgroup_path, (int)destuid);
+
+ fpath = lxc_append_paths(arg->cgroup_path, "tasks");
+ if (!fpath)
+ return -1;
+ if (chown(fpath, destuid, 0) < 0)
+ SYSERROR("Error chowning %s\n", fpath);
+ free(fpath);
+
+ fpath = lxc_append_paths(arg->cgroup_path, "cgroup.procs");
+ if (!fpath)
+ return -1;
+ if (chown(fpath, destuid, 0) < 0)
+ SYSERROR("Error chowning %s", fpath);
+ free(fpath);
+
+ return 0;
+}
+
+static bool do_cgfs_chown(char *cgroup_path, struct lxc_conf *conf)
+{
+ struct chown_data data;
+ char *fpath;
+
+ if (!dir_exists(cgroup_path))
+ return true;
+
+ if (lxc_list_empty(&conf->id_map))
+ /* If there's no mapping then we don't need to chown */
+ return true;
+
+ data.cgroup_path = cgroup_path;
+ data.origuid = geteuid();
+
+ /* Unpriv users can't chown it themselves, so chown from
+ * a child namespace mapping both our own and the target uid
+ */
+ if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
+ ERROR("Error requesting cgroup chown in new namespace");
+ return false;
+ }
+
+ /*
+ * Now chmod 775 the directory else the container cannot create cgroups.
+ * This can't be done in the child namespace because it only group-owns
+ * the cgroup
+ */
+ if (chmod(cgroup_path, 0775) < 0) {
+ SYSERROR("Error chmoding %s\n", cgroup_path);
+ return false;
+ }
+ fpath = lxc_append_paths(cgroup_path, "tasks");
+ if (!fpath)
+ return false;
+ if (chmod(fpath, 0664) < 0)
+ SYSERROR("Error chmoding %s\n", fpath);
+ free(fpath);
+ fpath = lxc_append_paths(cgroup_path, "cgroup.procs");
+ if (!fpath)
+ return false;
+ if (chmod(fpath, 0664) < 0)
+ SYSERROR("Error chmoding %s\n", fpath);
+ free(fpath);
+
+ return true;
+}
+
+static bool cgfs_chown(void *hdata, struct lxc_conf *conf)
+{
+ struct cgfs_data *d = hdata;
+ struct cgroup_process_info *info_ptr;
+ char *cgpath;
+ bool r = true;
+
+ if (!d)
+ return false;
+
+ for (info_ptr = d->info; info_ptr; info_ptr = info_ptr->next) {
+ if (!info_ptr->designated_mount_point) {
+ info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, info_ptr->cgroup_path, true);
+ if (!info_ptr->designated_mount_point) {
+ SYSERROR("Could not chown cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", info_ptr->cgroup_path);
+ return false;
+ }
+ }
+
+ cgpath = cgroup_to_absolute_path(info_ptr->designated_mount_point, info_ptr->cgroup_path, NULL);
+ if (!cgpath) {
+ SYSERROR("Could not chown cgroup %s: internal error", info_ptr->cgroup_path);
+ continue;
+ }
+ r = do_cgfs_chown(cgpath, conf);
+ if (!r && is_crucial_hierarchy(info_ptr->hierarchy)) {
+ ERROR("Failed chowning %s\n", cgpath);
+ free(cgpath);
+ return false;
+ }
+ free(cgpath);
+ }
+
+ return true;
+}
+
+static struct cgroup_ops cgfs_ops = {
+ .init = cgfs_init,
+ .destroy = cgfs_destroy,
+ .create = cgfs_create,
+ .enter = cgfs_enter,
+ .create_legacy = cgfs_create_legacy,
+ .get_cgroup = cgfs_get_cgroup,
+ .canonical_path = cgfs_canonical_path,
+ .escape = cgfs_escape,
+ .get = lxc_cgroupfs_get,
+ .set = lxc_cgroupfs_set,
+ .unfreeze = cgfs_unfreeze,
+ .setup_limits = cgroupfs_setup_limits,
+ .name = "cgroupfs",
+ .attach = lxc_cgroupfs_attach,
+ .chown = cgfs_chown,
+ .mount_cgroup = cgroupfs_mount_cgroup,
+ .nrtasks = cgfs_nrtasks,
+ .driver = CGFS,
+};
diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
new file mode 100644
index 0000000..27c2721
--- /dev/null
+++ b/src/lxc/cgroups/cgfsng.c
@@ -0,0 +1,1691 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2016 Canonical Ltd.
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at ubuntu.com>
+ *
+ * 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
+ */
+
+/*
+ * cgfs-ng.c: this is a new, simplified implementation of a filesystem
+ * cgroup backend. The original cgfs.c was designed to be as flexible
+ * as possible. It would try to find cgroup filesystems no matter where
+ * or how you had them mounted, and deduce the most usable mount for
+ * each controller. It also was not designed for unprivileged use, as
+ * that was reserved for cgmanager.
+ *
+ * This new implementation assumes that cgroup filesystems are mounted
+ * under /sys/fs/cgroup/clist where clist is either the controller, or
+ * a comman-separated list of controllers.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <grp.h>
+
+#include "bdev.h"
+#include "log.h"
+#include "cgroup.h"
+#include "utils.h"
+#include "commands.h"
+
+lxc_log_define(lxc_cgfsng, lxc);
+
+static struct cgroup_ops cgfsng_ops;
+
+/*
+ * A descriptor for a mounted hierarchy
+ * @controllers: either NULL, or a null-terminated list of all
+ * the co-mounted controllers
+ * @mountpoint: the mountpoint we will use. It will be either
+ * /sys/fs/cgroup/controller or /sys/fs/cgroup/controllerlist
+ * @base_cgroup: the cgroup under which the container cgroup path
+ is created. This will be either the caller's cgroup (if not
+ root), or init's cgroup (if root).
+ */
+struct hierarchy {
+ char **controllers;
+ char *mountpoint;
+ char *base_cgroup;
+ char *fullcgpath;
+};
+
+/*
+ * The cgroup data which is attached to the lxc_handler.
+ * @cgroup_pattern - a copy of the lxc.cgroup.pattern
+ * @container_cgroup - if not null, the cgroup which was created for
+ * the container. For each hierarchy, it is created under the
+ * @hierarchy->base_cgroup directory. Relative to the base_cgroup
+ * it is the same for all hierarchies.
+ * @name - the container name
+ */
+struct cgfsng_handler_data {
+ char *cgroup_pattern;
+ char *container_cgroup; // cgroup we created for the container
+ char *name; // container name
+};
+
+/*
+ * @hierarchies - a NULL-terminated array of struct hierarchy, one per
+ * hierarchy. No duplicates. First sufficient, writeable mounted
+ * hierarchy wins
+ */
+struct hierarchy **hierarchies;
+
+/*
+ * @cgroup_use - a copy of the lxc.cgroup.use
+ */
+char *cgroup_use;
+
+static void free_string_list(char **clist)
+{
+ if (clist) {
+ int i;
+
+ for (i = 0; clist[i]; i++)
+ free(clist[i]);
+ free(clist);
+ }
+}
+
+/* Re-alllocate a pointer, do not fail */
+static void *must_realloc(void *orig, size_t sz)
+{
+ void *ret;
+
+ do {
+ ret = realloc(orig, sz);
+ } while (!ret);
+ return ret;
+}
+
+/* Allocate a pointer, do not fail */
+static void *must_alloc(size_t sz)
+{
+ return must_realloc(NULL, sz);
+}
+
+/* return copy of string @entry; do not fail. */
+static char *must_copy_string(const char *entry)
+{
+ char *ret;
+
+ if (!entry)
+ return NULL;
+ do {
+ ret = strdup(entry);
+ } while (!ret);
+ return ret;
+}
+
+/*
+ * This is a special case - return a copy of @entry
+ * prepending 'name='. I.e. turn systemd into name=systemd.
+ * Do not fail.
+ */
+static char *must_prefix_named(char *entry)
+{
+ char *ret;
+ size_t len = strlen(entry);
+
+ ret = must_alloc(len + 6);
+ snprintf(ret, len + 6, "name=%s", entry);
+ return ret;
+}
+
+/*
+ * Given a pointer to a null-terminated array of pointers, realloc to
+ * add one entry, and point the new entry to NULL. Do not fail. Return
+ * the index to the second-to-last entry - that is, the one which is
+ * now available for use (keeping the list null-terminated).
+ */
+static int append_null_to_list(void ***list)
+{
+ int newentry = 0;
+
+ if (*list)
+ for (; (*list)[newentry]; newentry++);
+
+ *list = must_realloc(*list, (newentry + 2) * sizeof(void **));
+ (*list)[newentry + 1] = NULL;
+ return newentry;
+}
+
+/*
+ * Given a null-terminated array of strings, check whether @entry
+ * is one of the strings
+ */
+static bool string_in_list(char **list, const char *entry)
+{
+ int i;
+
+ if (!list)
+ return false;
+ for (i = 0; list[i]; i++)
+ if (strcmp(list[i], entry) == 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * append an entry to the clist. Do not fail.
+ * *clist must be NULL the first time we are called.
+ *
+ * We also handle named subsystems here. Any controller which is not a
+ * kernel subsystem, we prefix 'name='. Any which is both a kernel and
+ * named subsystem, we refuse to use because we're not sure which we
+ * have here. (TODO - we could work around this in some cases by just
+ * remounting to be unambiguous, or by comparing mountpoint contents
+ * with current cgroup)
+ *
+ * The last entry will always be NULL.
+ */
+static void must_append_controller(char **klist, char **nlist, char ***clist, char *entry)
+{
+ int newentry;
+ char *copy;
+
+ if (string_in_list(klist, entry) && string_in_list(nlist, entry)) {
+ ERROR("Refusing to use ambiguous controller '%s'", entry);
+ ERROR("It is both a named and kernel subsystem");
+ return;
+ }
+
+ newentry = append_null_to_list((void ***)clist);
+
+ if (strncmp(entry, "name=", 5) == 0)
+ copy = must_copy_string(entry);
+ else if (string_in_list(klist, entry))
+ copy = must_copy_string(entry);
+ else
+ copy = must_prefix_named(entry);
+
+ (*clist)[newentry] = copy;
+}
+
+static void free_handler_data(struct cgfsng_handler_data *d)
+{
+ free(d->cgroup_pattern);
+ free(d->container_cgroup);
+ free(d->name);
+ free(d);
+}
+
+/*
+ * Given a handler's cgroup data, return the struct hierarchy for the
+ * controller @c, or NULL if there is none.
+ */
+struct hierarchy *get_hierarchy(const char *c)
+{
+ int i;
+
+ if (!hierarchies)
+ return NULL;
+ for (i = 0; hierarchies[i]; i++) {
+ if (string_in_list(hierarchies[i]->controllers, c))
+ return hierarchies[i];
+ }
+ return NULL;
+}
+
+static char *must_make_path(const char *first, ...) __attribute__((sentinel));
+
+/* Copy contents of parent(@path)/@file to @path/@file */
+static bool copy_parent_file(char *path, char *file)
+{
+ char *lastslash, *value = NULL, *fpath, oldv;
+ int len = 0;
+ int ret;
+
+ lastslash = strrchr(path, '/');
+ if (!lastslash) { // bug... this shouldn't be possible
+ ERROR("cgfsng:copy_parent_file: bad path %s", path);
+ return false;
+ }
+ oldv = *lastslash;
+ *lastslash = '\0';
+ fpath = must_make_path(path, file, NULL);
+ len = lxc_read_from_file(fpath, NULL, 0);
+ if (len <= 0)
+ goto bad;
+ value = must_alloc(len + 1);
+ if (lxc_read_from_file(fpath, value, len) != len)
+ goto bad;
+ free(fpath);
+ *lastslash = oldv;
+ fpath = must_make_path(path, file, NULL);
+ ret = lxc_write_to_file(fpath, value, len, false);
+ if (ret < 0)
+ SYSERROR("Unable to write %s to %s", value, fpath);
+ free(fpath);
+ free(value);
+ return ret >= 0;
+
+bad:
+ SYSERROR("Error reading '%s'", fpath);
+ free(fpath);
+ free(value);
+ return false;
+}
+
+/*
+ * Initialize the cpuset hierarchy in first directory of @gname and
+ * set cgroup.clone_children so that children inherit settings.
+ * Since the h->base_path is populated by init or ourselves, we know
+ * it is already initialized.
+ */
+bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname)
+{
+ char *cgpath, *clonechildrenpath, v, *slash;
+
+ if (!string_in_list(h->controllers, "cpuset"))
+ return true;
+
+ if (*cgname == '/')
+ cgname++;
+ slash = strchr(cgname, '/');
+ if (slash)
+ *slash = '\0';
+
+ cgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
+ if (slash)
+ *slash = '/';
+ if (mkdir(cgpath, 0755) < 0 && errno != EEXIST) {
+ SYSERROR("Failed to create '%s'", cgpath);
+ free(cgpath);
+ return false;
+ }
+ clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
+ if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */
+ free(clonechildrenpath);
+ free(cgpath);
+ return true;
+ }
+ if (lxc_read_from_file(clonechildrenpath, &v, 1) < 0) {
+ SYSERROR("Failed to read '%s'", clonechildrenpath);
+ free(clonechildrenpath);
+ free(cgpath);
+ return false;
+ }
+
+ if (v == '1') { /* already set for us by someone else */
+ free(clonechildrenpath);
+ free(cgpath);
+ return true;
+ }
+
+ /* copy parent's settings */
+ if (!copy_parent_file(cgpath, "cpuset.cpus") ||
+ !copy_parent_file(cgpath, "cpuset.mems")) {
+ free(cgpath);
+ free(clonechildrenpath);
+ return false;
+ }
+ free(cgpath);
+
+ if (lxc_write_to_file(clonechildrenpath, "1", 1, false) < 0) {
+ /* Set clone_children so children inherit our settings */
+ SYSERROR("Failed to write 1 to %s", clonechildrenpath);
+ free(clonechildrenpath);
+ return false;
+ }
+ free(clonechildrenpath);
+ return true;
+}
+
+/*
+ * Given two null-terminated lists of strings, return true if any string
+ * is in both.
+ */
+static bool controller_lists_intersect(char **l1, char **l2)
+{
+ int i;
+
+ if (!l1 || !l2)
+ return false;
+
+ for (i = 0; l1[i]; i++) {
+ if (string_in_list(l2, l1[i]))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * For a null-terminated list of controllers @clist, return true if any of
+ * those controllers is already listed the null-terminated list of
+ * hierarchies @hlist. Realistically, if one is present, all must be present.
+ */
+static bool controller_list_is_dup(struct hierarchy **hlist, char **clist)
+{
+ int i;
+
+ if (!hlist)
+ return false;
+ for (i = 0; hlist[i]; i++)
+ if (controller_lists_intersect(hlist[i]->controllers, clist))
+ return true;
+ return false;
+
+}
+
+/*
+ * Return true if the controller @entry is found in the null-terminated
+ * list of hierarchies @hlist
+ */
+static bool controller_found(struct hierarchy **hlist, char *entry)
+{
+ int i;
+ if (!hlist)
+ return false;
+
+ for (i = 0; hlist[i]; i++)
+ if (string_in_list(hlist[i]->controllers, entry))
+ return true;
+ return false;
+}
+
+/*
+ * Return true if all of the controllers which we require have been found.
+ * The required list is freezer and anything in * lxc.cgroup.use.
+ */
+static bool all_controllers_found(void)
+{
+ char *p, *saveptr = NULL;
+ struct hierarchy ** hlist = hierarchies;
+
+ if (!controller_found(hlist, "freezer")) {
+ ERROR("no freezer controller mountpoint found");
+ return false;
+ }
+
+ if (!cgroup_use)
+ return true;
+ for (p = strtok_r(cgroup_use, ",", &saveptr); p;
+ p = strtok_r(NULL, ",", &saveptr)) {
+ if (!controller_found(hlist, p)) {
+ ERROR("no %s controller mountpoint found", p);
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Return true if the fs type is fuse.lxcfs */
+static bool is_lxcfs(const char *line)
+{
+ char *p = strstr(line, " - ");
+ if (!p)
+ return false;
+ return strncmp(p, " - fuse.lxcfs ", 14) == 0;
+}
+
+/*
+ * Get the controllers from a mountinfo line
+ * There are other ways we could get this info. For lxcfs, field 3
+ * is /cgroup/controller-list. For cgroupfs, we could parse the mount
+ * options. But we simply assume that the mountpoint must be
+ * /sys/fs/cgroup/controller-list
+ */
+static char **get_controllers(char **klist, char **nlist, char *line)
+{
+ // the fourth field is /sys/fs/cgroup/comma-delimited-controller-list
+ int i;
+ char *p = line, *p2, *tok, *saveptr = NULL;
+ char **aret = NULL;
+
+ for (i = 0; i < 4; i++) {
+ p = strchr(p, ' ');
+ if (!p)
+ return NULL;
+ p++;
+ }
+ if (!p)
+ return NULL;
+ /* note - if we change how mountinfo works, then our caller
+ * will need to verify /sys/fs/cgroup/ in this field */
+ if (strncmp(p, "/sys/fs/cgroup/", 15) != 0)
+ return NULL;
+ p += 15;
+ p2 = strchr(p, ' ');
+ if (!p2) {
+ ERROR("corrupt mountinfo");
+ return NULL;
+ }
+ *p2 = '\0';
+ for (tok = strtok_r(p, ",", &saveptr); tok;
+ tok = strtok_r(NULL, ",", &saveptr)) {
+ must_append_controller(klist, nlist, &aret, tok);
+ }
+
+ return aret;
+}
+
+/* return true if the fstype is cgroup */
+static bool is_cgroupfs(char *line)
+{
+ char *p = strstr(line, " - ");
+ if (!p)
+ return false;
+ return strncmp(p, " - cgroup ", 10) == 0;
+}
+
+/* Add a controller to our list of hierarchies */
+static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
+{
+ struct hierarchy *new;
+ int newentry;
+
+ new = must_alloc(sizeof(*new));
+ new->controllers = clist;
+ new->mountpoint = mountpoint;
+ new->base_cgroup = base_cgroup;
+ new->fullcgpath = NULL;
+
+ newentry = append_null_to_list((void ***)&hierarchies);
+ hierarchies[newentry] = new;
+}
+
+/*
+ * Get a copy of the mountpoint from @line, which is a line from
+ * /proc/self/mountinfo
+ */
+static char *get_mountpoint(char *line)
+{
+ int i;
+ char *p = line, *sret;
+ size_t len;
+
+ for (i = 0; i < 4; i++) {
+ p = strchr(p, ' ');
+ if (!p)
+ return NULL;
+ p++;
+ }
+ /* we've already stuck a \0 after the mountpoint */
+ len = strlen(p);
+ sret = must_alloc(len + 1);
+ memcpy(sret, p, len);
+ sret[len] = '\0';
+ return sret;
+}
+
+/*
+ * Given a multi-line string, return a null-terminated copy of the
+ * current line.
+ */
+static char *copy_to_eol(char *p)
+{
+ char *p2 = strchr(p, '\n'), *sret;
+ size_t len;
+
+ if (!p2)
+ return NULL;
+
+ len = p2 - p;
+ sret = must_alloc(len + 1);
+ memcpy(sret, p, len);
+ sret[len] = '\0';
+ return sret;
+}
+
+/*
+ * cgline: pointer to character after the first ':' in a line in a
+ * \n-terminated /proc/self/cgroup file. Check whether * controller c is
+ * present.
+ */
+static bool controller_in_clist(char *cgline, char *c)
+{
+ char *tok, *saveptr = NULL, *eol, *tmp;
+ size_t len;
+
+ eol = strchr(cgline, ':');
+ if (!eol)
+ return false;
+
+ len = eol - cgline;
+ tmp = alloca(len + 1);
+ memcpy(tmp, cgline, len);
+ tmp[len] = '\0';
+
+ for (tok = strtok_r(tmp, ",", &saveptr); tok;
+ tok = strtok_r(NULL, ",", &saveptr)) {
+ if (strcmp(tok, c) == 0)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * @basecginfo is a copy of /proc/$$/cgroup. Return the current
+ * cgroup for @controller
+ */
+static char *get_current_cgroup(char *basecginfo, char *controller)
+{
+ char *p = basecginfo;
+
+ while (1) {
+ p = strchr(p, ':');
+ if (!p)
+ return NULL;
+ p++;
+ if (controller_in_clist(p, controller)) {
+ p = strchr(p, ':');
+ if (!p)
+ return NULL;
+ p++;
+ return copy_to_eol(p);
+ }
+
+ p = strchr(p, '\n');
+ if (!p)
+ return NULL;
+ p++;
+ }
+}
+
+#define BATCH_SIZE 50
+static void batch_realloc(char **mem, size_t oldlen, size_t newlen)
+{
+ int newbatches = (newlen / BATCH_SIZE) + 1;
+ int oldbatches = (oldlen / BATCH_SIZE) + 1;
+
+ if (!*mem || newbatches > oldbatches) {
+ *mem = must_realloc(*mem, newbatches * BATCH_SIZE);
+ }
+}
+
+static void append_line(char **dest, size_t oldlen, char *new, size_t newlen)
+{
+ size_t full = oldlen + newlen;
+
+ batch_realloc(dest, oldlen, full + 1);
+
+ memcpy(*dest + oldlen, new, newlen + 1);
+}
+
+/* Slurp in a whole file */
+static char *read_file(char *fnam)
+{
+ FILE *f;
+ char *line = NULL, *buf = NULL;
+ size_t len = 0, fulllen = 0;
+ int linelen;
+
+ f = fopen(fnam, "r");
+ if (!f)
+ return NULL;
+ while ((linelen = getline(&line, &len, f)) != -1) {
+ append_line(&buf, fulllen, line, linelen);
+ fulllen += linelen;
+ }
+ fclose(f);
+ free(line);
+ return buf;
+}
+
+/*
+ * Given a hierarchy @mountpoint and base @path, verify that we can create
+ * directories underneath it.
+ */
+static bool test_writeable(char *mountpoint, char *path)
+{
+ char *fullpath = must_make_path(mountpoint, path, NULL);
+ int ret;
+
+ ret = access(fullpath, W_OK);
+ free(fullpath);
+ return ret == 0;
+}
+
+static void must_append_string(char ***list, char *entry)
+{
+ int newentry = append_null_to_list((void ***)list);
+ char *copy;
+
+ copy = must_copy_string(entry);
+ (*list)[newentry] = copy;
+}
+
+static void get_existing_subsystems(char ***klist, char ***nlist)
+{
+ FILE *f;
+ char *line = NULL;
+ size_t len = 0;
+
+ if ((f = fopen("/proc/self/cgroup", "r")) == NULL)
+ return;
+ while (getline(&line, &len, f) != -1) {
+ char *p, *p2, *tok, *saveptr = NULL;
+ p = strchr(line, ':');
+ if (!p)
+ continue;
+ p++;
+ p2 = strchr(p, ':');
+ if (!p2)
+ continue;
+ *p2 = '\0';
+ for (tok = strtok_r(p, ",", &saveptr); tok;
+ tok = strtok_r(NULL, ",", &saveptr)) {
+ if (strncmp(tok, "name=", 5) == 0)
+ must_append_string(nlist, tok);
+ else
+ must_append_string(klist, tok);
+ }
+ }
+
+ free(line);
+ fclose(f);
+}
+
+static void trim(char *s)
+{
+ size_t len = strlen(s);
+ while (s[len-1] == '\n')
+ s[--len] = '\0';
+}
+
+static void print_init_debuginfo(struct cgfsng_handler_data *d)
+{
+ int i;
+
+ if (!getenv("LXC_DEBUG_CGFSNG"))
+ return;
+
+ printf("Cgroup information:\n");
+ printf(" container name: %s\n", d->name);
+ printf(" lxc.cgroup.use: %s\n", cgroup_use ? cgroup_use : "(none)");
+ printf(" lxc.cgroup.pattern: %s\n", d->cgroup_pattern);
+ printf(" cgroup: %s\n", d->container_cgroup ? d->container_cgroup : "(none)");
+ if (!hierarchies) {
+ printf(" No hierarchies found.\n");
+ return;
+ }
+ printf(" Hierarchies:\n");
+ for (i = 0; hierarchies[i]; i++) {
+ struct hierarchy *h = hierarchies[i];
+ int j;
+ printf(" %d: base_cgroup %s\n", i, h->base_cgroup);
+ printf(" mountpoint %s\n", h->mountpoint);
+ printf(" controllers:\n");
+ for (j = 0; h->controllers[j]; j++)
+ printf(" %d: %s\n", j, h->controllers[j]);
+ }
+}
+
+static void print_basecg_debuginfo(char *basecginfo, char **klist, char **nlist)
+{
+ int k;
+ if (!getenv("LXC_DEBUG_CGFSNG"))
+ return;
+
+ printf("basecginfo is %s\n", basecginfo);
+
+ for (k = 0; klist[k]; k++)
+ printf("kernel subsystem %d: %s\n", k, klist[k]);
+ for (k = 0; nlist[k]; k++)
+ printf("named subsystem %d: %s\n", k, nlist[k]);
+}
+
+/*
+ * At startup, parse_hierarchies finds all the info we need about
+ * cgroup mountpoints and current cgroups, and stores it in @d.
+ */
+static bool parse_hierarchies(void)
+{
+ FILE *f;
+ char * line = NULL, *basecginfo;
+ char **klist = NULL, **nlist = NULL;
+ size_t len = 0;
+
+ /*
+ * Root spawned containers escape the current cgroup, so use init's
+ * cgroups as our base in that case.
+ */
+ if (geteuid())
+ basecginfo = read_file("/proc/self/cgroup");
+ else
+ basecginfo = read_file("/proc/1/cgroup");
+ if (!basecginfo)
+ return false;
+
+ if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) {
+ SYSERROR("Failed opening /proc/self/mountinfo");
+ return false;
+ }
+
+ get_existing_subsystems(&klist, &nlist);
+
+ print_basecg_debuginfo(basecginfo, klist, nlist);
+
+ /* we support simple cgroup mounts and lxcfs mounts */
+ while (getline(&line, &len, f) != -1) {
+ char **controller_list = NULL;
+ char *mountpoint, *base_cgroup;
+
+ if (!is_lxcfs(line) && !is_cgroupfs(line))
+ continue;
+
+ controller_list = get_controllers(klist, nlist, line);
+ if (!controller_list)
+ continue;
+
+ if (controller_list_is_dup(hierarchies, controller_list)) {
+ free(controller_list);
+ continue;
+ }
+
+ mountpoint = get_mountpoint(line);
+ if (!mountpoint) {
+ ERROR("Error reading mountinfo: bad line '%s'", line);
+ free_string_list(controller_list);
+ continue;
+ }
+
+ base_cgroup = get_current_cgroup(basecginfo, controller_list[0]);
+ if (!base_cgroup) {
+ ERROR("Failed to find current cgroup for controller '%s'", controller_list[0]);
+ free_string_list(controller_list);
+ free(mountpoint);
+ continue;
+ }
+ trim(base_cgroup);
+ prune_init_scope(base_cgroup);
+ if (!test_writeable(mountpoint, base_cgroup)) {
+ free_string_list(controller_list);
+ free(mountpoint);
+ free(base_cgroup);
+ continue;
+ }
+ add_controller(controller_list, mountpoint, base_cgroup);
+ }
+
+ free_string_list(klist);
+ free_string_list(nlist);
+
+ free(basecginfo);
+
+ fclose(f);
+ free(line);
+
+ /* verify that all controllers in cgroup.use and all crucial
+ * controllers are accounted for
+ */
+ if (!all_controllers_found())
+ return false;
+
+ return true;
+}
+
+static bool collect_hierarchy_info(void)
+{
+ const char *tmp;
+ errno = 0;
+ tmp = lxc_global_config_value("lxc.cgroup.use");
+ if (!cgroup_use && errno != 0) { // lxc.cgroup.use can be NULL
+ SYSERROR("cgfsng: error reading list of cgroups to use");
+ return false;
+ }
+ cgroup_use = must_copy_string(tmp);
+
+ return parse_hierarchies();
+}
+
+static void *cgfsng_init(const char *name)
+{
+ struct cgfsng_handler_data *d;
+ const char *cgroup_pattern;
+
+ d = must_alloc(sizeof(*d));
+ memset(d, 0, sizeof(*d));
+
+ d->name = must_copy_string(name);
+
+ cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
+ if (!cgroup_pattern) { // lxc.cgroup.pattern is only NULL on error
+ ERROR("Error getting cgroup pattern");
+ goto out_free;
+ }
+ d->cgroup_pattern = must_copy_string(cgroup_pattern);
+
+ print_init_debuginfo(d);
+
+ return d;
+
+out_free:
+ free_handler_data(d);
+ return NULL;
+}
+
+/*
+ * Concatenate all passed-in strings into one path. Do not fail. If any piece is
+ * not prefixed with '/', add a '/'.
+ */
+static char *must_make_path(const char *first, ...)
+{
+ va_list args;
+ char *cur, *dest;
+ size_t full_len = strlen(first);
+
+ dest = must_copy_string(first);
+
+ va_start(args, first);
+ while ((cur = va_arg(args, char *)) != NULL) {
+ full_len += strlen(cur);
+ if (cur[0] != '/')
+ full_len++;
+ dest = must_realloc(dest, full_len + 1);
+ if (cur[0] != '/')
+ strcat(dest, "/");
+ strcat(dest, cur);
+ }
+ va_end(args);
+
+ return dest;
+}
+
+static int cgroup_rmdir(char *dirname)
+{
+ struct dirent dirent, *direntp;
+ DIR *dir;
+ int r = 0;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return -1;
+
+ while (!readdir_r(dir, &dirent, &direntp)) {
+ struct stat mystat;
+ char *pathname;
+
+ if (!direntp)
+ break;
+
+ if (!strcmp(direntp->d_name, ".") ||
+ !strcmp(direntp->d_name, ".."))
+ continue;
+
+ pathname = must_make_path(dirname, direntp->d_name, NULL);
+
+ if (lstat(pathname, &mystat)) {
+ if (!r)
+ WARN("failed to stat %s", pathname);
+ r = -1;
+ goto next;
+ }
+
+ if (!S_ISDIR(mystat.st_mode))
+ goto next;
+ if (cgroup_rmdir(pathname) < 0)
+ r = -1;
+next:
+ free(pathname);
+ }
+
+ if (rmdir(dirname) < 0) {
+ if (!r)
+ WARN("%s: failed to delete %s: %m", __func__, dirname);
+ r = -1;
+ }
+
+ if (closedir(dir) < 0) {
+ if (!r)
+ WARN("%s: failed to delete %s: %m", __func__, dirname);
+ r = -1;
+ }
+ return r;
+}
+
+static int rmdir_wrapper(void *data)
+{
+ char *path = data;
+
+ if (setresgid(0,0,0) < 0)
+ SYSERROR("Failed to setgid to 0");
+ if (setresuid(0,0,0) < 0)
+ SYSERROR("Failed to setuid to 0");
+ if (setgroups(0, NULL) < 0)
+ SYSERROR("Failed to clear groups");
+
+ return cgroup_rmdir(path);
+}
+
+void recursive_destroy(char *path, struct lxc_conf *conf)
+{
+ int r;
+ if (conf && !lxc_list_empty(&conf->id_map))
+ r = userns_exec_1(conf, rmdir_wrapper, path);
+ else
+ r = cgroup_rmdir(path);
+
+ if (r < 0)
+ ERROR("Error destroying %s", path);
+}
+
+static void cgfsng_destroy(void *hdata, struct lxc_conf *conf)
+{
+ struct cgfsng_handler_data *d = hdata;
+
+ if (!d)
+ return;
+
+ if (d->container_cgroup && hierarchies) {
+ int i;
+ for (i = 0; hierarchies[i]; i++) {
+ struct hierarchy *h = hierarchies[i];
+ if (h->fullcgpath) {
+ recursive_destroy(h->fullcgpath, conf);
+ free(h->fullcgpath);
+ h->fullcgpath = NULL;
+ }
+ }
+ }
+
+ free_handler_data(d);
+}
+
+struct cgroup_ops *cgfsng_ops_init(void)
+{
+ if (!collect_hierarchy_info())
+ return NULL;
+ return &cgfsng_ops;
+}
+
+static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname)
+{
+ h->fullcgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
+ if (dir_exists(h->fullcgpath)) // it must not already exist
+ return false;
+ if (!handle_cpuset_hierarchy(h, cgname))
+ return false;
+ return mkdir_p(h->fullcgpath, 0755) == 0;
+}
+
+static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname)
+{
+ if (rmdir(h->fullcgpath) < 0)
+ SYSERROR("Failed to clean up cgroup %s from failed creation attempt", h->fullcgpath);
+ free(h->fullcgpath);
+ h->fullcgpath = NULL;
+}
+
+/*
+ * Try to create the same cgroup in all hierarchies.
+ * Start with cgroup_pattern; next cgroup_pattern-1, -2, ..., -999
+ */
+static inline bool cgfsng_create(void *hdata)
+{
+ struct cgfsng_handler_data *d = hdata;
+ char *tmp, *cgname, *offset;
+ int i, idx = 0;
+ size_t len;
+
+ if (!d)
+ return false;
+ if (d->container_cgroup) {
+ WARN("cgfsng_create called a second time");
+ return false;
+ }
+
+ tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
+ if (!tmp) {
+ ERROR("Failed expanding cgroup name pattern");
+ return false;
+ }
+ len = strlen(tmp) + 5; // leave room for -NNN\0
+ cgname = must_alloc(len);
+ strcpy(cgname, tmp);
+ free(tmp);
+ offset = cgname + len - 5;
+
+again:
+ if (idx == 1000) {
+ ERROR("Too many conflicting cgroup names");
+ goto out_free;
+ }
+ if (idx)
+ snprintf(offset, 5, "-%d", idx);
+ for (i = 0; hierarchies[i]; i++) {
+ if (!create_path_for_hierarchy(hierarchies[i], cgname)) {
+ int j;
+ SYSERROR("Failed to create %s: %s", hierarchies[i]->fullcgpath, strerror(errno));
+ free(hierarchies[i]->fullcgpath);
+ hierarchies[i]->fullcgpath = NULL;
+ for (j = 0; j < i; j++)
+ remove_path_for_hierarchy(hierarchies[j], cgname);
+ idx++;
+ goto again;
+ }
+ }
+ /* Done */
+ d->container_cgroup = cgname;
+ return true;
+
+out_free:
+ free(cgname);
+ return false;
+}
+
+static const char *cgfsng_canonical_path(void *hdata)
+{
+ struct cgfsng_handler_data *d = hdata;
+
+ return d->container_cgroup;
+}
+
+static bool cgfsng_enter(void *hdata, pid_t pid)
+{
+ char pidstr[25];
+ int i, len;
+
+ len = snprintf(pidstr, 25, "%d", pid);
+ if (len < 0 || len > 25)
+ return false;
+
+ for (i = 0; hierarchies[i]; i++) {
+ char *fullpath = must_make_path(hierarchies[i]->fullcgpath,
+ "cgroup.procs", NULL);
+ if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
+ SYSERROR("Failed to enter %s", fullpath);
+ free(fullpath);
+ return false;
+ }
+ free(fullpath);
+ }
+
+ return true;
+}
+
+struct chown_data {
+ struct cgfsng_handler_data *d;
+ uid_t origuid; // target uid in parent namespace
+};
+
+/*
+ * chgrp the container cgroups to container group. We leave
+ * the container owner as cgroup owner. So we must make the
+ * directories 775 so that the container can create sub-cgroups.
+ *
+ * Also chown the tasks and cgroup.procs files. Those may not
+ * exist depending on kernel version.
+ */
+static int chown_cgroup_wrapper(void *data)
+{
+ struct chown_data *arg = data;
+ uid_t destuid;
+ int i;
+
+ if (setresgid(0,0,0) < 0)
+ SYSERROR("Failed to setgid to 0");
+ if (setresuid(0,0,0) < 0)
+ SYSERROR("Failed to setuid to 0");
+ if (setgroups(0, NULL) < 0)
+ SYSERROR("Failed to clear groups");
+
+ destuid = get_ns_uid(arg->origuid);
+
+ for (i = 0; hierarchies[i]; i++) {
+ char *fullpath, *path = hierarchies[i]->fullcgpath;
+
+ if (chown(path, destuid, 0) < 0) {
+ SYSERROR("Error chowning %s to %d", path, (int) destuid);
+ return -1;
+ }
+
+ if (chmod(path, 0775) < 0) {
+ SYSERROR("Error chmoding %s", path);
+ return -1;
+ }
+
+ /*
+ * Failures to chown these are inconvenient but not detrimental
+ * We leave these owned by the container launcher, so that container
+ * root can write to the files to attach. We chmod them 664 so that
+ * container systemd can write to the files (which systemd in wily
+ * insists on doing)
+ */
+ fullpath = must_make_path(path, "tasks", NULL);
+ if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
+ WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
+ if (chmod(fullpath, 0664) < 0)
+ WARN("Error chmoding %s: %m", path);
+ free(fullpath);
+
+ fullpath = must_make_path(path, "cgroup.procs", NULL);
+ if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
+ WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
+ if (chmod(fullpath, 0664) < 0)
+ WARN("Error chmoding %s: %m", path);
+ free(fullpath);
+ }
+
+ return 0;
+}
+
+static bool cgfsns_chown(void *hdata, struct lxc_conf *conf)
+{
+ struct cgfsng_handler_data *d = hdata;
+ struct chown_data wrap;
+
+ if (!d)
+ return false;
+
+ if (lxc_list_empty(&conf->id_map))
+ return true;
+
+ wrap.d = d;
+ wrap.origuid = geteuid();
+
+ if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap) < 0) {
+ ERROR("Error requesting cgroup chown in new namespace");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * We've safe-mounted a tmpfs as parent, so we don't need to protect against
+ * symlinks any more - just use mount
+ */
+
+/* mount cgroup-full if requested */
+static int mount_cgroup_full(int type, struct hierarchy *h, char *dest,
+ char *container_cgroup)
+{
+ if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED)
+ return 0;
+ if (mount(h->mountpoint, dest, "cgroup", MS_BIND, NULL) < 0) {
+ SYSERROR("Error bind-mounting %s cgroup onto %s", h->mountpoint,
+ dest);
+ return -1;
+ }
+ if (type != LXC_AUTO_CGROUP_FULL_RW) {
+ unsigned long flags = MS_BIND | MS_NOSUID | MS_NOEXEC | MS_NODEV |
+ MS_REMOUNT | MS_RDONLY;
+ if (mount(NULL, dest, "cgroup", flags, NULL) < 0) {
+ SYSERROR("Error remounting %s readonly", dest);
+ return -1;
+ }
+ }
+
+ INFO("Bind mounted %s onto %s", h->mountpoint, dest);
+ if (type != LXC_AUTO_CGROUP_FULL_MIXED)
+ return 0;
+
+ /* mount just the container path rw */
+ char *source = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL);
+ char *rwpath = must_make_path(dest, h->base_cgroup, container_cgroup, NULL);
+ if (mount(source, rwpath, "cgroup", MS_BIND, NULL) < 0)
+ WARN("Failed to mount %s read-write: %m", rwpath);
+ INFO("Made %s read-write", rwpath);
+ free(rwpath);
+ free(source);
+ return 0;
+}
+
+/* cgroup-full:* is done, no need to create subdirs */
+static bool cg_mount_needs_subdirs(int type)
+{
+ if (type >= LXC_AUTO_CGROUP_FULL_RO)
+ return false;
+ return true;
+}
+
+/*
+ * After $rootfs/sys/fs/container/controller/the/cg/path has been
+ * created, remount controller ro if needed and bindmount the
+ * cgroupfs onto controll/the/cg/path
+ */
+static int
+do_secondstage_mounts_if_needed(int type, struct hierarchy *h,
+ char *controllerpath, char *cgpath,
+ const char *container_cgroup)
+{
+ if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_MIXED) {
+ if (mount(controllerpath, controllerpath, "cgroup", MS_BIND, NULL) < 0) {
+ SYSERROR("Error bind-mounting %s", controllerpath);
+ return -1;
+ }
+ if (mount(controllerpath, controllerpath, "cgroup",
+ MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) < 0) {
+ SYSERROR("Error remounting %s read-only", controllerpath);
+ return -1;
+ }
+ INFO("Remounted %s read-only", controllerpath);
+ }
+ char *sourcepath = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL);
+ int flags = MS_BIND;
+ if (type == LXC_AUTO_CGROUP_RO)
+ flags |= MS_RDONLY;
+ INFO("Mounting %s onto %s", sourcepath, cgpath);
+ if (mount(sourcepath, cgpath, "cgroup", flags, NULL) < 0) {
+ free(sourcepath);
+ SYSERROR("Error mounting cgroup %s onto %s", h->controllers[0],
+ cgpath);
+ return -1;
+ }
+ free(sourcepath);
+ INFO("Completed second stage cgroup automounts for %s", cgpath);
+ return 0;
+}
+
+static bool cgfsng_mount(void *hdata, const char *root, int type)
+{
+ struct cgfsng_handler_data *d = hdata;
+ char *tmpfspath = NULL;
+ bool retval = false;
+ int i;
+
+ if ((type & LXC_AUTO_CGROUP_MASK) == 0)
+ return true;
+
+ if (cgns_supported())
+ return true;
+
+ tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL);
+
+ if (type == LXC_AUTO_CGROUP_NOSPEC)
+ type = LXC_AUTO_CGROUP_MIXED;
+ else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
+ type = LXC_AUTO_CGROUP_FULL_MIXED;
+
+ /* Mount tmpfs */
+ if (safe_mount("cgroup_root", tmpfspath, "tmpfs",
+ MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
+ "size=10240k,mode=755",
+ root) < 0)
+ goto bad;
+
+ for (i = 0; hierarchies[i]; i++) {
+ char *controllerpath, *path2;
+ struct hierarchy *h = hierarchies[i];
+ char *controller = strrchr(h->mountpoint, '/');
+ int r;
+
+ if (!controller)
+ continue;
+ controller++;
+ controllerpath = must_make_path(tmpfspath, controller, NULL);
+ if (dir_exists(controllerpath)) {
+ free(controllerpath);
+ continue;
+ }
+ if (mkdir(controllerpath, 0755) < 0) {
+ SYSERROR("Error creating cgroup path: %s", controllerpath);
+ free(controllerpath);
+ goto bad;
+ }
+ if (mount_cgroup_full(type, h, controllerpath, d->container_cgroup) < 0) {
+ free(controllerpath);
+ goto bad;
+ }
+ if (!cg_mount_needs_subdirs(type)) {
+ free(controllerpath);
+ continue;
+ }
+ path2 = must_make_path(controllerpath, h->base_cgroup, d->container_cgroup, NULL);
+ if (mkdir_p(path2, 0755) < 0) {
+ free(controllerpath);
+ goto bad;
+ }
+
+ r = do_secondstage_mounts_if_needed(type, h, controllerpath, path2,
+ d->container_cgroup);
+ free(controllerpath);
+ free(path2);
+ if (r < 0)
+ goto bad;
+ }
+ retval = true;
+
+bad:
+ free(tmpfspath);
+ return retval;
+}
+
+static int recursive_count_nrtasks(char *dirname)
+{
+ struct dirent dirent, *direntp;
+ DIR *dir;
+ int count = 0, ret;
+ char *path;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+
+ while (!readdir_r(dir, &dirent, &direntp)) {
+ struct stat mystat;
+
+ if (!direntp)
+ break;
+
+ if (!strcmp(direntp->d_name, ".") ||
+ !strcmp(direntp->d_name, ".."))
+ continue;
+
+ path = must_make_path(dirname, direntp->d_name, NULL);
+
+ if (lstat(path, &mystat))
+ goto next;
+
+ if (!S_ISDIR(mystat.st_mode))
+ goto next;
+
+ count += recursive_count_nrtasks(path);
+next:
+ free(path);
+ }
+
+ path = must_make_path(dirname, "cgroup.procs", NULL);
+ ret = lxc_count_file_lines(path);
+ if (ret != -1)
+ count += ret;
+ free(path);
+
+ (void) closedir(dir);
+
+ return count;
+}
+
+static int cgfsng_nrtasks(void *hdata) {
+ struct cgfsng_handler_data *d = hdata;
+ char *path;
+ int count;
+
+ if (!d || !d->container_cgroup || !hierarchies)
+ return -1;
+ path = must_make_path(hierarchies[0]->fullcgpath, NULL);
+ count = recursive_count_nrtasks(path);
+ free(path);
+ return count;
+}
+
+/* Only root needs to escape to the cgroup of its init */
+static bool cgfsng_escape()
+{
+ struct cgfsng_handler_data *d;
+ int i;
+ bool ret = false;
+
+ if (geteuid())
+ return true;
+
+ d = cgfsng_init("criu-temp-cgfsng");
+ if (!d) {
+ ERROR("cgfsng_init failed");
+ return false;
+ }
+
+ for (i = 0; hierarchies[i]; i++) {
+ char *fullpath = must_make_path(hierarchies[i]->mountpoint,
+ hierarchies[i]->base_cgroup,
+ "cgroup.procs", NULL);
+ if (lxc_write_to_file(fullpath, "0", 2, false) != 0) {
+ SYSERROR("Failed to escape to %s", fullpath);
+ free(fullpath);
+ goto out;
+ }
+ free(fullpath);
+ }
+
+ ret = true;
+out:
+ free_handler_data(d);
+ return ret;
+}
+
+#define THAWED "THAWED"
+#define THAWED_LEN (strlen(THAWED))
+
+static bool cgfsng_unfreeze(void *hdata)
+{
+ char *fullpath;
+ struct hierarchy *h = get_hierarchy("freezer");
+
+ if (!h)
+ return false;
+ fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL);
+ if (lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false) != 0) {
+ free(fullpath);
+ return false;
+ }
+ free(fullpath);
+ return true;
+}
+
+static const char *cgfsng_get_cgroup(void *hdata, const char *subsystem)
+{
+ struct hierarchy *h = get_hierarchy(subsystem);
+ if (!h)
+ return NULL;
+
+ return h->fullcgpath ? h->fullcgpath + strlen(h->mountpoint) : NULL;
+}
+
+/*
+ * Given a cgroup path returned from lxc_cmd_get_cgroup_path, build a
+ * full path, which must be freed by the caller.
+ */
+static char *build_full_cgpath_from_monitorpath(struct hierarchy *h,
+ const char *inpath,
+ const char *filename)
+{
+ /*
+ * XXX Remove this case after 2.0 release. It's for dealing with
+ * containers spawned under the old buggy cgfsng which wasn't around
+ * for long.
+ */
+ if (strncmp(inpath, "/sys/fs/cgroup/", 15) == 0)
+ return must_make_path(inpath, filename, NULL);
+ return must_make_path(h->mountpoint, inpath, filename, NULL);
+}
+
+static bool cgfsng_attach(const char *name, const char *lxcpath, pid_t pid)
+{
+ char pidstr[25];
+ int i, len;
+
+ len = snprintf(pidstr, 25, "%d", pid);
+ if (len < 0 || len > 25)
+ return false;
+
+ for (i = 0; hierarchies[i]; i++) {
+ char *path, *fullpath;
+ struct hierarchy *h = hierarchies[i];
+
+ path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]);
+ if (!path) // not running
+ continue;
+
+ fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs");
+ free(path);
+ if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
+ SYSERROR("Failed to attach %d to %s", (int)pid, fullpath);
+ free(fullpath);
+ return false;
+ }
+ free(fullpath);
+ }
+
+ return true;
+}
+
+/*
+ * Called externally (i.e. from 'lxc-cgroup') to query cgroup limits.
+ * Here we don't have a cgroup_data set up, so we ask the running
+ * container through the commands API for the cgroup path
+ */
+static int cgfsng_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
+{
+ char *subsystem, *p, *path;
+ struct hierarchy *h;
+ int ret = -1;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
+ if (!path) // not running
+ return -1;
+
+ h = get_hierarchy(subsystem);
+ if (h) {
+ char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
+ ret = lxc_read_from_file(fullpath, value, len);
+ free(fullpath);
+ }
+
+ free(path);
+
+ return ret;
+}
+
+/*
+ * Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits.
+ * Here we don't have a cgroup_data set up, so we ask the running
+ * container through the commands API for the cgroup path
+ */
+static int cgfsng_set(const char *filename, const char *value, const char *name, const char *lxcpath)
+{
+ char *subsystem, *p, *path;
+ struct hierarchy *h;
+ int ret = -1;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
+ if (!path) // not running
+ return -1;
+
+ h = get_hierarchy(subsystem);
+ if (h) {
+ char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
+ ret = lxc_write_to_file(fullpath, value, strlen(value), false);
+ free(fullpath);
+ }
+
+ free(path);
+
+ return ret;
+}
+
+/*
+ * Called from setup_limits - here we have the container's cgroup_data because
+ * we created the cgroups
+ */
+static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfsng_handler_data *d)
+{
+ char *subsystem = NULL, *p;
+ int ret = -1;
+ struct hierarchy *h;
+
+ subsystem = alloca(strlen(filename) + 1);
+ strcpy(subsystem, filename);
+ if ((p = strchr(subsystem, '.')) != NULL)
+ *p = '\0';
+
+ h = get_hierarchy(subsystem);
+ if (h) {
+ char *fullpath = must_make_path(h->fullcgpath, filename, NULL);
+ ret = lxc_write_to_file(fullpath, value, strlen(value), false);
+ free(fullpath);
+ }
+ return ret;
+}
+
+static bool cgfsng_setup_limits(void *hdata, struct lxc_list *cgroup_settings,
+ bool do_devices)
+{
+ struct cgfsng_handler_data *d = hdata;
+ struct lxc_list *iterator, *sorted_cgroup_settings, *next;
+ struct lxc_cgroup *cg;
+ bool ret = false;
+
+ if (lxc_list_empty(cgroup_settings))
+ return true;
+
+ sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
+ if (!sorted_cgroup_settings) {
+ return false;
+ }
+
+ lxc_list_for_each(iterator, sorted_cgroup_settings) {
+ cg = iterator->elem;
+
+ if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
+ if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
+ if (do_devices && (errno == EACCES || errno == EPERM)) {
+ WARN("Error setting %s to %s for %s",
+ cg->subsystem, cg->value, d->name);
+ continue;
+ }
+ SYSERROR("Error setting %s to %s for %s",
+ cg->subsystem, cg->value, d->name);
+ goto out;
+ }
+ }
+
+ DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
+ }
+
+ ret = true;
+ INFO("cgroup has been setup");
+out:
+ lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
+ lxc_list_del(iterator);
+ free(iterator);
+ }
+ free(sorted_cgroup_settings);
+ return ret;
+}
+
+static struct cgroup_ops cgfsng_ops = {
+ .init = cgfsng_init,
+ .destroy = cgfsng_destroy,
+ .create = cgfsng_create,
+ .enter = cgfsng_enter,
+ .canonical_path = cgfsng_canonical_path,
+ .escape = cgfsng_escape,
+ .get_cgroup = cgfsng_get_cgroup,
+ .get = cgfsng_get,
+ .set = cgfsng_set,
+ .unfreeze = cgfsng_unfreeze,
+ .setup_limits = cgfsng_setup_limits,
+ .name = "cgroupfs-ng",
+ .attach = cgfsng_attach,
+ .chown = cgfsns_chown,
+ .mount_cgroup = cgfsng_mount,
+ .nrtasks = cgfsng_nrtasks,
+ .driver = CGFSNG,
+
+ /* unsupported */
+ .create_legacy = NULL,
+};
diff --git a/src/lxc/cgroups/cgmanager.c b/src/lxc/cgroups/cgmanager.c
new file mode 100644
index 0000000..4da891d
--- /dev/null
+++ b/src/lxc/cgroups/cgmanager.c
@@ -0,0 +1,1672 @@
+/*
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <poll.h>
+
+#include "bdev.h"
+#include "error.h"
+#include "commands.h"
+#include "list.h"
+#include "conf.h"
+#include "utils.h"
+#include "log.h"
+#include "cgroup.h"
+#include "start.h"
+#include "state.h"
+
+#define CGM_SUPPORTS_GET_ABS 3
+#define CGM_SUPPORTS_NAMED 4
+#define CGM_SUPPORTS_MULT_CONTROLLERS 10
+
+#ifdef HAVE_CGMANAGER
+lxc_log_define(lxc_cgmanager, lxc);
+
+#include <nih-dbus/dbus_connection.h>
+#include <cgmanager/cgmanager-client.h>
+#include <nih/alloc.h>
+#include <nih/error.h>
+#include <nih/string.h>
+
+struct cgm_data {
+ char *name;
+ char *cgroup_path;
+ const char *cgroup_pattern;
+};
+
+static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void lock_mutex(pthread_mutex_t *l)
+{
+ int ret;
+
+ if ((ret = pthread_mutex_lock(l)) != 0) {
+ fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
+ exit(1);
+ }
+}
+
+static void unlock_mutex(pthread_mutex_t *l)
+{
+ int ret;
+
+ if ((ret = pthread_mutex_unlock(l)) != 0) {
+ fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
+ exit(1);
+ }
+}
+
+void cgm_lock(void)
+{
+ lock_mutex(&cgm_mutex);
+}
+
+void cgm_unlock(void)
+{
+ unlock_mutex(&cgm_mutex);
+}
+
+#ifdef HAVE_PTHREAD_ATFORK
+__attribute__((constructor))
+static void process_lock_setup_atfork(void)
+{
+ pthread_atfork(cgm_lock, cgm_unlock, cgm_unlock);
+}
+#endif
+
+static NihDBusProxy *cgroup_manager = NULL;
+static int32_t api_version;
+
+static struct cgroup_ops cgmanager_ops;
+static int nr_subsystems;
+static char **subsystems, **subsystems_inone;
+static bool dbus_threads_initialized = false;
+static void cull_user_controllers(void);
+
+static void cgm_dbus_disconnect(void)
+{
+ if (cgroup_manager) {
+ dbus_connection_flush(cgroup_manager->connection);
+ dbus_connection_close(cgroup_manager->connection);
+ nih_free(cgroup_manager);
+ }
+ cgroup_manager = NULL;
+ cgm_unlock();
+}
+
+#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
+static bool cgm_dbus_connect(void)
+{
+ DBusError dbus_error;
+ static DBusConnection *connection;
+
+ cgm_lock();
+ if (!dbus_threads_initialized) {
+ // tell dbus to do struct locking for thread safety
+ dbus_threads_init_default();
+ dbus_threads_initialized = true;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error);
+ if (!connection) {
+ DEBUG("Failed opening dbus connection: %s: %s",
+ dbus_error.name, dbus_error.message);
+ dbus_error_free(&dbus_error);
+ cgm_unlock();
+ return false;
+ }
+ dbus_connection_set_exit_on_disconnect(connection, FALSE);
+ dbus_error_free(&dbus_error);
+ cgroup_manager = nih_dbus_proxy_new(NULL, connection,
+ NULL /* p2p */,
+ "/org/linuxcontainers/cgmanager", NULL, NULL);
+ dbus_connection_unref(connection);
+ if (!cgroup_manager) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("Error opening cgmanager proxy: %s", nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ // get the api version
+ if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("Error cgroup manager api version: %s", nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+ if (api_version < CGM_SUPPORTS_NAMED)
+ cull_user_controllers();
+ return true;
+}
+
+static bool cgm_all_controllers_same;
+
+/*
+ * Check whether we can use "all" when talking to cgmanager.
+ * We check two things:
+ * 1. whether cgmanager is new enough to support this.
+ * 2. whether the task we are interested in is in the same
+ * cgroup for all controllers.
+ * In cgm_init (before an lxc-start) we care about our own
+ * cgroup. In cgm_attach, we care about the target task's
+ * cgroup.
+ */
+static void check_supports_multiple_controllers(pid_t pid)
+{
+ FILE *f;
+ char *line = NULL, *prevpath = NULL;
+ size_t sz = 0;
+ char path[100];
+
+ cgm_all_controllers_same = false;
+
+ if (pid == -1)
+ sprintf(path, "/proc/self/cgroup");
+ else
+ sprintf(path, "/proc/%d/cgroup", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return;
+
+ cgm_all_controllers_same = true;
+
+ while (getline(&line, &sz, f) != -1) {
+ /* file format: hierarchy:subsystems:group */
+ char *colon;
+ if (!line[0])
+ continue;
+
+ colon = strchr(line, ':');
+ if (!colon)
+ continue;
+ colon = strchr(colon+1, ':');
+ if (!colon)
+ continue;
+ colon++;
+ if (!prevpath) {
+ prevpath = alloca(strlen(colon)+1);
+ strcpy(prevpath, colon);
+ continue;
+ }
+ if (strcmp(prevpath, colon) != 0) {
+ cgm_all_controllers_same = false;
+ break;
+ }
+ }
+
+ fclose(f);
+ free(line);
+}
+
+static int send_creds(int sock, int rpid, int ruid, int rgid)
+{
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ struct ucred cred = {
+ .pid = rpid,
+ .uid = ruid,
+ .gid = rgid,
+ };
+ char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+ char buf[1];
+ buf[0] = 'p';
+
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (sendmsg(sock, &msg, 0) < 0)
+ return -1;
+ return 0;
+}
+
+static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
+{
+ bool ret = true;
+ if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
+ cgroup_path, existed) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ ERROR("Failed to create %s:%s", controller, cgroup_path);
+ ret = false;
+ }
+
+ return ret;
+}
+
+/*
+ * Escape to the root cgroup if we are root, so that the container will
+ * be in "/lxc/c1" rather than "/user/..../c1"
+ * called internally with connection already open
+ */
+static bool cgm_escape(void *hdata)
+{
+ bool ret = true, cgm_needs_disconnect = false;
+ pid_t me = getpid();
+ char **slist = subsystems;
+ int i;
+
+ if (!cgroup_manager) {
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+ cgm_needs_disconnect = true;
+ }
+
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+
+ for (i = 0; slist[i]; i++) {
+ if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
+ slist[i], "/", me) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
+ slist[i], nerr->message);
+ nih_free(nerr);
+ ret = false;
+ break;
+ }
+ }
+
+ if (cgm_needs_disconnect)
+ cgm_dbus_disconnect();
+
+ return ret;
+}
+
+struct chown_data {
+ const char *cgroup_path;
+ uid_t origuid;
+};
+
+static int do_chown_cgroup(const char *controller, const char *cgroup_path,
+ uid_t newuid)
+{
+ int sv[2] = {-1, -1}, optval = 1, ret = -1;
+ char buf[1];
+ struct pollfd fds;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+ SYSERROR("Error creating socketpair");
+ goto out;
+ }
+ if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+ SYSERROR("setsockopt failed");
+ goto out;
+ }
+ if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+ SYSERROR("setsockopt failed");
+ goto out;
+ }
+ if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
+ cgroup_path, sv[1]) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ goto out;
+ }
+ /* now send credentials */
+
+ fds.fd = sv[0];
+ fds.events = POLLIN;
+ fds.revents = 0;
+ if (poll(&fds, 1, -1) <= 0) {
+ ERROR("Error getting go-ahead from server: %s", strerror(errno));
+ goto out;
+ }
+ if (read(sv[0], &buf, 1) != 1) {
+ ERROR("Error getting reply from server over socketpair");
+ goto out;
+ }
+ if (send_creds(sv[0], getpid(), getuid(), getgid())) {
+ SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
+ goto out;
+ }
+ fds.fd = sv[0];
+ fds.events = POLLIN;
+ fds.revents = 0;
+ if (poll(&fds, 1, -1) <= 0) {
+ ERROR("Error getting go-ahead from server: %s", strerror(errno));
+ goto out;
+ }
+ if (read(sv[0], &buf, 1) != 1) {
+ ERROR("Error getting reply from server over socketpair");
+ goto out;
+ }
+ if (send_creds(sv[0], getpid(), newuid, 0)) {
+ SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
+ goto out;
+ }
+ fds.fd = sv[0];
+ fds.events = POLLIN;
+ fds.revents = 0;
+ if (poll(&fds, 1, -1) <= 0) {
+ ERROR("Error getting go-ahead from server: %s", strerror(errno));
+ goto out;
+ }
+ ret = read(sv[0], buf, 1);
+out:
+ close(sv[0]);
+ close(sv[1]);
+ if (ret == 1 && *buf == '1')
+ return 0;
+ return -1;
+}
+
+static int chown_cgroup_wrapper(void *data)
+{
+ struct chown_data *arg = data;
+ char **slist = subsystems;
+ int i, ret = -1;
+ uid_t destuid;
+
+ if (setresgid(0,0,0) < 0)
+ SYSERROR("Failed to setgid to 0");
+ if (setresuid(0,0,0) < 0)
+ SYSERROR("Failed to setuid to 0");
+ if (setgroups(0, NULL) < 0)
+ SYSERROR("Failed to clear groups");
+ cgm_dbus_disconnect();
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return -1;
+ }
+ destuid = get_ns_uid(arg->origuid);
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+
+ for (i = 0; slist[i]; i++) {
+ if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) {
+ ERROR("Failed to chown %s:%s to container root",
+ slist[i], arg->cgroup_path);
+ goto fail;
+ }
+ }
+ ret = 0;
+fail:
+ cgm_dbus_disconnect();
+ return ret;
+}
+
+/* Internal helper. Must be called with the cgmanager dbus socket open */
+static bool lxc_cgmanager_chmod(const char *controller,
+ const char *cgroup_path, const char *file, int mode)
+{
+ if (cgmanager_chmod_sync(NULL, cgroup_manager, controller,
+ cgroup_path, file, mode) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ return false;
+ }
+ return true;
+}
+
+/* Internal helper. Must be called with the cgmanager dbus socket open */
+static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf)
+{
+ struct chown_data data;
+ char **slist = subsystems;
+ int i;
+
+ if (lxc_list_empty(&conf->id_map))
+ /* If there's no mapping then we don't need to chown */
+ return true;
+
+ data.cgroup_path = cgroup_path;
+ data.origuid = geteuid();
+
+ /* Unpriv users can't chown it themselves, so chown from
+ * a child namespace mapping both our own and the target uid
+ */
+ if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
+ ERROR("Error requesting cgroup chown in new namespace");
+ return false;
+ }
+
+ /*
+ * Now chmod 775 the directory else the container cannot create cgroups.
+ * This can't be done in the child namespace because it only group-owns
+ * the cgroup
+ */
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+
+ for (i = 0; slist[i]; i++) {
+ if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775))
+ return false;
+ if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0664))
+ return false;
+ if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0664))
+ return false;
+ }
+
+ return true;
+}
+
+#define CG_REMOVE_RECURSIVE 1
+/* Internal helper. Must be called with the cgmanager dbus socket open */
+static void cgm_remove_cgroup(const char *controller, const char *path)
+{
+ int existed;
+ if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
+ path, CG_REMOVE_RECURSIVE, &existed) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ ERROR("Error removing %s:%s", controller, path);
+ }
+ if (existed == -1)
+ INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
+}
+
+static void *cgm_init(const char *name)
+{
+ struct cgm_data *d;
+
+ d = malloc(sizeof(*d));
+ if (!d)
+ return NULL;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ goto err1;
+ }
+
+ memset(d, 0, sizeof(*d));
+ d->name = strdup(name);
+ if (!d->name) {
+ cgm_dbus_disconnect();
+ goto err1;
+ }
+
+ d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
+
+ // cgm_create immediately gets called so keep the connection open
+ return d;
+
+err1:
+ free(d);
+ return NULL;
+}
+
+/* Called after a failed container startup */
+static void cgm_destroy(void *hdata, struct lxc_conf *conf)
+{
+ struct cgm_data *d = hdata;
+ char **slist = subsystems;
+ int i;
+
+ if (!d || !d->cgroup_path)
+ return;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return;
+ }
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+ for (i = 0; slist[i]; i++)
+ cgm_remove_cgroup(slist[i], d->cgroup_path);
+
+ free(d->name);
+ free(d->cgroup_path);
+ free(d);
+ cgm_dbus_disconnect();
+}
+
+/*
+ * remove all the cgroups created
+ * called internally with dbus connection open
+ */
+static inline void cleanup_cgroups(char *path)
+{
+ int i;
+ char **slist = subsystems;
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+ for (i = 0; slist[i]; i++)
+ cgm_remove_cgroup(slist[i], path);
+}
+
+static inline bool cgm_create(void *hdata)
+{
+ struct cgm_data *d = hdata;
+ char **slist = subsystems;
+ int i, index=0, baselen, ret;
+ int32_t existed;
+ char result[MAXPATHLEN], *tmp, *cgroup_path;
+
+ if (!d)
+ return false;
+// XXX we should send a hint to the cgmanager that when these
+// cgroups become empty they should be deleted. Requires a cgmanager
+// extension
+
+ memset(result, 0, MAXPATHLEN);
+ tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
+ if (!tmp)
+ goto bad;
+ if (strlen(tmp) >= MAXPATHLEN) {
+ free(tmp);
+ goto bad;
+ }
+ strcpy(result, tmp);
+ baselen = strlen(result);
+ free(tmp);
+ tmp = result;
+ while (*tmp == '/')
+ tmp++;
+again:
+ if (index == 100) { // turn this into a warn later
+ ERROR("cgroup error? 100 cgroups with this name already running");
+ goto bad;
+ }
+ if (index) {
+ ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
+ if (ret < 0 || ret >= MAXPATHLEN-baselen)
+ goto bad;
+ }
+ existed = 0;
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+
+ for (i = 0; slist[i]; i++) {
+ if (!lxc_cgmanager_create(slist[i], tmp, &existed)) {
+ ERROR("Error creating cgroup %s:%s", slist[i], result);
+ cleanup_cgroups(tmp);
+ goto bad;
+ }
+ if (existed == 1)
+ goto next;
+ }
+ // success
+ cgroup_path = strdup(tmp);
+ if (!cgroup_path) {
+ cleanup_cgroups(tmp);
+ goto bad;
+ }
+ d->cgroup_path = cgroup_path;
+ cgm_dbus_disconnect();
+ return true;
+
+next:
+ index++;
+ goto again;
+bad:
+ cgm_dbus_disconnect();
+ return false;
+}
+
+/*
+ * Use the cgmanager to move a task into a cgroup for a particular
+ * hierarchy.
+ * All the subsystems in this hierarchy are co-mounted, so we only
+ * need to transition the task into one of the cgroups
+ *
+ * Internal helper, must be called with cgmanager dbus socket open
+ */
+static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
+ const char *cgroup_path, bool abs)
+{
+ int ret;
+
+ if (abs)
+ ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
+ controller, cgroup_path, pid);
+ else
+ ret = cgmanager_move_pid_sync(NULL, cgroup_manager,
+ controller, cgroup_path, pid);
+ if (ret != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ WARN("call to cgmanager_move_pid_%ssync failed: %s",
+ abs ? "abs_" : "", nerr->message);
+ nih_free(nerr);
+ return false;
+ }
+ return true;
+}
+
+static inline bool cgm_enter(void *hdata, pid_t pid)
+{
+ struct cgm_data *d = hdata;
+ char **slist = subsystems;
+ bool ret = false;
+ int i;
+
+ if (!d || !d->cgroup_path)
+ return false;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+
+ if (cgm_all_controllers_same)
+ slist = subsystems_inone;
+
+ for (i = 0; slist[i]; i++) {
+ if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false))
+ goto out;
+ }
+ ret = true;
+out:
+ cgm_dbus_disconnect();
+ return ret;
+}
+
+static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
+{
+ struct cgm_data *d = hdata;
+
+ if (!d || !d->cgroup_path)
+ return NULL;
+ return d->cgroup_path;
+}
+
+static const char *cgm_canonical_path(void *hdata)
+{
+ struct cgm_data *d = hdata;
+
+ if (!d || !d->cgroup_path)
+ return NULL;
+ return d->cgroup_path;
+}
+
+#if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC
+static inline bool abs_cgroup_supported(void) {
+ return api_version >= CGM_SUPPORTS_GET_ABS;
+}
+#else
+static inline bool abs_cgroup_supported(void) {
+ return false;
+}
+#define cgmanager_get_pid_cgroup_abs_sync(...) -1
+#endif
+
+static char *try_get_abs_cgroup(const char *name, const char *lxcpath,
+ const char *controller)
+{
+ char *cgroup = NULL;
+
+ if (abs_cgroup_supported()) {
+ /* get the container init pid and ask for its abs cgroup */
+ pid_t pid = lxc_cmd_get_init_pid(name, lxcpath);
+ if (pid < 0)
+ return NULL;
+ if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager,
+ controller, pid, &cgroup) != 0) {
+ cgroup = NULL;
+ NihError *nerr;
+ nerr = nih_error_get();
+ nih_free(nerr);
+ } else
+ prune_init_scope(cgroup);
+ return cgroup;
+ }
+
+ /* use the command interface to look for the cgroup */
+ return lxc_cmd_get_cgroup_path(name, lxcpath, controller);
+}
+
+/*
+ * nrtasks is called by the utmp helper by the container monitor.
+ * cgmanager socket was closed after cgroup setup was complete, so we need
+ * to reopen here.
+ *
+ * Return -1 on error.
+ */
+static int cgm_get_nrtasks(void *hdata)
+{
+ struct cgm_data *d = hdata;
+ int32_t *pids;
+ size_t pids_len;
+
+ if (!d || !d->cgroup_path)
+ return -1;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return -1;
+ }
+ if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0],
+ d->cgroup_path, &pids, &pids_len) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ pids_len = -1;
+ goto out;
+ }
+ nih_free(pids);
+out:
+ cgm_dbus_disconnect();
+ return pids_len;
+}
+
+#if HAVE_CGMANAGER_LIST_CONTROLLERS
+static bool lxc_list_controllers(char ***list)
+{
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+ if (cgmanager_list_controllers_sync(NULL, cgroup_manager, list) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_list_controllers_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+#else
+static bool lxc_list_controllers(char ***list)
+{
+ return false;
+}
+#endif
+
+static inline void free_abs_cgroup(char *cgroup)
+{
+ if (!cgroup)
+ return;
+ if (abs_cgroup_supported())
+ nih_free(cgroup);
+ else
+ free(cgroup);
+}
+
+static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue)
+{
+ char *controller, *key, *cgroup = NULL, *cglast;
+ int len = -1;
+ int ret;
+ nih_local char *result = NULL;
+
+ controller = alloca(strlen(filename)+1);
+ strcpy(controller, filename);
+ key = strchr(controller, '.');
+ if (!key) {
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ exit(1);
+ }
+ *key = '\0';
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ exit(1);
+ }
+ cgroup = try_get_abs_cgroup(name, lxcpath, controller);
+ if (!cgroup) {
+ cgm_dbus_disconnect();
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ exit(1);
+ }
+ cglast = strrchr(cgroup, '/');
+ if (!cglast) {
+ cgm_dbus_disconnect();
+ free_abs_cgroup(cgroup);
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ exit(1);
+ }
+ *cglast = '\0';
+ if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
+ WARN("Failed to enter container cgroup %s:%s", controller, cgroup);
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ cgm_dbus_disconnect();
+ free_abs_cgroup(cgroup);
+ exit(1);
+ }
+ if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ nih_free(nerr);
+ free_abs_cgroup(cgroup);
+ cgm_dbus_disconnect();
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len))
+ WARN("Failed to warn cgm_get of error; parent may hang");
+ exit(1);
+ }
+ free_abs_cgroup(cgroup);
+ cgm_dbus_disconnect();
+ len = strlen(result);
+ ret = write(outp, &len, sizeof(len));
+ if (ret != sizeof(len)) {
+ WARN("Failed to send length to parent");
+ exit(1);
+ }
+ if (!len || !sendvalue) {
+ exit(0);
+ }
+ ret = write(outp, result, len);
+ if (ret < 0)
+ exit(1);
+ exit(0);
+}
+
+/* cgm_get is called to get container cgroup settings, not during startup */
+static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
+{
+ pid_t pid;
+ int p[2], ret, newlen, readlen;
+
+ if (pipe(p) < 0)
+ return -1;
+ if ((pid = fork()) < 0) {
+ close(p[0]);
+ close(p[1]);
+ return -1;
+ }
+ if (!pid) // do_cgm_get exits
+ do_cgm_get(name, lxcpath, filename, p[1], len && value);
+ close(p[1]);
+ ret = read(p[0], &newlen, sizeof(newlen));
+ if (ret != sizeof(newlen)) {
+ close(p[0]);
+ ret = -1;
+ goto out;
+ }
+ if (!len || !value) {
+ close(p[0]);
+ ret = newlen;
+ goto out;
+ }
+ memset(value, 0, len);
+ if (newlen < 0) { // child is reporting an error
+ close(p[0]);
+ ret = -1;
+ goto out;
+ }
+ if (newlen == 0) { // empty read
+ close(p[0]);
+ ret = 0;
+ goto out;
+ }
+ readlen = newlen > len ? len : newlen;
+ ret = read(p[0], value, readlen);
+ close(p[0]);
+ if (ret != readlen) {
+ ret = -1;
+ goto out;
+ }
+ if (newlen >= len) {
+ value[len-1] = '\0';
+ newlen = len-1;
+ } else if (newlen+1 < len) {
+ // cgmanager doesn't add eol to last entry
+ value[newlen++] = '\n';
+ value[newlen] = '\0';
+ }
+ ret = newlen;
+out:
+ if (wait_for_pid(pid))
+ WARN("do_cgm_get exited with error");
+ return ret;
+}
+
+static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp)
+{
+ char *controller, *key, *cgroup = NULL;
+ int retval = 0; // value we are sending to the parent over outp
+ int ret;
+ char *cglast;
+
+ controller = alloca(strlen(filename)+1);
+ strcpy(controller, filename);
+ key = strchr(controller, '.');
+ if (!key) {
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ exit(1);
+ }
+ *key = '\0';
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ exit(1);
+ }
+ cgroup = try_get_abs_cgroup(name, lxcpath, controller);
+ if (!cgroup) {
+ cgm_dbus_disconnect();
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ exit(1);
+ }
+ cglast = strrchr(cgroup, '/');
+ if (!cglast) {
+ cgm_dbus_disconnect();
+ free_abs_cgroup(cgroup);
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ exit(1);
+ }
+ *cglast = '\0';
+ if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
+ ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ cgm_dbus_disconnect();
+ free_abs_cgroup(cgroup);
+ exit(1);
+ }
+ if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup);
+ ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ free_abs_cgroup(cgroup);
+ cgm_dbus_disconnect();
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval))
+ WARN("Failed to warn cgm_set of error; parent may hang");
+ exit(1);
+ }
+ free_abs_cgroup(cgroup);
+ cgm_dbus_disconnect();
+ /* tell parent that we are done */
+ retval = 1;
+ ret = write(outp, &retval, sizeof(retval));
+ if (ret != sizeof(retval)) {
+ exit(1);
+ }
+ exit(0);
+}
+
+/* cgm_set is called to change cgroup settings, not during startup */
+static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
+{
+ pid_t pid;
+ int p[2], ret, v;
+
+ if (pipe(p) < 0)
+ return -1;
+ if ((pid = fork()) < 0) {
+ close(p[1]);
+ close(p[0]);
+ return -1;
+ }
+ if (!pid) // do_cgm_set exits
+ do_cgm_set(name, lxcpath, filename, value, p[1]);
+ close(p[1]);
+ ret = read(p[0], &v, sizeof(v));
+ close(p[0]);
+ if (wait_for_pid(pid))
+ WARN("do_cgm_set exited with error");
+ if (ret != sizeof(v) || !v)
+ return -1;
+ return 0;
+}
+
+static void free_subsystems(void)
+{
+ int i;
+
+ for (i = 0; i < nr_subsystems; i++)
+ free(subsystems[i]);
+ free(subsystems);
+ subsystems = NULL;
+ nr_subsystems = 0;
+}
+
+static void cull_user_controllers(void)
+{
+ int i, j;
+
+ for (i = 0; i < nr_subsystems; i++) {
+ if (strncmp(subsystems[i], "name=", 5) != 0)
+ continue;
+ for (j = i; j < nr_subsystems-1; j++)
+ subsystems[j] = subsystems[j+1];
+ nr_subsystems--;
+ }
+}
+
+/*
+ * return true if inword is in the comma-delimited list cgroup_use
+ */
+static bool in_comma_list(const char *inword, const char *cgroup_use)
+{
+ char *e;
+ size_t inlen = strlen(inword), len;
+
+ do {
+ e = strchr(cgroup_use, ',');
+ len = e ? e - cgroup_use : strlen(cgroup_use);
+ if (len == inlen && strncmp(inword, cgroup_use, len) == 0)
+ return true;
+ cgroup_use = e + 1;
+ } while (e);
+
+ return false;
+}
+
+/*
+ * inlist is a comma-delimited list of cgroups; so is checklist. Return
+ * true if any member of inlist is in checklist.
+ */
+static bool any_in_comma_list(const char *inlist, const char *checklist)
+{
+ char *tmp = alloca(strlen(inlist) + 1), *tok, *saveptr = NULL;
+
+ strcpy(tmp, inlist);
+ for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) {
+ if (in_comma_list(tok, checklist))
+ return true;
+ }
+
+ return false;
+}
+
+static bool in_subsystem_list(const char *c)
+{
+ int i;
+
+ for (i = 0; i < nr_subsystems; i++) {
+ if (strcmp(c, subsystems[i]) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory",
+ * then clear out any other subsystems, and make sure that freezer
+ * and memory are both enabled
+ */
+static bool verify_and_prune(const char *cgroup_use)
+{
+ const char *p;
+ char *e;
+ int i, j;
+
+ for (p = cgroup_use; p && *p; p = e + 1) {
+ e = strchr(p, ',');
+ if (e)
+ *e = '\0';
+
+ if (!in_subsystem_list(p)) {
+ ERROR("Controller %s required by lxc.cgroup.use but not available\n", p);
+ return false;
+ }
+
+ if (e)
+ *e = ',';
+ if (!e)
+ break;
+ }
+
+ for (i = 0; i < nr_subsystems;) {
+ if (in_comma_list(subsystems[i], cgroup_use)) {
+ i++;
+ continue;
+ }
+ free(subsystems[i]);
+ for (j = i; j < nr_subsystems-1; j++)
+ subsystems[j] = subsystems[j+1];
+ subsystems[nr_subsystems-1] = NULL;
+ nr_subsystems--;
+ }
+
+ return true;
+}
+
+static void drop_subsystem(int which)
+{
+ int i;
+
+ if (which < 0 || which >= nr_subsystems) {
+ ERROR("code error: dropping invalid subsystem index\n");
+ exit(1);
+ }
+
+ free(subsystems[which]);
+ /* note - we have nr_subsystems+1 entries, last one a NULL */
+ for (i = which; i < nr_subsystems; i++)
+ subsystems[i] = subsystems[i+1];
+ nr_subsystems -= 1;
+}
+
+/*
+ * Check whether we can create the cgroups we would want
+ */
+static bool subsys_is_writeable(const char *controller, const char *probe)
+{
+ int32_t existed;
+ bool ret = true;
+
+ if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
+ probe, &existed) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ ERROR("Failed to create %s:%s", controller, probe);
+ ret = false;
+ }
+
+ return ret;
+}
+
+static char *get_last_controller_in_list(char *list)
+{
+ char *p;
+
+ while ((p = strchr(list, ',')) != NULL)
+ list = p + 1;
+
+ return list;
+}
+
+/*
+ * Make sure that all the controllers are writeable.
+ * If any are not, then
+ * - if they are listed in lxc.cgroup.use, refuse to start
+ * - else if they are crucial subsystems, refuse to start
+ * - else warn and do not use them
+ */
+static bool verify_final_subsystems(const char *cgroup_use)
+{
+ int i;
+ bool dropped_any = false;
+ bool bret = false;
+ const char *cgroup_pattern;
+ char tmpnam[50], *probe;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+
+ cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
+ i = snprintf(tmpnam, 50, "lxcprobe-%d", getpid());
+ if (i < 0 || i >= 50) {
+ ERROR("Attack - format string modified?");
+ return false;
+ }
+ probe = lxc_string_replace("%n", tmpnam, cgroup_pattern);
+ if (!probe)
+ goto out;
+
+ i = 0;
+ while (i < nr_subsystems) {
+ char *p = get_last_controller_in_list(subsystems[i]);
+
+ if (!subsys_is_writeable(p, probe)) {
+ if (is_crucial_cgroup_subsystem(p)) {
+ ERROR("Cannot write to crucial subsystem %s\n",
+ subsystems[i]);
+ goto out;
+ }
+ if (cgroup_use && any_in_comma_list(subsystems[i], cgroup_use)) {
+ ERROR("Cannot write to subsystem %s which is requested in lxc.cgroup.use\n",
+ subsystems[i]);
+ goto out;
+ }
+ WARN("Cannot write to subsystem %s, continuing with out it\n",
+ subsystems[i]);
+ dropped_any = true;
+ drop_subsystem(i);
+ } else {
+ cgm_remove_cgroup(subsystems[i], probe);
+ i++;
+ }
+ }
+
+ if (dropped_any)
+ cgm_all_controllers_same = false;
+ bret = true;
+
+out:
+ free(probe);
+ cgm_dbus_disconnect();
+ return bret;
+}
+
+static bool collect_subsystems(void)
+{
+ char *line = NULL;
+ nih_local char **cgm_subsys_list = NULL;
+ size_t sz = 0;
+ FILE *f = NULL;
+
+ if (subsystems) // already initialized
+ return true;
+
+ subsystems_inone = malloc(2 * sizeof(char *));
+ if (!subsystems_inone)
+ return false;
+ subsystems_inone[0] = "all";
+ subsystems_inone[1] = NULL;
+
+ if (lxc_list_controllers(&cgm_subsys_list)) {
+ while (cgm_subsys_list[nr_subsystems]) {
+ char **tmp = NIH_MUST( realloc(subsystems,
+ (nr_subsystems+2)*sizeof(char *)) );
+ tmp[nr_subsystems] = NIH_MUST(
+ strdup(cgm_subsys_list[nr_subsystems++]) );
+ subsystems = tmp;
+ }
+ if (nr_subsystems)
+ subsystems[nr_subsystems] = NULL;
+ goto collected;
+ }
+
+ INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups");
+ f = fopen_cloexec("/proc/self/cgroup", "r");
+ if (!f) {
+ f = fopen_cloexec("/proc/1/cgroup", "r");
+ if (!f)
+ return false;
+ }
+ while (getline(&line, &sz, f) != -1) {
+ /* file format: hierarchy:subsystems:group,
+ * with multiple subsystems being ,-separated */
+ char *slist, *end, *p, *saveptr = NULL, **tmp;
+
+ if (!line[0])
+ continue;
+
+ slist = strchr(line, ':');
+ if (!slist)
+ continue;
+ slist++;
+ end = strchr(slist, ':');
+ if (!end)
+ continue;
+ *end = '\0';
+
+ for (p = strtok_r(slist, ",", &saveptr);
+ p;
+ p = strtok_r(NULL, ",", &saveptr)) {
+ tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *));
+ if (!tmp)
+ goto out_free;
+
+ subsystems = tmp;
+ tmp[nr_subsystems] = strdup(p);
+ tmp[nr_subsystems+1] = NULL;
+ if (!tmp[nr_subsystems])
+ goto out_free;
+ nr_subsystems++;
+ }
+ }
+ fclose(f);
+ f = NULL;
+
+ free(line);
+ line = NULL;
+
+collected:
+ if (!nr_subsystems) {
+ ERROR("No cgroup subsystems found");
+ return false;
+ }
+
+ /* make sure that cgroup.use can be and is honored */
+ const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use");
+ if (!cgroup_use && errno != 0)
+ goto final_verify;
+ if (cgroup_use) {
+ if (!verify_and_prune(cgroup_use)) {
+ free_subsystems();
+ return false;
+ }
+ subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) );
+ cgm_all_controllers_same = false;
+ }
+
+final_verify:
+ return verify_final_subsystems(cgroup_use);
+
+out_free:
+ free(line);
+ if (f)
+ fclose(f);
+ free_subsystems();
+ return false;
+}
+
+/*
+ * called during cgroup.c:cgroup_ops_init(), at startup. No threads.
+ * We check whether we can talk to cgmanager, escape to root cgroup if
+ * we are root, then close the connection.
+ */
+struct cgroup_ops *cgm_ops_init(void)
+{
+ check_supports_multiple_controllers(-1);
+ if (!collect_subsystems())
+ return NULL;
+
+ if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS)
+ cgm_all_controllers_same = false;
+
+ // if root, try to escape to root cgroup
+ if (geteuid() == 0 && !cgm_escape(NULL)) {
+ free_subsystems();
+ return NULL;
+ }
+
+ return &cgmanager_ops;
+}
+
+/* unfreeze is called by the command api after killing a container. */
+static bool cgm_unfreeze(void *hdata)
+{
+ struct cgm_data *d = hdata;
+ bool ret = true;
+
+ if (!d || !d->cgroup_path)
+ return false;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+ if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
+ "freezer.state", "THAWED") != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ ERROR("Error unfreezing %s", d->cgroup_path);
+ ret = false;
+ }
+ cgm_dbus_disconnect();
+ return ret;
+}
+
+static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
+{
+ struct cgm_data *d = hdata;
+ struct lxc_list *iterator, *sorted_cgroup_settings, *next;
+ struct lxc_cgroup *cg;
+ bool ret = false;
+
+ if (lxc_list_empty(cgroup_settings))
+ return true;
+
+ if (!d || !d->cgroup_path)
+ return false;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+
+ sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
+ if (!sorted_cgroup_settings) {
+ return false;
+ }
+
+ lxc_list_for_each(iterator, sorted_cgroup_settings) {
+ char controller[100], *p;
+ cg = iterator->elem;
+ if (do_devices != !strncmp("devices", cg->subsystem, 7))
+ continue;
+ if (strlen(cg->subsystem) > 100) // i smell a rat
+ goto out;
+ strcpy(controller, cg->subsystem);
+ p = strchr(controller, '.');
+ if (p)
+ *p = '\0';
+ if (cgmanager_set_value_sync(NULL, cgroup_manager, controller,
+ d->cgroup_path, cg->subsystem, cg->value) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ if (do_devices) {
+ WARN("call to cgmanager_set_value_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ WARN("Error setting cgroup %s:%s limit type %s", controller,
+ d->cgroup_path, cg->subsystem);
+ continue;
+ }
+
+ ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
+ nih_free(nerr);
+ ERROR("Error setting cgroup %s:%s limit type %s", controller,
+ d->cgroup_path, cg->subsystem);
+ goto out;
+ }
+
+ DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
+ }
+
+ ret = true;
+ INFO("cgroup limits have been setup");
+out:
+ lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
+ lxc_list_del(iterator);
+ free(iterator);
+ }
+ free(sorted_cgroup_settings);
+ cgm_dbus_disconnect();
+ return ret;
+}
+
+static bool cgm_chown(void *hdata, struct lxc_conf *conf)
+{
+ struct cgm_data *d = hdata;
+
+ if (!d || !d->cgroup_path)
+ return false;
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+ if (!chown_cgroup(d->cgroup_path, conf))
+ WARN("Failed to chown %s to container root", d->cgroup_path);
+ cgm_dbus_disconnect();
+ return true;
+}
+
+/*
+ * TODO: this should be re-written to use the get_config_item("lxc.id_map")
+ * cmd api instead of getting the idmap from c->lxc_conf. The reason is
+ * that the id_maps may be different if the container was started with a
+ * -f or -s argument.
+ * The reason I'm punting on that is because we'll need to parse the
+ * idmap results.
+ */
+static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
+{
+ bool pass = true;
+ char *cgroup = NULL;
+ char **slist = subsystems;
+ int i;
+
+ if (!cgm_dbus_connect()) {
+ ERROR("Error connecting to cgroup manager");
+ return false;
+ }
+
+ for (i = 0; slist[i]; i++) {
+ cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]);
+ if (!cgroup) {
+ ERROR("Failed to get cgroup for controller %s", slist[i]);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) {
+ pass = false;
+ break;
+ }
+
+ }
+ cgm_dbus_disconnect();
+ if (!pass)
+ ERROR("Failed to enter group %s", cgroup);
+
+ free_abs_cgroup(cgroup);
+ return pass;
+}
+
+static bool cgm_bind_dir(const char *root, const char *dirname)
+{
+ nih_local char *cgpath = NULL;
+
+ /* /sys should have been mounted by now */
+ cgpath = NIH_MUST( nih_strdup(NULL, root) );
+ NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
+
+ if (!dir_exists(cgpath)) {
+ ERROR("%s does not exist", cgpath);
+ return false;
+ }
+
+ /* mount a tmpfs there so we can create subdirs */
+ if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) {
+ SYSERROR("Failed to mount tmpfs at %s", cgpath);
+ return false;
+ }
+ NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
+
+ if (mkdir(cgpath, 0755) < 0) {
+ SYSERROR("Failed to create %s", cgpath);
+ return false;
+ }
+
+ if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) {
+ SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * cgm_mount_cgroup:
+ * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
+ * /sys/fs/cgroup/cgmanager/ in the container.
+ * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
+ * Else do nothing
+ */
+#define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
+#define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
+static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
+{
+ if (dir_exists(CGMANAGER_LOWER_SOCK))
+ return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
+ if (dir_exists(CGMANAGER_UPPER_SOCK))
+ return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
+ // Host doesn't have cgmanager running? Then how did we get here?
+ return false;
+}
+
+static struct cgroup_ops cgmanager_ops = {
+ .init = cgm_init,
+ .destroy = cgm_destroy,
+ .create = cgm_create,
+ .enter = cgm_enter,
+ .create_legacy = NULL,
+ .get_cgroup = cgm_get_cgroup,
+ .canonical_path = cgm_canonical_path,
+ .escape = cgm_escape,
+ .get = cgm_get,
+ .set = cgm_set,
+ .unfreeze = cgm_unfreeze,
+ .setup_limits = cgm_setup_limits,
+ .name = "cgmanager",
+ .chown = cgm_chown,
+ .attach = cgm_attach,
+ .mount_cgroup = cgm_mount_cgroup,
+ .nrtasks = cgm_get_nrtasks,
+ .disconnect = NULL,
+ .driver = CGMANAGER,
+};
+#endif
diff --git a/src/lxc/cgroups/cgroup.c b/src/lxc/cgroups/cgroup.c
new file mode 100644
index 0000000..91ef359
--- /dev/null
+++ b/src/lxc/cgroups/cgroup.c
@@ -0,0 +1,245 @@
+/*
+ * 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 <unistd.h>
+#include <sys/types.h>
+
+#include "cgroup.h"
+#include "conf.h"
+#include "log.h"
+#include "start.h"
+
+lxc_log_define(lxc_cgroup, lxc);
+
+static struct cgroup_ops *ops = NULL;
+
+extern struct cgroup_ops *cgfs_ops_init(void);
+extern struct cgroup_ops *cgfsng_ops_init(void);
+extern struct cgroup_ops *cgm_ops_init(void);
+
+__attribute__((constructor))
+void cgroup_ops_init(void)
+{
+ if (ops) {
+ INFO("cgroup driver %s", ops->name);
+ return;
+ }
+
+ DEBUG("cgroup_init");
+ #if HAVE_CGMANAGER
+ ops = cgm_ops_init();
+ #endif
+ if (!ops)
+ ops = cgfsng_ops_init();
+ if (!ops)
+ ops = cgfs_ops_init();
+ if (ops)
+ INFO("Initialized cgroup driver %s", ops->name);
+}
+
+bool cgroup_init(struct lxc_handler *handler)
+{
+ if (handler->cgroup_data) {
+ ERROR("cgroup_init called on already inited handler");
+ return true;
+ }
+
+ if (ops) {
+ INFO("cgroup driver %s initing for %s", ops->name, handler->name);
+ handler->cgroup_data = ops->init(handler->name);
+ }
+ return handler->cgroup_data != NULL;
+}
+
+void cgroup_destroy(struct lxc_handler *handler)
+{
+ if (ops) {
+ ops->destroy(handler->cgroup_data, handler->conf);
+ handler->cgroup_data = NULL;
+ }
+}
+
+/* Create the container cgroups for all requested controllers */
+bool cgroup_create(struct lxc_handler *handler)
+{
+ if (ops)
+ return ops->create(handler->cgroup_data);
+ return false;
+}
+
+/*
+ * Enter the container init into its new cgroups for all
+ * requested controllers
+ */
+bool cgroup_enter(struct lxc_handler *handler)
+{
+ if (ops)
+ return ops->enter(handler->cgroup_data, handler->pid);
+ return false;
+}
+
+bool cgroup_create_legacy(struct lxc_handler *handler)
+{
+ if (ops && ops->create_legacy)
+ return ops->create_legacy(handler->cgroup_data, handler->pid);
+ return true;
+}
+
+const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem)
+{
+ if (ops)
+ return ops->get_cgroup(handler->cgroup_data, subsystem);
+ return NULL;
+}
+
+bool cgroup_escape(struct lxc_handler *handler)
+{
+ if (ops)
+ return ops->escape(handler->cgroup_data);
+ return false;
+}
+
+const char *cgroup_canonical_path(struct lxc_handler *handler)
+{
+ if (geteuid()) {
+ WARN("cgroup_canonical_path only makes sense for privileged containers.\n");
+ return NULL;
+ }
+
+ if (ops)
+ return ops->canonical_path(handler->cgroup_data);
+
+ return NULL;
+}
+
+bool cgroup_unfreeze(struct lxc_handler *handler)
+{
+ if (ops)
+ return ops->unfreeze(handler->cgroup_data);
+ return false;
+}
+
+bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
+{
+ if (ops)
+ return ops->setup_limits(handler->cgroup_data,
+ &handler->conf->cgroup, with_devices);
+ return false;
+}
+
+bool cgroup_chown(struct lxc_handler *handler)
+{
+ if (ops && ops->chown)
+ return ops->chown(handler->cgroup_data, handler->conf);
+ return true;
+}
+
+bool cgroup_mount(const char *root, struct lxc_handler *handler, int type)
+{
+ if (ops) {
+ return ops->mount_cgroup(handler->cgroup_data, root, type);
+ }
+ return false;
+}
+
+int cgroup_nrtasks(struct lxc_handler *handler)
+{
+ if (ops) {
+ if (ops->nrtasks)
+ return ops->nrtasks(handler->cgroup_data);
+ else
+ WARN("CGROUP driver %s doesn't implement nrtasks", ops->name);
+ }
+ return -1;
+}
+
+bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid)
+{
+ if (ops)
+ return ops->attach(name, lxcpath, pid);
+ return false;
+}
+
+int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath)
+{
+ if (ops)
+ return ops->set(filename, value, name, lxcpath);
+ return -1;
+}
+
+int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
+{
+ if (ops)
+ return ops->get(filename, value, len, name, lxcpath);
+ return -1;
+}
+
+void cgroup_disconnect(void)
+{
+ if (ops && ops->disconnect)
+ ops->disconnect();
+}
+
+cgroup_driver_t cgroup_driver(void)
+{
+ return ops->driver;
+}
+
+#define INIT_SCOPE "/init.scope"
+void prune_init_scope(char *cg)
+{
+ char *point;
+
+ if (!cg)
+ return;
+
+ point = cg + strlen(cg) - strlen(INIT_SCOPE);
+ if (point < cg)
+ return;
+ if (strcmp(point, INIT_SCOPE) == 0) {
+ if (point == cg)
+ *(point+1) = '\0';
+ else
+ *point = '\0';
+ }
+}
+
+/*
+ * Return true if this is a subsystem which we cannot do
+ * without.
+ *
+ * systemd is questionable here. The way callers currently
+ * use this, if systemd is not mounted then it will be ignored.
+ * But if systemd is mounted, then it must be setup so that lxc
+ * can create cgroups in it, else containers will fail.
+ */
+bool is_crucial_cgroup_subsystem(const char *s)
+{
+ if (strcmp(s, "systemd") == 0)
+ return true;
+ if (strcmp(s, "name=systemd") == 0)
+ return true;
+ if (strcmp(s, "freezer") == 0)
+ return true;
+ return false;
+}
diff --git a/src/lxc/cgroups/cgroup.h b/src/lxc/cgroups/cgroup.h
new file mode 100644
index 0000000..e56a115
--- /dev/null
+++ b/src/lxc/cgroups/cgroup.h
@@ -0,0 +1,89 @@
+/*
+ * 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_CGROUP_H
+#define __LXC_CGROUP_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+struct lxc_handler;
+struct lxc_conf;
+struct lxc_list;
+
+typedef enum {
+ CGFS,
+ CGMANAGER,
+ CGFSNG,
+} cgroup_driver_t;
+
+struct cgroup_ops {
+ const char *name;
+
+ void *(*init)(const char *name);
+ void (*destroy)(void *hdata, struct lxc_conf *conf);
+ bool (*create)(void *hdata);
+ bool (*enter)(void *hdata, pid_t pid);
+ bool (*create_legacy)(void *hdata, pid_t pid);
+ const char *(*get_cgroup)(void *hdata, const char *subsystem);
+ const char *(*canonical_path)(void *hdata);
+ bool (*escape)();
+ int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath);
+ int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
+ bool (*unfreeze)(void *hdata);
+ bool (*setup_limits)(void *hdata, struct lxc_list *cgroup_conf, bool with_devices);
+ bool (*chown)(void *hdata, struct lxc_conf *conf);
+ bool (*attach)(const char *name, const char *lxcpath, pid_t pid);
+ bool (*mount_cgroup)(void *hdata, const char *root, int type);
+ int (*nrtasks)(void *hdata);
+ void (*disconnect)(void);
+ cgroup_driver_t driver;
+};
+
+extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid);
+extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type);
+extern void cgroup_destroy(struct lxc_handler *handler);
+extern bool cgroup_init(struct lxc_handler *handler);
+extern bool cgroup_create(struct lxc_handler *handler);
+extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices);
+extern bool cgroup_chown(struct lxc_handler *handler);
+extern bool cgroup_enter(struct lxc_handler *handler);
+extern void cgroup_cleanup(struct lxc_handler *handler);
+extern bool cgroup_create_legacy(struct lxc_handler *handler);
+extern int cgroup_nrtasks(struct lxc_handler *handler);
+extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem);
+extern bool cgroup_escape();
+
+/*
+ * Currently, this call only makes sense for privileged containers.
+ */
+extern const char *cgroup_canonical_path(struct lxc_handler *handler);
+extern bool cgroup_unfreeze(struct lxc_handler *handler);
+extern void cgroup_disconnect(void);
+extern cgroup_driver_t cgroup_driver(void);
+
+extern void prune_init_scope(char *cg);
+extern bool is_crucial_cgroup_subsystem(const char *s);
+
+#endif
From 9296b9d47d66126064a4284fe90f9dcbbee02c82 Mon Sep 17 00:00:00 2001
From: Christian Brauner <cbrauner at suse.de>
Date: Sun, 31 Jul 2016 12:40:49 +0200
Subject: [PATCH 3/3] tools: move lxc commands to common subfolder
Signed-off-by: Christian Brauner <cbrauner at suse.de>
---
configure.ac | 4 +-
src/lxc/Makefile.am | 58 +-
src/lxc/lxc-checkconfig.in | 156 -----
src/lxc/lxc-start-ephemeral.in | 412 ------------
src/lxc/lxc-top.lua | 243 -------
src/lxc/lxc_attach.c | 440 ------------
src/lxc/lxc_autostart.c | 526 ---------------
src/lxc/lxc_cgroup.c | 122 ----
src/lxc/lxc_checkpoint.c | 236 -------
src/lxc/lxc_clone.c | 217 ------
src/lxc/lxc_config.c | 81 ---
src/lxc/lxc_console.c | 134 ----
src/lxc/lxc_copy.c | 877 ------------------------
src/lxc/lxc_create.c | 326 ---------
src/lxc/lxc_destroy.c | 258 --------
src/lxc/lxc_device.c | 178 -----
src/lxc/lxc_execute.c | 161 -----
src/lxc/lxc_freeze.c | 92 ---
src/lxc/lxc_info.c | 397 -----------
src/lxc/lxc_init.c | 261 --------
src/lxc/lxc_ls.c | 1213 ----------------------------------
src/lxc/lxc_monitor.c | 211 ------
src/lxc/lxc_snapshot.c | 284 --------
src/lxc/lxc_start.c | 357 ----------
src/lxc/lxc_stop.c | 246 -------
src/lxc/lxc_top.c | 510 --------------
src/lxc/lxc_unfreeze.c | 90 ---
src/lxc/lxc_unshare.c | 257 -------
src/lxc/lxc_usernsexec.c | 385 -----------
src/lxc/lxc_wait.c | 112 ----
src/lxc/tools/lxc-checkconfig.in | 156 +++++
src/lxc/tools/lxc-start-ephemeral.in | 412 ++++++++++++
src/lxc/tools/lxc-top.lua | 243 +++++++
src/lxc/tools/lxc_attach.c | 440 ++++++++++++
src/lxc/tools/lxc_autostart.c | 526 +++++++++++++++
src/lxc/tools/lxc_cgroup.c | 122 ++++
src/lxc/tools/lxc_checkpoint.c | 236 +++++++
src/lxc/tools/lxc_clone.c | 217 ++++++
src/lxc/tools/lxc_config.c | 81 +++
src/lxc/tools/lxc_console.c | 134 ++++
src/lxc/tools/lxc_copy.c | 877 ++++++++++++++++++++++++
src/lxc/tools/lxc_create.c | 326 +++++++++
src/lxc/tools/lxc_destroy.c | 258 ++++++++
src/lxc/tools/lxc_device.c | 178 +++++
src/lxc/tools/lxc_execute.c | 161 +++++
src/lxc/tools/lxc_freeze.c | 92 +++
src/lxc/tools/lxc_info.c | 397 +++++++++++
src/lxc/tools/lxc_init.c | 261 ++++++++
src/lxc/tools/lxc_ls.c | 1213 ++++++++++++++++++++++++++++++++++
src/lxc/tools/lxc_monitor.c | 211 ++++++
src/lxc/tools/lxc_snapshot.c | 284 ++++++++
src/lxc/tools/lxc_start.c | 357 ++++++++++
src/lxc/tools/lxc_stop.c | 246 +++++++
src/lxc/tools/lxc_top.c | 510 ++++++++++++++
src/lxc/tools/lxc_unfreeze.c | 90 +++
src/lxc/tools/lxc_unshare.c | 257 +++++++
src/lxc/tools/lxc_usernsexec.c | 385 +++++++++++
src/lxc/tools/lxc_wait.c | 112 ++++
58 files changed, 8813 insertions(+), 8813 deletions(-)
delete mode 100644 src/lxc/lxc-checkconfig.in
delete mode 100644 src/lxc/lxc-start-ephemeral.in
delete mode 100755 src/lxc/lxc-top.lua
delete mode 100644 src/lxc/lxc_attach.c
delete mode 100644 src/lxc/lxc_autostart.c
delete mode 100644 src/lxc/lxc_cgroup.c
delete mode 100644 src/lxc/lxc_checkpoint.c
delete mode 100644 src/lxc/lxc_clone.c
delete mode 100644 src/lxc/lxc_config.c
delete mode 100644 src/lxc/lxc_console.c
delete mode 100644 src/lxc/lxc_copy.c
delete mode 100644 src/lxc/lxc_create.c
delete mode 100644 src/lxc/lxc_destroy.c
delete mode 100644 src/lxc/lxc_device.c
delete mode 100644 src/lxc/lxc_execute.c
delete mode 100644 src/lxc/lxc_freeze.c
delete mode 100644 src/lxc/lxc_info.c
delete mode 100644 src/lxc/lxc_init.c
delete mode 100644 src/lxc/lxc_ls.c
delete mode 100644 src/lxc/lxc_monitor.c
delete mode 100644 src/lxc/lxc_snapshot.c
delete mode 100644 src/lxc/lxc_start.c
delete mode 100644 src/lxc/lxc_stop.c
delete mode 100644 src/lxc/lxc_top.c
delete mode 100644 src/lxc/lxc_unfreeze.c
delete mode 100644 src/lxc/lxc_unshare.c
delete mode 100644 src/lxc/lxc_usernsexec.c
delete mode 100644 src/lxc/lxc_wait.c
create mode 100644 src/lxc/tools/lxc-checkconfig.in
create mode 100644 src/lxc/tools/lxc-start-ephemeral.in
create mode 100755 src/lxc/tools/lxc-top.lua
create mode 100644 src/lxc/tools/lxc_attach.c
create mode 100644 src/lxc/tools/lxc_autostart.c
create mode 100644 src/lxc/tools/lxc_cgroup.c
create mode 100644 src/lxc/tools/lxc_checkpoint.c
create mode 100644 src/lxc/tools/lxc_clone.c
create mode 100644 src/lxc/tools/lxc_config.c
create mode 100644 src/lxc/tools/lxc_console.c
create mode 100644 src/lxc/tools/lxc_copy.c
create mode 100644 src/lxc/tools/lxc_create.c
create mode 100644 src/lxc/tools/lxc_destroy.c
create mode 100644 src/lxc/tools/lxc_device.c
create mode 100644 src/lxc/tools/lxc_execute.c
create mode 100644 src/lxc/tools/lxc_freeze.c
create mode 100644 src/lxc/tools/lxc_info.c
create mode 100644 src/lxc/tools/lxc_init.c
create mode 100644 src/lxc/tools/lxc_ls.c
create mode 100644 src/lxc/tools/lxc_monitor.c
create mode 100644 src/lxc/tools/lxc_snapshot.c
create mode 100644 src/lxc/tools/lxc_start.c
create mode 100644 src/lxc/tools/lxc_stop.c
create mode 100644 src/lxc/tools/lxc_top.c
create mode 100644 src/lxc/tools/lxc_unfreeze.c
create mode 100644 src/lxc/tools/lxc_unshare.c
create mode 100644 src/lxc/tools/lxc_usernsexec.c
create mode 100644 src/lxc/tools/lxc_wait.c
diff --git a/configure.ac b/configure.ac
index 13820ef..2af0735 100644
--- a/configure.ac
+++ b/configure.ac
@@ -842,9 +842,9 @@ AC_CONFIG_FILES([
src/Makefile
src/lxc/Makefile
- src/lxc/lxc-checkconfig
- src/lxc/lxc-start-ephemeral
src/lxc/lxc.functions
+ src/lxc/tools/lxc-checkconfig
+ src/lxc/tools/lxc-start-ephemeral
src/lxc/version.h
src/python-lxc/Makefile
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 9a87c15..5be5625 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -192,14 +192,14 @@ liblxc_so_LDADD += $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS)
liblxc_so_CFLAGS += $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS)
endif
-bin_SCRIPTS = lxc-checkconfig
+bin_SCRIPTS = tools/lxc-checkconfig
EXTRA_DIST = \
- lxc-top.lua
+ tools/lxc-top.lua
if ENABLE_DEPRECATED
if ENABLE_PYTHON
-bin_SCRIPTS += lxc-start-ephemeral
+bin_SCRIPTS += tools/lxc-start-ephemeral
endif
endif
@@ -243,35 +243,35 @@ AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
endif
LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SELINUX_LIBS@ @SECCOMP_LIBS@
-lxc_attach_SOURCES = lxc_attach.c
-lxc_autostart_SOURCES = lxc_autostart.c
-lxc_cgroup_SOURCES = lxc_cgroup.c
-lxc_config_SOURCES = lxc_config.c
-lxc_console_SOURCES = lxc_console.c
-lxc_destroy_SOURCES = lxc_destroy.c
-lxc_device_SOURCES = lxc_device.c
-lxc_execute_SOURCES = lxc_execute.c
-lxc_freeze_SOURCES = lxc_freeze.c
-lxc_info_SOURCES = lxc_info.c
-init_lxc_SOURCES = lxc_init.c
-lxc_monitor_SOURCES = lxc_monitor.c
-lxc_monitord_SOURCES = lxc_monitord.c
-lxc_ls_SOURCES = lxc_ls.c
-lxc_copy_SOURCES = lxc_copy.c
-lxc_start_SOURCES = lxc_start.c
-lxc_stop_SOURCES = lxc_stop.c
-lxc_top_SOURCES = lxc_top.c
-lxc_unfreeze_SOURCES = lxc_unfreeze.c
-lxc_unshare_SOURCES = lxc_unshare.c
-lxc_wait_SOURCES = lxc_wait.c
-lxc_create_SOURCES = lxc_create.c
-lxc_snapshot_SOURCES = lxc_snapshot.c
-lxc_usernsexec_SOURCES = lxc_usernsexec.c
+lxc_attach_SOURCES = tools/lxc_attach.c
+lxc_autostart_SOURCES = tools/lxc_autostart.c
+lxc_cgroup_SOURCES = tools/lxc_cgroup.c
+lxc_config_SOURCES = tools/lxc_config.c
+lxc_console_SOURCES = tools/lxc_console.c
+lxc_destroy_SOURCES = tools/lxc_destroy.c
+lxc_device_SOURCES = tools/lxc_device.c
+lxc_execute_SOURCES = tools/lxc_execute.c
+lxc_freeze_SOURCES = tools/lxc_freeze.c
+lxc_info_SOURCES = tools/lxc_info.c
+init_lxc_SOURCES = tools/lxc_init.c
+lxc_monitor_SOURCES = tools/lxc_monitor.c
+lxc_ls_SOURCES = tools/lxc_ls.c
+lxc_copy_SOURCES = tools/lxc_copy.c
+lxc_start_SOURCES = tools/lxc_start.c
+lxc_stop_SOURCES = tools/lxc_stop.c
+lxc_top_SOURCES = tools/lxc_top.c
+lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c
+lxc_unshare_SOURCES = tools/lxc_unshare.c
+lxc_wait_SOURCES = tools/lxc_wait.c
+lxc_create_SOURCES = tools/lxc_create.c
+lxc_snapshot_SOURCES = tools/lxc_snapshot.c
+lxc_usernsexec_SOURCES = tools/lxc_usernsexec.c
+lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c
lxc_user_nic_SOURCES = lxc_user_nic.c network.c network.h
-lxc_checkpoint_SOURCES = lxc_checkpoint.c
+lxc_monitord_SOURCES = lxc_monitord.c
if ENABLE_DEPRECATED
-lxc_clone_SOURCES = lxc_clone.c
+lxc_clone_SOURCES = tools/lxc_clone.c
endif
if !HAVE_GETSUBOPT
diff --git a/src/lxc/lxc-checkconfig.in b/src/lxc/lxc-checkconfig.in
deleted file mode 100644
index 29586f8..0000000
--- a/src/lxc/lxc-checkconfig.in
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/bin/sh
-
-# Allow environment variables to override config
-: ${CONFIG:=/proc/config.gz}
-: ${MODNAME:=configs}
-
-CAT="cat"
-
-if [ -t 1 ]; then
- SETCOLOR_SUCCESS="printf \\033[1;32m"
- SETCOLOR_FAILURE="printf \\033[1;31m"
- SETCOLOR_WARNING="printf \\033[1;33m"
- SETCOLOR_NORMAL="printf \\033[0;39m"
-else
- SETCOLOR_SUCCESS=":"
- SETCOLOR_FAILURE=":"
- SETCOLOR_WARNING=":"
- SETCOLOR_NORMAL=":"
-fi
-
-is_set() {
- $CAT $CONFIG | grep "$1=[y|m]" > /dev/null
- return $?
-}
-
-is_enabled() {
- mandatory=$2
-
- is_set $1
- RES=$?
-
- if [ $RES -eq 0 ]; then
- $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
- else
- if [ ! -z "$mandatory" ] && [ "$mandatory" = yes ]; then
- $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL
- else
- $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL
- fi
- fi
-}
-
-if [ ! -f $CONFIG ]; then
- echo "Kernel configuration not found at $CONFIG; searching..."
- KVER="`uname -r`"
- HEADERS_CONFIG="/lib/modules/$KVER/build/.config"
- BOOT_CONFIG="/boot/config-$KVER"
- [ -f "${HEADERS_CONFIG}" ] && CONFIG=${HEADERS_CONFIG}
- [ -f "${BOOT_CONFIG}" ] && CONFIG=${BOOT_CONFIG}
- if [ ! -f "$CONFIG" ]; then
- MODULEFILE=$(modinfo -k $KVER -n $MODNAME 2> /dev/null)
- # don't want to modprobe, so give user a hint
- # although scripts/extract-ikconfig could be used to extract contents without loading kernel module
- # http://svn.pld-linux.org/trac/svn/browser/geninitrd/trunk/geninitrd?rev=12696#L327
- fi
- if [ ! -f $CONFIG ]; then
- echo "$(basename $0): unable to retrieve kernel configuration" >&2
- echo >&2
- if [ -f "$MODULEFILE" ]; then
- echo "Try modprobe $MODNAME module, or" >&2
- fi
- echo "Try recompiling with IKCONFIG_PROC, installing the kernel headers," >&2
- echo "or specifying the kernel configuration path with:" >&2
- echo " CONFIG=<path> $(basename $0)" >&2
- exit 1
- else
- echo "Kernel configuration found at $CONFIG"
- fi
-fi
-
-if gunzip -tq < $CONFIG 2>/dev/null; then
- CAT="zcat"
-fi
-
-echo "--- Namespaces ---"
-echo -n "Namespaces: " && is_enabled CONFIG_NAMESPACES yes
-echo -n "Utsname namespace: " && is_enabled CONFIG_UTS_NS
-echo -n "Ipc namespace: " && is_enabled CONFIG_IPC_NS yes
-echo -n "Pid namespace: " && is_enabled CONFIG_PID_NS yes
-echo -n "User namespace: " && is_enabled CONFIG_USER_NS
-echo -n "Network namespace: " && is_enabled CONFIG_NET_NS
-echo -n "Multiple /dev/pts instances: " && is_enabled DEVPTS_MULTIPLE_INSTANCES
-echo
-echo "--- Control groups ---"
-
-print_cgroups() {
- # print all mountpoints for cgroup filesystems
- awk '$1 !~ /#/ && $3 == mp { print $2; } ; END { exit(0); } ' "mp=$1" "$2" ;
-}
-
-CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -n 1`
-KVER_MAJOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
- sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/')
-if [ "$KVER_MAJOR" = "2" ]; then
-KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
- sed -r 's/.* 2.6.([0-9]{2}).*/\1/')
-else
-KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
- sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/')
-fi
-
-echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes
-
-if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then
- echo -n "Cgroup clone_children flag: " &&
- $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
-else
- echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes
-fi
-echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE
-echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED
-echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT
-echo -n "Cgroup memory controller: "
-if ([ $KVER_MAJOR -ge 3 ] && [ $KVER_MINOR -ge 6 ]) || ([ $KVER_MAJOR -gt 3 ]); then
- is_enabled CONFIG_MEMCG
-else
- is_enabled CONFIG_CGROUP_MEM_RES_CTLR
-fi
-is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS
-echo
-echo "--- Misc ---"
-echo -n "Veth pair device: " && is_enabled CONFIG_VETH
-echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN
-echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q
-echo -n "Bridges: " && is_enabled CONFIG_BRIDGE
-echo -n "Advanced netfilter: " && is_enabled CONFIG_NETFILTER_ADVANCED
-echo -n "CONFIG_NF_NAT_IPV4: " && is_enabled CONFIG_NF_NAT_IPV4
-echo -n "CONFIG_NF_NAT_IPV6: " && is_enabled CONFIG_NF_NAT_IPV6
-echo -n "CONFIG_IP_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP_NF_TARGET_MASQUERADE
-echo -n "CONFIG_IP6_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP6_NF_TARGET_MASQUERADE
-echo -n "CONFIG_NETFILTER_XT_TARGET_CHECKSUM: " && is_enabled CONFIG_NETFILTER_XT_TARGET_CHECKSUM
-echo -n "FUSE (for use with lxcfs): " && is_enabled CONFIG_FUSE_FS
-
-echo
-echo "--- Checkpoint/Restore ---"
-echo -n "checkpoint restore: " && is_enabled CONFIG_CHECKPOINT_RESTORE
-echo -n "CONFIG_FHANDLE: " && is_enabled CONFIG_FHANDLE
-echo -n "CONFIG_EVENTFD: " && is_enabled CONFIG_EVENTFD
-echo -n "CONFIG_EPOLL: " && is_enabled CONFIG_EPOLL
-echo -n "CONFIG_UNIX_DIAG: " && is_enabled CONFIG_UNIX_DIAG
-echo -n "CONFIG_INET_DIAG: " && is_enabled CONFIG_INET_DIAG
-echo -n "CONFIG_PACKET_DIAG: " && is_enabled CONFIG_PACKET_DIAG
-echo -n "CONFIG_NETLINK_DIAG: " && is_enabled CONFIG_NETLINK_DIAG
-
-echo -n "File capabilities: " && \
- ( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \
- is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || \
- ( ( [ "${KVER_MAJOR}" = "2" ] && [ ${KVER_MINOR} -gt 32 ] ) || \
- [ ${KVER_MAJOR} -gt 2 ] && $SETCOLOR_SUCCESS && \
- echo "enabled" && $SETCOLOR_NORMAL )
-
-echo
-echo "Note : Before booting a new kernel, you can check its configuration"
-echo "usage : CONFIG=/path/to/config $0"
-echo
-
diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in
deleted file mode 100644
index 7e0c8ea..0000000
--- a/src/lxc/lxc-start-ephemeral.in
+++ /dev/null
@@ -1,412 +0,0 @@
-#!/usr/bin/env python3
-#
-# lxc-start-ephemeral: Start a copy of a container using an overlay
-#
-# This python implementation is based on the work done in the original
-# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
-#
-# (C) Copyright Canonical Ltd. 2012
-#
-# Authors:
-# Stéphane Graber <stgraber at ubuntu.com>
-#
-# 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
-#
-
-import argparse
-import gettext
-import lxc
-import os
-import sys
-import subprocess
-import tempfile
-
-_ = gettext.gettext
-gettext.textdomain("lxc-start-ephemeral")
-
-# Other functions
-
-
-def printstderr(*args):
- print("lxc-start-ephemeral is deprecated in favor of lxc-copy\n",
- *args, file=sys.stderr)
-
-
-def randomMAC():
- import random
-
- mac = [0x00, 0x16, 0x3e,
- random.randint(0x00, 0x7f),
- random.randint(0x00, 0xff),
- random.randint(0x00, 0xff)]
- return ':'.join(map(lambda x: "%02x" % x, mac))
-
-
-def get_rundir():
- if os.geteuid() == 0:
- return "@RUNTIME_PATH@"
-
- if "XDG_RUNTIME_DIR" in os.environ:
- return os.environ["XDG_RUNTIME_DIR"]
-
- if "HOME" in os.environ:
- return "%s/.cache/lxc/run/" % os.environ["HOME"]
-
- raise Exception("Unable to find a runtime directory")
-
-
-# Inform that lxc-start-ephemeral is deprecated
-printstderr()
-
-# Begin parsing the command line
-parser = argparse.ArgumentParser(description=_(
- "LXC: Start an ephemeral container"),
- formatter_class=argparse.RawTextHelpFormatter,
- epilog=_("If a COMMAND is given, then the "
- """container will run only as long
-as the command runs.
-If no COMMAND is given, this command will attach to tty1 and stop the
-container when exiting (with ctrl-a-q).
-
-If no COMMAND is given and -d is used, the name and IP addresses of the
-container will be printed to the console."""))
-
-parser.add_argument("--lxcpath", "-P", dest="lxcpath", metavar="PATH",
- help=_("Use specified container path"), default=None)
-
-parser.add_argument("--orig", "-o", type=str, required=True,
- help=_("name of the original container"))
-
-parser.add_argument("--name", "-n", type=str,
- help=_("name of the target container"))
-
-parser.add_argument("--bdir", "-b", type=str, action="append", default=[],
- help=_("directory to bind mount into container, "
- "either --bdir=/src-path or --bdir=/src-path:/dst-path"))
-
-parser.add_argument("--cdir", "-c", type=str, action="append", default=[],
- help=_("directory to cow mount into container"))
-
-parser.add_argument("--user", "-u", type=str,
- help=_("the user to run the command as"))
-
-parser.add_argument("--key", "-S", type=str,
- help=_("the path to the key to use to connect "
- "(when using ssh)"))
-
-parser.add_argument("--daemon", "-d", action="store_true",
- help=_("run in the background"))
-
-parser.add_argument("--storage-type", "-s", type=str, default=None,
- choices=("tmpfs", "dir"),
- help=("type of storage use by the container"))
-
-parser.add_argument("--union-type", "-U", type=str, default="overlayfs",
- choices=("overlayfs", "aufs"),
- help=_("type of union (overlayfs or aufs), "
- "defaults to overlayfs."))
-
-parser.add_argument("--keep-data", "-k", action="store_true",
- help=_("don't wipe everything clean at the end"))
-
-parser.add_argument("command", metavar='CMD', type=str, nargs="*",
- help=_("Run specific command in container "
- "(command as argument)"))
-
-parser.add_argument("--version", action="version", version=lxc.version)
-
-args = parser.parse_args()
-
-# Check that -d and CMD aren't used at the same time
-if args.command and args.daemon:
- parser.error(_("You can't use -d and a command at the same time."))
-
-# Check that -k isn't used with -s tmpfs
-if not args.storage_type:
- if args.keep_data:
- args.storage_type = "dir"
- else:
- args.storage_type = "tmpfs"
-
-if args.keep_data and args.storage_type == "tmpfs":
- parser.error(_("You can't use -k with the tmpfs storage type."))
-
-# Load the orig container
-orig = lxc.Container(args.orig, args.lxcpath)
-if not orig.defined:
- parser.error(_("Source container '%s' doesn't exist." % args.orig))
-
-# Create the new container paths
-if not args.lxcpath:
- lxc_path = lxc.default_config_path
-else:
- lxc_path = args.lxcpath
-
-if args.name:
- if os.path.exists("%s/%s" % (lxc_path, args.name)):
- parser.error(_("A container named '%s' already exists." % args.name))
- dest_path = "%s/%s" % (lxc_path, args.name)
- os.mkdir(dest_path)
-else:
- dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir=lxc_path)
-os.mkdir(os.path.join(dest_path, "rootfs"))
-os.chmod(dest_path, 0o770)
-
-# Setup the new container's configuration
-dest = lxc.Container(os.path.basename(dest_path), args.lxcpath)
-dest.load_config(orig.config_file_name)
-dest.set_config_item("lxc.utsname", dest.name)
-dest.set_config_item("lxc.rootfs", os.path.join(dest_path, "rootfs"))
-print("setting rootfs to .%s.", os.path.join(dest_path, "rootfs"))
-for nic in dest.network:
- if hasattr(nic, 'hwaddr'):
- nic.hwaddr = randomMAC()
-
-overlay_dirs = [(orig.get_config_item("lxc.rootfs"), "%s/rootfs/" % dest_path)]
-
-# Generate a new fstab
-if orig.get_config_item("lxc.mount"):
- dest.set_config_item("lxc.mount", os.path.join(dest_path, "fstab"))
- with open(orig.get_config_item("lxc.mount"), "r") as orig_fd:
- with open(dest.get_config_item("lxc.mount"), "w+") as dest_fd:
- for line in orig_fd.read().split("\n"):
- # Start by replacing any reference to the container rootfs
- line.replace(orig.get_config_item("lxc.rootfs"),
- dest.get_config_item("lxc.rootfs"))
-
- fields = line.split()
-
- # Skip invalid entries
- if len(fields) < 4:
- continue
-
- # Non-bind mounts are kept as-is
- if "bind" not in fields[3]:
- dest_fd.write("%s\n" % line)
- continue
-
- # Bind mounts of virtual filesystems are also kept as-is
- src_path = fields[0].split("/")
- if len(src_path) > 1 and src_path[1] in ("proc", "sys"):
- dest_fd.write("%s\n" % line)
- continue
-
- # Skip invalid mount points
- dest_mount = os.path.abspath(os.path.join("%s/rootfs/" % (
- dest_path), fields[1]))
-
- if "%s/rootfs/" % dest_path not in dest_mount:
- print(_("Skipping mount entry '%s' as it's outside "
- "of the container rootfs.") % line)
-
- # Setup an overlay for anything remaining
- overlay_dirs += [(fields[0], dest_mount)]
-
-for entry in args.cdir:
- if not os.path.exists(entry):
- print(_("Path '%s' doesn't exist, won't be cow-mounted.") %
- entry)
- else:
- src_path = os.path.abspath(entry)
- dst_path = "%s/rootfs/%s" % (dest_path, src_path)
- overlay_dirs += [(src_path, dst_path)]
-
-# do we have the new overlay fs which requires workdir, or the older
-# overlayfs which does not?
-have_new_overlay = False
-with open("/proc/filesystems", "r") as fd:
- for line in fd:
- if line == "nodev\toverlay\n":
- have_new_overlay = True
-
-# Generate pre-mount script
-with open(os.path.join(dest_path, "pre-mount"), "w+") as fd:
- os.fchmod(fd.fileno(), 0o755)
- fd.write("""#!/bin/sh
-LXC_DIR="%s"
-LXC_BASE="%s"
-LXC_NAME="%s"
-""" % (dest_path, orig.name, dest.name))
-
- count = 0
- for entry in overlay_dirs:
- tmpdir = "%s/tmpfs" % dest_path
- fd.write("mkdir -p %s\n" % (tmpdir))
- if args.storage_type == "tmpfs":
- fd.write("mount -n -t tmpfs -o mode=0755 none %s\n" % (tmpdir))
- deltdir = "%s/delta%s" % (tmpdir, count)
- workdir = "%s/work%s" % (tmpdir, count)
- fd.write("mkdir -p %s %s\n" % (deltdir, entry[1]))
- if have_new_overlay:
- fd.write("mkdir -p %s\n" % workdir)
-
- fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" %
- (entry[0], deltdir))
- fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" %
- (entry[0], entry[1]))
-
- if args.union_type == "overlayfs":
- if have_new_overlay:
- fd.write("mount -n -t overlay"
- " -oupperdir=%s,lowerdir=%s,workdir=%s none %s\n" % (
- deltdir,
- entry[0],
- workdir,
- entry[1]))
- else:
- fd.write("mount -n -t overlayfs"
- " -oupperdir=%s,lowerdir=%s none %s\n" % (
- deltdir,
- entry[0],
- entry[1]))
- elif args.union_type == "aufs":
- xino_path = "/dev/shm/aufs.xino"
- if not os.path.exists(os.path.basename(xino_path)):
- os.makedirs(os.path.basename(xino_path))
-
- fd.write("mount -n -t aufs "
- "-o br=%s=rw:%s=ro,noplink,xino=%s none %s\n" % (
- deltdir,
- entry[0],
- xino_path,
- entry[1]))
- count += 1
-
- for entry in args.bdir:
- if ':' in entry:
- src_path, dst_path = entry.split(":")
- else:
- src_path = entry
- dst_path = os.path.abspath(entry)
-
- if not os.path.exists(src_path):
- print(_("Path '%s' doesn't exist, won't be bind-mounted.") %
- src_path)
- else:
- src_path = os.path.abspath(src_path)
- dst_path = "%s/rootfs/%s" % (dest_path, dst_path)
- fd.write("mkdir -p %s\nmount -n --bind %s %s\n" % (
- dst_path, src_path, dst_path))
-
- fd.write("""
-[ -e $LXC_DIR/configured ] && exit 0
-for file in $LXC_DIR/rootfs/etc/hostname \\
- $LXC_DIR/rootfs/etc/hosts \\
- $LXC_DIR/rootfs/etc/sysconfig/network \\
- $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do
- [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file
-done
-touch $LXC_DIR/configured
-""")
-
-dest.set_config_item("lxc.hook.pre-mount",
- os.path.join(dest_path, "pre-mount"))
-
-if not args.keep_data:
- dest.set_config_item("lxc.ephemeral", "1")
-
-dest.save_config()
-
-# Start the container
-if not dest.start() or not dest.wait("RUNNING", timeout=5):
- print(_("The container '%s' failed to start.") % dest.name)
- dest.stop()
- if dest.defined:
- dest.destroy()
- sys.exit(1)
-
-# Deal with the case where we just attach to the container's console
-if not args.command and not args.daemon:
- dest.console()
- if not dest.shutdown(timeout=5):
- dest.stop()
- sys.exit(0)
-
-# Try to get the IP addresses
-ips = dest.get_ips(timeout=10)
-
-# Deal with the case where we just print info about the container
-if args.daemon:
- print(_("""The ephemeral container is now started.
-
-You can enter it from the command line with: lxc-console -n %s
-The following IP addresses have be found in the container:
-%s""") % (dest.name,
- "\n".join([" - %s" % entry for entry in ips]
- or [" - %s" % _("No address could be found")])))
- sys.exit(0)
-
-# Now deal with the case where we want to run a command in the container
-if not ips:
- print(_("Failed to get an IP for container '%s'.") % dest.name)
- dest.stop()
- if dest.defined:
- dest.destroy()
- sys.exit(1)
-
-if os.path.exists("/proc/self/ns/pid"):
- def attach_as_user(command):
- try:
- username = "root"
- if args.user:
- username = args.user
-
- line = subprocess.check_output(
- ["getent", "passwd", username],
- universal_newlines=True).rstrip("\n")
- _, _, pw_uid, pw_gid, _, pw_dir, _ = line.split(":", 6)
- pw_uid = int(pw_uid)
- pw_gid = int(pw_gid)
- os.setgid(pw_gid)
- os.initgroups(username, pw_gid)
- os.setuid(pw_uid)
- os.chdir(pw_dir)
- os.environ['HOME'] = pw_dir
- except:
- print(_("Unable to switch to user: %s" % username))
- sys.exit(1)
-
- return lxc.attach_run_command(command)
-
- retval = dest.attach_wait(attach_as_user, args.command,
- env_policy=lxc.LXC_ATTACH_CLEAR_ENV)
-
-else:
- cmd = ["ssh",
- "-o", "StrictHostKeyChecking=no",
- "-o", "UserKnownHostsFile=/dev/null"]
-
- if args.user:
- cmd += ["-l", args.user]
-
- if args.key:
- cmd += ["-i", args.key]
-
- for ip in ips:
- ssh_cmd = cmd + [ip] + args.command
- retval = subprocess.call(ssh_cmd, universal_newlines=True)
- if retval == 255:
- print(_("SSH failed to connect, trying next IP address."))
- continue
-
- if retval != 0:
- print(_("Command returned with non-zero return code: %s") % retval)
- break
-
-# Shutdown the container
-if not dest.shutdown(timeout=5):
- dest.stop()
-
-sys.exit(retval)
diff --git a/src/lxc/lxc-top.lua b/src/lxc/lxc-top.lua
deleted file mode 100755
index b5b3a69..0000000
--- a/src/lxc/lxc-top.lua
+++ /dev/null
@@ -1,243 +0,0 @@
-#!/usr/bin/env lua
---
--- top(1) like monitor for lxc containers
---
--- Copyright © 2012 Oracle.
---
--- Authors:
--- Dwight Engen <dwight.engen at oracle.com>
---
--- This library is free software; you can redistribute it and/or modify
--- it under the terms of the GNU General Public License version 2, as
--- published by the Free Software Foundation.
---
--- This program 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 General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along
--- with this program; if not, write to the Free Software Foundation, Inc.,
--- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
---
-
-local lxc = require("lxc")
-local core = require("lxc.core")
-local getopt = require("alt_getopt")
-
-local USER_HZ = 100
-local ESC = string.format("%c", 27)
-local TERMCLEAR = ESC.."[H"..ESC.."[J"
-local TERMNORM = ESC.."[0m"
-local TERMBOLD = ESC.."[1m"
-local TERMRVRS = ESC.."[7m"
-
-local containers = {}
-local stats = {}
-local stats_total = {}
-local max_containers
-
-function printf(...)
- local function wrapper(...) io.write(string.format(...)) end
- local status, result = pcall(wrapper, ...)
- if not status then
- error(result, 2)
- end
-end
-
-function string:split(delim, max_cols)
- local cols = {}
- local start = 1
- local nextc
- repeat
- nextc = string.find(self, delim, start)
- if (nextc and #cols ~= max_cols - 1) then
- table.insert(cols, string.sub(self, start, nextc-1))
- start = nextc + #delim
- else
- table.insert(cols, string.sub(self, start, string.len(self)))
- nextc = nil
- end
- until nextc == nil or start > #self
- return cols
-end
-
-function strsisize(size, width)
- local KiB = 1024
- local MiB = 1048576
- local GiB = 1073741824
- local TiB = 1099511627776
- local PiB = 1125899906842624
- local EiB = 1152921504606846976
- local ZiB = 1180591620717411303424
-
- if (size >= ZiB) then
- return string.format("%d.%2.2d ZB", size / ZiB, (math.floor(size % ZiB) * 100) / ZiB)
- end
- if (size >= EiB) then
- return string.format("%d.%2.2d EB", size / EiB, (math.floor(size % EiB) * 100) / EiB)
- end
- if (size >= PiB) then
- return string.format("%d.%2.2d PB", size / PiB, (math.floor(size % PiB) * 100) / PiB)
- end
- if (size >= TiB) then
- return string.format("%d.%2.2d TB", size / TiB, (math.floor(size % TiB) * 100) / TiB)
- end
- if (size >= GiB) then
- return string.format("%d.%2.2d GB", size / GiB, (math.floor(size % GiB) * 100) / GiB)
- end
- if (size >= MiB) then
- return string.format("%d.%2.2d MB", size / MiB, (math.floor(size % MiB) * 1000) / (MiB * 10))
- end
- if (size >= KiB) then
- return string.format("%d.%2.2d KB", size / KiB, (math.floor(size % KiB) * 1000) / (KiB * 10))
- end
- return string.format("%3d.00 ", size)
-end
-
-function tty_lines()
- local rows = 25
- local f = assert(io.popen("stty -a | head -n 1"))
- for line in f:lines() do
- local stty_rows
- _,_,stty_rows = string.find(line, "rows (%d+)")
- if (stty_rows ~= nil) then
- rows = stty_rows
- break
- end
- end
- f:close()
- return rows
-end
-
-function container_sort(a, b)
- if (optarg["r"]) then
- if (optarg["s"] == "n") then return (a > b)
- elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos < stats[b].cpu_use_nanos)
- elseif (optarg["s"] == "d") then return (stats[a].blkio < stats[b].blkio)
- elseif (optarg["s"] == "m") then return (stats[a].mem_used < stats[b].mem_used)
- elseif (optarg["s"] == "k") then return (stats[a].kmem_used < stats[b].kmem_used)
- end
- else
- if (optarg["s"] == "n") then return (a < b)
- elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos > stats[b].cpu_use_nanos)
- elseif (optarg["s"] == "d") then return (stats[a].blkio > stats[b].blkio)
- elseif (optarg["s"] == "m") then return (stats[a].mem_used > stats[b].mem_used)
- elseif (optarg["s"] == "k") then return (stats[a].kmem_used > stats[b].kmem_used)
- end
- end
-end
-
-function container_list_update()
- local now_running
-
- now_running = lxc.containers_running(true)
-
- -- check for newly started containers
- for _,v in ipairs(now_running) do
- if (containers[v] == nil) then
- local ct = lxc.container:new(v)
- -- note, this is a "mixed" table, ie both dictionary and list
- containers[v] = ct
- table.insert(containers, v)
- end
- end
-
- -- check for newly stopped containers
- local indx = 1
- while (indx <= #containers) do
- local ctname = containers[indx]
- if (now_running[ctname] == nil) then
- containers[ctname] = nil
- stats[ctname] = nil
- table.remove(containers, indx)
- else
- indx = indx + 1
- end
- end
-
- -- get stats for all current containers and resort the list
- lxc.stats_clear(stats_total)
- for _,ctname in ipairs(containers) do
- stats[ctname] = containers[ctname]:stats_get(stats_total)
- end
- table.sort(containers, container_sort)
-end
-
-function stats_print_header(stats_total)
- printf(TERMRVRS .. TERMBOLD)
- printf("%-15s %8s %8s %8s %10s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem")
- if (stats_total.kmem_used > 0) then printf(" %10s", "KMem") end
- printf("\n")
-
- printf("%-15s %8s %8s %8s %10s %10s", "Name", "Used", "Sys", "User", "Total", "Used")
- if (stats_total.kmem_used > 0) then printf(" %10s", "Used") end
- printf("\n")
- printf(TERMNORM)
-end
-
-function stats_print(name, stats, stats_total)
- printf("%-15s %8.2f %8.2f %8.2f %10s %10s",
- name,
- stats.cpu_use_nanos / 1000000000,
- stats.cpu_use_sys / USER_HZ,
- stats.cpu_use_user / USER_HZ,
- strsisize(stats.blkio),
- strsisize(stats.mem_used))
- if (stats_total.kmem_used > 0) then
- printf(" %10s", strsisize(stats.kmem_used))
- end
-end
-
-function usage()
- printf("Usage: lxc-top [options]\n" ..
- " -h|--help print this help message\n" ..
- " -m|--max display maximum number of containers\n" ..
- " -d|--delay delay in seconds between refreshes (default: 3.0)\n" ..
- " -s|--sort sort by [n,c,d,m] (default: n) where\n" ..
- " n = Name\n" ..
- " c = CPU use\n" ..
- " d = Disk I/O use\n" ..
- " m = Memory use\n" ..
- " k = Kernel memory use\n" ..
- " -r|--reverse sort in reverse (descending) order\n"
- )
- os.exit(1)
-end
-
-local long_opts = {
- help = "h",
- delay = "d",
- max = "m",
- reverse = "r",
- sort = "s",
-}
-
-optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts)
-optarg["d"] = tonumber(optarg["d"]) or 3.0
-optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3)
-optarg["r"] = optarg["r"] or false
-optarg["s"] = optarg["s"] or "n"
-if (optarg["h"] ~= nil) then
- usage()
-end
-
-while true
-do
- container_list_update()
- -- if some terminal we care about doesn't support the simple escapes, we
- -- may fall back to this, or ncurses. ug.
- --os.execute("tput clear")
- printf(TERMCLEAR)
- stats_print_header(stats_total)
- for index,ctname in ipairs(containers) do
- stats_print(ctname, stats[ctname], stats_total)
- printf("\n")
- if (index >= optarg["m"]) then
- break
- end
- end
- stats_print(string.format("TOTAL (%-2d)", #containers), stats_total, stats_total)
- io.flush()
- core.usleep(optarg["d"] * 1000000)
-end
diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
deleted file mode 100644
index 58f658b..0000000
--- a/src/lxc/lxc_attach.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * lxc: linux Container library
- *
- * (C) Copyright IBM Corp. 2007, 2010
- *
- * 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 <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "attach.h"
-#include "arguments.h"
-#include "caps.h"
-#include "confile.h"
-#include "console.h"
-#include "log.h"
-#include "list.h"
-#include "mainloop.h"
-#include "utils.h"
-
-#if HAVE_PTY_H
-#include <pty.h>
-#else
-#include <../include/openpty.h>
-#endif
-
-lxc_log_define(lxc_attach_ui, lxc);
-
-static const struct option my_longopts[] = {
- {"elevated-privileges", optional_argument, 0, 'e'},
- {"arch", required_argument, 0, 'a'},
- {"namespaces", required_argument, 0, 's'},
- {"remount-sys-proc", no_argument, 0, 'R'},
- /* TODO: decide upon short option names */
- {"clear-env", no_argument, 0, 500},
- {"keep-env", no_argument, 0, 501},
- {"keep-var", required_argument, 0, 502},
- {"set-var", required_argument, 0, 'v'},
- {"pty-log", required_argument, 0, 'L'},
- LXC_COMMON_OPTIONS
-};
-
-static int elevated_privileges = 0;
-static signed long new_personality = -1;
-static int namespace_flags = -1;
-static int remount_sys_proc = 0;
-static lxc_attach_env_policy_t env_policy = LXC_ATTACH_KEEP_ENV;
-static char **extra_env = NULL;
-static ssize_t extra_env_size = 0;
-static char **extra_keep = NULL;
-static ssize_t extra_keep_size = 0;
-
-static int add_to_simple_array(char ***array, ssize_t *capacity, char *value)
-{
- ssize_t count = 0;
-
- assert(array);
-
- if (*array)
- for (; (*array)[count]; count++);
-
- /* we have to reallocate */
- if (count >= *capacity - 1) {
- ssize_t new_capacity = ((count + 1) / 32 + 1) * 32;
- char **new_array = realloc((void*)*array, sizeof(char *) * new_capacity);
- if (!new_array)
- return -1;
- memset(&new_array[count], 0, sizeof(char*)*(new_capacity - count));
- *array = new_array;
- *capacity = new_capacity;
- }
-
- assert(*array);
-
- (*array)[count] = value;
- return 0;
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- int ret;
-
- switch (c) {
- case 'e':
- ret = lxc_fill_elevated_privileges(arg, &elevated_privileges);
- if (ret)
- return -1;
- break;
- case 'R': remount_sys_proc = 1; break;
- case 'a':
- new_personality = lxc_config_parse_arch(arg);
- if (new_personality < 0) {
- lxc_error(args, "invalid architecture specified: %s", arg);
- return -1;
- }
- break;
- case 's':
- namespace_flags = 0;
- ret = lxc_fill_namespace_flags(arg, &namespace_flags);
- if (ret)
- return -1;
- /* -s implies -e */
- lxc_fill_elevated_privileges(NULL, &elevated_privileges);
- break;
- case 500: /* clear-env */
- env_policy = LXC_ATTACH_CLEAR_ENV;
- break;
- case 501: /* keep-env */
- env_policy = LXC_ATTACH_KEEP_ENV;
- break;
- case 502: /* keep-var */
- ret = add_to_simple_array(&extra_keep, &extra_keep_size, arg);
- if (ret < 0) {
- lxc_error(args, "memory allocation error");
- return -1;
- }
- break;
- case 'v':
- ret = add_to_simple_array(&extra_env, &extra_env_size, arg);
- if (ret < 0) {
- lxc_error(args, "memory allocation error");
- return -1;
- }
- break;
- case 'L':
- args->console_log = arg;
- break;
- }
-
- return 0;
-}
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-attach",
- .help = "\
---name=NAME [-- COMMAND]\n\
-\n\
-Execute the specified COMMAND - enter the container NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -e, --elevated-privileges=PRIVILEGES\n\
- Use elevated privileges instead of those of the\n\
- container. If you don't specify privileges to be\n\
- elevated as OR'd list: CAP, CGROUP and LSM (capabilities,\n\
- cgroup and restrictions, respectively) then all of them\n\
- will be elevated.\n\
- WARNING: This may leak privileges into the container.\n\
- Use with care.\n\
- -a, --arch=ARCH Use ARCH for program instead of container's own\n\
- architecture.\n\
- -s, --namespaces=FLAGS\n\
- Don't attach to all the namespaces of the container\n\
- but just to the following OR'd list of flags:\n\
- MOUNT, PID, UTSNAME, IPC, USER or NETWORK.\n\
- WARNING: Using -s implies -e with all privileges\n\
- elevated, it may therefore leak privileges into the\n\
- container. Use with care.\n\
- -R, --remount-sys-proc\n\
- Remount /sys and /proc if not attaching to the\n\
- mount namespace when using -s in order to properly\n\
- reflect the correct namespace context. See the\n\
- lxc-attach(1) manual page for details.\n\
- --clear-env Clear all environment variables before attaching.\n\
- The attached shell/program will start with only\n\
- container=lxc set.\n\
- --keep-env Keep all current environment variables. This\n\
- is the current default behaviour, but is likely to\n\
- change in the future.\n\
- -L, --pty-log=FILE\n\
- Log pty output to FILE\n\
- -v, --set-var Set an additional variable that is seen by the\n\
- attached program in the container. May be specified\n\
- multiple times.\n\
- --keep-var Keep an additional environment variable. Only\n\
- applicable if --clear-env is specified. May be used\n\
- multiple times.\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
-};
-
-struct wrapargs {
- lxc_attach_options_t *options;
- lxc_attach_command_t *command;
- struct lxc_console *console;
- int ptyfd;
-};
-
-/* Minimalistic login_tty() implementation. */
-static int login_pty(int fd)
-{
- setsid();
- if (ioctl(fd, TIOCSCTTY, NULL) < 0)
- return -1;
- if (lxc_console_set_stdfds(fd) < 0)
- return -1;
- if (fd > STDERR_FILENO)
- close(fd);
- return 0;
-}
-
-static int get_pty_on_host_callback(void *p)
-{
- struct wrapargs *wrap = p;
-
- close(wrap->console->master);
- if (login_pty(wrap->console->slave) < 0)
- return -1;
-
- if (wrap->command->program)
- lxc_attach_run_command(wrap->command);
- else
- lxc_attach_run_shell(NULL);
- return -1;
-}
-
-static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *pid)
-{
- int ret = -1;
- struct wrapargs *args = wrap;
- struct lxc_epoll_descr descr;
- struct lxc_conf *conf;
- struct lxc_tty_state *ts;
-
- INFO("Trying to allocate a pty on the host");
-
- if (!isatty(args->ptyfd)) {
- ERROR("Standard file descriptor does not refer to a pty\n.");
- return -1;
- }
-
- conf = c->lxc_conf;
- free(conf->console.log_path);
- if (my_args.console_log)
- conf->console.log_path = strdup(my_args.console_log);
- else
- conf->console.log_path = NULL;
-
- /* In the case of lxc-attach our peer pty will always be the current
- * controlling terminal. We clear whatever was set by the user for
- * lxc.console.path here and set it to "/dev/tty". Doing this will (a)
- * prevent segfaults when the container has been setup with
- * lxc.console = none and (b) provide an easy way to ensure that we
- * always do the correct thing. strdup() must be used since console.path
- * is free()ed when we call lxc_container_put(). */
- free(conf->console.path);
- conf->console.path = strdup("/dev/tty");
- if (!conf->console.path)
- return -1;
-
- /* Create pty on the host. */
- if (lxc_console_create(conf) < 0)
- return -1;
- ts = conf->console.tty_state;
- conf->console.descr = &descr;
-
- /* Shift ttys to container. */
- if (ttys_shift_ids(conf) < 0) {
- ERROR("Failed to shift tty into container");
- goto err1;
- }
-
- /* Send wrapper function on its way. */
- wrap->console = &conf->console;
- if (c->attach(c, get_pty_on_host_callback, wrap, wrap->options, pid) < 0)
- goto err1;
- close(conf->console.slave); /* Close slave side. */
-
- ret = lxc_mainloop_open(&descr);
- if (ret) {
- ERROR("failed to create mainloop");
- goto err2;
- }
-
- if (lxc_console_mainloop_add(&descr, conf) < 0) {
- ERROR("Failed to add handlers to lxc mainloop.");
- goto err3;
- }
-
- ret = lxc_mainloop(&descr, -1);
- if (ret) {
- ERROR("mainloop returned an error");
- goto err3;
- }
- ret = 0;
-
-err3:
- lxc_mainloop_close(&descr);
-err2:
- if (ts->sigfd != -1)
- lxc_console_sigwinch_fini(ts);
-err1:
- lxc_console_delete(&conf->console);
-
- return ret;
-}
-
-static int stdfd_is_pty(void)
-{
- if (isatty(STDIN_FILENO))
- return STDIN_FILENO;
- if (isatty(STDOUT_FILENO))
- return STDOUT_FILENO;
- if (isatty(STDERR_FILENO))
- return STDERR_FILENO;
-
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- int ret = -1, r;
- int wexit = 0;
- pid_t pid;
- lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
- lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL};
-
- r = lxc_caps_init();
- if (r)
- exit(EXIT_FAILURE);
-
- r = lxc_arguments_parse(&my_args, argc, argv);
- if (r)
- exit(EXIT_FAILURE);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- r = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]);
- if (r)
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
-
- if (geteuid()) {
- if (access(my_args.lxcpath[0], O_RDWR) < 0) {
- if (!my_args.quiet)
- fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
- exit(EXIT_FAILURE);
- }
- }
-
- struct lxc_container *c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c)
- exit(EXIT_FAILURE);
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- if (!c->is_defined(c)) {
- fprintf(stderr, "Error: container %s is not defined\n", c->name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- if (remount_sys_proc)
- attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS;
- if (elevated_privileges)
- attach_options.attach_flags &= ~(elevated_privileges);
- attach_options.namespaces = namespace_flags;
- attach_options.personality = new_personality;
- attach_options.env_policy = env_policy;
- attach_options.extra_env_vars = extra_env;
- attach_options.extra_keep_env = extra_keep;
-
- if (my_args.argc > 0) {
- command.program = my_args.argv[0];
- command.argv = (char**)my_args.argv;
- }
-
- struct wrapargs wrap = (struct wrapargs){
- .command = &command,
- .options = &attach_options
- };
-
- wrap.ptyfd = stdfd_is_pty();
- if (wrap.ptyfd >= 0) {
- if ((!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) && my_args.console_log) {
- fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
- goto out;
- }
- ret = get_pty_on_host(c, &wrap, &pid);
- } else {
- if (my_args.console_log) {
- fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
- goto out;
- }
- if (command.program)
- ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
- else
- ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
- }
-
- if (ret < 0)
- goto out;
-
- ret = lxc_wait_for_pid_status(pid);
- if (ret < 0)
- goto out;
-
- if (WIFEXITED(ret))
- wexit = WEXITSTATUS(ret);
-out:
- lxc_container_put(c);
- if (ret >= 0)
- exit(wexit);
- exit(EXIT_FAILURE);
-}
diff --git a/src/lxc/lxc_autostart.c b/src/lxc/lxc_autostart.c
deleted file mode 100644
index eed0f5f..0000000
--- a/src/lxc/lxc_autostart.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/* lxc_autostart
- *
- * Copyright © 2013 Stéphane Graber <stgraber at ubuntu.com>
- * Copyright © 2013 Canonical Ltd.
- *
- * 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 <string.h>
-#include <unistd.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "arguments.h"
-#include "list.h"
-#include "log.h"
-
-lxc_log_define(lxc_autostart_ui, lxc);
-static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list);
-
-struct lxc_list *cmd_groups_list = NULL;
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'k': args->hardstop = 1; break;
- case 'L': args->list = 1; break;
- case 'r': args->reboot = 1; break;
- case 's': args->shutdown = 1; break;
- case 'a': args->all = 1; break;
- case 'A': args->ignore_auto = 1; break;
- case 'g': cmd_groups_list = accumulate_list( arg, ",", cmd_groups_list); break;
- case 't': args->timeout = atoi(arg); break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"kill", no_argument, 0, 'k'},
- {"list", no_argument, 0, 'L'},
- {"reboot", no_argument, 0, 'r'},
- {"shutdown", no_argument, 0, 's'},
- {"all", no_argument, 0, 'a'},
- {"ignore-auto", no_argument, 0, 'A'},
- {"groups", required_argument, 0, 'g'},
- {"timeout", required_argument, 0, 't'},
- {"help", no_argument, 0, 'h'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-autostart",
- .help = "\
-\n\
-lxc-autostart managed auto-started containers\n\
-\n\
-Options:\n\
- -k, --kill kill the containers instead of starting them\n\
- -L, --list list all affected containers and wait delay\n\
- -r, --reboot reboot the containers instead of starting them\n\
- -s, --shutdown shutdown the containers instead of starting them\n\
-\n\
- -a, --all list all auto-started containers (ignore groups)\n\
- -A, --ignore-auto ignore lxc.start.auto and select all matching containers\n\
- -g, --groups list of groups (comma separated) to select\n\
- -t, --timeout=T wait T seconds before hard-stopping\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .timeout = 60,
-};
-
-int list_contains_entry( char *str_ptr, struct lxc_list *p1 ) {
- struct lxc_list *it1;
-
- /*
- * If the entry is NULL or the empty string and the list
- * is NULL, we have a match
- */
- if (! p1 && ! str_ptr)
- return 1;
- if (! p1 && ! *str_ptr)
- return 1;
-
- if (!p1)
- return 0;
-
- lxc_list_for_each(it1, p1) {
- if (strcmp(it1->elem, str_ptr) == 0)
- return 1;
- }
-
- return 0;
-}
-
-int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) {
- struct lxc_list *it1;
- struct lxc_list *it2;
-
- if (!p1 && !p2)
- return 1;
-
- if (!p1)
- return 0;
-
- if (!p2)
- return 0;
-
- lxc_list_for_each(it1, p1) {
- lxc_list_for_each(it2, p2) {
- if (strcmp(it1->elem, it2->elem) == 0)
- return 1;
- }
- }
-
- return 0;
-}
-
-/*
- * This is a variation of get_list below it.
- * This version allows two additional features.
- * If a list is passed to it, it adds to it.
- * It allows for empty entries (i.e. "group1,,group2") generating
- * and empty list entry.
- */
-static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list) {
- char *workstr = NULL;
- char *workptr = NULL;
- char *next_ptr = NULL;
- struct lxc_list *worklist;
- struct lxc_list *workstr_list;
-
- workstr = strdup(input);
- if (!workstr) {
- return NULL;
- }
-
- workstr_list = str_list;
- if ( ! workstr_list ) {
- workstr_list = malloc(sizeof(*workstr_list));
- lxc_list_init(workstr_list);
- }
-
- for (workptr = workstr; workptr; workptr = next_ptr) {
- /*
- * We can't use strtok_r here because it collapses
- * multiple delimiters into 1 making empty fields
- * impossible...
- */
- /* token = strtok_r(workptr, delimiter, &sptr); */
- next_ptr = strchr( workptr, *delimiter );
-
- if( next_ptr ) {
- *next_ptr++ = '\0';
- }
-
- /*
- * At this point, we'd like to check to see if this
- * group is already contained in the list and ignore
- * it if it is... This also helps us with any
- * corner cases where a string begins or ends with a
- * delimiter.
- */
-
- if ( list_contains_entry( workptr, workstr_list ) ) {
- if ( *workptr ) {
- fprintf(stderr, "Duplicate group \"%s\" in list - ignoring\n", workptr );
- fflush(stderr);
- } else {
- fprintf(stderr, "Duplicate NULL group in list - ignoring\n" );
- fflush(stderr);
- }
- } else {
- worklist = malloc(sizeof(*worklist));
- if (!worklist)
- break;
-
- worklist->elem = strdup(workptr);
- if (!worklist->elem) {
- free(worklist);
- break;
- }
-
- lxc_list_add_tail(workstr_list, worklist);
- }
- }
-
- free(workstr);
-
- return workstr_list;
-}
-
-static struct lxc_list *get_list(char *input, char *delimiter) {
- char *workstr = NULL;
- char *workptr = NULL;
- char *sptr = NULL;
- char *token = NULL;
- struct lxc_list *worklist;
- struct lxc_list *workstr_list;
-
- workstr_list = malloc(sizeof(*workstr_list));
- lxc_list_init(workstr_list);
-
- workstr = strdup(input);
- if (!workstr) {
- free(workstr_list);
- return NULL;
- }
-
- for (workptr = workstr;;workptr = NULL) {
- token = strtok_r(workptr, delimiter, &sptr);
- if (!token) {
- break;
- }
-
- worklist = malloc(sizeof(*worklist));
- if (!worklist)
- break;
-
- worklist->elem = strdup(token);
- if (!worklist->elem) {
- free(worklist);
- break;
- }
-
- lxc_list_add_tail(workstr_list, worklist);
- }
-
- free(workstr);
-
- return workstr_list;
-}
-
-static struct lxc_list *get_config_list(struct lxc_container *c, char *key) {
- int len = 0;
- char* value = NULL;
- struct lxc_list *config_list = NULL;
-
- len = c->get_config_item(c, key, NULL, 0);
- if (len < 0)
- return NULL;
-
- value = (char*) malloc(sizeof(char)*len + 1);
- if (value == NULL)
- return NULL;
-
- if (c->get_config_item(c, key, value, len + 1) != len) {
- free(value);
- return NULL;
- }
-
- if (strlen(value) == 0) {
- free(value);
- return NULL;
- }
-
- config_list = get_list(value, "\n");
- free(value);
-
- return config_list;
-}
-
-static int get_config_integer(struct lxc_container *c, char *key) {
- int len = 0;
- int ret = 0;
- char* value = NULL;
-
- len = c->get_config_item(c, key, NULL, 0);
- if (len < 0)
- return 0;
-
- value = (char*) malloc(sizeof(char)*len + 1);
- if (value == NULL)
- return 0;
-
- if (c->get_config_item(c, key, value, len + 1) != len) {
- free(value);
- return 0;
- }
-
- ret = atoi(value);
- free(value);
-
- return ret;
-}
-
-static int cmporder(const void *p1, const void *p2) {
- struct lxc_container *c1 = *(struct lxc_container **)p1;
- struct lxc_container *c2 = *(struct lxc_container **)p2;
-
- int c1_order = get_config_integer(c1, "lxc.start.order");
- int c2_order = get_config_integer(c2, "lxc.start.order");
-
- if (c1_order == c2_order)
- return strcmp(c1->name, c2->name);
- else
- return (c1_order - c2_order);
-}
-
-static int toss_list( struct lxc_list *c_groups_list ) {
- struct lxc_list *it, *next;
-
- if (c_groups_list) {
- lxc_list_for_each_safe(it, c_groups_list, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
- }
- free(c_groups_list);
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[])
-{
- int count = 0;
- int i = 0;
- int ret = 0;
- struct lxc_container **containers = NULL;
- struct lxc_list **c_groups_lists = NULL;
- struct lxc_list *cmd_group;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- count = list_defined_containers(my_args.lxcpath[0], NULL, &containers);
-
- if (count < 0)
- return 1;
-
- if (!my_args.all) {
- /* Allocate an array for our container group lists */
- c_groups_lists = calloc( count, sizeof( struct lxc_list * ) );
- }
-
- qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
-
- if (cmd_groups_list && my_args.all) {
- fprintf(stderr, "Specifying -a (all) with -g (groups) doesn't make sense. All option overrides.\n");
- fflush(stderr);
- }
-
- if (!cmd_groups_list) {
- /*
- * We need a default cmd_groups_list even for the -a
- * case in order to force a pass through the loop for
- * the NULL group. This, someday, could be taken from
- * a config file somewhere...
- */
- cmd_groups_list = accumulate_list( "" , ",", NULL );
- }
-
- lxc_list_for_each(cmd_group, cmd_groups_list) {
-
- /*
- * Prograpmmers Note:
- * Because we may take several passes through the container list
- * We'll switch on if the container pointer is NULL and if we process a
- * container (run it or decide to ignore it) and call lxc_container_put
- * then we'll NULL it out and not check it again.
- */
- for (i = 0; i < count; i++) {
- struct lxc_container *c = containers[i];
-
- if (!c)
- /* Skip - must have been already processed */
- continue;
-
- /*
- * We haven't loaded the container groups yet so
- * these next two checks don't need to free them
- * if they fail. They'll fail on the first pass.
- */
- if (!c->may_control(c)) {
- /* We're done with this container */
- if ( lxc_container_put(c) > 0 )
- containers[i] = NULL;
- continue;
- }
-
- if (!my_args.ignore_auto &&
- get_config_integer(c, "lxc.start.auto") != 1) {
- /* We're done with this container */
- if ( lxc_container_put(c) > 0 )
- containers[i] = NULL;
- continue;
- }
-
- if (!my_args.all) {
- /* Filter by group */
- if( ! c_groups_lists[i] ) {
- /* Now we're loading up a container's groups */
- c_groups_lists[i] = get_config_list(c, "lxc.group");
- }
-
- ret = list_contains_entry(cmd_group->elem, c_groups_lists[i]);
-
- if ( ret == 0 ) {
- /* Not in the target group this pass */
- /* Leave in the list for subsequent passes */
- continue;
- }
- }
-
- /* We have a candidate continer to process */
- c->want_daemonize(c, 1);
-
- if (my_args.shutdown) {
- /* Shutdown the container */
- if (c->is_running(c)) {
- if (my_args.list) {
- printf("%s\n", c->name);
- fflush(stdout);
- }
- else {
- if (!c->shutdown(c, my_args.timeout)) {
- if (!c->stop(c)) {
- fprintf(stderr, "Error shutting down container: %s\n", c->name);
- fflush(stderr);
- }
- }
- }
- }
- }
- else if (my_args.hardstop) {
- /* Kill the container */
- if (c->is_running(c)) {
- if (my_args.list) {
- printf("%s\n", c->name);
- fflush(stdout);
- }
- else {
- if (!c->stop(c)) {
- fprintf(stderr, "Error killing container: %s\n", c->name);
- fflush(stderr);
- }
- }
- }
- }
- else if (my_args.reboot) {
- /* Reboot the container */
- if (c->is_running(c)) {
- if (my_args.list) {
- printf("%s %d\n", c->name,
- get_config_integer(c, "lxc.start.delay"));
- fflush(stdout);
- }
- else {
- if (!c->reboot(c)) {
- fprintf(stderr, "Error rebooting container: %s\n", c->name);
- fflush(stderr);
- }
- else
- sleep(get_config_integer(c, "lxc.start.delay"));
- }
- }
- }
- else {
- /* Start the container */
- if (!c->is_running(c)) {
- if (my_args.list) {
- printf("%s %d\n", c->name,
- get_config_integer(c, "lxc.start.delay"));
- fflush(stdout);
- }
- else {
- if (!c->start(c, 0, NULL)) {
- fprintf(stderr, "Error starting container: %s\n", c->name);
- fflush(stderr);
- }
- else
- sleep(get_config_integer(c, "lxc.start.delay"));
- }
- }
- }
-
- /*
- * If we get this far and we haven't hit any skip "continue"
- * then we're done with this container... We can dump any
- * c_groups_list and the container itself.
- */
- if ( lxc_container_put(c) > 0 ) {
- containers[i] = NULL;
- }
- if ( c_groups_lists ) {
- toss_list(c_groups_lists[i]);
- c_groups_lists[i] = NULL;
- }
- }
-
- }
-
- /* clean up any lingering detritus */
- for (i = 0; i < count; i++) {
- if ( containers[i] ) {
- lxc_container_put(containers[i]);
- }
- if ( c_groups_lists && c_groups_lists[i] ) {
- toss_list(c_groups_lists[i]);
- }
- }
-
- free(c_groups_lists);
- toss_list( cmd_groups_list );
- free(containers);
-
- return 0;
-}
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
deleted file mode 100644
index dd60fd1..0000000
--- a/src/lxc/lxc_cgroup.c
+++ /dev/null
@@ -1,122 +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 <stdio.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "arguments.h"
-
-lxc_log_define(lxc_cgroup_ui, lxc);
-
-static int my_checker(const struct lxc_arguments* args)
-{
- if (!args->argc) {
- lxc_error(args, "missing state object");
- return -1;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-cgroup",
- .help = "\
---name=NAME state-object [value]\n\
-\n\
-Get or set the value of a state object (for example, 'cpuset.cpus')\n\
-in the container's cgroup for the corresponding subsystem.\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container",
- .options = my_longopts,
- .parser = NULL,
- .checker = my_checker,
-};
-
-int main(int argc, char *argv[])
-{
- char *state_object = NULL, *value = NULL;
- struct lxc_container *c;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- state_object = my_args.argv[0];
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c)
- return 1;
-
- if (!c->may_control(c)) {
- ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- return 1;
- }
-
- if (!c->is_running(c)) {
- ERROR("'%s:%s' is not running", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- return 1;
- }
-
- if ((my_args.argc) > 1) {
- value = my_args.argv[1];
- if (!c->set_cgroup_item(c, state_object, value)) {
- ERROR("failed to assign '%s' value to '%s' for '%s'",
- value, state_object, my_args.name);
- lxc_container_put(c);
- return 1;
- }
- } else {
- int len = 4096;
- char buffer[len];
- int ret = c->get_cgroup_item(c, state_object, buffer, len);
- if (ret < 0) {
- ERROR("failed to retrieve value of '%s' for '%s:%s'",
- state_object, my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- return 1;
- }
- printf("%*s", ret, buffer);
- }
-
- lxc_container_put(c);
- return 0;
-}
diff --git a/src/lxc/lxc_checkpoint.c b/src/lxc/lxc_checkpoint.c
deleted file mode 100644
index 7130245..0000000
--- a/src/lxc/lxc_checkpoint.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- *
- * Copyright © 2014 Tycho Andersen <tycho.andersen at canonical.com>.
- * Copyright © 2014 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "log.h"
-#include "config.h"
-#include "lxc.h"
-#include "arguments.h"
-#include "utils.h"
-
-static char *checkpoint_dir = NULL;
-static bool stop = false;
-static bool verbose = false;
-static bool do_restore = false;
-static bool daemonize_set = false;
-
-static const struct option my_longopts[] = {
- {"checkpoint-dir", required_argument, 0, 'D'},
- {"stop", no_argument, 0, 's'},
- {"verbose", no_argument, 0, 'v'},
- {"restore", no_argument, 0, 'r'},
- {"daemon", no_argument, 0, 'd'},
- {"foreground", no_argument, 0, 'F'},
- LXC_COMMON_OPTIONS
-};
-
-static int my_checker(const struct lxc_arguments *args)
-{
- if (do_restore && stop) {
- lxc_error(args, "-s not compatible with -r.");
- return -1;
-
- } else if (!do_restore && daemonize_set) {
- lxc_error(args, "-d/-F not compatible with -r.");
- return -1;
- }
-
- if (checkpoint_dir == NULL) {
- lxc_error(args, "-D is required.");
- return -1;
- }
-
- return 0;
-}
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg)
-{
- switch (c) {
- case 'D':
- checkpoint_dir = strdup(arg);
- if (!checkpoint_dir)
- return -1;
- break;
- case 's':
- stop = true;
- break;
- case 'v':
- verbose = true;
- break;
- case 'r':
- do_restore = true;
- break;
- case 'd':
- args->daemonize = 1;
- daemonize_set = true;
- break;
- case 'F':
- args->daemonize = 0;
- daemonize_set = true;
- break;
- }
- return 0;
-}
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-checkpoint",
- .help = "\
---name=NAME\n\
-\n\
-lxc-checkpoint checkpoints and restores a container\n\
- Serializes a container's running state to disk to allow restoring it in\n\
- its running state at a later time.\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -r, --restore Restore container\n\
- -D, --checkpoint-dir=DIR directory to save the checkpoint in\n\
- -v, --verbose Enable verbose criu logs\n\
- Checkpoint options:\n\
- -s, --stop Stop the container after checkpointing.\n\
- Restore options:\n\
- -d, --daemon Daemonize the container (default)\n\
- -F, --foreground Start with the current tty attached to /dev/console\n\
-",
- .options = my_longopts,
- .parser = my_parser,
- .daemonize = 1,
- .checker = my_checker,
-};
-
-static bool checkpoint(struct lxc_container *c)
-{
- bool ret;
-
- if (!c->is_running(c)) {
- fprintf(stderr, "%s not running, not checkpointing.\n", my_args.name);
- lxc_container_put(c);
- return false;
- }
-
- ret = c->checkpoint(c, checkpoint_dir, stop, verbose);
- lxc_container_put(c);
-
- if (!ret) {
- fprintf(stderr, "Checkpointing %s failed.\n", my_args.name);
- return false;
- }
-
- return true;
-}
-
-static bool restore_finalize(struct lxc_container *c)
-{
- bool ret = c->restore(c, checkpoint_dir, verbose);
- if (!ret) {
- fprintf(stderr, "Restoring %s failed.\n", my_args.name);
- }
-
- lxc_container_put(c);
- return ret;
-}
-
-static bool restore(struct lxc_container *c)
-{
- if (c->is_running(c)) {
- fprintf(stderr, "%s is running, not restoring.\n", my_args.name);
- lxc_container_put(c);
- return false;
- }
-
- if (my_args.daemonize) {
- pid_t pid;
-
- pid = fork();
- if (pid < 0) {
- perror("fork");
- return false;
- }
-
- if (pid == 0) {
- close(0);
- close(1);
-
- exit(!restore_finalize(c));
- } else {
- return wait_for_pid(pid) == 0;
- }
- } else {
- int status;
-
- if (!restore_finalize(c))
- return false;
-
- if (waitpid(-1, &status, 0) < 0)
- return false;
-
- return WIFEXITED(status) && WEXITSTATUS(status) == 0;
- }
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- bool ret;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(1);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(1);
-
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- fprintf(stderr, "System error loading %s\n", my_args.name);
- exit(1);
- }
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
- if (!c->is_defined(c)) {
- fprintf(stderr, "%s is not defined\n", my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
-
- if (do_restore)
- ret = restore(c);
- else
- ret = checkpoint(c);
-
- return !ret;
-}
diff --git a/src/lxc/lxc_clone.c b/src/lxc/lxc_clone.c
deleted file mode 100644
index 6bd2226..0000000
--- a/src/lxc/lxc_clone.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- *
- * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2013 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <unistd.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <ctype.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "log.h"
-#include "config.h"
-#include "lxc.h"
-#include "conf.h"
-#include "state.h"
-
-lxc_log_define(lxc_clone_ui, lxc);
-
-/* we pass fssize in bytes */
-static uint64_t get_fssize(char *s)
-{
- uint64_t ret;
- char *end;
-
- ret = strtoull(s, &end, 0);
- if (end == s)
- {
- fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
- return 0;
- }
- while (isblank(*end))
- end++;
- if (*end == '\0')
- ret *= 1024ULL * 1024ULL; // MB by default
- else if (*end == 'b' || *end == 'B')
- ret *= 1ULL;
- else if (*end == 'k' || *end == 'K')
- ret *= 1024ULL;
- else if (*end == 'm' || *end == 'M')
- ret *= 1024ULL * 1024ULL;
- else if (*end == 'g' || *end == 'G')
- ret *= 1024ULL * 1024ULL * 1024ULL;
- else if (*end == 't' || *end == 'T')
- ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
- else
- {
- fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s);
- return 0;
- }
- return ret;
-}
-
-static void usage(const char *me)
-{
- printf("Usage: %s [-s] [-B backingstore] [-L size[unit]] [-K] [-M] [-H]\n", me);
- printf(" [-p lxcpath] [-P newlxcpath] orig new\n");
- printf("\n");
- printf(" -s: snapshot rather than copy\n");
- printf(" -B: use specified new backingstore. Default is the same as\n");
- printf(" the original. Options include aufs, btrfs, lvm, overlayfs, \n");
- printf(" dir and loop\n");
- printf(" -L: for blockdev-backed backingstore, use specified size * specified\n");
- printf(" unit. Default size is the size of the source blockdev, default\n");
- printf(" unit is MB\n");
- printf(" -K: Keep name - do not change the container name\n");
- printf(" -M: Keep macaddr - do not choose a random new mac address\n");
- printf(" -p: use container orig from custom lxcpath\n");
- printf(" -P: create container new in custom lxcpath\n");
- printf(" -R: rename existing container\n");
- exit(1);
-}
-
-static struct option options[] = {
- { "snapshot", no_argument, 0, 's'},
- { "backingstore", required_argument, 0, 'B'},
- { "size", required_argument, 0, 'L'},
- { "orig", required_argument, 0, 'o'},
- { "new", required_argument, 0, 'n'},
- { "vgname", required_argument, 0, 'v'},
- { "rename", no_argument, 0, 'R'},
- { "keepname", no_argument, 0, 'K'},
- { "keepmac", no_argument, 0, 'M'},
- { "lxcpath", required_argument, 0, 'p'},
- { "newpath", required_argument, 0, 'P'},
- { "fstype", required_argument, 0, 't'},
- { "help", no_argument, 0, 'h'},
- { 0, 0, 0, 0 },
-};
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c1 = NULL, *c2 = NULL;
- int snapshot = 0, keepname = 0, keepmac = 0, rename = 0;
- int flags = 0, option_index;
- uint64_t newsize = 0;
- char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL;
- char *orig = NULL, *new = NULL, *vgname = NULL;
- char **args = NULL;
- int c;
- bool ret;
-
- fprintf(stderr, "lxc-clone is deprecated in favor of lxc-copy.\n\n");
-
- if (argc < 3)
- usage(argv[0]);
-
- while (1) {
- c = getopt_long(argc, argv, "sB:L:o:n:v:KMHp:P:Rt:h", options, &option_index);
- if (c == -1)
- break;
- switch (c) {
- case 's': snapshot = 1; break;
- case 'B': bdevtype = optarg; break;
- case 'L': newsize = get_fssize(optarg); break;
- case 'o': orig = optarg; break;
- case 'n': new = optarg; break;
- case 'v': vgname = optarg; break;
- case 'K': keepname = 1; break;
- case 'M': keepmac = 1; break;
- case 'p': lxcpath = optarg; break;
- case 'P': newpath = optarg; break;
- case 'R': rename = 1; break;
- case 't': fstype = optarg; break;
- case 'h': usage(argv[0]);
- default: break;
- }
- }
- if (optind < argc && !orig)
- orig = argv[optind++];
- if (optind < argc && !new)
- new = argv[optind++];
- if (optind < argc)
- /* arguments for the clone hook */
- args = &argv[optind];
- if (!new || !orig) {
- printf("Error: you must provide orig and new names\n");
- usage(argv[0]);
- }
-
- if (snapshot) flags |= LXC_CLONE_SNAPSHOT;
- if (keepname) flags |= LXC_CLONE_KEEPNAME;
- if (keepmac) flags |= LXC_CLONE_KEEPMACADDR;
-
- // vgname and fstype could be supported by sending them through the
- // bdevdata. However, they currently are not yet. I'm not convinced
- // they are worthwhile.
- if (vgname) {
- printf("Error: vgname not supported\n");
- usage(argv[0]);
- }
- if (fstype) {
- printf("Error: fstype not supported\n");
- usage(argv[0]);
- }
-
- c1 = lxc_container_new(orig, lxcpath);
- if (!c1)
- exit(EXIT_FAILURE);
-
- if (!c1->may_control(c1)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", orig);
- lxc_container_put(c1);
- exit(EXIT_FAILURE);
- }
-
- if (!c1->is_defined(c1)) {
- fprintf(stderr, "Error: container %s is not defined\n", orig);
- lxc_container_put(c1);
- exit(EXIT_FAILURE);
- }
- if (rename) {
- ret = c1->rename(c1, new);
- if (!ret) {
- fprintf(stderr,
- "Error: Renaming container %s to %s failed\n",
- c1->name, new);
- lxc_container_put(c1);
- exit(EXIT_FAILURE);
- }
- } else {
- c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize,
- args);
- if (c2 == NULL) {
- lxc_container_put(c1);
- fprintf(stderr, "clone failed\n");
- exit(EXIT_FAILURE);
- }
- printf("Created container %s as %s of %s\n", new,
- snapshot ? "snapshot" : "copy", orig);
- lxc_container_put(c2);
- }
- lxc_container_put(c1);
-
- exit(EXIT_SUCCESS);
-}
diff --git a/src/lxc/lxc_config.c b/src/lxc/lxc_config.c
deleted file mode 100644
index d146ad8..0000000
--- a/src/lxc/lxc_config.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* lxc_config
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- * 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 <stdio.h>
-#include <string.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "config.h"
-
-struct lxc_config_items {
- char *name;
-};
-
-static struct lxc_config_items items[] =
-{
- { .name = "lxc.default_config", },
- { .name = "lxc.lxcpath", },
- { .name = "lxc.bdev.lvm.vg", },
- { .name = "lxc.bdev.lvm.thin_pool", },
- { .name = "lxc.bdev.zfs.root", },
- { .name = "lxc.cgroup.use", },
- { .name = "lxc.cgroup.pattern", },
- { .name = NULL, },
-};
-
-static void usage(char *me)
-{
- printf("Usage: %s -l: list all available configuration items\n", me);
- printf(" %s item: print configuration item\n", me);
- exit(1);
-}
-
-static void list_config_items(void)
-{
- struct lxc_config_items *i;
-
- for (i = &items[0]; i->name; i++)
- printf("%s\n", i->name);
- exit(0);
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_config_items *i;
- const char *value;
-
- if (argc < 2)
- usage(argv[0]);
- if (strcmp(argv[1], "-l") == 0)
- list_config_items();
- for (i = &items[0]; i->name; i++) {
- if (strcmp(argv[1], i->name) == 0) {
- value = lxc_get_global_config_item(i->name);
- if (value)
- printf("%s\n", value);
- else
- printf("%s is not set.\n", argv[1]);
- exit(0);
- }
- }
- printf("Unknown configuration item: %s\n", argv[1]);
- exit(1);
-}
diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c
deleted file mode 100644
index adbd7e0..0000000
--- a/src/lxc/lxc_console.c
+++ /dev/null
@@ -1,134 +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
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#undef _GNU_SOURCE
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <libgen.h>
-#include <poll.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "error.h"
-#include "lxc.h"
-#include "log.h"
-#include "mainloop.h"
-#include "arguments.h"
-#include "commands.h"
-
-lxc_log_define(lxc_console_ui, lxc);
-
-static char etoc(const char *expr)
-{
- /* returns "control code" of given expression */
- char c = expr[0] == '^' ? expr[1] : expr[0];
- return 1 + ((c > 'Z') ? (c - 'a') : (c - 'Z'));
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 't': args->ttynum = atoi(arg); break;
- case 'e': args->escape = etoc(arg); break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"tty", required_argument, 0, 't'},
- {"escape", required_argument, 0, 'e'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-console",
- .help = "\
---name=NAME [--tty NUMBER]\n\
-\n\
-lxc-console logs on the container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -t, --tty=NUMBER console tty number\n\
- -e, --escape=PREFIX prefix for escape command\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .ttynum = -1,
- .escape = 1,
-};
-
-int main(int argc, char *argv[])
-{
- int ret;
- struct lxc_container *c;
-
- ret = lxc_arguments_parse(&my_args, argc, argv);
- if (ret)
- return EXIT_FAILURE;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]);
- if (ret)
- return EXIT_FAILURE;
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- fprintf(stderr, "System error loading container\n");
- exit(EXIT_FAILURE);
- }
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- if (!c->is_running(c)) {
- fprintf(stderr, "%s is not running\n", my_args.name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- ret = c->console(c, my_args.ttynum, 0, 1, 2, my_args.escape);
- if (ret < 0) {
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
- lxc_container_put(c);
- return EXIT_SUCCESS;
-}
diff --git a/src/lxc/lxc_copy.c b/src/lxc/lxc_copy.c
deleted file mode 100644
index 9f653e3..0000000
--- a/src/lxc/lxc_copy.c
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- *
- * Copyright © 2015 Christian Brauner <christian.brauner at mailbox.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define _GNU_SOURCE
-#include "config.h"
-
-#include <unistd.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <time.h>
-#include <stdbool.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "attach.h"
-#include "bdev.h"
-#include "log.h"
-#include "confile.h"
-#include "arguments.h"
-#include "lxc.h"
-#include "conf.h"
-#include "state.h"
-#include "utils.h"
-
-#ifndef HAVE_GETSUBOPT
-#include <../include/getsubopt.h>
-#endif
-
-lxc_log_define(lxc_copy_ui, lxc);
-
-enum mnttype {
- LXC_MNT_BIND,
- LXC_MNT_AUFS,
- LXC_MNT_OVL,
-};
-
-struct mnts {
- enum mnttype mnt_type;
- char *src;
- char *dest;
- char *options;
- char *upper;
- char *workdir;
- char *lower;
-};
-
-static unsigned int mnt_table_size = 0;
-static struct mnts *mnt_table = NULL;
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg);
-
-static const struct option my_longopts[] = {
- { "newname", required_argument, 0, 'N'},
- { "newpath", required_argument, 0, 'p'},
- { "rename", no_argument, 0, 'R'},
- { "snapshot", no_argument, 0, 's'},
- { "foreground", no_argument, 0, 'F'},
- { "daemon", no_argument, 0, 'd'},
- { "ephemeral", no_argument, 0, 'e'},
- { "mount", required_argument, 0, 'm'},
- { "backingstore", required_argument, 0, 'B'},
- { "fssize", required_argument, 0, 'L'},
- { "keepdata", no_argument, 0, 'D'},
- { "keepname", no_argument, 0, 'K'},
- { "keepmac", no_argument, 0, 'M'},
- { "tmpfs", no_argument, 0, 't'},
- LXC_COMMON_OPTIONS
-};
-
-/* mount keys */
-static char *const keys[] = {
- [LXC_MNT_BIND] = "bind",
- [LXC_MNT_AUFS] = "aufs",
- [LXC_MNT_OVL] = "overlay",
- NULL
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-copy",
- .help = "\n\
---name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\
---name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,aufs,overlay}=/src:/dest] -- hook options\n\
---name=NAME [-P lxcpath] -N newname -R\n\
-\n\
-lxc-copy clone a container\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -N, --newname=NEWNAME NEWNAME for the restored container\n\
- -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
- -R, --rename rename container\n\
- -s, --snapshot create snapshot instead of clone\n\
- -F, --foreground start with current tty attached to /dev/console\n\
- -d, --daemon daemonize the container (default)\n\
- -e, --ephemeral start ephemeral container\n\
- -m, --mount directory to mount into container, either \n\
- {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\
- -B, --backingstorage=TYPE backingstorage type for the container\n\
- -t, --tmpfs place ephemeral container on a tmpfs\n\
- (WARNING: On reboot all changes made to the container will be lost.)\n\
- -L, --fssize size of the new block device for block device containers\n\
- -D, --keedata pass together with -e start a persistent snapshot \n\
- -K, --keepname keep the hostname of the original container\n\
- -- hook options arguments passed to the hook program\n\
- -M, --keepmac keep the MAC address of the original container\n",
- .options = my_longopts,
- .parser = my_parser,
- .task = CLONE,
- .daemonize = 1,
- .quiet = false,
- .tmpfs = false,
-};
-
-static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num,
- enum mnttype type);
-static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num,
- struct lxc_arguments *arg);
-static char *construct_path(char *path, bool as_prefix);
-static char *set_mnt_entry(struct mnts *m);
-static int do_clone(struct lxc_container *c, char *newname, char *newpath,
- int flags, char *bdevtype, uint64_t fssize, enum task task,
- char **args);
-static int do_clone_ephemeral(struct lxc_container *c,
- struct lxc_arguments *arg, char **args,
- int flags);
-static int do_clone_rename(struct lxc_container *c, char *newname);
-static int do_clone_task(struct lxc_container *c, enum task task, int flags,
- char **args);
-static void free_mnts(void);
-static uint64_t get_fssize(char *s);
-
-/* Place an ephemeral container started with -e flag on a tmpfs. Restrictions
- * are that you cannot request the data to be kept while placing the container
- * on a tmpfs and that either overlay or aufs backing storage must be used.
- */
-static char *mount_tmpfs(const char *oldname, const char *newname,
- const char *path, struct lxc_arguments *arg);
-static int parse_mntsubopts(char *subopts, char *const *keys,
- char *mntparameters);
-static int parse_aufs_mnt(char *mntstring, enum mnttype type);
-static int parse_bind_mnt(char *mntstring, enum mnttype type);
-static int parse_ovl_mnt(char *mntstring, enum mnttype type);
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- int flags = 0;
- int ret = EXIT_FAILURE;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(ret);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(ret);
- lxc_log_options_no_override();
-
- if (geteuid()) {
- if (access(my_args.lxcpath[0], O_RDWR) < 0) {
- if (!my_args.quiet)
- fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
- exit(ret);
- }
- }
-
- if (!my_args.newname && !(my_args.task == DESTROY)) {
- if (!my_args.quiet)
- printf("Error: You must provide a NEWNAME for the clone.\n");
- exit(ret);
- }
-
- if (my_args.task == SNAP || my_args.task == DESTROY)
- flags |= LXC_CLONE_SNAPSHOT;
- if (my_args.keepname)
- flags |= LXC_CLONE_KEEPNAME;
- if (my_args.keepmac)
- flags |= LXC_CLONE_KEEPMACADDR;
-
- if (!my_args.newpath)
- my_args.newpath = (char *)my_args.lxcpath[0];
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c)
- exit(ret);
-
- if (!c->may_control(c)) {
- if (!my_args.quiet)
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
- goto out;
- }
-
- if (!c->is_defined(c)) {
- if (!my_args.quiet)
- fprintf(stderr, "Error: container %s is not defined\n", c->name);
- goto out;
- }
-
- ret = do_clone_task(c, my_args.task, flags, &argv[optind]);
-
-out:
- lxc_container_put(c);
-
- if (ret == 0)
- exit(EXIT_SUCCESS);
- exit(EXIT_FAILURE);
-}
-
-static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, enum mnttype type)
-{
- struct mnts *m, *n;
-
- n = realloc(*mnts, (*num + 1) * sizeof(struct mnts));
- if (!n)
- return NULL;
-
- *mnts = n;
- m = *mnts + *num;
- (*num)++;
-
- *m = (struct mnts) {.mnt_type = type};
-
- return m;
-}
-
-static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_arguments *arg)
-{
- char upperdir[MAXPATHLEN];
- char workdir[MAXPATHLEN];
- unsigned int i;
- int ret;
- struct mnts *m = NULL;
-
- for (i = 0, m = mnts; i < num; i++, m++) {
- if ((m->mnt_type == LXC_MNT_OVL) || (m->mnt_type == LXC_MNT_AUFS)) {
- ret = snprintf(upperdir, MAXPATHLEN, "%s/%s/delta#XXXXXX",
- arg->newpath, arg->newname);
- if (ret < 0 || ret >= MAXPATHLEN)
- return -1;
- if (!mkdtemp(upperdir))
- return -1;
- m->upper = strdup(upperdir);
- if (!m->upper)
- return -1;
- }
-
- if (m->mnt_type == LXC_MNT_OVL) {
- ret = snprintf(workdir, MAXPATHLEN, "%s/%s/work#XXXXXX",
- arg->newpath, arg->newname);
- if (ret < 0 || ret >= MAXPATHLEN)
- return -1;
- if (!mkdtemp(workdir))
- return -1;
- m->workdir = strdup(workdir);
- if (!m->workdir)
- return -1;
- }
- }
-
- return 0;
-}
-
-static char *construct_path(char *path, bool as_prefix)
-{
- char **components = NULL;
- char *cleanpath = NULL;
-
- components = lxc_normalize_path(path);
- if (!components)
- return NULL;
-
- cleanpath = lxc_string_join("/", (const char **)components, as_prefix);
- lxc_free_array((void **)components, free);
-
- return cleanpath;
-}
-
-static char *set_mnt_entry(struct mnts *m)
-{
- char *mntentry = NULL;
- int ret = 0;
- size_t len = 0;
-
- if (m->mnt_type == LXC_MNT_AUFS) {
- len = strlen(" aufs br==rw:=ro,xino=,create=dir") +
- 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
- strlen(m->workdir) + 1;
-
- mntentry = malloc(len);
- if (!mntentry)
- goto err;
-
- ret = snprintf(mntentry, len, "%s %s aufs br=%s=rw:%s=ro,xino=%s,create=dir",
- m->src, m->dest, m->upper, m->src, m->workdir);
- if (ret < 0 || (size_t)ret >= len)
- goto err;
- } else if (m->mnt_type == LXC_MNT_OVL) {
- len = strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") +
- 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
- strlen(m->workdir) + 1;
-
- mntentry = malloc(len);
- if (!mntentry)
- goto err;
-
- ret = snprintf(mntentry, len, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
- m->src, m->dest, m->src, m->upper, m->workdir);
- if (ret < 0 || (size_t)ret >= len)
- goto err;
- } else if (m->mnt_type == LXC_MNT_BIND) {
- len = strlen(" none bind,optional,, 0 0") +
- strlen(is_dir(m->src) ? "create=dir" : "create=file") +
- strlen(m->src) + strlen(m->dest) + strlen(m->options) + 1;
-
- mntentry = malloc(len);
- if (!mntentry)
- goto err;
-
- ret = snprintf(mntentry, len, "%s %s none bind,optional,%s,%s 0 0",
- m->src, m->dest, m->options,
- is_dir(m->src) ? "create=dir" : "create=file");
- if (ret < 0 || (size_t)ret >= len)
- goto err;
- }
-
- return mntentry;
-
-err:
- free(mntentry);
- return NULL;
-}
-
-static int do_clone(struct lxc_container *c, char *newname, char *newpath,
- int flags, char *bdevtype, uint64_t fssize, enum task task,
- char **args)
-{
- struct lxc_container *clone;
-
- clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize,
- args);
- if (!clone) {
- if (!my_args.quiet)
- fprintf(stderr, "clone failed\n");
- return -1;
- }
-
- INFO("Created %s as %s of %s\n", newname, task ? "snapshot" : "copy", c->name);
-
- lxc_container_put(clone);
-
- return 0;
-}
-
-static int do_clone_ephemeral(struct lxc_container *c,
- struct lxc_arguments *arg, char **args, int flags)
-{
- char *bdev;
- char *premount;
- char randname[MAXPATHLEN];
- unsigned int i;
- int ret = 0;
- bool bret = true, started = false;
- struct lxc_container *clone;
- lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
- attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
-
- if (!arg->newname) {
- ret = snprintf(randname, MAXPATHLEN, "%s/%s_XXXXXX", arg->newpath, arg->name);
- if (ret < 0 || ret >= MAXPATHLEN)
- return -1;
- if (!mkdtemp(randname))
- return -1;
- if (chmod(randname, 0770) < 0) {
- remove(randname);
- return -1;
- }
- arg->newname = randname + strlen(arg->newpath) + 1;
- }
-
- clone = c->clone(c, arg->newname, arg->newpath, flags,
- arg->bdevtype, NULL, arg->fssize, args);
- if (!clone)
- return -1;
-
- if (arg->tmpfs) {
- bdev = c->lxc_conf->rootfs.bdev_type;
- if (bdev && strcmp(bdev, "dir")) {
- fprintf(stderr, "Cannot currently use tmpfs with %s storage backend.\n", bdev);
- goto destroy_and_put;
- }
-
- premount = mount_tmpfs(arg->name, arg->newname, arg->newpath, arg);
- if (!premount)
- goto destroy_and_put;
-
- bret = clone->set_config_item(clone, "lxc.hook.pre-mount", premount);
- free(premount);
- if (!bret)
- goto destroy_and_put;
- }
-
- if (!arg->keepdata)
- if (!clone->set_config_item(clone, "lxc.ephemeral", "1"))
- goto destroy_and_put;
-
- /* allocate and create random upper- and workdirs for overlay mounts */
- if (mk_rand_ovl_dirs(mnt_table, mnt_table_size, arg) < 0)
- goto destroy_and_put;
-
- /* allocate and set mount entries */
- struct mnts *n = NULL;
- for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) {
- char *mntentry = NULL;
- mntentry = set_mnt_entry(n);
- if (!mntentry)
- goto destroy_and_put;
- bret = clone->set_config_item(clone, "lxc.mount.entry", mntentry);
- free(mntentry);
- if (!bret)
- goto destroy_and_put;
- }
-
- if (!clone->save_config(clone, NULL))
- goto destroy_and_put;
-
- if (!my_args.quiet)
- printf("Created %s as clone of %s\n", arg->newname, arg->name);
-
- if (arg->tmpfs && !my_args.quiet)
- printf("Container is placed on tmpfs.\nRebooting will cause "
- "all changes made to it to be lost!");
-
- if (!arg->daemonize && arg->argc) {
- clone->want_daemonize(clone, true);
- arg->daemonize = 1;
- } else if (!arg->daemonize) {
- clone->want_daemonize(clone, false);
- }
-
- started = clone->start(clone, 0, NULL);
- if (!started)
- goto destroy_and_put;
-
- if (arg->daemonize && arg->argc) {
- ret = clone->attach_run_wait(clone, &attach_options, arg->argv[0], (const char *const *)arg->argv);
- if (ret < 0)
- goto destroy_and_put;
- clone->shutdown(clone, -1);
- }
-
- free_mnts();
- lxc_container_put(clone);
- return 0;
-
-destroy_and_put:
- if (started)
- clone->shutdown(clone, -1);
- if (!started || clone->lxc_conf->ephemeral != 1)
- clone->destroy(clone);
- free_mnts();
- lxc_container_put(clone);
- return -1;
-}
-
-static int do_clone_rename(struct lxc_container *c, char *newname)
-{
- if (!c->rename(c, newname)) {
- ERROR("Error: Renaming container %s to %s failed\n", c->name, newname);
- return -1;
- }
-
- INFO("Renamed container %s to %s\n", c->name, newname);
-
- return 0;
-}
-
-static int do_clone_task(struct lxc_container *c, enum task task, int flags,
- char **args)
-{
- int ret = 0;
-
- switch (task) {
- case DESTROY:
- ret = do_clone_ephemeral(c, &my_args, args, flags);
- break;
- case RENAME:
- ret = do_clone_rename(c, my_args.newname);
- break;
- default:
- ret = do_clone(c, my_args.newname, my_args.newpath, flags,
- my_args.bdevtype, my_args.fssize, my_args.task,
- args);
- break;
- }
-
- return ret;
-}
-
-static void free_mnts()
-{
- unsigned int i;
- struct mnts *n = NULL;
-
- for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) {
- free(n->src);
- free(n->dest);
- free(n->options);
- free(n->upper);
- free(n->workdir);
- }
- free(mnt_table);
- mnt_table = NULL;
- mnt_table_size = 0;
-}
-
-/* we pass fssize in bytes */
-static uint64_t get_fssize(char *s)
-{
- uint64_t ret;
- char *end;
-
- ret = strtoull(s, &end, 0);
- if (end == s) {
- if (!my_args.quiet)
- fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
- return 0;
- }
- while (isblank(*end))
- end++;
- if (*end == '\0') {
- ret *= 1024ULL * 1024ULL; // MB by default
- } else if (*end == 'b' || *end == 'B') {
- ret *= 1ULL;
- } else if (*end == 'k' || *end == 'K') {
- ret *= 1024ULL;
- } else if (*end == 'm' || *end == 'M') {
- ret *= 1024ULL * 1024ULL;
- } else if (*end == 'g' || *end == 'G') {
- ret *= 1024ULL * 1024ULL * 1024ULL;
- } else if (*end == 't' || *end == 'T') {
- ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
- } else {
- if (!my_args.quiet)
- fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end, s);
- return 0;
- }
-
- return ret;
-}
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg)
-{
- char *subopts = NULL;
- char *mntparameters = NULL;
- switch (c) {
- case 'N':
- args->newname = arg;
- break;
- case 'p':
- args->newpath = arg;
- break;
- case 'R':
- args->task = RENAME;
- break;
- case 's':
- args->task = SNAP;
- break;
- case 'F':
- args->daemonize = 0;
- break;
- case 'd':
- args->daemonize = 1;
- break;
- case 'e':
- args->task = DESTROY;
- break;
- case 'm':
- subopts = optarg;
- if (parse_mntsubopts(subopts, keys, mntparameters) < 0)
- return -1;
- break;
- case 'B':
- args->bdevtype = arg;
- break;
- case 't':
- args->tmpfs = true;
- break;
- case 'L':
- args->fssize = get_fssize(optarg);
- break;
- case 'D':
- args->keepdata = 1;
- break;
- case 'K':
- args->keepname = 1;
- break;
- case 'M':
- args->keepmac = 1;
- break;
- }
-
- return 0;
-}
-
-static int parse_aufs_mnt(char *mntstring, enum mnttype type)
-{
- int len = 0;
- const char *xinopath = "/dev/shm/aufs.xino";
- char **mntarray = NULL;
- struct mnts *m = NULL;
-
- m = add_mnt(&mnt_table, &mnt_table_size, type);
- if (!m)
- goto err;
-
- mntarray = lxc_string_split(mntstring, ':');
- if (!mntarray)
- goto err;
-
- m->src = construct_path(mntarray[0], true);
- if (!m->src)
- goto err;
-
- len = lxc_array_len((void **)mntarray);
- if (len == 1) /* aufs=src */
- m->dest = construct_path(mntarray[0], false);
- else if (len == 2) /* aufs=src:dest */
- m->dest = construct_path(mntarray[1], false);
- else
- INFO("Excess elements in mount specification");
-
- if (!m->dest)
- goto err;
-
- m->workdir = strdup(xinopath);
- if (!m->workdir)
- goto err;
-
- lxc_free_array((void **)mntarray, free);
- return 0;
-
-err:
- free_mnts();
- lxc_free_array((void **)mntarray, free);
- return -1;
-}
-
-static int parse_bind_mnt(char *mntstring, enum mnttype type)
-{
- int len = 0;
- char **mntarray = NULL;
- struct mnts *m = NULL;
-
- m = add_mnt(&mnt_table, &mnt_table_size, type);
- if (!m)
- goto err;
-
- mntarray = lxc_string_split(mntstring, ':');
- if (!mntarray)
- goto err;
-
- m->src = construct_path(mntarray[0], true);
- if (!m->src)
- goto err;
-
- len = lxc_array_len((void **)mntarray);
- if (len == 1) { /* bind=src */
- m->dest = construct_path(mntarray[0], false);
- } else if (len == 2) { /* bind=src:option or bind=src:dest */
- if (strncmp(mntarray[1], "rw", strlen(mntarray[1])) == 0)
- m->options = strdup("rw");
-
- if (strncmp(mntarray[1], "ro", strlen(mntarray[1])) == 0)
- m->options = strdup("ro");
-
- if (m->options)
- m->dest = construct_path(mntarray[0], false);
- else
- m->dest = construct_path(mntarray[1], false);
- } else if (len == 3) { /* bind=src:dest:option */
- m->dest = construct_path(mntarray[1], false);
- m->options = strdup(mntarray[2]);
- } else {
- INFO("Excess elements in mount specification");
- }
-
- if (!m->dest)
- goto err;
-
- if (!m->options)
- m->options = strdup("rw");
-
- if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) &&
- strncmp(m->options, "ro", strlen(m->options))))
- goto err;
-
- lxc_free_array((void **)mntarray, free);
- return 0;
-
-err:
- free_mnts();
- lxc_free_array((void **)mntarray, free);
- return -1;
-}
-
-static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters)
-{
- while (*subopts != '\0') {
- switch (getsubopt(&subopts, keys, &mntparameters)) {
- case LXC_MNT_BIND:
- if (parse_bind_mnt(mntparameters, LXC_MNT_BIND) < 0)
- return -1;
- break;
- case LXC_MNT_OVL:
- if (parse_ovl_mnt(mntparameters, LXC_MNT_OVL) < 0)
- return -1;
- break;
- case LXC_MNT_AUFS:
- if (parse_aufs_mnt(mntparameters, LXC_MNT_AUFS) < 0)
- return -1;
- break;
- default:
- break;
- }
- }
- return 0;
-}
-
-static int parse_ovl_mnt(char *mntstring, enum mnttype type)
-{
- int len = 0;
- char **mntarray = NULL;
- struct mnts *m;
-
- m = add_mnt(&mnt_table, &mnt_table_size, type);
- if (!m)
- goto err;
-
- mntarray = lxc_string_split(mntstring, ':');
- if (!mntarray)
- goto err;
-
- m->src = construct_path(mntarray[0], true);
- if (!m->src)
- goto err;
-
- len = lxc_array_len((void **)mntarray);
- if (len == 1) /* overlay=src */
- m->dest = construct_path(mntarray[0], false);
- else if (len == 2) /* overlay=src:dest */
- m->dest = construct_path(mntarray[1], false);
- else
- INFO("Excess elements in mount specification");
-
- if (!m->dest)
- goto err;
-
- lxc_free_array((void **)mntarray, free);
- return 0;
-
-err:
- free_mnts();
- lxc_free_array((void **)mntarray, free);
- return -1;
-}
-
-/* For ephemeral snapshots backed by overlay or aufs filesystems, this function
- * mounts a fresh tmpfs over the containers directory if the user requests it.
- * Because we mount a fresh tmpfs over the directory of the container the
- * updated /etc/hostname file created during the clone residing in the upperdir
- * (currently named "delta0" by default) will be hidden. Hence, if the user
- * requests that the old name is not to be kept for the clone, we recreate this
- * file on the tmpfs. This should be all that is required to restore the exact
- * behaviour we would get with a normal clone.
- */
-static char *mount_tmpfs(const char *oldname, const char *newname,
- const char *path, struct lxc_arguments *arg)
-{
- int ret, fd;
- size_t len;
- char *premount = NULL;
- FILE *fp;
-
- if (arg->tmpfs && arg->keepdata) {
- fprintf(stderr, "%s\n", "A container can only be placed on a "
- "tmpfs when storage backend is overlay "
- "or aufs.");
- goto err_free;
- }
-
- if (arg->tmpfs && !arg->bdevtype) {
- arg->bdevtype = "overlayfs";
- } else if (arg->tmpfs && arg->bdevtype && strcmp(arg->bdevtype, "overlayfs") && strcmp(arg->bdevtype, "aufs")) {
- fprintf(stderr, "%s\n", "A container can only be placed on a "
- "tmpfs when storage backend is overlay "
- "or aufs.");
- goto err_free;
- }
-
- len = strlen(path) + strlen(newname) + strlen("pre-start-XXXXXX") + /* //\0 */ 3;
- premount = malloc(len);
- if (!premount)
- goto err_free;
-
- ret = snprintf(premount, len, "%s/%s/pre-start-XXXXXX", path, newname);
- if (ret < 0 || (size_t)ret >= len)
- goto err_free;
-
- fd = mkstemp(premount);
- if (fd < 0)
- goto err_free;
-
- if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
- SYSERROR("Failed to set close-on-exec on file descriptor.");
- goto err_close;
- }
-
- if (chmod(premount, 0755) < 0)
- goto err_close;
-
- fp = fdopen(fd, "r+");
- if (!fp)
- goto err_close;
- fd = -1;
-
- ret = fprintf(fp, "#! /bin/sh\n"
- "mount -n -t tmpfs -o mode=0755 none %s/%s\n",
- path, newname);
- if (ret < 0)
- goto err_close;
-
- if (!arg->keepname) {
- ret = fprintf(fp, "mkdir -p %s/%s/delta0/etc\n"
- "echo %s > %s/%s/delta0/etc/hostname\n",
- path, newname, newname, path, newname);
- if (ret < 0)
- goto err_close;
- }
-
- close(fd);
- return premount;
-
-err_close:
- if (fd > 0)
- close(fd);
- else
- fclose(fp);
-err_free:
- free(premount);
- return NULL;
-}
diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c
deleted file mode 100644
index 4b0e9d7..0000000
--- a/src/lxc/lxc_create.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- *
- * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2013 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <lxc/lxccontainer.h>
-#include <sys/types.h>
-
-#include "arguments.h"
-#include "bdev.h"
-#include "log.h"
-#include "lxc.h"
-#include "utils.h"
-
-lxc_log_define(lxc_create_ui, lxc);
-
-static uint64_t get_fssize(char *s)
-{
- uint64_t ret;
- char *end;
-
- ret = strtoull(s, &end, 0);
- if (end == s)
- {
- fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
- return 0;
- }
- while (isblank(*end))
- end++;
- if (*end == '\0')
- ret *= 1024ULL * 1024ULL; // MB by default
- else if (*end == 'b' || *end == 'B')
- ret *= 1ULL;
- else if (*end == 'k' || *end == 'K')
- ret *= 1024ULL;
- else if (*end == 'm' || *end == 'M')
- ret *= 1024ULL * 1024ULL;
- else if (*end == 'g' || *end == 'G')
- ret *= 1024ULL * 1024ULL * 1024ULL;
- else if (*end == 't' || *end == 'T')
- ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
- else
- {
- fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s);
- return 0;
- }
- return ret;
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'B': args->bdevtype = arg; break;
- case 'f': args->configfile = arg; break;
- case 't': args->template = arg; break;
- case '0': args->lvname = arg; break;
- case '1': args->vgname = arg; break;
- case '2': args->thinpool = arg; break;
- case '3': args->fstype = arg; break;
- case '4': args->fssize = get_fssize(arg); break;
- case '5': args->zfsroot = arg; break;
- case '6': args->dir = arg; break;
- case '7': args->rbdname = arg; break;
- case '8': args->rbdpool = arg; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"bdev", required_argument, 0, 'B'},
- {"config", required_argument, 0, 'f'},
- {"template", required_argument, 0, 't'},
- {"lvname", required_argument, 0, '0'},
- {"vgname", required_argument, 0, '1'},
- {"thinpool", required_argument, 0, '2'},
- {"fstype", required_argument, 0, '3'},
- {"fssize", required_argument, 0, '4'},
- {"zfsroot", required_argument, 0, '5'},
- {"dir", required_argument, 0, '6'},
- {"rbdname", required_argument, 0, '7'},
- {"rbdpool", required_argument, 0, '8'},
- LXC_COMMON_OPTIONS
-};
-
-static void create_helpfn(const struct lxc_arguments *args)
-{
- char *argv[3], *path;
- pid_t pid;
-
- if (!args->template)
- return;
-
- pid = fork();
- if (pid) {
- wait_for_pid(pid);
- return;
- }
-
- path = get_template_path(args->template);
-
- argv[0] = path;
- argv[1] = "-h";
- argv[2] = NULL;
-
- execv(path, argv);
- ERROR("Error executing %s -h", path);
- exit(EXIT_FAILURE);
-}
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-create",
- .helpfn = create_helpfn,
- .help = "\
---name=NAME --template=TEMPLATE [OPTION...]\n\
-\n\
-lxc-create creates a container\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -f, --config=CONFIG Initial configuration file\n\
- -t, --template=TEMPLATE Template to use to setup container\n\
- -B, --bdev=BDEV Backing store type to use\n\
- --dir=DIR Place rootfs directory under DIR\n\
-\n\
- BDEV options for LVM (with -B/--bdev lvm):\n\
- --lvname=LVNAME Use LVM lv name LVNAME\n\
- (Default: container name)\n\
- --vgname=VG Use LVM vg called VG\n\
- (Default: lxc)\n\
- --thinpool=TP Use LVM thin pool called TP\n\
- (Default: lxc)\n\
-\n\
- BDEV options for Ceph RBD (with -B/--bdev rbd) :\n\
- --rbdname=RBDNAME Use Ceph RBD name RBDNAME\n\
- (Default: container name)\n\
- --rbdpool=POOL Use Ceph RBD pool name POOL\n\
- (Default: lxc)\n\
-\n\
- BDEV option for ZFS (with -B/--bdev zfs) :\n\
- --zfsroot=PATH Create zfs under given zfsroot\n\
- (Default: tank/lxc)\n\
-\n\
- BDEV options for LVM or Loop (with -B/--bdev lvm/loop) :\n\
- --fstype=TYPE Create fstype TYPE\n\
- (Default: ext3)\n\
- --fssize=SIZE[U] Create filesystem of\n\
- size SIZE * unit U (bBkKmMgGtT)\n\
- (Default: 1G, default unit: M)\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
-};
-
-static bool validate_bdev_args(struct lxc_arguments *a)
-{
- if (strcmp(a->bdevtype, "best") != 0) {
- if (a->fstype || a->fssize) {
- if (strcmp(a->bdevtype, "lvm") != 0 &&
- strcmp(a->bdevtype, "loop") != 0 &&
- strcmp(a->bdevtype, "rbd") != 0) {
- fprintf(stderr, "filesystem type and size are only valid with block devices\n");
- return false;
- }
- }
- if (strcmp(a->bdevtype, "lvm") != 0) {
- if (a->lvname || a->vgname || a->thinpool) {
- fprintf(stderr, "--lvname, --vgname and --thinpool are only valid with -B lvm\n");
- return false;
- }
- }
- if (strcmp(a->bdevtype, "rbd") != 0) {
- if (a->rbdname || a->rbdpool) {
- fprintf(stderr, "--rbdname and --rbdpool are only valid with -B rbd\n");
- return false;
- }
- }
- if (strcmp(a->bdevtype, "zfs") != 0) {
- if (a->zfsroot) {
- fprintf(stderr, "zfsroot is only valid with -B zfs\n");
- return false;
- }
- }
- }
- return true;
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- struct bdev_specs spec;
- int flags = 0;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(EXIT_FAILURE);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
-
- if (!my_args.template) {
- fprintf(stderr, "A template must be specified.\n");
- fprintf(stderr, "Use \"none\" if you really want a container without a rootfs.\n");
- exit(EXIT_FAILURE);
- }
-
- if (strcmp(my_args.template, "none") == 0)
- my_args.template = NULL;
-
- memset(&spec, 0, sizeof(spec));
- if (!my_args.bdevtype)
- my_args.bdevtype = "_unset";
-
- if (!validate_bdev_args(&my_args))
- exit(EXIT_FAILURE);
-
- if (strcmp(my_args.bdevtype, "none") == 0)
- my_args.bdevtype = "dir";
-
- // Final check whether the user gave use a valid bdev type.
- if (strcmp(my_args.bdevtype, "best") &&
- strcmp(my_args.bdevtype, "_unset") &&
- !is_valid_bdev_type(my_args.bdevtype)) {
- fprintf(stderr, "%s is not a valid backing storage type.\n", my_args.bdevtype);
- exit(EXIT_FAILURE);
- }
-
- if (geteuid()) {
- if (mkdir_p(my_args.lxcpath[0], 0755)) {
- exit(EXIT_FAILURE);
- }
- if (access(my_args.lxcpath[0], O_RDWR) < 0) {
- fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
- exit(EXIT_FAILURE);
- }
- if (strcmp(my_args.bdevtype, "dir") && strcmp(my_args.bdevtype, "_unset") &&
- strcmp(my_args.bdevtype, "btrfs")) {
- fprintf(stderr, "Unprivileged users cannot create %s containers", my_args.bdevtype);
- exit(EXIT_FAILURE);
- }
- }
-
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- fprintf(stderr, "Failed to create lxc container.\n");
- exit(EXIT_FAILURE);
- }
- if (c->is_defined(c)) {
- lxc_container_put(c);
- fprintf(stderr, "Container already exists\n");
- exit(EXIT_FAILURE);
- }
- if (my_args.configfile)
- c->load_config(c, my_args.configfile);
- else
- c->load_config(c, lxc_global_config_value("lxc.default_config"));
-
- if (my_args.fstype)
- spec.fstype = my_args.fstype;
- if (my_args.fssize)
- spec.fssize = my_args.fssize;
-
- if ((strcmp(my_args.bdevtype, "zfs") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
- if (my_args.zfsroot)
- spec.zfs.zfsroot = my_args.zfsroot;
- }
-
- if ((strcmp(my_args.bdevtype, "lvm") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
- if (my_args.lvname)
- spec.lvm.lv = my_args.lvname;
- if (my_args.vgname)
- spec.lvm.vg = my_args.vgname;
- if (my_args.thinpool)
- spec.lvm.thinpool = my_args.thinpool;
- }
-
- if ((strcmp(my_args.bdevtype, "rbd") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
- if (my_args.rbdname)
- spec.rbd.rbdname = my_args.rbdname;
- if (my_args.rbdpool)
- spec.rbd.rbdpool = my_args.rbdpool;
- }
-
- if (my_args.dir)
- spec.dir = my_args.dir;
-
- if (strcmp(my_args.bdevtype, "_unset") == 0)
- my_args.bdevtype = NULL;
-
- if (my_args.quiet)
- flags = LXC_CREATE_QUIET;
-
- if (!c->create(c, my_args.template, my_args.bdevtype, &spec, flags, &argv[optind])) {
- ERROR("Error creating container %s", c->name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- lxc_container_put(c);
- INFO("container %s created", c->name);
- exit(EXIT_SUCCESS);
-}
diff --git a/src/lxc/lxc_destroy.c b/src/lxc/lxc_destroy.c
deleted file mode 100644
index b521739..0000000
--- a/src/lxc/lxc_destroy.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- *
- * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2013 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define _GNU_SOURCE
-#include "config.h"
-
-#include <libgen.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "arguments.h"
-#include "log.h"
-#include "lxc.h"
-#include "utils.h"
-
-lxc_log_define(lxc_destroy_ui, lxc);
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg);
-static bool quiet;
-
-static const struct option my_longopts[] = {
- {"force", no_argument, 0, 'f'},
- {"snapshots", no_argument, 0, 's'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-destroy",
- .help = "\
---name=NAME [-f] [-P lxcpath]\n\
-\n\
-lxc-destroy destroys a container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -s, --snapshots destroy including all snapshots\n\
- -f, --force wait for the container to shut down\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .task = DESTROY,
-};
-
-static bool do_destroy(struct lxc_container *c);
-static bool do_destroy_with_snapshots(struct lxc_container *c);
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- bool bret;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(EXIT_FAILURE);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
- if (my_args.quiet)
- quiet = true;
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- if (!quiet)
- fprintf(stderr, "System error loading container\n");
- exit(EXIT_FAILURE);
- }
-
- if (!c->may_control(c)) {
- if (!quiet)
- fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- if (!c->is_defined(c)) {
- if (!quiet)
- fprintf(stderr, "Container is not defined\n");
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- if (my_args.task == SNAP) {
- bret = do_destroy_with_snapshots(c);
- if (bret && !quiet)
- printf("Destroyed container %s including snapshots \n", my_args.name);
- } else {
- bret = do_destroy(c);
- if (bret && !quiet)
- printf("Destroyed container %s\n", my_args.name);
- }
-
- lxc_container_put(c);
-
- if (bret)
- exit(EXIT_SUCCESS);
- exit(EXIT_FAILURE);
-}
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg)
-{
- switch (c) {
- case 'f': args->force = 1; break;
- case 's': args->task = SNAP; break;
- }
- return 0;
-}
-
-static bool do_destroy(struct lxc_container *c)
-{
- bool bret = true;
- char path[MAXPATHLEN];
-
- /* First check whether the container has dependent clones or snapshots. */
- int ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name);
- if (ret < 0 || ret >= MAXPATHLEN)
- return false;
-
- if (file_exists(path)) {
- if (!quiet)
- fprintf(stdout, "Destroying %s failed: %s has clones.\n", c->name, c->name);
- return false;
- }
-
- ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
- if (ret < 0 || ret >= MAXPATHLEN)
- return false;
-
- if (dir_exists(path)) {
- if (!quiet)
- fprintf(stdout, "Destroying %s failed: %s has snapshots.\n", c->name, c->name);
- return false;
- }
-
- if (c->is_running(c)) {
- if (!my_args.force && !quiet) {
- fprintf(stderr, "%s is running\n", my_args.name);
- return false;
- }
- /* If the container was ephemeral it will be removed on shutdown. */
- c->stop(c);
- }
-
- /* If the container was ephemeral we have already removed it when we
- * stopped it. */
- if (c->is_defined(c) && !c->lxc_conf->ephemeral)
- bret = c->destroy(c);
-
- if (!bret) {
- if (!quiet)
- fprintf(stderr, "Destroying %s failed\n", my_args.name);
- return false;
- }
-
- return true;
-}
-
-static bool do_destroy_with_snapshots(struct lxc_container *c)
-{
- struct lxc_container *c1;
- struct stat fbuf;
- bool bret = false;
- char path[MAXPATHLEN];
- char *buf = NULL;
- char *lxcpath = NULL;
- char *lxcname = NULL;
- char *scratch = NULL;
- int fd;
- int ret;
- int counter = 0;
-
- /* Destroy clones. */
- ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name);
- if (ret < 0 || ret >= MAXPATHLEN)
- return false;
-
- fd = open(path, O_RDONLY | O_CLOEXEC);
- if (fd >= 0) {
- ret = fstat(fd, &fbuf);
- if (ret < 0) {
- close(fd);
- return false;
- }
-
- /* Make sure that the string is \0 terminated. */
- buf = calloc(fbuf.st_size + 1, sizeof(char));
- if (!buf) {
- SYSERROR("failed to allocate memory");
- close(fd);
- return false;
- }
-
- ret = read(fd, buf, fbuf.st_size);
- if (ret < 0) {
- ERROR("could not read %s", path);
- close(fd);
- free(buf);
- return false;
- }
- close(fd);
-
- while ((lxcpath = strtok_r(!counter ? buf : NULL, "\n", &scratch))) {
- if (!(lxcname = strtok_r(NULL, "\n", &scratch)))
- break;
- c1 = lxc_container_new(lxcname, lxcpath);
- if (!c1) {
- counter++;
- continue;
- }
- /* We do not destroy recursively. If a clone of a clone
- * has clones or snapshots the user should remove it
- * explicitly. */
- if (!do_destroy(c1)) {
- lxc_container_put(c1);
- free(buf);
- return false;
- }
- lxc_container_put(c1);
- counter++;
- }
- free(buf);
- }
-
- /* Destroy snapshots located in the containers snap/ folder. */
- ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
- if (ret < 0 || ret >= MAXPATHLEN)
- return false;
-
- if (dir_exists(path))
- bret = c->destroy_with_snapshots(c);
- else
- bret = do_destroy(c);
-
- return bret;
-}
-
diff --git a/src/lxc/lxc_device.c b/src/lxc/lxc_device.c
deleted file mode 100644
index 0c9e066..0000000
--- a/src/lxc/lxc_device.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * lxc: linux Container library
- *
- * Authors:
- * Dongsheng Yang <yangds.fnst at cn.fujitsu.com>
- *
- * 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 <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <libgen.h>
-#include <string.h>
-#include <limits.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "utils.h"
-#include "lxc.h"
-#include "log.h"
-
-#include "arguments.h"
-
-#if HAVE_IFADDRS_H
-#include <ifaddrs.h>
-#else
-#include <../include/ifaddrs.h>
-#endif
-
-lxc_log_define(lxc_device, lxc);
-
-static const struct option my_longopts[] = {
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-device",
- .help = "\
---name=NAME -- add|del DEV\n\
-\n\
-lxc-device attach or detach DEV to or from container.\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container",
- .options = my_longopts,
- .parser = NULL,
- .checker = NULL,
-};
-
-static bool is_interface(const char* dev_name, pid_t pid)
-{
- pid_t p = fork();
-
- if (p < 0) {
- SYSERROR("failed to fork task.");
- exit(1);
- }
-
- if (p == 0) {
- struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
-
- if (!switch_to_ns(pid, "net")) {
- ERROR("failed to enter netns of container.");
- exit(-1);
- }
-
- /* Grab the list of interfaces */
- if (getifaddrs(&interfaceArray)) {
- ERROR("failed to get interfaces list");
- exit(-1);
- }
-
- /* Iterate through the interfaces */
- for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
- if (strcmp(tempIfAddr->ifa_name, dev_name) == 0) {
- exit(0);
- }
- }
- exit(1);
- }
-
- if (wait_for_pid(p) == 0) {
- return true;
- }
- return false;
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- char *cmd, *dev_name, *dst_name;
- int ret = 1;
-
- if (geteuid() != 0) {
- ERROR("%s must be run as root", argv[0]);
- exit(1);
- }
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- goto err;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- goto err;
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- ERROR("%s doesn't exist", my_args.name);
- goto err;
- }
-
- if (!c->is_running(c)) {
- ERROR("Container %s is not running.", c->name);
- goto err1;
- }
-
- if (my_args.argc < 2) {
- ERROR("Error: no command given (Please see --help output)");
- goto err1;
- }
-
- cmd = my_args.argv[0];
- dev_name = my_args.argv[1];
- if (my_args.argc < 3)
- dst_name = dev_name;
- else
- dst_name = my_args.argv[2];
-
- if (strcmp(cmd, "add") == 0) {
- if (is_interface(dev_name, 1)) {
- ret = c->attach_interface(c, dev_name, dst_name);
- } else {
- ret = c->add_device_node(c, dev_name, dst_name);
- }
- if (ret != true) {
- ERROR("Failed to add %s to %s.", dev_name, c->name);
- ret = 1;
- goto err1;
- }
- INFO("Add %s to %s.", dev_name, c->name);
- } else if (strcmp(cmd, "del") == 0) {
- if (is_interface(dev_name, c->init_pid(c))) {
- ret = c->detach_interface(c, dev_name, dst_name);
- } else {
- ret = c->remove_device_node(c, dev_name, dst_name);
- }
- if (ret != true) {
- ERROR("Failed to del %s from %s.", dev_name, c->name);
- ret = 1;
- goto err1;
- }
- INFO("Delete %s from %s.", dev_name, c->name);
- } else {
- ERROR("Error: Please use add or del (Please see --help output)");
- goto err1;
- }
- exit(0);
-err1:
- lxc_container_put(c);
-err:
- exit(ret);
-}
diff --git a/src/lxc/lxc_execute.c b/src/lxc/lxc_execute.c
deleted file mode 100644
index 50d481f..0000000
--- a/src/lxc/lxc_execute.c
+++ /dev/null
@@ -1,161 +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
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <libgen.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-
-#include "caps.h"
-#include "lxc.h"
-#include "log.h"
-#include "conf.h"
-#include "confile.h"
-#include "arguments.h"
-#include "config.h"
-#include "start.h"
-#include "utils.h"
-
-lxc_log_define(lxc_execute_ui, lxc);
-
-static struct lxc_list defines;
-
-static int my_checker(const struct lxc_arguments* args)
-{
- if (!args->argc) {
- lxc_error(args, "missing command to execute !");
- return -1;
- }
-
- return 0;
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'f': args->rcfile = arg; break;
- case 's': return lxc_config_define_add(&defines, arg); break;
- case 'u': args->uid = atoi(arg); break;
- case 'g': args->gid = atoi(arg);
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"rcfile", required_argument, 0, 'f'},
- {"define", required_argument, 0, 's'},
- {"uid", required_argument, 0, 'u'},
- {"gid", required_argument, 0, 'g'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-execute",
- .help = "\
---name=NAME -- COMMAND\n\
-\n\
-lxc-execute creates a container with the identifier NAME\n\
-and execs COMMAND into this container.\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -f, --rcfile=FILE Load configuration file FILE\n\
- -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\
- -u, --uid=UID Execute COMMAND with UID inside the container\n\
- -g, --gid=GID Execute COMMAND with GID inside the container\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = my_checker,
-};
-
-int main(int argc, char *argv[])
-{
- char *rcfile;
- struct lxc_conf *conf;
- int ret;
-
- lxc_list_init(&defines);
-
- if (lxc_caps_init())
- return 1;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- /* rcfile is specified in the cli option */
- if (my_args.rcfile)
- rcfile = (char *)my_args.rcfile;
- else {
- int rc;
-
- rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name);
- if (rc == -1) {
- SYSERROR("failed to allocate memory");
- return 1;
- }
-
- /* container configuration does not exist */
- if (access(rcfile, F_OK)) {
- free(rcfile);
- rcfile = NULL;
- }
- }
-
- conf = lxc_conf_init();
- if (!conf) {
- ERROR("failed to initialize configuration");
- return 1;
- }
-
- if (rcfile && lxc_config_read(rcfile, conf, NULL)) {
- ERROR("failed to read configuration file");
- return 1;
- }
-
- if (lxc_config_define_load(&defines, conf))
- return 1;
-
- if (my_args.uid)
- conf->init_uid = my_args.uid;
-
- if (my_args.gid)
- conf->init_gid = my_args.gid;
-
- ret = lxc_execute(my_args.name, my_args.argv, my_args.quiet, conf, my_args.lxcpath[0], false);
-
- lxc_conf_free(conf);
-
- if (ret < 0)
- return 1;
- return ret;
-}
diff --git a/src/lxc/lxc_freeze.c b/src/lxc/lxc_freeze.c
deleted file mode 100644
index ea8bd3e..0000000
--- a/src/lxc/lxc_freeze.c
+++ /dev/null
@@ -1,92 +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 <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <libgen.h>
-#include <string.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-
-#include "arguments.h"
-
-lxc_log_define(lxc_freeze_ui, lxc);
-
-static const struct option my_longopts[] = {
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-freeze",
- .help = "\
---name=NAME\n\
-\n\
-lxc-freeze freezes a container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container",
- .options = my_longopts,
- .parser = NULL,
- .checker = NULL,
-};
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(1);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(1);
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name);
- exit(1);
- }
-
- if (!c->may_control(c)) {
- ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
- if (!c->freeze(c)) {
- ERROR("Failed to freeze %s:%s", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
- lxc_container_put(c);
-
- exit(0);
-}
diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c
deleted file mode 100644
index 58ff619..0000000
--- a/src/lxc/lxc_info.c
+++ /dev/null
@@ -1,397 +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 <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <libgen.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "utils.h"
-#include "commands.h"
-#include "arguments.h"
-
-lxc_log_define(lxc_info_ui, lxc);
-
-static bool ips;
-static bool state;
-static bool pid;
-static bool stats;
-static bool humanize = true;
-static char **key = NULL;
-static int keys = 0;
-static int filter_count = 0;
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- char **newk;
- switch (c) {
- case 'c':
- newk = realloc(key, (keys + 1) * sizeof(key[0]));
- if (!newk)
- return -1;
- key = newk;
- key[keys] = arg;
- keys++;
- break;
- case 'i': ips = true; filter_count += 1; break;
- case 's': state = true; filter_count += 1; break;
- case 'p': pid = true; filter_count += 1; break;
- case 'S': stats = true; filter_count += 5; break;
- case 'H': humanize = false; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"config", required_argument, 0, 'c'},
- {"ips", no_argument, 0, 'i'},
- {"state", no_argument, 0, 's'},
- {"pid", no_argument, 0, 'p'},
- {"stats", no_argument, 0, 'S'},
- {"no-humanize", no_argument, 0, 'H'},
- LXC_COMMON_OPTIONS,
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-info",
- .help = "\
---name=NAME\n\
-\n\
-lxc-info display some information about a container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -c, --config=KEY show configuration variable KEY from running container\n\
- -i, --ips shows the IP addresses\n\
- -p, --pid shows the process id of the init container\n\
- -S, --stats shows usage stats\n\
- -H, --no-humanize shows stats as raw numbers, not humanized\n\
- -s, --state shows the state of the container\n",
- .name = NULL,
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
-};
-
-static void str_chomp(char *buf)
-{
- char *ch;
-
- /* remove trailing whitespace from buf */
- for(ch = &buf[strlen(buf)-1];
- ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' ');
- ch--)
- *ch = '\0';
-}
-
-static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
-{
- if (val > 1 << 30) {
- snprintf(buf, bufsz, "%u.%2.2u GiB",
- (int)(val >> 30),
- (int)(val & ((1 << 30) - 1)) / 10737419);
- } else if (val > 1 << 20) {
- int x = val + 5243; /* for rounding */
- snprintf(buf, bufsz, "%u.%2.2u MiB",
- x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
- } else if (val > 1 << 10) {
- int x = val + 5; /* for rounding */
- snprintf(buf, bufsz, "%u.%2.2u KiB",
- x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
- } else {
- snprintf(buf, bufsz, "%u bytes", (int)val);
- }
-}
-
-static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
-{
- unsigned long long val;
- char *end = NULL;
-
- val = strtoull(iobuf, &end, 0);
- if (humanize) {
- if (*end == '\0' || *end == '\n')
- size_humanize(val, iobuf, iobufsz);
- else
- *iobuf = '\0';
- }
- return val;
-}
-
-static void print_net_stats(struct lxc_container *c)
-{
- int rc,netnr;
- unsigned long long rx_bytes = 0, tx_bytes = 0;
- char *ifname, *type;
- char path[PATH_MAX];
- char buf[256];
-
- for(netnr = 0; ;netnr++) {
- sprintf(buf, "lxc.network.%d.type", netnr);
- type = c->get_running_config_item(c, buf);
- if (!type)
- break;
-
- if (!strcmp(type, "veth")) {
- sprintf(buf, "lxc.network.%d.veth.pair", netnr);
- } else {
- sprintf(buf, "lxc.network.%d.link", netnr);
- }
- free(type);
- ifname = c->get_running_config_item(c, buf);
- if (!ifname)
- return;
- printf("%-15s %s\n", "Link:", ifname);
- fflush(stdout);
-
- /* XXX: tx and rx are reversed from the host vs container
- * perspective, print them from the container perspective
- */
- snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname);
- rc = lxc_read_from_file(path, buf, sizeof(buf));
- if (rc > 0) {
- str_chomp(buf);
- rx_bytes = str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", " TX bytes:", buf);
- fflush(stdout);
- }
-
- snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname);
- rc = lxc_read_from_file(path, buf, sizeof(buf));
- if (rc > 0) {
- str_chomp(buf);
- tx_bytes = str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", " RX bytes:", buf);
- fflush(stdout);
- }
-
- sprintf(buf, "%llu", rx_bytes + tx_bytes);
- str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", " Total bytes:", buf);
- fflush(stdout);
- free(ifname);
- }
-}
-
-static void print_stats(struct lxc_container *c)
-{
- int i, ret;
- char buf[256];
-
- ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
- if (ret > 0 && ret < sizeof(buf)) {
- str_chomp(buf);
- if (humanize) {
- float seconds = strtof(buf, NULL) / 1000000000.0;
- printf("%-15s %.2f seconds\n", "CPU use:", seconds);
- } else {
- printf("%-15s %s\n", "CPU use:", buf);
- }
- fflush(stdout);
- }
-
- ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
- if (ret > 0 && ret < sizeof(buf)) {
- char *ch;
-
- /* put ch on last "Total" line */
- str_chomp(buf);
- for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--)
- ;
- if (*ch == '\n')
- ch++;
-
- if (strncmp(ch, "Total", 5) == 0) {
- ch += 6;
- memmove(buf, ch, strlen(ch)+1);
- str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", "BlkIO use:", buf);
- }
- fflush(stdout);
- }
-
- static const struct {
- const char *name;
- const char *file;
- } lxstat[] = {
- { "Memory use:", "memory.usage_in_bytes" },
- { "KMem use:", "memory.kmem.usage_in_bytes" },
- { NULL, NULL },
- };
-
- for (i = 0; lxstat[i].name; i++) {
- ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
- if (ret > 0 && ret < sizeof(buf)) {
- str_chomp(buf);
- str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", lxstat[i].name, buf);
- fflush(stdout);
- }
- }
-}
-
-static void print_info_msg_int(const char *key, int value)
-{
- if (humanize)
- printf("%-15s %d\n", key, value);
- else {
- if (filter_count == 1)
- printf("%d\n", value);
- else
- printf("%-15s %d\n", key, value);
- }
- fflush(stdout);
-}
-
-static void print_info_msg_str(const char *key, const char *value)
-{
- if (humanize)
- printf("%-15s %s\n", key, value);
- else {
- if (filter_count == 1)
- printf("%s\n", value);
- else
- printf("%-15s %s\n", key, value);
- }
- fflush(stdout);
-}
-
-static int print_info(const char *name, const char *lxcpath)
-{
- int i;
- struct lxc_container *c;
-
- c = lxc_container_new(name, lxcpath);
- if (!c) {
- fprintf(stderr, "Failure to retrieve information on %s:%s\n", lxcpath ? lxcpath : "null",
- name ? name : "null");
- return -1;
- }
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
- lxc_container_put(c);
- return -1;
- }
-
- if (!c->is_running(c) && !c->is_defined(c)) {
- fprintf(stderr, "%s doesn't exist\n", c->name);
- lxc_container_put(c);
- return -1;
- }
-
- if (!state && !pid && !ips && !stats && keys <= 0) {
- state = pid = ips = stats = true;
- print_info_msg_str("Name:", c->name);
- }
-
- if (state) {
- print_info_msg_str("State:", c->state(c));
- }
-
- if (c->is_running(c)) {
- if (pid) {
- pid_t initpid;
-
- initpid = c->init_pid(c);
- if (initpid >= 0)
- print_info_msg_int("PID:", initpid);
- }
-
- if (ips) {
- fflush(stdout);
- char **addresses = c->get_ips(c, NULL, NULL, 0);
- if (addresses) {
- char *address;
- i = 0;
- while (addresses[i]) {
- address = addresses[i];
- print_info_msg_str("IP:", address);
- i++;
- }
- }
- }
- }
-
- if (stats) {
- print_stats(c);
- print_net_stats(c);
- }
-
- for(i = 0; i < keys; i++) {
- int len = c->get_config_item(c, key[i], NULL, 0);
-
- if (len > 0) {
- char *val = (char*) malloc(sizeof(char)*len + 1);
-
- if (c->get_config_item(c, key[i], val, len + 1) != len) {
- fprintf(stderr, "unable to read %s from configuration\n", key[i]);
- } else {
- if (!humanize && keys == 1)
- printf("%s\n", val);
- else
- printf("%s = %s\n", key[i], val);
- }
- free(val);
- } else if (len == 0) {
- if (!humanize && keys == 1)
- printf("\n");
- else
- printf("%s =\n", key[i]);
- } else {
- fprintf(stderr, "%s invalid\n", key[i]);
- }
- fflush(stdout);
- }
-
- lxc_container_put(c);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- int ret = EXIT_FAILURE;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return ret;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return ret;
- lxc_log_options_no_override();
-
- if (print_info(my_args.name, my_args.lxcpath[0]) == 0)
- ret = EXIT_SUCCESS;
-
- return ret;
-}
diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c
deleted file mode 100644
index 5dd29af..0000000
--- a/src/lxc/lxc_init.c
+++ /dev/null
@@ -1,261 +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 <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <signal.h>
-#include <libgen.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include "log.h"
-#include "caps.h"
-#include "error.h"
-#include "initutils.h"
-
-lxc_log_define(lxc_init, lxc);
-
-static int quiet;
-
-static const struct option options[] = {
- { "name", required_argument, NULL, 'n' },
- { "logpriority", required_argument, NULL, 'l' },
- { "quiet", no_argument, NULL, 'q' },
- { "lxcpath", required_argument, NULL, 'P' },
- { 0, 0, 0, 0 },
-};
-
-static sig_atomic_t was_interrupted = 0;
-
-static void interrupt_handler(int sig)
-{
- if (!was_interrupted)
- was_interrupted = sig;
-}
-
-static void usage(void) {
- fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n"
- "Common options :\n"
- " -n, --name=NAME NAME of the container\n"
- " -l, --logpriority=LEVEL Set log priority to LEVEL\n"
- " -q, --quiet Don't produce any output\n"
- " -P, --lxcpath=PATH Use specified container path\n"
- " -?, --help Give this help list\n"
- "\n"
- "Mandatory or optional arguments to long options are also mandatory or optional\n"
- "for any corresponding short options.\n"
- "\n"
- "NOTE: lxc-init is intended for use by lxc internally\n"
- " and does not need to be run by hand\n\n");
-}
-
-int main(int argc, char *argv[])
-{
- pid_t pid;
- int err;
- char **aargv;
- sigset_t mask, omask;
- int i, have_status = 0, shutdown = 0;
- int opt;
- char *lxcpath = NULL, *name = NULL, *logpriority = NULL;
-
- while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) {
- switch(opt) {
- case 'n':
- name = optarg;
- break;
- case 'l':
- logpriority = optarg;
- break;
- case 'q':
- quiet = 1;
- break;
- case 'P':
- lxcpath = optarg;
- break;
- default: /* '?' */
- usage();
- exit(EXIT_FAILURE);
- }
- }
-
- err = lxc_log_init(name, name ? NULL : "none", logpriority,
- basename(argv[0]), quiet, lxcpath);
- if (err < 0)
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
-
- if (!argv[optind]) {
- ERROR("missing command to launch");
- exit(EXIT_FAILURE);
- }
-
- aargv = &argv[optind];
-
- /*
- * mask all the signals so we are safe to install a
- * signal handler and to fork
- */
- if (sigfillset(&mask) ||
- sigdelset(&mask, SIGILL) ||
- sigdelset(&mask, SIGSEGV) ||
- sigdelset(&mask, SIGBUS) ||
- sigprocmask(SIG_SETMASK, &mask, &omask)) {
- SYSERROR("failed to set signal mask");
- exit(EXIT_FAILURE);
- }
-
- for (i = 1; i < NSIG; i++) {
- struct sigaction act;
-
- /* Exclude some signals: ILL, SEGV and BUS are likely to
- * reveal a bug and we want a core. STOP and KILL cannot be
- * handled anyway: they're here for documentation.
- */
- if (i == SIGILL ||
- i == SIGSEGV ||
- i == SIGBUS ||
- i == SIGSTOP ||
- i == SIGKILL ||
- i == 32 || i == 33)
- continue;
-
- if (sigfillset(&act.sa_mask) ||
- sigdelset(&act.sa_mask, SIGILL) ||
- sigdelset(&act.sa_mask, SIGSEGV) ||
- sigdelset(&act.sa_mask, SIGBUS) ||
- sigdelset(&act.sa_mask, SIGSTOP) ||
- sigdelset(&act.sa_mask, SIGKILL)) {
- ERROR("failed to set signal");
- exit(EXIT_FAILURE);
- }
-
- act.sa_flags = 0;
- act.sa_handler = interrupt_handler;
- if (sigaction(i, &act, NULL) && errno != EINVAL) {
- SYSERROR("failed to sigaction");
- exit(EXIT_FAILURE);
- }
- }
-
- lxc_setup_fs();
-
- pid = fork();
-
- if (pid < 0)
- exit(EXIT_FAILURE);
-
- if (!pid) {
-
- /* restore default signal handlers */
- for (i = 1; i < NSIG; i++)
- signal(i, SIG_DFL);
-
- if (sigprocmask(SIG_SETMASK, &omask, NULL)) {
- SYSERROR("failed to set signal mask");
- exit(EXIT_FAILURE);
- }
-
- NOTICE("about to exec '%s'", aargv[0]);
-
- execvp(aargv[0], aargv);
- ERROR("failed to exec: '%s' : %m", aargv[0]);
- exit(err);
- }
-
- /* let's process the signals now */
- if (sigdelset(&omask, SIGALRM) ||
- sigprocmask(SIG_SETMASK, &omask, NULL)) {
- SYSERROR("failed to set signal mask");
- exit(EXIT_FAILURE);
- }
-
- /* no need of other inherited fds but stderr */
- close(fileno(stdin));
- close(fileno(stdout));
-
- err = EXIT_SUCCESS;
- for (;;) {
- int status;
- pid_t waited_pid;
-
- switch (was_interrupted) {
-
- case 0:
- break;
-
- case SIGPWR:
- case SIGTERM:
- if (!shutdown) {
- shutdown = 1;
- kill(-1, SIGTERM);
- alarm(1);
- }
- break;
-
- case SIGALRM:
- kill(-1, SIGKILL);
- break;
-
- default:
- kill(pid, was_interrupted);
- break;
- }
-
- was_interrupted = 0;
- waited_pid = wait(&status);
- if (waited_pid < 0) {
- if (errno == ECHILD)
- goto out;
- if (errno == EINTR)
- continue;
-
- ERROR("failed to wait child : %s",
- strerror(errno));
- goto out;
- }
-
- /* reset timer each time a process exited */
- if (shutdown)
- alarm(1);
-
- /*
- * keep the exit code of started application
- * (not wrapped pid) and continue to wait for
- * the end of the orphan group.
- */
- if (waited_pid == pid && !have_status) {
- err = lxc_error_set_and_log(waited_pid, status);
- have_status = 1;
- }
- }
-out:
- if (err < 0)
- exit(EXIT_FAILURE);
- exit(err);
-}
diff --git a/src/lxc/lxc_ls.c b/src/lxc/lxc_ls.c
deleted file mode 100644
index e2a4c34..0000000
--- a/src/lxc/lxc_ls.c
+++ /dev/null
@@ -1,1213 +0,0 @@
-/*
- *
- * Copyright © 2016 Christian Brauner <christian.brauner at mailbox.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-
-#include <getopt.h>
-#include <regex.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <termios.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "arguments.h"
-#include "conf.h"
-#include "confile.h"
-#include "log.h"
-#include "lxc.h"
-#include "utils.h"
-
-lxc_log_define(lxc_ls, lxc);
-
-#define LINELEN 1024
-/* Per default we only allow five levels of recursion to protect the stack at
- * least a little bit. */
-#define MAX_NESTLVL 5
-
-#define LS_FROZEN 1
-#define LS_STOPPED 2
-#define LS_ACTIVE 3
-#define LS_RUNNING 4
-#define LS_NESTING 5
-#define LS_FILTER 6
-
-#ifndef SOCK_CLOEXEC
-# define SOCK_CLOEXEC 02000000
-#endif
-
-/* Store container info. */
-struct ls {
- char *name;
- char *state;
- char *groups;
- char *interface;
- char *ipv4;
- char *ipv6;
- unsigned int nestlvl;
- pid_t init;
- double ram;
- double swap;
- bool autostart;
- bool running;
-};
-
-/* Keep track of field widths for printing. */
-struct lengths {
- unsigned int name_length;
- unsigned int state_length;
- unsigned int groups_length;
- unsigned int interface_length;
- unsigned int ipv4_length;
- unsigned int ipv6_length;
- unsigned int init_length;
- unsigned int ram_length;
- unsigned int swap_length;
- unsigned int autostart_length;
-};
-
-static int ls_deserialize(int rpipefd, struct ls **m, size_t *len);
-static void ls_field_width(const struct ls *l, const size_t size,
- struct lengths *lht);
-static void ls_free(struct ls *l, size_t size);
-static void ls_free_arr(char **arr, size_t size);
-static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args,
- const char *basepath, const char *parent, unsigned int lvl,
- char **lockpath, size_t len_lockpath, char **grps_must,
- size_t grps_must_len);
-static char *ls_get_cgroup_item(struct lxc_container *c, const char *item);
-static char *ls_get_config_item(struct lxc_container *c, const char *item,
- bool running);
-static char *ls_get_groups(struct lxc_container *c, bool running);
-static char *ls_get_ips(struct lxc_container *c, const char *inet);
-static int ls_recv_str(int fd, char **buf);
-static int ls_send_str(int fd, const char *buf);
-
-struct wrapargs {
- const struct lxc_arguments *args;
- char **grps_must;
- size_t grps_must_len;
- int pipefd[2];
- size_t *size;
- const char *parent;
- unsigned int nestlvl;
-};
-
-/*
- * Takes struct wrapargs as argument.
- */
-static int ls_get_wrapper(void *wrap);
-
-/*
- * To calculate swap usage we should not simply check memory.usage_in_bytes and
- * memory.memsw.usage_in_bytes and then do:
- * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes;
- * because we might receive an incorrect/negative value.
- * Instead we check memory.stat and check the "swap" value.
- */
-static double ls_get_swap(struct lxc_container *c);
-static unsigned int ls_get_term_width(void);
-static char *ls_get_interface(struct lxc_container *c);
-static bool ls_has_all_grps(const char *has, char **must, size_t must_len);
-static struct ls *ls_new(struct ls **ls, size_t *size);
-
-/*
- * Print user-specified fancy format.
- */
-static void ls_print_fancy_format(struct ls *l, struct lengths *lht,
- size_t size, const char *fancy_fmt);
-
-/*
- * Only print names of containers.
- */
-static void ls_print_names(struct ls *l, struct lengths *lht,
- size_t ls_arr, size_t termwidth);
-
-/*
- * Print default fancy format.
- */
-static void ls_print_table(struct ls *l, struct lengths *lht,
- size_t size);
-
-/*
- * id can only be 79 + \0 chars long.
- */
-static int ls_remove_lock(const char *path, const char *name,
- char **lockpath, size_t *len_lockpath, bool recalc);
-static int ls_serialize(int wpipefd, struct ls *n);
-static int my_parser(struct lxc_arguments *args, int c, char *arg);
-
-static const struct option my_longopts[] = {
- {"line", no_argument, 0, '1'},
- {"fancy", no_argument, 0, 'f'},
- {"fancy-format", required_argument, 0, 'F'},
- {"active", no_argument, 0, LS_ACTIVE},
- {"running", no_argument, 0, LS_RUNNING},
- {"frozen", no_argument, 0, LS_FROZEN},
- {"stopped", no_argument, 0, LS_STOPPED},
- {"nesting", optional_argument, 0, LS_NESTING},
- {"groups", required_argument, 0, 'g'},
- {"filter", required_argument, 0, LS_FILTER},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-ls",
- .help = "\n\
-[-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
-[-1] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
-[-f] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
-\n\
-lxc-ls list containers\n\
-\n\
-Options :\n\
- -1, --line show one entry per line\n\
- -f, --fancy column-based output\n\
- -F, --fancy-format column-based output\n\
- --active list only active containers\n\
- --running list only running containers\n\
- --frozen list only frozen containers\n\
- --stopped list only stopped containers\n\
- --nesting=NUM list nested containers up to NUM (default is 5) levels of nesting\n\
- --filter=REGEX filter container names by regular expression\n\
- -g --groups comma separated list of groups a container must have to be displayed\n",
- .options = my_longopts,
- .parser = my_parser,
- .ls_nesting = 0,
-};
-
-int main(int argc, char *argv[])
-{
- int ret = EXIT_FAILURE;
- /*
- * The lxc parser requires that my_args.name is set. So let's satisfy
- * that condition by setting a dummy name which is never used.
- */
- my_args.name = "";
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(EXIT_FAILURE);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- /*
- * We set the first argument that usually takes my_args.name to NULL so
- * that the log is only used when the user specifies a file.
- */
- if (lxc_log_init(NULL, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
-
- struct lengths max_len = {
- /* default header length */
- .name_length = 4, /* NAME */
- .state_length = 5, /* STATE */
- .groups_length = 6, /* GROUPS */
- .interface_length = 9, /* INTERFACE */
- .ipv4_length = 4, /* IPV4 */
- .ipv6_length = 4, /* IPV6 */
- .init_length = 3, /* PID */
- .ram_length = 3, /* RAM */
- .swap_length = 4, /* SWAP */
- .autostart_length = 9, /* AUTOSTART */
- };
-
- char **grps = NULL;
- size_t ngrps = 0;
- if (my_args.groups) {
- grps = lxc_string_split_and_trim(my_args.groups, ',');
- ngrps = lxc_array_len((void **)grps);
- }
-
- struct ls *ls_arr = NULL;
- size_t ls_size = 0;
- /* &(char *){NULL} is no magic. It's just a compound literal which
- * avoids having a pointless variable in main() that serves no purpose
- * here. */
- int status = ls_get(&ls_arr, &ls_size, &my_args, "", NULL, 0, &(char *){NULL}, 0, grps, ngrps);
- if (!ls_arr && status == 0)
- /* We did not fail. There was just nothing to do. */
- exit(EXIT_SUCCESS);
- else if (!ls_arr || status == -1)
- goto out;
-
- ls_field_width(ls_arr, ls_size, &max_len);
- if (my_args.ls_fancy && !my_args.ls_fancy_format) {
- ls_print_table(ls_arr, &max_len, ls_size);
- } else if (my_args.ls_fancy && my_args.ls_fancy_format) {
- ls_print_fancy_format(ls_arr, &max_len, ls_size, my_args.ls_fancy_format);
- } else {
- unsigned int cols = 0;
- if (!my_args.ls_line)
- cols = ls_get_term_width();
- ls_print_names(ls_arr, &max_len, ls_size, cols);
- }
-
- ret = EXIT_SUCCESS;
-
-out:
- ls_free(ls_arr, ls_size);
- lxc_free_array((void **)grps, free);
-
- exit(ret);
-}
-
-static void ls_free(struct ls *l, size_t size)
-{
- size_t i;
- struct ls *m = NULL;
- for (i = 0, m = l; i < size; i++, m++) {
- free(m->groups);
- free(m->interface);
- free(m->ipv4);
- free(m->ipv6);
- free(m->name);
- free(m->state);
- }
- free(l);
-}
-
-static char *ls_get_config_item(struct lxc_container *c, const char *item,
- bool running)
-{
- if (running)
- return c->get_running_config_item(c, item);
-
- size_t len = c->get_config_item(c, item, NULL, 0);
- if (len <= 0)
- return NULL;
-
- char *val = malloc((len + 1) * sizeof(*val));
- if (!val)
- return NULL;
-
- if ((size_t)c->get_config_item(c, item, val, len + 1) != len) {
- free(val);
- val = NULL;
- }
-
- return val;
-}
-
-static void ls_free_arr(char **arr, size_t size)
-{
- size_t i;
- for (i = 0; i < size; i++)
- free(arr[i]);
- free(arr);
-}
-
-static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args,
- const char *basepath, const char *parent, unsigned int lvl,
- char **lockpath, size_t len_lockpath, char **grps_must,
- size_t grps_must_len)
-{
- /* As ls_get() is non-tail recursive we face the inherent danger of
- * blowing up the stack at some level of nesting. To have at least some
- * security we define MAX_NESTLVL to be 5. That should be sufficient for
- * most users. The argument lvl can be used to keep track of the level
- * of nesting we are at. If lvl is greater than the allowed default
- * level or the level the user specified on the command line we return
- * and unwind the stack. */
- if (lvl > args->ls_nesting)
- return 0;
-
- int num = 0, ret = -1;
- char **containers = NULL;
- /* If we, at some level of nesting, encounter a stopped container but
- * want to retrieve nested containers we need to build an absolute path
- * beginning from it. Initially, at nesting level 0, basepath will
- * simply be the empty string and path will simply be whatever the
- * default lxcpath or the path the user gave us is. Basepath will also
- * be the empty string in case we encounter a running container since we
- * can simply attach to its namespace to retrieve nested containers. */
- char *path = lxc_append_paths(basepath, args->lxcpath[0]);
- if (!path)
- goto out;
-
- if (!dir_exists(path)) {
- ret = 0;
- goto out;
- }
-
- /* Do not do more work than is necessary right from the start. */
- if (args->ls_active || (args->ls_active && args->ls_frozen))
- num = list_active_containers(path, &containers, NULL);
- else
- num = list_all_containers(path, &containers, NULL);
- if (num == -1) {
- num = 0;
- goto out;
- }
-
- char *tmp = NULL;
- int check;
- struct ls *l = NULL;
- struct lxc_container *c = NULL;
- size_t i;
- for (i = 0; i < (size_t)num; i++) {
- char *name = containers[i];
-
- /* Filter container names by regex the user gave us. */
- if (args->ls_filter || args->argc == 1) {
- regex_t preg;
- tmp = args->ls_filter ? args->ls_filter : args->argv[0];
- check = regcomp(&preg, tmp, REG_NOSUB | REG_EXTENDED);
- if (check == REG_ESPACE) /* we're out of memory */
- goto out;
- else if (check != 0)
- continue;
- check = regexec(&preg, name, 0, NULL, 0);
- regfree(&preg);
- if (check != 0)
- continue;
- }
-
- errno = 0;
- c = lxc_container_new(name, path);
- if ((errno == ENOMEM) && !c)
- goto out;
- else if (!c)
- continue;
-
- if (!c->is_defined(c))
- goto put_and_next;
-
- /* This does not allocate memory so no worries about freeing it
- * when we goto next or out. */
- const char *state_tmp = c->state(c);
- if (!state_tmp)
- state_tmp = "UNKNOWN";
-
- if (args->ls_running && !c->is_running(c))
- goto put_and_next;
-
- if (args->ls_frozen && !args->ls_active && strcmp(state_tmp, "FROZEN"))
- goto put_and_next;
-
- if (args->ls_stopped && strcmp(state_tmp, "STOPPED"))
- goto put_and_next;
-
- bool running = c->is_running(c);
-
- char *grp_tmp = ls_get_groups(c, running);
- if (!ls_has_all_grps(grp_tmp, grps_must, grps_must_len)) {
- free(grp_tmp);
- goto put_and_next;
- }
-
- /* Now it makes sense to allocate memory. */
- l = ls_new(m, size);
- if (!l) {
- free(grp_tmp);
- goto put_and_next;
- }
-
- /* How deeply nested are we? */
- l->nestlvl = lvl;
-
- l->groups = grp_tmp;
-
- l->running = running;
-
- if (parent && args->ls_nesting && (args->ls_line || !args->ls_fancy))
- /* Prepend the name of the container with all its parents when
- * the user requests it. */
- l->name = lxc_append_paths(parent, name);
- else
- /* Otherwise simply record the name. */
- l->name = strdup(name);
- if (!l->name)
- goto put_and_next;
-
- /* Do not record stuff the user did not explictly request. */
- if (args->ls_fancy) {
- /* Maybe we should even consider the name sensitive and
- * hide it when you're not allowed to control the
- * container. */
- if (!c->may_control(c))
- goto put_and_next;
-
- l->state = strdup(state_tmp);
- if (!l->state)
- goto put_and_next;
-
- tmp = ls_get_config_item(c, "lxc.start.auto", running);
- if (tmp)
- l->autostart = atoi(tmp);
- free(tmp);
-
- if (running) {
- l->init = c->init_pid(c);
-
- l->interface = ls_get_interface(c);
-
- l->ipv4 = ls_get_ips(c, "inet");
-
- l->ipv6 = ls_get_ips(c, "inet6");
-
- tmp = ls_get_cgroup_item(c, "memory.usage_in_bytes");
- if (tmp) {
- l->ram = strtoull(tmp, NULL, 0);
- l->ram = l->ram / 1024 /1024;
- free(tmp);
- }
-
- l->swap = ls_get_swap(c);
- }
- }
-
- /* Get nested containers: Only do this after we have gathered
- * all other information we need. */
- if (args->ls_nesting && running) {
- struct wrapargs wargs = (struct wrapargs){.args = NULL};
- /* Open a socket so that the child can communicate with us. */
- check = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wargs.pipefd);
- if (check == -1)
- goto put_and_next;
-
- /* Set the next nesting level. */
- wargs.nestlvl = lvl + 1;
- /* Send in the parent for the next nesting level. */
- wargs.parent = l->name;
- wargs.args = args;
- wargs.grps_must = grps_must;
- wargs.grps_must_len = grps_must_len;
-
- pid_t out;
-
- lxc_attach_options_t aopt = LXC_ATTACH_OPTIONS_DEFAULT;
- aopt.env_policy = LXC_ATTACH_CLEAR_ENV;
-
- /* fork(): Attach to the namespace of the container and
- * run ls_get() in it which is called in ls_get_wrapper(). */
- check = c->attach(c, ls_get_wrapper, &wargs, &aopt, &out);
- /* close the socket */
- close(wargs.pipefd[1]);
-
- /* Retrieve all information we want from the child. */
- if (check == 0)
- if (ls_deserialize(wargs.pipefd[0], m, size) == -1)
- goto put_and_next;
-
- /* Wait for the child to finish. */
- wait_for_pid(out);
-
- /* We've done all the communication we need so shutdown
- * the socket and close it. */
- shutdown(wargs.pipefd[0], SHUT_RDWR);
- close(wargs.pipefd[0]);
- } else if (args->ls_nesting && !running) {
- /* This way of extracting the rootfs is not safe since
- * it will return very different things depending on the
- * storage backend that is used for the container. We
- * need a path-extractor function. We face the same
- * problem with the ovl_mkdir() function in
- * lxcoverlay.{c,h}. */
- char *curr_path = ls_get_config_item(c, "lxc.rootfs", running);
- if (!curr_path)
- goto put_and_next;
-
- /* Since the container is not running and we cannot
- * attach to it we need another strategy to retrieve
- * nested containers. What we do is simply create a
- * growing path which will lead us into the rootfs of
- * the next container where it stores its containers. */
- char *newpath = lxc_append_paths(basepath, curr_path);
- free(curr_path);
- if (!newpath)
- goto put_and_next;
-
- /* We want to remove all locks we create under
- * /run/lxc/lock so we create a string pointing us to
- * the lock path for the current container. */
- if (ls_remove_lock(path, name, lockpath, &len_lockpath, true) == -1)
- goto put_and_next;
-
- ls_get(m, size, args, newpath, l->name, lvl + 1, lockpath, len_lockpath, grps_must, grps_must_len);
- free(newpath);
-
- /* Remove the lock. No need to check for failure here. */
- ls_remove_lock(path, name, lockpath, &len_lockpath, false);
- }
-
-put_and_next:
- lxc_container_put(c);
- }
- ret = 0;
-
-out:
- ls_free_arr(containers, num);
- free(path);
- /* lockpath is shared amongst all non-fork()ing recursive calls to
- * ls_get() so only free it on the uppermost level. */
- if (lvl == 0)
- free(*lockpath);
-
- return ret;
-}
-
-static char *ls_get_cgroup_item(struct lxc_container *c, const char *item)
-{
- size_t len = c->get_cgroup_item(c, item, NULL, 0);
- if (len <= 0)
- return NULL;
-
- char *val = malloc((len + 1) * sizeof(*val));
- if (!val)
- return NULL;
-
- if ((size_t)c->get_cgroup_item(c, item, val, len + 1) != len) {
- free(val);
- val = NULL;
- }
-
- return val;
-}
-
-static char *ls_get_groups(struct lxc_container *c, bool running)
-{
- size_t len = 0;
- char *val = NULL;
-
- if (running)
- val = c->get_running_config_item(c, "lxc.group");
- else
- len = c->get_config_item(c, "lxc.group", NULL, 0);
-
- if (!val && (len > 0)) {
- val = malloc((len + 1) * sizeof(*val));
- if ((size_t)c->get_config_item(c, "lxc.group", val, len + 1) != len) {
- free(val);
- return NULL;
- }
- }
-
- if (val) {
- char *tmp;
- if ((tmp = strrchr(val, '\n')))
- *tmp = '\0';
-
- tmp = lxc_string_replace("\n", ", ", val);
- free(val);
- val = tmp;
- }
-
- return val;
-}
-
-static char *ls_get_ips(struct lxc_container *c, const char *inet)
-{
- char *ips = NULL;
- char **iptmp = c->get_ips(c, NULL, inet, 0);
- if (iptmp)
- ips = lxc_string_join(", ", (const char **)iptmp, false);
-
- lxc_free_array((void **)iptmp, free);
-
- return ips;
-}
-
-static char *ls_get_interface(struct lxc_container *c)
-{
- char **interfaces = c->get_interfaces(c);
- if (!interfaces)
- return NULL;
-
- char *interface = lxc_string_join(", ", (const char **)interfaces, false);
-
- lxc_free_array((void **)interfaces, free);
-
- return interface;
-}
-
-/*
- * To calculate swap usage we should not simply check memory.usage_in_bytes and
- * memory.memsw.usage_in_bytes and then do:
- * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes;
- * because we might receive an incorrect/negative value.
- * Instead we check memory.stat and check the "swap" value.
- */
-static double ls_get_swap(struct lxc_container *c)
-{
- unsigned long long int num = 0;
- char *stat = ls_get_cgroup_item(c, "memory.stat");
- if (!stat)
- goto out;
-
- char *swap = strstr(stat, "\nswap");
- if (!swap)
- goto out;
-
- swap = 1 + swap + 4 + 1; // start_of_swap_value = '\n' + strlen(swap) + ' '
-
- char *tmp = strchr(swap, '\n'); // find end of swap value
- if (!tmp)
- goto out;
-
- *tmp = '\0';
-
- num = strtoull(swap, NULL, 0);
- num = num / 1024 / 1024;
-
-out:
- free(stat);
-
- return num;
-}
-
-static unsigned int ls_get_term_width(void)
-{
- struct winsize ws;
- if (((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) &&
- (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1) &&
- (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)) ||
- (ws.ws_col == 0))
- return 0;
-
- return ws.ws_col;
-}
-
-static bool ls_has_all_grps(const char *has, char **must, size_t must_len)
-{
- bool bret = false;
-
- if (!has && must)
- return false;
- else if (!must)
- return true;
-
- char **tmp_has = lxc_string_split_and_trim(has, ',');
- size_t tmp_has_len = lxc_array_len((void **)tmp_has);
-
- /* Don't do any unnecessary work. */
- if (must_len > tmp_has_len)
- goto out;
-
- size_t i, j;
- for (i = 0; i < must_len; i++) {
- for (j = 0; j < tmp_has_len; j++)
- if (strcmp(must[i], tmp_has[j]) == 0)
- break;
- if (j == tmp_has_len)
- break;
- }
- if (i == must_len)
- bret = true;
-
-out:
- lxc_free_array((void **)tmp_has, free);
-
- return bret;
-}
-
-static struct ls *ls_new(struct ls **ls, size_t *size)
-{
- struct ls *m, *n;
-
- n = realloc(*ls, (*size + 1) * sizeof(struct ls));
- if (!n)
- return NULL;
-
- *ls = n;
- m = *ls + *size;
- (*size)++;
-
- *m = (struct ls){.name = NULL, .init = -1};
-
- return m;
-}
-
-static void ls_print_names(struct ls *l, struct lengths *lht,
- size_t size, size_t termwidth)
-{
- /* If list is empty do nothing. */
- if (size == 0)
- return;
-
- size_t i, len = 0;
- struct ls *m = NULL;
- for (i = 0, m = l; i < size; i++, m++) {
- printf("%-*s", lht->name_length, m->name ? m->name : "-");
- len += lht->name_length;
- if ((len + lht->name_length) >= termwidth) {
- printf("\n");
- len = 0;
- } else {
- printf(" ");
- len++;
- }
- }
- if (len > 0)
- printf("\n");
-}
-
-static void ls_print_fancy_format(struct ls *l, struct lengths *lht,
- size_t size, const char *fancy_fmt)
-{
- /* If list is empty do nothing. */
- if (size == 0)
- return;
-
- char **tmp = lxc_string_split_and_trim(fancy_fmt, ',');
- if (!tmp)
- return;
-
- char **s;
- /* Check for invalid keys. */
- for (s = tmp; s && *s; s++) {
- if (strcasecmp(*s, "NAME") && strcasecmp(*s, "STATE") &&
- strcasecmp(*s, "PID") && strcasecmp(*s, "RAM") &&
- strcasecmp(*s, "SWAP") && strcasecmp(*s, "AUTOSTART") &&
- strcasecmp(*s, "GROUPS") && strcasecmp(*s, "INTERFACE") &&
- strcasecmp(*s, "IPV4") && strcasecmp(*s, "IPV6")) {
- fprintf(stderr, "Invalid key: %s\n", *s);
- return;
- }
- }
-
- /* print header */
- for (s = tmp; s && *s; s++) {
- if (strcasecmp(*s, "NAME") == 0)
- printf("%-*s ", lht->name_length, "NAME");
- else if (strcasecmp(*s, "STATE") == 0)
- printf("%-*s ", lht->state_length, "STATE");
- else if (strcasecmp(*s, "PID") == 0)
- printf("%-*s ", lht->init_length, "PID");
- else if (strcasecmp(*s, "RAM") == 0)
- printf("%-*s ", lht->ram_length + 2, "RAM");
- else if (strcasecmp(*s, "SWAP") == 0)
- printf("%-*s ", lht->swap_length + 2, "SWAP");
- else if (strcasecmp(*s, "AUTOSTART") == 0)
- printf("%-*s ", lht->autostart_length, "AUTOSTART");
- else if (strcasecmp(*s, "GROUPS") == 0)
- printf("%-*s ", lht->groups_length, "GROUPS");
- else if (strcasecmp(*s, "INTERFACE") == 0)
- printf("%-*s ", lht->interface_length, "INTERFACE");
- else if (strcasecmp(*s, "IPV4") == 0)
- printf("%-*s ", lht->ipv4_length, "IPV4");
- else if (strcasecmp(*s, "IPV6") == 0)
- printf("%-*s ", lht->ipv6_length, "IPV6");
- }
- printf("\n");
-
- struct ls *m = NULL;
- size_t i;
- for (i = 0, m = l; i < size; i++, m++) {
- for (s = tmp; s && *s; s++) {
- if (strcasecmp(*s, "NAME") == 0) {
- if (m->nestlvl > 0) {
- printf("%*s", m->nestlvl, "\\");
- printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-");
- } else {
- printf("%-*s ", lht->name_length, m->name ? m->name : "-");
- }
- } else if (strcasecmp(*s, "STATE") == 0) {
- printf("%-*s ", lht->state_length, m->state ? m->state : "-");
- } else if (strcasecmp(*s, "PID") == 0) {
- if (m->init > 0)
- printf("%-*d ", lht->init_length, m->init);
- else
- printf("%-*s ", lht->init_length, "-");
- } else if (strcasecmp(*s, "RAM") == 0) {
- if ((m->ram >= 0) && m->running)
- printf("%*.2fMB ", lht->ram_length, m->ram);
- else
- printf("%-*s ", lht->ram_length, "-");
- } else if (strcasecmp(*s, "SWAP") == 0) {
- if ((m->swap >= 0) && m->running)
- printf("%*.2fMB ", lht->swap_length, m->swap);
- else
- printf("%-*s ", lht->swap_length, "-");
- } else if (strcasecmp(*s, "AUTOSTART") == 0) {
- printf("%-*d ", lht->autostart_length, m->autostart);
- } else if (strcasecmp(*s, "GROUPS") == 0) {
- printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-");
- } else if (strcasecmp(*s, "INTERFACE") == 0) {
- printf("%-*s ", lht->interface_length, m->interface ? m->interface : "-");
- } else if (strcasecmp(*s, "IPV4") == 0) {
- printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-");
- } else if (strcasecmp(*s, "IPV6") == 0) {
- printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-");
- }
- }
- printf("\n");
- }
-}
-
-static void ls_print_table(struct ls *l, struct lengths *lht,
- size_t size)
-{
- /* If list is empty do nothing. */
- if (size == 0)
- return;
-
- struct ls *m = NULL;
-
- /* print header */
- printf("%-*s ", lht->name_length, "NAME");
- printf("%-*s ", lht->state_length, "STATE");
- printf("%-*s ", lht->autostart_length, "AUTOSTART");
- printf("%-*s ", lht->groups_length, "GROUPS");
- printf("%-*s ", lht->ipv4_length, "IPV4");
- printf("%-*s ", lht->ipv6_length, "IPV6");
- printf("\n");
-
- size_t i;
- for (i = 0, m = l; i < size; i++, m++) {
- if (m->nestlvl > 0) {
- printf("%*s", m->nestlvl, "\\");
- printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-");
- } else {
- printf("%-*s ", lht->name_length, m->name ? m->name : "-");
- }
- printf("%-*s ", lht->state_length, m->state ? m->state : "-");
- printf("%-*d ", lht->autostart_length, m->autostart);
- printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-");
- printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-");
- printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-");
- printf("\n");
- }
-}
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg)
-{
- char *invalid;
- unsigned long int m, n = MAX_NESTLVL;
- switch (c) {
- case '1':
- args->ls_line = true;
- break;
- case 'f':
- args->ls_fancy = true;
- break;
- case LS_ACTIVE:
- args->ls_active = true;
- break;
- case LS_FROZEN:
- args->ls_frozen = true;
- break;
- case LS_RUNNING:
- args->ls_running = true;
- break;
- case LS_STOPPED:
- args->ls_stopped = true;
- break;
- case LS_NESTING:
- /* In case strtoul() receives a string that represents a
- * negative number it will return ULONG_MAX - the number that
- * the string represents if the number the string represents is
- * < ULONG_MAX and ULONG_MAX otherwise. But it will consider
- * this valid input and not set errno. So we check manually if
- * the first character of num_string == '-'. Otherwise the
- * default level remains set. */
- if (arg && !(*arg == '-')) {
- errno = 0;
- m = strtoul(arg, &invalid, 0);
- /* ls_nesting has type unsigned int. */
- if (!errno && (*invalid == '\0') && (m <= UINT_MAX))
- n = m;
- }
- args->ls_nesting = n;
- break;
- case 'g':
- args->groups = arg;
- break;
- case LS_FILTER:
- args->ls_filter = arg;
- break;
- case 'F':
- args->ls_fancy_format = arg;
- break;
- }
-
- return 0;
-}
-
-static int ls_get_wrapper(void *wrap)
-{
- int ret = -1;
- size_t len = 0;
- struct wrapargs *wargs = (struct wrapargs *)wrap;
- struct ls *m = NULL, *n = NULL;
-
- /* close pipe */
- close(wargs->pipefd[0]);
-
- /* &(char *){NULL} is no magic. It's just a compound literal which
- * allows us to avoid keeping a pointless variable around. */
- ls_get(&m, &len, wargs->args, "", wargs->parent, wargs->nestlvl, &(char *){NULL}, 0, wargs->grps_must, wargs->grps_must_len);
- if (!m)
- goto out;
-
- /* send length */
- if (lxc_write_nointr(wargs->pipefd[1], &len, sizeof(len)) <= 0)
- goto out;
-
- size_t i;
- for (i = 0, n = m; i < len; i++, n++) {
- if (ls_serialize(wargs->pipefd[1], n) == -1)
- goto out;
- }
- ret = 0;
-
-out:
- shutdown(wargs->pipefd[1], SHUT_RDWR);
- close(wargs->pipefd[1]);
- ls_free(m, len);
-
- return ret;
-}
-
-static int ls_remove_lock(const char *path, const char *name,
- char **lockpath, size_t *len_lockpath, bool recalc)
-{
- /* Avoid doing unnecessary work if we can. */
- if (recalc) {
- size_t newlen = strlen(path) + strlen(name) + strlen(RUNTIME_PATH) + /* / + lxc + / + lock + / + / = */ 11 + 1;
- if (newlen > *len_lockpath) {
- char *tmp = realloc(*lockpath, newlen * 2);
- if (!tmp)
- return -1;
- *lockpath = tmp;
- *len_lockpath = newlen * 2;
- }
- }
-
- int check = snprintf(*lockpath, *len_lockpath, "%s/lxc/lock/%s/%s", RUNTIME_PATH, path, name);
- if (check < 0 || (size_t)check >= *len_lockpath)
- return -1;
-
- lxc_rmdir_onedev(*lockpath, NULL);
-
- return 0;
-}
-
-static int ls_send_str(int fd, const char *buf)
-{
- size_t slen = 0;
- if (buf)
- slen = strlen(buf);
- if (lxc_write_nointr(fd, &slen, sizeof(slen)) != sizeof(slen))
- return -1;
- if (slen > 0) {
- if (lxc_write_nointr(fd, buf, slen) != (ssize_t)slen)
- return -1;
- }
- return 0;
-}
-
-static int ls_serialize(int wpipefd, struct ls *n)
-{
- ssize_t nbytes = sizeof(n->ram);
- if (lxc_write_nointr(wpipefd, &n->ram, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->swap);
- if (lxc_write_nointr(wpipefd, &n->swap, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->init);
- if (lxc_write_nointr(wpipefd, &n->init, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->autostart);
- if (lxc_write_nointr(wpipefd, &n->autostart, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->running);
- if (lxc_write_nointr(wpipefd, &n->running, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->nestlvl);
- if (lxc_write_nointr(wpipefd, &n->nestlvl, (size_t)nbytes) != nbytes)
- return -1;
-
- /* NAME */
- if (ls_send_str(wpipefd, n->name) < 0)
- return -1;
-
- /* STATE */
- if (ls_send_str(wpipefd, n->state) < 0)
- return -1;
-
- /* GROUPS */
- if (ls_send_str(wpipefd, n->groups) < 0)
- return -1;
-
- /* INTERFACE */
- if (ls_send_str(wpipefd, n->interface) < 0)
- return -1;
-
- /* IPV4 */
- if (ls_send_str(wpipefd, n->ipv4) < 0)
- return -1;
-
- /* IPV6 */
- if (ls_send_str(wpipefd, n->ipv6) < 0)
- return -1;
-
- return 0;
-}
-
-static int ls_recv_str(int fd, char **buf)
-{
- size_t slen = 0;
- if (lxc_read_nointr(fd, &slen, sizeof(slen)) != sizeof(slen))
- return -1;
- if (slen > 0) {
- *buf = malloc(sizeof(char) * (slen + 1));
- if (!*buf)
- return -1;
- if (lxc_read_nointr(fd, *buf, slen) != (ssize_t)slen)
- return -1;
- (*buf)[slen] = '\0';
- }
- return 0;
-}
-
-static int ls_deserialize(int rpipefd, struct ls **m, size_t *len)
-{
- struct ls *n;
- size_t sublen = 0;
- ssize_t nbytes = 0;
-
- /* get length */
- nbytes = sizeof(sublen);
- if (lxc_read_nointr(rpipefd, &sublen, (size_t)nbytes) != nbytes)
- return -1;
-
- while (sublen-- > 0) {
- n = ls_new(m, len);
- if (!n)
- return -1;
-
- nbytes = sizeof(n->ram);
- if (lxc_read_nointr(rpipefd, &n->ram, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->swap);
- if (lxc_read_nointr(rpipefd, &n->swap, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->init);
- if (lxc_read_nointr(rpipefd, &n->init, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->autostart);
- if (lxc_read_nointr(rpipefd, &n->autostart, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->running);
- if (lxc_read_nointr(rpipefd, &n->running, (size_t)nbytes) != nbytes)
- return -1;
-
- nbytes = sizeof(n->nestlvl);
- if (lxc_read_nointr(rpipefd, &n->nestlvl, (size_t)nbytes) != nbytes)
- return -1;
-
- /* NAME */
- if (ls_recv_str(rpipefd, &n->name) < 0)
- return -1;
-
- /* STATE */
- if (ls_recv_str(rpipefd, &n->state) < 0)
- return -1;
-
- /* GROUPS */
- if (ls_recv_str(rpipefd, &n->groups) < 0)
- return -1;
-
- /* INTERFACE */
- if (ls_recv_str(rpipefd, &n->interface) < 0)
- return -1;
-
- /* IPV4 */
- if (ls_recv_str(rpipefd, &n->ipv4) < 0)
- return -1;
-
- /* IPV6 */
- if (ls_recv_str(rpipefd, &n->ipv6) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static void ls_field_width(const struct ls *l, const size_t size,
- struct lengths *lht)
-{
- const struct ls *m;
- size_t i, len = 0;
- for (i = 0, m = l; i < size; i++, m++) {
- if (m->name) {
- len = strlen(m->name) + m->nestlvl;
- if (len > lht->name_length)
- lht->name_length = len;
- }
-
- if (m->state) {
- len = strlen(m->state);
- if (len > lht->state_length)
- lht->state_length = len;
- }
-
- if (m->interface) {
- len = strlen(m->interface);
- if (len > lht->interface_length)
- lht->interface_length = len;
- }
-
- if (m->groups) {
- len = strlen(m->groups);
- if (len > lht->groups_length)
- lht->groups_length = len;
- }
- if (m->ipv4) {
- len = strlen(m->ipv4);
- if (len > lht->ipv4_length)
- lht->ipv4_length = len;
- }
-
- if (m->ipv6) {
- len = strlen(m->ipv6);
- if (len > lht->ipv6_length)
- lht->ipv6_length = len;
- }
-
- if ((len = snprintf(NULL, 0, "%.2f", m->ram)) > lht->ram_length)
- lht->ram_length = len;
-
- if ((len = snprintf(NULL, 0, "%.2f", m->swap)) > lht->swap_length)
- lht->swap_length = len;
-
- if (m->init != -1) {
- if ((len = snprintf(NULL, 0, "%d", m->init)) > lht->init_length)
- lht->init_length = len;
- }
- }
-}
diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c
deleted file mode 100644
index 797ae8b..0000000
--- a/src/lxc/lxc_monitor.c
+++ /dev/null
@@ -1,211 +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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <regex.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <poll.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "monitor.h"
-#include "arguments.h"
-
-lxc_log_define(lxc_monitor_ui, lxc);
-
-static bool quit_monitord;
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'Q': quit_monitord = true; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"quit", no_argument, 0, 'Q'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-monitor",
- .help = "\
-[--name=NAME]\n\
-\n\
-lxc-monitor monitors the state of the NAME container\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- NAME may be a regular expression\n\
- -Q, --quit tell lxc-monitord to quit\n",
- .name = ".*",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .lxcpath_additional = -1,
-};
-
-static void close_fds(struct pollfd *fds, nfds_t nfds)
-{
- nfds_t i;
-
- if (nfds < 1)
- return;
-
- for (i = 0; i < nfds; ++i) {
- close(fds[i].fd);
- }
-}
-
-int main(int argc, char *argv[])
-{
- char *regexp;
- struct lxc_msg msg;
- regex_t preg;
- struct pollfd *fds;
- nfds_t nfds;
- int len, rc_main, rc_snp, i;
-
- rc_main = 0;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- if (quit_monitord) {
- int ret = EXIT_SUCCESS;
- for (i = 0; i < my_args.lxcpath_cnt; i++) {
- int fd;
-
- fd = lxc_monitor_open(my_args.lxcpath[i]);
- if (fd < 0) {
- ERROR("Unable to open monitor on path: %s", my_args.lxcpath[i]);
- ret = EXIT_FAILURE;
- continue;
- }
- if (write(fd, "quit", 4) < 0) {
- SYSERROR("Unable to close monitor on path: %s", my_args.lxcpath[i]);
- ret = EXIT_FAILURE;
- close(fd);
- continue;
- }
- close(fd);
- }
- return ret;
- }
-
- len = strlen(my_args.name) + 3;
- regexp = malloc(len + 3);
- if (!regexp) {
- ERROR("failed to allocate memory");
- return 1;
- }
- rc_snp = snprintf(regexp, len, "^%s$", my_args.name);
- if (rc_snp < 0 || rc_snp >= len) {
- ERROR("Name too long");
- rc_main = 1;
- goto error;
- }
-
- if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) {
- ERROR("failed to compile the regex '%s'", my_args.name);
- rc_main = 1;
- goto error;
- }
-
- fds = malloc(my_args.lxcpath_cnt * sizeof(struct pollfd));
- if (!fds) {
- SYSERROR("out of memory");
- rc_main = -1;
- goto cleanup;
- }
-
- nfds = my_args.lxcpath_cnt;
- for (i = 0; i < nfds; i++) {
- int fd;
-
- lxc_monitord_spawn(my_args.lxcpath[i]);
-
- fd = lxc_monitor_open(my_args.lxcpath[i]);
- if (fd < 0) {
- close_fds(fds, i);
- rc_main = 1;
- goto cleanup;
- }
- fds[i].fd = fd;
- fds[i].events = POLLIN;
- fds[i].revents = 0;
- }
-
- setlinebuf(stdout);
-
- for (;;) {
- if (lxc_monitor_read_fdset(fds, nfds, &msg, -1) < 0) {
- rc_main = 1;
- goto close_and_clean;
- }
-
- msg.name[sizeof(msg.name)-1] = '\0';
- if (regexec(&preg, msg.name, 0, NULL, 0))
- continue;
-
- switch (msg.type) {
- case lxc_msg_state:
- printf("'%s' changed state to [%s]\n",
- msg.name, lxc_state2str(msg.value));
- break;
- case lxc_msg_exit_code:
- printf("'%s' exited with status [%d]\n",
- msg.name, WEXITSTATUS(msg.value));
- break;
- default:
- /* ignore garbage */
- break;
- }
- }
-
-close_and_clean:
- close_fds(fds, nfds);
-
-cleanup:
- regfree(&preg);
- free(fds);
-
-error:
- free(regexp);
-
- return rc_main;
-}
diff --git a/src/lxc/lxc_snapshot.c b/src/lxc/lxc_snapshot.c
deleted file mode 100644
index 8f44891..0000000
--- a/src/lxc/lxc_snapshot.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- *
- * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2013 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "confile.h"
-#include <stdio.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <fcntl.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "bdev.h"
-#include "lxc.h"
-#include "log.h"
-#include "arguments.h"
-#include "utils.h"
-
-lxc_log_define(lxc_snapshot_ui, lxc);
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg);
-
-static const struct option my_longopts[] = {
- {"list", no_argument, 0, 'L'},
- {"restore", required_argument, 0, 'r'},
- {"newname", required_argument, 0, 'N'},
- {"destroy", required_argument, 0, 'd'},
- {"comment", required_argument, 0, 'c'},
- {"showcomments", no_argument, 0, 'C'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-snapshot",
- .help = "\
---name=NAME [-P lxcpath] [-L [-C]] [-c commentfile] [-r snapname [-N newname]]\n\
-\n\
-lxc-snapshot snapshots a container\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -L, --list list all snapshots\n\
- -r, --restore=NAME restore snapshot NAME, e.g. 'snap0'\n\
- -N, --newname=NEWNAME NEWNAME for the restored container\n\
- -d, --destroy=NAME destroy snapshot NAME, e.g. 'snap0'\n\
- use ALL to destroy all snapshots\n\
- -c, --comment=FILE add FILE as a comment\n\
- -C, --showcomments show snapshot comments\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .task = SNAP,
-};
-
-static int do_snapshot(struct lxc_container *c, char *commentfile);
-static int do_snapshot_destroy(struct lxc_container *c, char *snapname);
-static int do_snapshot_list(struct lxc_container *c, int print_comments);
-static int do_snapshot_restore(struct lxc_container *c,
- struct lxc_arguments *args);
-static int do_snapshot_task(struct lxc_container *c, enum task task);
-static void print_file(char *path);
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- int ret;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(EXIT_FAILURE);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(EXIT_FAILURE);
- lxc_log_options_no_override();
-
- if (geteuid()) {
- if (access(my_args.lxcpath[0], O_RDWR) < 0) {
- fprintf(stderr, "You lack access to %s\n",
- my_args.lxcpath[0]);
- exit(EXIT_FAILURE);
- }
- }
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- fprintf(stderr, "System error loading container\n");
- exit(EXIT_FAILURE);
- }
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n",
- my_args.name);
- lxc_container_put(c);
- exit(EXIT_FAILURE);
- }
-
- ret = do_snapshot_task(c, my_args.task);
-
- lxc_container_put(c);
-
- if (ret == 0)
- exit(EXIT_SUCCESS);
- exit(EXIT_FAILURE);
-}
-
-static int do_snapshot_task(struct lxc_container *c, enum task task)
-{
- int ret = 0;
-
- switch (task) {
- case DESTROY:
- ret = do_snapshot_destroy(c, my_args.snapname);
- break;
- case LIST:
- ret = do_snapshot_list(c, my_args.print_comments);
- break;
- case RESTORE:
- ret = do_snapshot_restore(c, &my_args);
- break;
- case SNAP:
- ret = do_snapshot(c, my_args.commentfile);
- break;
- default:
- ret = 0;
- break;
- }
-
- return ret;
-}
-
-static int my_parser(struct lxc_arguments *args, int c, char *arg)
-{
- switch (c) {
- case 'L':
- args->task = LIST;
- break;
- case 'r':
- args->task = RESTORE;
- args->snapname = arg;
- break;
- case 'N':
- args->newname = arg;
- break;
- case 'd':
- args->task = DESTROY;
- args->snapname = arg;
- break;
- case 'c':
- args->commentfile = arg;
- break;
- case 'C':
- args->print_comments = 1;
- break;
- }
-
- return 0;
-}
-
-static int do_snapshot(struct lxc_container *c, char *commentfile)
-{
- int ret;
-
- ret = c->snapshot(c, commentfile);
- if (ret < 0) {
- ERROR("Error creating a snapshot");
- return -1;
- }
-
- INFO("Created snapshot snap%d", ret);
-
- return 0;
-}
-
-static int do_snapshot_destroy(struct lxc_container *c, char *snapname)
-{
- bool ret;
-
- if (strcmp(snapname, "ALL") == 0)
- ret = c->snapshot_destroy_all(c);
- else
- ret = c->snapshot_destroy(c, snapname);
-
- if (!ret) {
- ERROR("Error destroying snapshot %s", snapname);
- return -1;
- }
-
- return 0;
-}
-
-static int do_snapshot_list(struct lxc_container *c, int print_comments)
-{
- struct lxc_snapshot *s;
- int i, n;
-
- n = c->snapshot_list(c, &s);
- if (n < 0) {
- ERROR("Error listing snapshots");
- return -1;
- }
- if (n == 0) {
- printf("No snapshots\n");
- return 0;
- }
-
- for (i = 0; i < n; i++) {
- printf("%s (%s) %s\n", s[i].name, s[i].lxcpath, s[i].timestamp);
- if (print_comments)
- print_file(s[i].comment_pathname);
- s[i].free(&s[i]);
- }
-
- free(s);
-
- return 0;
-}
-
-static int do_snapshot_restore(struct lxc_container *c,
- struct lxc_arguments *args)
-{
- int bret;
-
- /* When restoring a snapshot, the last optional argument if not given
- * explicitly via the corresponding command line option is the name to
- * use for the restored container. If no name is given, then the
- * original container will be destroyed and the restored container will
- * take its place. */
- if ((!args->newname) && (args->argc > 1)) {
- lxc_error(args, "Too many arguments");
- return -1;
- }
-
- if ((!args->newname) && (args->argc == 1))
- args->newname = args->argv[0];
-
- bret = c->snapshot_restore(c, args->snapname, args->newname);
- if (!bret) {
- ERROR("Error restoring snapshot %s", args->snapname);
- return -1;
- }
-
- return 0;
-}
-
-static void print_file(char *path)
-{
- if (!path)
- return;
-
- FILE *f = fopen(path, "r");
- char *line = NULL;
- size_t sz = 0;
-
- if (!f)
- return;
-
- while (getline(&line, &sz, f) != -1) {
- printf("%s", line);
- }
-
- free(line);
- fclose(f);
-}
-
diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c
deleted file mode 100644
index 6b942ac..0000000
--- a/src/lxc/lxc_start.c
+++ /dev/null
@@ -1,357 +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 <libgen.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/param.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <net/if.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "log.h"
-#include "caps.h"
-#include "lxc.h"
-#include "conf.h"
-#include "cgroup.h"
-#include "utils.h"
-#include "confile.h"
-#include "arguments.h"
-
-#define OPT_SHARE_NET OPT_USAGE+1
-#define OPT_SHARE_IPC OPT_USAGE+2
-#define OPT_SHARE_UTS OPT_USAGE+3
-
-lxc_log_define(lxc_start_ui, lxc);
-
-static struct lxc_list defines;
-
-static int ensure_path(char **confpath, const char *path)
-{
- int err = -1, fd;
- char *fullpath = NULL;
-
- if (path) {
- if (access(path, W_OK)) {
- fd = creat(path, 0600);
- if (fd < 0 && errno != EEXIST) {
- SYSERROR("failed to create '%s'", path);
- goto err;
- }
- if (fd >= 0)
- close(fd);
- }
-
- fullpath = realpath(path, NULL);
- if (!fullpath) {
- SYSERROR("failed to get the real path of '%s'", path);
- goto err;
- }
-
- *confpath = strdup(fullpath);
- if (!*confpath) {
- ERROR("failed to dup string '%s'", fullpath);
- goto err;
- }
- }
- err = 0;
-
-err:
- free(fullpath);
- return err;
-}
-
-static int pid_from_lxcname(const char *lxcname_or_pid, const char *lxcpath) {
- char *eptr;
- int pid = strtol(lxcname_or_pid, &eptr, 10);
- if (*eptr != '\0' || pid < 1) {
- struct lxc_container *s;
- s = lxc_container_new(lxcname_or_pid, lxcpath);
- if (!s) {
- SYSERROR("'%s' is not a valid pid nor a container name", lxcname_or_pid);
- return -1;
- }
-
- if (!s->may_control(s)) {
- SYSERROR("Insufficient privileges to control container '%s'", s->name);
- lxc_container_put(s);
- return -1;
- }
-
- pid = s->init_pid(s);
- if (pid < 1) {
- SYSERROR("Is container '%s' running?", s->name);
- lxc_container_put(s);
- return -1;
- }
-
- lxc_container_put(s);
- }
- if (kill(pid, 0) < 0) {
- SYSERROR("Can't send signal to pid %d", pid);
- return -1;
- }
-
- return pid;
-}
-
-static int open_ns(int pid, const char *ns_proc_name) {
- int fd;
- char path[MAXPATHLEN];
- snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns_proc_name);
-
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- SYSERROR("failed to open %s", path);
- return -1;
- }
- return fd;
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'c': args->console = arg; break;
- case 'L': args->console_log = arg; break;
- case 'd': args->daemonize = 1; break;
- case 'F': args->daemonize = 0; break;
- case 'f': args->rcfile = arg; break;
- case 'C': args->close_all_fds = 1; break;
- case 's': return lxc_config_define_add(&defines, arg);
- case 'p': args->pidfile = arg; break;
- case OPT_SHARE_NET: args->share_ns[LXC_NS_NET] = arg; break;
- case OPT_SHARE_IPC: args->share_ns[LXC_NS_IPC] = arg; break;
- case OPT_SHARE_UTS: args->share_ns[LXC_NS_UTS] = arg; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"daemon", no_argument, 0, 'd'},
- {"foreground", no_argument, 0, 'F'},
- {"rcfile", required_argument, 0, 'f'},
- {"define", required_argument, 0, 's'},
- {"console", required_argument, 0, 'c'},
- {"console-log", required_argument, 0, 'L'},
- {"close-all-fds", no_argument, 0, 'C'},
- {"pidfile", required_argument, 0, 'p'},
- {"share-net", required_argument, 0, OPT_SHARE_NET},
- {"share-ipc", required_argument, 0, OPT_SHARE_IPC},
- {"share-uts", required_argument, 0, OPT_SHARE_UTS},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-start",
- .help = "\
---name=NAME -- COMMAND\n\
-\n\
-lxc-start start COMMAND in specified container NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -d, --daemon Daemonize the container (default)\n\
- -F, --foreground Start with the current tty attached to /dev/console\n\
- -p, --pidfile=FILE Create a file with the process id\n\
- -f, --rcfile=FILE Load configuration file FILE\n\
- -c, --console=FILE Use specified FILE for the container console\n\
- -L, --console-log=FILE Log container console output to FILE\n\
- -C, --close-all-fds If any fds are inherited, close them\n\
- If not specified, exit with failure instead\n\
- Note: --daemon implies --close-all-fds\n\
- -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\
- --share-[net|ipc|uts]=NAME Share a namespace with another container or pid\n\
-",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .daemonize = 1,
- .pidfile = NULL,
-};
-
-int main(int argc, char *argv[])
-{
- int err = 1;
- struct lxc_conf *conf;
- char *const *args;
- char *rcfile = NULL;
- char *const default_args[] = {
- "/sbin/init",
- NULL,
- };
- struct lxc_container *c;
-
- lxc_list_init(&defines);
-
- if (lxc_caps_init())
- return err;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return err;
-
- if (!my_args.argc)
- args = default_args;
- else
- args = my_args.argv;
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return err;
- lxc_log_options_no_override();
-
- const char *lxcpath = my_args.lxcpath[0];
-
- /*
- * rcfile possibilities:
- * 1. rcfile from random path specified in cli option
- * 2. rcfile not specified, use $lxcpath/$lxcname/config
- * 3. rcfile not specified and does not exist.
- */
- /* rcfile is specified in the cli option */
- if (my_args.rcfile) {
- rcfile = (char *)my_args.rcfile;
- c = lxc_container_new(my_args.name, lxcpath);
- if (!c) {
- ERROR("Failed to create lxc_container");
- return err;
- }
- c->clear_config(c);
- if (!c->load_config(c, rcfile)) {
- ERROR("Failed to load rcfile");
- lxc_container_put(c);
- return err;
- }
- } else {
- int rc;
-
- rc = asprintf(&rcfile, "%s/%s/config", lxcpath, my_args.name);
- if (rc == -1) {
- SYSERROR("failed to allocate memory");
- return err;
- }
- INFO("using rcfile %s", rcfile);
-
- /* container configuration does not exist */
- if (access(rcfile, F_OK)) {
- free(rcfile);
- rcfile = NULL;
- }
- c = lxc_container_new(my_args.name, lxcpath);
- if (!c) {
- ERROR("Failed to create lxc_container");
- return err;
- }
- }
-
- if (c->is_running(c)) {
- ERROR("Container is already running.");
- err = 0;
- goto out;
- }
- /*
- * We should use set_config_item() over &defines, which would handle
- * unset c->lxc_conf for us and let us not use lxc_config_define_load()
- */
- if (!c->lxc_conf)
- c->lxc_conf = lxc_conf_init();
- conf = c->lxc_conf;
-
- if (lxc_config_define_load(&defines, conf))
- goto out;
-
- if (!rcfile && !strcmp("/sbin/init", args[0])) {
- ERROR("Executing '/sbin/init' with no configuration file may crash the host");
- goto out;
- }
-
- if (ensure_path(&conf->console.path, my_args.console) < 0) {
- ERROR("failed to ensure console path '%s'", my_args.console);
- goto out;
- }
-
- if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) {
- ERROR("failed to ensure console log '%s'", my_args.console_log);
- goto out;
- }
-
- if (my_args.pidfile != NULL) {
- if (ensure_path(&c->pidfile, my_args.pidfile) < 0) {
- ERROR("failed to ensure pidfile '%s'", my_args.pidfile);
- goto out;
- }
- }
-
- int i;
- for (i = 0; i < LXC_NS_MAX; i++) {
- if (my_args.share_ns[i] == NULL)
- continue;
-
- int pid = pid_from_lxcname(my_args.share_ns[i], lxcpath);
- if (pid < 1)
- goto out;
-
- int fd = open_ns(pid, ns_info[i].proc_name);
- if (fd < 0)
- goto out;
- conf->inherit_ns_fd[i] = fd;
- }
-
- if (!my_args.daemonize) {
- c->want_daemonize(c, false);
- }
-
- if (my_args.close_all_fds)
- c->want_close_all_fds(c, true);
-
- if (args == default_args)
- err = c->start(c, 0, NULL) ? 0 : 1;
- else
- err = c->start(c, 0, args) ? 0 : 1;
-
- if (err) {
- ERROR("The container failed to start.");
- if (my_args.daemonize)
- ERROR("To get more details, run the container in foreground mode.");
- ERROR("Additional information can be obtained by setting the "
- "--logfile and --logpriority options.");
- err = c->error_num;
- lxc_container_put(c);
- return err;
- }
-
-out:
- lxc_container_put(c);
- return err;
-}
diff --git a/src/lxc/lxc_stop.c b/src/lxc/lxc_stop.c
deleted file mode 100644
index 10ddce6..0000000
--- a/src/lxc/lxc_stop.c
+++ /dev/null
@@ -1,246 +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 <stdio.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "arguments.h"
-#include "commands.h"
-#include "utils.h"
-
-#define OPT_NO_LOCK OPT_USAGE+1
-#define OPT_NO_KILL OPT_USAGE+2
-
-lxc_log_define(lxc_stop_ui, lxc);
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'r': args->reboot = 1; break;
- case 'W': args->nowait = 1; break;
- case 't': args->timeout = atoi(arg); break;
- case 'k': args->hardstop = 1; break;
- case OPT_NO_LOCK: args->nolock = 1; break;
- case OPT_NO_KILL: args->nokill = 1; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"reboot", no_argument, 0, 'r'},
- {"nowait", no_argument, 0, 'W'},
- {"timeout", required_argument, 0, 't'},
- {"kill", no_argument, 0, 'k'},
- {"nokill", no_argument, 0, OPT_NO_KILL},
- {"nolock", no_argument, 0, OPT_NO_LOCK},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-stop",
- .help = "\
---name=NAME\n\
-\n\
-lxc-stop stops a container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -r, --reboot reboot the container\n\
- -W, --nowait don't wait for shutdown or reboot to complete\n\
- -t, --timeout=T wait T seconds before hard-stopping\n\
- -k, --kill kill container rather than request clean shutdown\n\
- --nolock Avoid using API locks\n\
- --nokill Only request clean shutdown, don't force kill after timeout\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .timeout = -2,
-};
-
-/* returns -1 on failure, 0 on success */
-static int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c)
-{
- int ret;
- pid_t pid;
- pid_t newpid;
- int timeout = a->timeout;
-
- pid = c->init_pid(c);
- if (pid == -1)
- return -1;
- if (!c->reboot(c))
- return -1;
- if (a->nowait)
- return 0;
- if (timeout == 0)
- goto out;
-
- for (;;) {
- /* can we use c-> wait for this, assuming it will
- * re-enter RUNNING? For now just sleep */
- int elapsed_time, curtime = 0;
- struct timeval tv;
-
- newpid = c->init_pid(c);
- if (newpid != -1 && newpid != pid)
- return 0;
-
- if (timeout != -1) {
- ret = gettimeofday(&tv, NULL);
- if (ret)
- break;
- curtime = tv.tv_sec;
- }
-
- sleep(1);
- if (timeout != -1) {
- ret = gettimeofday(&tv, NULL);
- if (ret)
- break;
- elapsed_time = tv.tv_sec - curtime;
- if (timeout - elapsed_time <= 0)
- break;
- timeout -= elapsed_time;
- }
- }
-
-out:
- newpid = c->init_pid(c);
- if (newpid == -1 || newpid == pid) {
- printf("Reboot did not complete before timeout\n");
- return -1;
- }
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
- bool s;
- int ret = 1;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- /* Set default timeout */
- if (my_args.timeout == -2) {
- if (my_args.hardstop) {
- my_args.timeout = 0;
- }
- else {
- my_args.timeout = 60;
- }
- }
-
- if (my_args.nowait) {
- my_args.timeout = 0;
- }
-
- /* some checks */
- if (!my_args.hardstop && my_args.timeout < -1) {
- fprintf(stderr, "invalid timeout\n");
- return 1;
- }
-
- if (my_args.hardstop && my_args.nokill) {
- fprintf(stderr, "-k can't be used with --nokill\n");
- return 1;
- }
-
- if (my_args.hardstop && my_args.reboot) {
- fprintf(stderr, "-k can't be used with -r\n");
- return 1;
- }
-
- if (my_args.hardstop && my_args.timeout) {
- fprintf(stderr, "-k doesn't allow timeouts\n");
- return 1;
- }
-
- if (my_args.nolock && !my_args.hardstop) {
- fprintf(stderr, "--nolock may only be used with -k\n");
- return 1;
- }
-
- /* shortcut - if locking is bogus, we should be able to kill
- * containers at least */
- if (my_args.nolock)
- return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]);
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- fprintf(stderr, "Error opening container\n");
- goto out;
- }
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
- goto out;
- }
-
- if (!c->is_running(c)) {
- fprintf(stderr, "%s is not running\n", c->name);
- ret = 2;
- goto out;
- }
-
- /* kill */
- if (my_args.hardstop) {
- ret = c->stop(c) ? 0 : 1;
- goto out;
- }
-
- /* reboot */
- if (my_args.reboot) {
- ret = do_reboot_and_check(&my_args, c);
- goto out;
- }
-
- /* shutdown */
- s = c->shutdown(c, my_args.timeout);
- if (!s) {
- if (my_args.timeout == 0)
- ret = 0;
- else if (my_args.nokill)
- ret = 1;
- else
- ret = c->stop(c) ? 0 : 1;
- } else
- ret = 0;
-
-out:
- lxc_container_put(c);
- if (ret < 0)
- return 1;
- return ret;
-}
diff --git a/src/lxc/lxc_top.c b/src/lxc/lxc_top.c
deleted file mode 100644
index c4cb871..0000000
--- a/src/lxc/lxc_top.c
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * lxc: linux Container library
- *
- * Copyright © 2014 Oracle.
- *
- * Authors:
- * Dwight Engen <dwight.engen at oracle.com>
- *
- * 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 <errno.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <termios.h>
-#include <unistd.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <lxc/lxccontainer.h>
-
-#include "arguments.h"
-#include "log.h"
-#include "lxc.h"
-#include "mainloop.h"
-#include "utils.h"
-
-lxc_log_define(lxc_top_ui, lxc);
-
-#define USER_HZ 100
-#define ESC "\033"
-#define TERMCLEAR ESC "[H" ESC "[J"
-#define TERMNORM ESC "[0m"
-#define TERMBOLD ESC "[1m"
-#define TERMRVRS ESC "[7m"
-
-struct stats {
- uint64_t mem_used;
- uint64_t mem_limit;
- uint64_t kmem_used;
- uint64_t kmem_limit;
- uint64_t cpu_use_nanos;
- uint64_t cpu_use_user;
- uint64_t cpu_use_sys;
- uint64_t blkio;
-};
-
-struct ct {
- struct lxc_container *c;
- struct stats *stats;
-};
-
-static int delay = 3;
-static char sort_by = 'n';
-static int sort_reverse = 0;
-
-static struct termios oldtios;
-static struct ct *ct = NULL;
-static int ct_alloc_cnt = 0;
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 'd': delay = atoi(arg); break;
- case 's': sort_by = arg[0]; break;
- case 'r': sort_reverse = 1; break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"delay", required_argument, 0, 'd'},
- {"sort", required_argument, 0, 's'},
- {"reverse", no_argument, 0, 'r'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-top",
- .help = "\
-[--name=NAME]\n\
-\n\
-lxc-top monitors the state of the active containers\n\
-\n\
-Options :\n\
- -d, --delay delay in seconds between refreshes (default: 3.0)\n\
- -s, --sort sort by [n,c,b,m] (default: n) where\n\
- n = Name\n\
- c = CPU use\n\
- b = Block I/O use\n\
- m = Memory use\n\
- k = Kernel memory use\n\
- -r, --reverse sort in reverse (descending) order\n",
- .name = ".*",
- .options = my_longopts,
- .parser = my_parser,
- .checker = NULL,
- .lxcpath_additional = -1,
-};
-
-static void stdin_tios_restore(void)
-{
- tcsetattr(0, TCSAFLUSH, &oldtios);
- fprintf(stderr, "\n");
-}
-
-static int stdin_tios_setup(void)
-{
- struct termios newtios;
-
- if (!isatty(0)) {
- ERROR("stdin is not a tty");
- return -1;
- }
-
- if (tcgetattr(0, &oldtios)) {
- SYSERROR("failed to get current terminal settings");
- return -1;
- }
-
- newtios = oldtios;
-
- /* turn off echo and line buffering */
- newtios.c_iflag &= ~IGNBRK;
- newtios.c_iflag &= BRKINT;
- newtios.c_lflag &= ~(ECHO|ICANON);
- newtios.c_cc[VMIN] = 1;
- newtios.c_cc[VTIME] = 0;
-
- if (tcsetattr(0, TCSAFLUSH, &newtios)) {
- ERROR("failed to set new terminal settings");
- return -1;
- }
-
- return 0;
-}
-
-static int stdin_tios_rows(void)
-{
- struct winsize wsz;
- if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0)
- return wsz.ws_row;
- return 25;
-}
-
-static int stdin_handler(int fd, uint32_t events, void *data,
- struct lxc_epoll_descr *descr)
-{
- char *in_char = data;
-
- if (events & EPOLLIN) {
- int rc;
-
- rc = read(fd, in_char, sizeof(*in_char));
- if (rc <= 0)
- *in_char = '\0';
- }
-
- if (events & EPOLLHUP)
- *in_char = 'q';
- return 1;
-}
-
-static void sig_handler(int sig)
-{
- exit(EXIT_SUCCESS);
-}
-
-static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
-{
- if (val > 1 << 30) {
- snprintf(buf, bufsz, "%u.%2.2u GB",
- (int)(val >> 30),
- (int)(val & ((1 << 30) - 1)) / 10737419);
- } else if (val > 1 << 20) {
- int x = val + 5243; /* for rounding */
- snprintf(buf, bufsz, "%u.%2.2u MB",
- x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
- } else if (val > 1 << 10) {
- int x = val + 5; /* for rounding */
- snprintf(buf, bufsz, "%u.%2.2u KB",
- x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
- } else {
- snprintf(buf, bufsz, "%3u.00 ", (int)val);
- }
-}
-
-static uint64_t stat_get_int(struct lxc_container *c, const char *item)
-{
- char buf[80];
- int len;
- uint64_t val;
-
- len = c->get_cgroup_item(c, item, buf, sizeof(buf));
- if (len <= 0) {
- ERROR("unable to read cgroup item %s", item);
- return 0;
- }
-
- val = strtoull(buf, NULL, 0);
- return val;
-}
-
-static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
- const char *match, int column)
-{
- char buf[4096];
- int i,j,len;
- uint64_t val = 0;
- char **lines, **cols;
- size_t matchlen;
-
- len = c->get_cgroup_item(c, item, buf, sizeof(buf));
- if (len <= 0) {
- ERROR("unable to read cgroup item %s", item);
- goto out;
- }
-
- lines = lxc_string_split_and_trim(buf, '\n');
- if (!lines)
- goto out;
-
- matchlen = strlen(match);
- for (i = 0; lines[i]; i++) {
- if (strncmp(lines[i], match, matchlen) == 0) {
- cols = lxc_string_split_and_trim(lines[i], ' ');
- if (!cols)
- goto err1;
- for (j = 0; cols[j]; j++) {
- if (j == column) {
- val = strtoull(cols[j], NULL, 0);
- break;
- }
- }
- lxc_free_array((void **)cols, free);
- break;
- }
- }
-err1:
- lxc_free_array((void **)lines, free);
-out:
- return val;
-}
-
-static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total)
-{
- ct->c = c;
- ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
- ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
- ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
- ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
- ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
- ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
- ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
- ct->stats->blkio = stat_match_get_int(c, "blkio.throttle.io_service_bytes", "Total", 1);
-
- if (total) {
- total->mem_used = total->mem_used + ct->stats->mem_used;
- total->mem_limit = total->mem_limit + ct->stats->mem_limit;
- total->kmem_used = total->kmem_used + ct->stats->kmem_used;
- total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
- total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
- total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
- total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
- total->blkio = total->blkio + ct->stats->blkio;
- }
-}
-
-static void stats_print_header(struct stats *stats)
-{
- printf(TERMRVRS TERMBOLD);
- printf("%-18s %12s %12s %12s %14s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
- if (stats->kmem_used > 0)
- printf(" %10s", "KMem");
- printf("\n");
-
- printf("%-18s %12s %12s %12s %14s %10s", "Name", "Used", "Sys", "User", "Total", "Used");
- if (stats->kmem_used > 0)
- printf(" %10s", "Used");
- printf("\n");
- printf(TERMNORM);
-}
-
-static void stats_print(const char *name, const struct stats *stats,
- const struct stats *total)
-{
- char blkio_str[20];
- char mem_used_str[20];
- char kmem_used_str[20];
-
- size_humanize(stats->blkio, blkio_str, sizeof(blkio_str));
- size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
-
- printf("%-18.18s %12.2f %12.2f %12.2f %14s %10s",
- name,
- (float)stats->cpu_use_nanos / 1000000000,
- (float)stats->cpu_use_sys / USER_HZ,
- (float)stats->cpu_use_user / USER_HZ,
- blkio_str,
- mem_used_str);
- if (total->kmem_used > 0) {
- size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
- printf(" %10s", kmem_used_str);
- }
-}
-
-static int cmp_name(const void *sct1, const void *sct2)
-{
- const struct ct *ct1 = sct1;
- const struct ct *ct2 = sct2;
-
- if (sort_reverse)
- return strcmp(ct2->c->name, ct1->c->name);
- return strcmp(ct1->c->name, ct2->c->name);
-}
-
-static int cmp_cpuuse(const void *sct1, const void *sct2)
-{
- const struct ct *ct1 = sct1;
- const struct ct *ct2 = sct2;
-
- if (sort_reverse)
- return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
- return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
-}
-
-static int cmp_blkio(const void *sct1, const void *sct2)
-{
- const struct ct *ct1 = sct1;
- const struct ct *ct2 = sct2;
-
- if (sort_reverse)
- return ct2->stats->blkio < ct1->stats->blkio;
- return ct1->stats->blkio < ct2->stats->blkio;
-}
-
-static int cmp_memory(const void *sct1, const void *sct2)
-{
- const struct ct *ct1 = sct1;
- const struct ct *ct2 = sct2;
-
- if (sort_reverse)
- return ct2->stats->mem_used < ct1->stats->mem_used;
- return ct1->stats->mem_used < ct2->stats->mem_used;
-}
-
-static int cmp_kmemory(const void *sct1, const void *sct2)
-{
- const struct ct *ct1 = sct1;
- const struct ct *ct2 = sct2;
-
- if (sort_reverse)
- return ct2->stats->kmem_used < ct1->stats->kmem_used;
- return ct1->stats->kmem_used < ct2->stats->kmem_used;
-}
-
-static void ct_sort(int active)
-{
- int (*cmp_func)(const void *, const void *);
-
- switch(sort_by) {
- default:
- case 'n': cmp_func = cmp_name; break;
- case 'c': cmp_func = cmp_cpuuse; break;
- case 'b': cmp_func = cmp_blkio; break;
- case 'm': cmp_func = cmp_memory; break;
- case 'k': cmp_func = cmp_kmemory; break;
- }
- qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func);
-}
-
-static void ct_free(void)
-{
- int i;
-
- for (i = 0; i < ct_alloc_cnt; i++) {
- if (ct[i].c) {
- lxc_container_put(ct[i].c);
- ct[i].c = NULL;
- }
- free(ct[i].stats);
- ct[i].stats = NULL;
- }
-}
-
-static void ct_realloc(int active_cnt)
-{
- int i;
-
- if (active_cnt > ct_alloc_cnt) {
- ct_free();
- ct = realloc(ct, sizeof(*ct) * active_cnt);
- if (!ct) {
- ERROR("cannot alloc mem");
- exit(EXIT_FAILURE);
- }
- for (i = 0; i < active_cnt; i++) {
- ct[i].stats = malloc(sizeof(*ct[0].stats));
- if (!ct[i].stats) {
- ERROR("cannot alloc mem");
- exit(EXIT_FAILURE);
- }
- }
- ct_alloc_cnt = active_cnt;
- }
-}
-
-int main(int argc, char *argv[])
-{
- struct lxc_epoll_descr descr;
- int ret, ct_print_cnt;
- char in_char;
-
- ret = EXIT_FAILURE;
- if (lxc_arguments_parse(&my_args, argc, argv))
- goto out;
-
- ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */
- if (stdin_tios_setup() < 0) {
- ERROR("failed to setup terminal");
- goto out;
- }
-
- /* ensure the terminal gets restored */
- atexit(stdin_tios_restore);
- signal(SIGINT, sig_handler);
- signal(SIGQUIT, sig_handler);
-
- if (lxc_mainloop_open(&descr)) {
- ERROR("failed to create mainloop");
- goto out;
- }
-
- ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char);
- if (ret) {
- ERROR("failed to add stdin handler");
- ret = EXIT_FAILURE;
- goto err1;
- }
-
- for(;;) {
- struct lxc_container **active;
- int i, active_cnt;
- struct stats total;
- char total_name[30];
-
- active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active);
- ct_realloc(active_cnt);
-
- memset(&total, 0, sizeof(total));
- for (i = 0; i < active_cnt; i++)
- stats_get(active[i], &ct[i], &total);
-
- ct_sort(active_cnt);
-
- printf(TERMCLEAR);
- stats_print_header(&total);
- for (i = 0; i < active_cnt && i < ct_print_cnt; i++) {
- stats_print(ct[i].c->name, ct[i].stats, &total);
- printf("\n");
- }
- sprintf(total_name, "TOTAL %d of %d", i, active_cnt);
- stats_print(total_name, &total, &total);
- fflush(stdout);
-
- for (i = 0; i < active_cnt; i++) {
- lxc_container_put(ct[i].c);
- ct[i].c = NULL;
- }
-
- in_char = '\0';
- ret = lxc_mainloop(&descr, 1000 * delay);
- if (ret != 0 || in_char == 'q')
- break;
- switch(in_char) {
- case 'r':
- sort_reverse ^= 1;
- break;
- case 'n':
- case 'c':
- case 'b':
- case 'm':
- case 'k':
- if (sort_by == in_char)
- sort_reverse ^= 1;
- else
- sort_reverse = 0;
- sort_by = in_char;
- }
- }
- ret = EXIT_SUCCESS;
-
-err1:
- lxc_mainloop_close(&descr);
-out:
- return ret;
-}
diff --git a/src/lxc/lxc_unfreeze.c b/src/lxc/lxc_unfreeze.c
deleted file mode 100644
index 3a13d37..0000000
--- a/src/lxc/lxc_unfreeze.c
+++ /dev/null
@@ -1,90 +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 <stdio.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "arguments.h"
-
-lxc_log_define(lxc_unfreeze_ui, lxc);
-
-static const struct option my_longopts[] = {
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-unfreeze",
- .help = "\
---name=NAME\n\
-\n\
-lxc-unfreeze unfreezes a container with the identifier NAME\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n",
- .options = my_longopts,
- .parser = NULL,
- .checker = NULL,
-};
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- exit(1);
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- exit(1);
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c) {
- ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name);
- exit(1);
- }
-
- if (!c->may_control(c)) {
- ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
- if (!c->unfreeze(c)) {
- ERROR("Failed to unfreeze %s:%s", my_args.lxcpath[0], my_args.name);
- lxc_container_put(c);
- exit(1);
- }
-
- lxc_container_put(c);
-
- exit(0);
-}
diff --git a/src/lxc/lxc_unshare.c b/src/lxc/lxc_unshare.c
deleted file mode 100644
index e629525..0000000
--- a/src/lxc/lxc_unshare.c
+++ /dev/null
@@ -1,257 +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 <errno.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <netinet/in.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "caps.h"
-#include "cgroup.h"
-#include "error.h"
-#include "log.h"
-#include "namespace.h"
-#include "network.h"
-#include "utils.h"
-
-/* Define sethostname() if missing from the C library */
-#ifndef HAVE_SETHOSTNAME
-static int sethostname(const char * name, size_t len)
-{
-#ifdef __NR_sethostname
-return syscall(__NR_sethostname, name, len);
-#else
-errno = ENOSYS;
-return -1;
-#endif
-}
-#endif
-
-lxc_log_define(lxc_unshare_ui, lxc);
-
-struct my_iflist
-{
- char *mi_ifname;
- struct my_iflist *mi_next;
-};
-
-static void usage(char *cmd)
-{
- fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
- fprintf(stderr, "Options are:\n");
- fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \
- "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n");
- fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
- fprintf(stderr, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
- fprintf(stderr, "\t -H <hostname>: Set the hostname in the container\n");
- fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n");
- fprintf(stderr, "\t -M : reMount default fs inside container (/proc /dev/shm /dev/mqueue)\n");
- _exit(1);
-}
-
-static bool lookup_user(const char *optarg, uid_t *uid)
-{
- char name[sysconf(_SC_LOGIN_NAME_MAX)];
- struct passwd *pwent = NULL;
-
- if (!optarg || (optarg[0] == '\0'))
- return false;
-
- if (sscanf(optarg, "%u", uid) < 1) {
- /* not a uid -- perhaps a username */
- if (sscanf(optarg, "%s", name) < 1)
- return false;
-
- pwent = getpwnam(name);
- if (!pwent) {
- ERROR("invalid username %s", name);
- return false;
- }
- *uid = pwent->pw_uid;
- } else {
- pwent = getpwuid(*uid);
- if (!pwent) {
- ERROR("invalid uid %u", *uid);
- return false;
- }
- }
- return true;
-}
-
-
-struct start_arg {
- char ***args;
- int *flags;
- uid_t *uid;
- bool setuid;
- int want_default_mounts;
- const char *want_hostname;
-};
-
-static int do_start(void *arg)
-{
- struct start_arg *start_arg = arg;
- char **args = *start_arg->args;
- int flags = *start_arg->flags;
- uid_t uid = *start_arg->uid;
- int want_default_mounts = start_arg->want_default_mounts;
- const char *want_hostname = start_arg->want_hostname;
-
- if ((flags & CLONE_NEWNS) && want_default_mounts)
- lxc_setup_fs();
-
- if ((flags & CLONE_NEWUTS) && want_hostname)
- if (sethostname(want_hostname, strlen(want_hostname)) < 0) {
- ERROR("failed to set hostname %s: %s", want_hostname, strerror(errno));
- exit(1);
- }
-
- // Setuid is useful even without a new user id space
- if (start_arg->setuid && setuid(uid)) {
- ERROR("failed to set uid %d: %s", uid, strerror(errno));
- exit(1);
- }
-
- execvp(args[0], args);
-
- ERROR("failed to exec: '%s': %s", args[0], strerror(errno));
- return 1;
-}
-
-int main(int argc, char *argv[])
-{
- int opt, status;
- int ret;
- char *namespaces = NULL;
- char **args;
- int flags = 0;
- int daemonize = 0;
- uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */
- pid_t pid;
- struct my_iflist *tmpif, *my_iflist = NULL;
- struct start_arg start_arg = {
- .args = &args,
- .uid = &uid,
- .setuid = false,
- .flags = &flags,
- .want_hostname = NULL,
- .want_default_mounts = 0,
- };
-
- while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) {
- switch (opt) {
- case 's':
- namespaces = optarg;
- break;
- case 'i':
- if (!(tmpif = malloc(sizeof(*tmpif)))) {
- perror("malloc");
- exit(1);
- }
- tmpif->mi_ifname = optarg;
- tmpif->mi_next = my_iflist;
- my_iflist = tmpif;
- break;
- case 'd':
- daemonize = 1;
- break;
- case 'M':
- start_arg.want_default_mounts = 1;
- break;
- case 'H':
- start_arg.want_hostname = optarg;
- break;
- case 'h':
- usage(argv[0]);
- break;
- case 'u':
- if (!lookup_user(optarg, &uid))
- return 1;
- start_arg.setuid = true;
- }
- }
-
- if (argv[optind] == NULL) {
- ERROR("a command to execute in the new namespace is required");
- return 1;
- }
-
- args = &argv[optind];
-
- ret = lxc_caps_init();
- if (ret)
- return 1;
-
- ret = lxc_fill_namespace_flags(namespaces, &flags);
- if (ret)
- usage(argv[0]);
-
- if (!(flags & CLONE_NEWNET) && my_iflist) {
- ERROR("-i <interfacename> needs -s NETWORK option");
- return 1;
- }
-
- if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) {
- ERROR("-H <hostname> needs -s UTSNAME option");
- return 1;
- }
-
- if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) {
- ERROR("-M needs -s MOUNT option");
- return 1;
- }
-
- pid = lxc_clone(do_start, &start_arg, flags);
- if (pid < 0) {
- ERROR("failed to clone");
- return 1;
- }
-
- if (my_iflist) {
- for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) {
- if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid, NULL) < 0)
- fprintf(stderr,"Could not move interface %s into container %d: %s\n", tmpif->mi_ifname, pid, strerror(errno));
- }
- }
-
- if (daemonize)
- exit(0);
-
- if (waitpid(pid, &status, 0) < 0) {
- ERROR("failed to wait for '%d'", pid);
- return 1;
- }
-
- return lxc_error_set_and_log(pid, status);
-}
diff --git a/src/lxc/lxc_usernsexec.c b/src/lxc/lxc_usernsexec.c
deleted file mode 100644
index 6745ac3..0000000
--- a/src/lxc/lxc_usernsexec.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * (C) Copyright IBM Corp. 2008
- * (C) Copyright Canonical, Inc 2010-2013
- *
- * Authors:
- * Serge Hallyn <serge.hallyn at ubuntu.com>
- * (Once upon a time, this was based on nsexec from the IBM
- * container tools)
- *
- * 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 <stdlib.h>
-#include <unistd.h>
-#include <sched.h>
-#include <sys/syscall.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <libgen.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-#include <sys/wait.h>
-#include <sched.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "conf.h"
-#include "namespace.h"
-#include "utils.h"
-
-#ifndef MS_REC
-#define MS_REC 16384
-#endif
-
-#ifndef MS_SLAVE
-#define MS_SLAVE (1<<19)
-#endif
-
-int unshare(int flags);
-
-static void usage(const char *name)
-{
- printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
- printf("\n");
- printf(" -h this message\n");
- printf("\n");
- printf(" -m <uid-maps> uid maps to use\n");
- printf("\n");
- printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
- printf(" [u|g|b]: map user id, group id, or both\n");
- printf(" ns_id: the base id in the new namespace\n");
- printf(" host_id: the base id in the parent namespace\n");
- printf(" range: how many ids to map\n");
- printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n");
- printf(" As such, /etc/subuid and /etc/subgid must grant the\n");
- printf(" calling user permission to use the mapped ranges\n");
- exit(1);
-}
-
-static void opentty(const char * tty, int which) {
- int fd, flags;
-
- if (tty[0] == '\0')
- return;
-
- fd = open(tty, O_RDWR | O_NONBLOCK);
- if (fd == -1) {
- printf("WARN: could not reopen tty: %s\n", strerror(errno));
- return;
- }
-
- flags = fcntl(fd, F_GETFL);
- flags &= ~O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) < 0) {
- printf("WARN: could not set fd flags: %s\n", strerror(errno));
- return;
- }
-
- close(which);
- if (fd != which) {
- dup2(fd, which);
- close(fd);
- }
-}
-// Code copy end
-
-static int do_child(void *vargv)
-{
- char **argv = (char **)vargv;
-
- // Assume we want to become root
- if (setgid(0) < 0) {
- perror("setgid");
- return -1;
- }
- if (setuid(0) < 0) {
- perror("setuid");
- return -1;
- }
- if (setgroups(0, NULL) < 0) {
- perror("setgroups");
- return -1;
- }
- if (unshare(CLONE_NEWNS) < 0) {
- perror("unshare CLONE_NEWNS");
- return -1;
- }
- if (detect_shared_rootfs()) {
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
- printf("Failed to make / rslave");
- return -1;
- }
- }
- execvp(argv[0], argv);
- perror("execvpe");
- return -1;
-}
-
-static struct lxc_list active_map;
-
-/*
- * given a string like "b:0:100000:10", map both uids and gids
- * 0-10 to 100000 to 100010
- */
-static int parse_map(char *map)
-{
- struct id_map *newmap;
- struct lxc_list *tmp = NULL;
- int ret;
- int i;
- char types[2] = {'u', 'g'};
- char which;
- long host_id, ns_id, range;
-
- if (!map)
- return -1;
-
- ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
- if (ret != 4)
- return -1;
-
- if (which != 'b' && which != 'u' && which != 'g')
- return -1;
-
- for (i = 0; i < 2; i++) {
- if (which != types[i] && which != 'b')
- continue;
-
- newmap = malloc(sizeof(*newmap));
- if (!newmap)
- return -1;
-
- newmap->hostid = host_id;
- newmap->nsid = ns_id;
- newmap->range = range;
-
- if (types[i] == 'u')
- newmap->idtype = ID_TYPE_UID;
- else
- newmap->idtype = ID_TYPE_GID;
-
- tmp = malloc(sizeof(*tmp));
- if (!tmp) {
- free(newmap);
- return -1;
- }
-
- tmp->elem = newmap;
- lxc_list_add_tail(&active_map, tmp);
- }
-
- return 0;
-}
-
-/*
- * This is called if the user did not pass any uid ranges in
- * through -m flags. It's called once to get the default uid
- * map, and once for the default gid map.
- * Go through /etc/subuids and /etc/subgids to find this user's
- * allowed map. We only use the first one for each of uid and
- * gid, because otherwise we're not sure which entries the user
- * wanted.
- */
-static int read_default_map(char *fnam, int which, char *username)
-{
- FILE *fin;
- char *line = NULL;
- size_t sz = 0;
- struct id_map *newmap;
- struct lxc_list *tmp = NULL;
- char *p1, *p2;
-
- fin = fopen(fnam, "r");
- if (!fin)
- return -1;
- while (getline(&line, &sz, fin) != -1) {
- if (sz <= strlen(username) ||
- strncmp(line, username, strlen(username)) != 0 ||
- line[strlen(username)] != ':')
- continue;
- p1 = strchr(line, ':');
- if (!p1)
- continue;
- p2 = strchr(p1+1, ':');
- if (!p2)
- continue;
- newmap = malloc(sizeof(*newmap));
- if (!newmap) {
- fclose(fin);
- free(line);
- return -1;
- }
- newmap->hostid = atol(p1+1);
- newmap->range = atol(p2+1);
- newmap->nsid = 0;
- newmap->idtype = which;
-
- tmp = malloc(sizeof(*tmp));
- if (!tmp) {
- fclose(fin);
- free(line);
- free(newmap);
- return -1;
- }
-
- tmp->elem = newmap;
- lxc_list_add_tail(&active_map, tmp);
- break;
- }
-
- free(line);
- fclose(fin);
- return 0;
-}
-
-static int find_default_map(void)
-{
- struct passwd *p = getpwuid(getuid());
- if (!p)
- return -1;
- if (read_default_map(subuidfile, ID_TYPE_UID, p->pw_name) < 0)
- return -1;
- if (read_default_map(subgidfile, ID_TYPE_GID, p->pw_name) < 0)
- return -1;
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- int c;
- unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
- char ttyname0[256], ttyname1[256], ttyname2[256];
- int status;
- int ret;
- int pid;
- char *default_args[] = {"/bin/sh", NULL};
- char buf[1];
- int pipe1[2], // child tells parent it has unshared
- pipe2[2]; // parent tells child it is mapped and may proceed
-
- memset(ttyname0, '\0', sizeof(ttyname0));
- memset(ttyname1, '\0', sizeof(ttyname1));
- memset(ttyname2, '\0', sizeof(ttyname2));
- if (isatty(0)) {
- ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
- if (ret < 0) {
- perror("unable to open stdin.");
- exit(1);
- }
- ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
- if (ret < 0) {
- printf("Warning: unable to open stdout, continuing.");
- memset(ttyname1, '\0', sizeof(ttyname1));
- }
- ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
- if (ret < 0) {
- printf("Warning: unable to open stderr, continuing.");
- memset(ttyname2, '\0', sizeof(ttyname2));
- }
- }
-
- lxc_list_init(&active_map);
-
- while ((c = getopt(argc, argv, "m:h")) != EOF) {
- switch (c) {
- case 'm': if (parse_map(optarg)) usage(argv[0]); break;
- case 'h':
- default:
- usage(argv[0]);
- }
- };
-
- if (lxc_list_empty(&active_map)) {
- if (find_default_map()) {
- fprintf(stderr, "You have no allocated subuids or subgids\n");
- exit(1);
- }
- }
-
- argv = &argv[optind];
- argc = argc - optind;
- if (argc < 1) {
- argv = default_args;
- argc = 1;
- }
-
- if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
- perror("pipe");
- exit(1);
- }
- if ((pid = fork()) == 0) {
- // Child.
-
- close(pipe1[0]);
- close(pipe2[1]);
- opentty(ttyname0, 0);
- opentty(ttyname1, 1);
- opentty(ttyname2, 2);
-
- ret = unshare(flags);
- if (ret < 0) {
- perror("unshare");
- return 1;
- }
- buf[0] = '1';
- if (write(pipe1[1], buf, 1) < 1) {
- perror("write pipe");
- exit(1);
- }
- if (read(pipe2[0], buf, 1) < 1) {
- perror("read pipe");
- exit(1);
- }
- if (buf[0] != '1') {
- fprintf(stderr, "parent had an error, child exiting\n");
- exit(1);
- }
-
- close(pipe1[1]);
- close(pipe2[0]);
- return do_child((void*)argv);
- }
-
- close(pipe1[1]);
- close(pipe2[0]);
- if (read(pipe1[0], buf, 1) < 1) {
- perror("read pipe");
- exit(1);
- }
-
- buf[0] = '1';
-
- if (lxc_map_ids(&active_map, pid)) {
- fprintf(stderr, "error mapping child\n");
- ret = 0;
- }
- if (write(pipe2[1], buf, 1) < 0) {
- perror("write to pipe");
- exit(1);
- }
-
- if ((ret = waitpid(pid, &status, __WALL)) < 0) {
- printf("waitpid() returns %d, errno %d\n", ret, errno);
- exit(1);
- }
-
- exit(WEXITSTATUS(status));
-}
diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c
deleted file mode 100644
index 712ba52..0000000
--- a/src/lxc/lxc_wait.c
+++ /dev/null
@@ -1,112 +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 <stdio.h>
-#include <string.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/types.h>
-
-#include <lxc/lxccontainer.h>
-
-#include "lxc.h"
-#include "log.h"
-#include "arguments.h"
-
-lxc_log_define(lxc_wait_ui, lxc);
-
-static int my_checker(const struct lxc_arguments* args)
-{
- if (!args->states) {
- lxc_error(args, "missing state option to wait for.");
- return -1;
- }
- return 0;
-}
-
-static int my_parser(struct lxc_arguments* args, int c, char* arg)
-{
- switch (c) {
- case 's': args->states = optarg; break;
- case 't': args->timeout = atol(optarg); break;
- }
- return 0;
-}
-
-static const struct option my_longopts[] = {
- {"state", required_argument, 0, 's'},
- {"timeout", required_argument, 0, 't'},
- LXC_COMMON_OPTIONS
-};
-
-static struct lxc_arguments my_args = {
- .progname = "lxc-wait",
- .help = "\
---name=NAME --state=STATE\n\
-\n\
-lxc-wait waits for NAME container state to reach STATE\n\
-\n\
-Options :\n\
- -n, --name=NAME NAME of the container\n\
- -s, --state=STATE ORed states to wait for\n\
- STOPPED, STARTING, RUNNING, STOPPING,\n\
- ABORTING, FREEZING, FROZEN, THAWED\n\
- -t, --timeout=TMO Seconds to wait for state changes\n",
- .options = my_longopts,
- .parser = my_parser,
- .checker = my_checker,
- .timeout = -1,
-};
-
-int main(int argc, char *argv[])
-{
- struct lxc_container *c;
-
- if (lxc_arguments_parse(&my_args, argc, argv))
- return 1;
-
- if (!my_args.log_file)
- my_args.log_file = "none";
-
- if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
- my_args.progname, my_args.quiet, my_args.lxcpath[0]))
- return 1;
- lxc_log_options_no_override();
-
- c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
- if (!c)
- return 1;
-
- if (!c->may_control(c)) {
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
- lxc_container_put(c);
- return 1;
- }
-
- if (!c->wait(c, my_args.states, my_args.timeout)) {
- lxc_container_put(c);
- return 1;
- }
- return 0;
-}
diff --git a/src/lxc/tools/lxc-checkconfig.in b/src/lxc/tools/lxc-checkconfig.in
new file mode 100644
index 0000000..29586f8
--- /dev/null
+++ b/src/lxc/tools/lxc-checkconfig.in
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+# Allow environment variables to override config
+: ${CONFIG:=/proc/config.gz}
+: ${MODNAME:=configs}
+
+CAT="cat"
+
+if [ -t 1 ]; then
+ SETCOLOR_SUCCESS="printf \\033[1;32m"
+ SETCOLOR_FAILURE="printf \\033[1;31m"
+ SETCOLOR_WARNING="printf \\033[1;33m"
+ SETCOLOR_NORMAL="printf \\033[0;39m"
+else
+ SETCOLOR_SUCCESS=":"
+ SETCOLOR_FAILURE=":"
+ SETCOLOR_WARNING=":"
+ SETCOLOR_NORMAL=":"
+fi
+
+is_set() {
+ $CAT $CONFIG | grep "$1=[y|m]" > /dev/null
+ return $?
+}
+
+is_enabled() {
+ mandatory=$2
+
+ is_set $1
+ RES=$?
+
+ if [ $RES -eq 0 ]; then
+ $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
+ else
+ if [ ! -z "$mandatory" ] && [ "$mandatory" = yes ]; then
+ $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL
+ else
+ $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL
+ fi
+ fi
+}
+
+if [ ! -f $CONFIG ]; then
+ echo "Kernel configuration not found at $CONFIG; searching..."
+ KVER="`uname -r`"
+ HEADERS_CONFIG="/lib/modules/$KVER/build/.config"
+ BOOT_CONFIG="/boot/config-$KVER"
+ [ -f "${HEADERS_CONFIG}" ] && CONFIG=${HEADERS_CONFIG}
+ [ -f "${BOOT_CONFIG}" ] && CONFIG=${BOOT_CONFIG}
+ if [ ! -f "$CONFIG" ]; then
+ MODULEFILE=$(modinfo -k $KVER -n $MODNAME 2> /dev/null)
+ # don't want to modprobe, so give user a hint
+ # although scripts/extract-ikconfig could be used to extract contents without loading kernel module
+ # http://svn.pld-linux.org/trac/svn/browser/geninitrd/trunk/geninitrd?rev=12696#L327
+ fi
+ if [ ! -f $CONFIG ]; then
+ echo "$(basename $0): unable to retrieve kernel configuration" >&2
+ echo >&2
+ if [ -f "$MODULEFILE" ]; then
+ echo "Try modprobe $MODNAME module, or" >&2
+ fi
+ echo "Try recompiling with IKCONFIG_PROC, installing the kernel headers," >&2
+ echo "or specifying the kernel configuration path with:" >&2
+ echo " CONFIG=<path> $(basename $0)" >&2
+ exit 1
+ else
+ echo "Kernel configuration found at $CONFIG"
+ fi
+fi
+
+if gunzip -tq < $CONFIG 2>/dev/null; then
+ CAT="zcat"
+fi
+
+echo "--- Namespaces ---"
+echo -n "Namespaces: " && is_enabled CONFIG_NAMESPACES yes
+echo -n "Utsname namespace: " && is_enabled CONFIG_UTS_NS
+echo -n "Ipc namespace: " && is_enabled CONFIG_IPC_NS yes
+echo -n "Pid namespace: " && is_enabled CONFIG_PID_NS yes
+echo -n "User namespace: " && is_enabled CONFIG_USER_NS
+echo -n "Network namespace: " && is_enabled CONFIG_NET_NS
+echo -n "Multiple /dev/pts instances: " && is_enabled DEVPTS_MULTIPLE_INSTANCES
+echo
+echo "--- Control groups ---"
+
+print_cgroups() {
+ # print all mountpoints for cgroup filesystems
+ awk '$1 !~ /#/ && $3 == mp { print $2; } ; END { exit(0); } ' "mp=$1" "$2" ;
+}
+
+CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -n 1`
+KVER_MAJOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
+ sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/')
+if [ "$KVER_MAJOR" = "2" ]; then
+KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
+ sed -r 's/.* 2.6.([0-9]{2}).*/\1/')
+else
+KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \
+ sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/')
+fi
+
+echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes
+
+if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then
+ echo -n "Cgroup clone_children flag: " &&
+ $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
+else
+ echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes
+fi
+echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE
+echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED
+echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT
+echo -n "Cgroup memory controller: "
+if ([ $KVER_MAJOR -ge 3 ] && [ $KVER_MINOR -ge 6 ]) || ([ $KVER_MAJOR -gt 3 ]); then
+ is_enabled CONFIG_MEMCG
+else
+ is_enabled CONFIG_CGROUP_MEM_RES_CTLR
+fi
+is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS
+echo
+echo "--- Misc ---"
+echo -n "Veth pair device: " && is_enabled CONFIG_VETH
+echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN
+echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q
+echo -n "Bridges: " && is_enabled CONFIG_BRIDGE
+echo -n "Advanced netfilter: " && is_enabled CONFIG_NETFILTER_ADVANCED
+echo -n "CONFIG_NF_NAT_IPV4: " && is_enabled CONFIG_NF_NAT_IPV4
+echo -n "CONFIG_NF_NAT_IPV6: " && is_enabled CONFIG_NF_NAT_IPV6
+echo -n "CONFIG_IP_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP_NF_TARGET_MASQUERADE
+echo -n "CONFIG_IP6_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP6_NF_TARGET_MASQUERADE
+echo -n "CONFIG_NETFILTER_XT_TARGET_CHECKSUM: " && is_enabled CONFIG_NETFILTER_XT_TARGET_CHECKSUM
+echo -n "FUSE (for use with lxcfs): " && is_enabled CONFIG_FUSE_FS
+
+echo
+echo "--- Checkpoint/Restore ---"
+echo -n "checkpoint restore: " && is_enabled CONFIG_CHECKPOINT_RESTORE
+echo -n "CONFIG_FHANDLE: " && is_enabled CONFIG_FHANDLE
+echo -n "CONFIG_EVENTFD: " && is_enabled CONFIG_EVENTFD
+echo -n "CONFIG_EPOLL: " && is_enabled CONFIG_EPOLL
+echo -n "CONFIG_UNIX_DIAG: " && is_enabled CONFIG_UNIX_DIAG
+echo -n "CONFIG_INET_DIAG: " && is_enabled CONFIG_INET_DIAG
+echo -n "CONFIG_PACKET_DIAG: " && is_enabled CONFIG_PACKET_DIAG
+echo -n "CONFIG_NETLINK_DIAG: " && is_enabled CONFIG_NETLINK_DIAG
+
+echo -n "File capabilities: " && \
+ ( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \
+ is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || \
+ ( ( [ "${KVER_MAJOR}" = "2" ] && [ ${KVER_MINOR} -gt 32 ] ) || \
+ [ ${KVER_MAJOR} -gt 2 ] && $SETCOLOR_SUCCESS && \
+ echo "enabled" && $SETCOLOR_NORMAL )
+
+echo
+echo "Note : Before booting a new kernel, you can check its configuration"
+echo "usage : CONFIG=/path/to/config $0"
+echo
+
diff --git a/src/lxc/tools/lxc-start-ephemeral.in b/src/lxc/tools/lxc-start-ephemeral.in
new file mode 100644
index 0000000..7e0c8ea
--- /dev/null
+++ b/src/lxc/tools/lxc-start-ephemeral.in
@@ -0,0 +1,412 @@
+#!/usr/bin/env python3
+#
+# lxc-start-ephemeral: Start a copy of a container using an overlay
+#
+# This python implementation is based on the work done in the original
+# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
+#
+# (C) Copyright Canonical Ltd. 2012
+#
+# Authors:
+# Stéphane Graber <stgraber at ubuntu.com>
+#
+# 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
+#
+
+import argparse
+import gettext
+import lxc
+import os
+import sys
+import subprocess
+import tempfile
+
+_ = gettext.gettext
+gettext.textdomain("lxc-start-ephemeral")
+
+# Other functions
+
+
+def printstderr(*args):
+ print("lxc-start-ephemeral is deprecated in favor of lxc-copy\n",
+ *args, file=sys.stderr)
+
+
+def randomMAC():
+ import random
+
+ mac = [0x00, 0x16, 0x3e,
+ random.randint(0x00, 0x7f),
+ random.randint(0x00, 0xff),
+ random.randint(0x00, 0xff)]
+ return ':'.join(map(lambda x: "%02x" % x, mac))
+
+
+def get_rundir():
+ if os.geteuid() == 0:
+ return "@RUNTIME_PATH@"
+
+ if "XDG_RUNTIME_DIR" in os.environ:
+ return os.environ["XDG_RUNTIME_DIR"]
+
+ if "HOME" in os.environ:
+ return "%s/.cache/lxc/run/" % os.environ["HOME"]
+
+ raise Exception("Unable to find a runtime directory")
+
+
+# Inform that lxc-start-ephemeral is deprecated
+printstderr()
+
+# Begin parsing the command line
+parser = argparse.ArgumentParser(description=_(
+ "LXC: Start an ephemeral container"),
+ formatter_class=argparse.RawTextHelpFormatter,
+ epilog=_("If a COMMAND is given, then the "
+ """container will run only as long
+as the command runs.
+If no COMMAND is given, this command will attach to tty1 and stop the
+container when exiting (with ctrl-a-q).
+
+If no COMMAND is given and -d is used, the name and IP addresses of the
+container will be printed to the console."""))
+
+parser.add_argument("--lxcpath", "-P", dest="lxcpath", metavar="PATH",
+ help=_("Use specified container path"), default=None)
+
+parser.add_argument("--orig", "-o", type=str, required=True,
+ help=_("name of the original container"))
+
+parser.add_argument("--name", "-n", type=str,
+ help=_("name of the target container"))
+
+parser.add_argument("--bdir", "-b", type=str, action="append", default=[],
+ help=_("directory to bind mount into container, "
+ "either --bdir=/src-path or --bdir=/src-path:/dst-path"))
+
+parser.add_argument("--cdir", "-c", type=str, action="append", default=[],
+ help=_("directory to cow mount into container"))
+
+parser.add_argument("--user", "-u", type=str,
+ help=_("the user to run the command as"))
+
+parser.add_argument("--key", "-S", type=str,
+ help=_("the path to the key to use to connect "
+ "(when using ssh)"))
+
+parser.add_argument("--daemon", "-d", action="store_true",
+ help=_("run in the background"))
+
+parser.add_argument("--storage-type", "-s", type=str, default=None,
+ choices=("tmpfs", "dir"),
+ help=("type of storage use by the container"))
+
+parser.add_argument("--union-type", "-U", type=str, default="overlayfs",
+ choices=("overlayfs", "aufs"),
+ help=_("type of union (overlayfs or aufs), "
+ "defaults to overlayfs."))
+
+parser.add_argument("--keep-data", "-k", action="store_true",
+ help=_("don't wipe everything clean at the end"))
+
+parser.add_argument("command", metavar='CMD', type=str, nargs="*",
+ help=_("Run specific command in container "
+ "(command as argument)"))
+
+parser.add_argument("--version", action="version", version=lxc.version)
+
+args = parser.parse_args()
+
+# Check that -d and CMD aren't used at the same time
+if args.command and args.daemon:
+ parser.error(_("You can't use -d and a command at the same time."))
+
+# Check that -k isn't used with -s tmpfs
+if not args.storage_type:
+ if args.keep_data:
+ args.storage_type = "dir"
+ else:
+ args.storage_type = "tmpfs"
+
+if args.keep_data and args.storage_type == "tmpfs":
+ parser.error(_("You can't use -k with the tmpfs storage type."))
+
+# Load the orig container
+orig = lxc.Container(args.orig, args.lxcpath)
+if not orig.defined:
+ parser.error(_("Source container '%s' doesn't exist." % args.orig))
+
+# Create the new container paths
+if not args.lxcpath:
+ lxc_path = lxc.default_config_path
+else:
+ lxc_path = args.lxcpath
+
+if args.name:
+ if os.path.exists("%s/%s" % (lxc_path, args.name)):
+ parser.error(_("A container named '%s' already exists." % args.name))
+ dest_path = "%s/%s" % (lxc_path, args.name)
+ os.mkdir(dest_path)
+else:
+ dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir=lxc_path)
+os.mkdir(os.path.join(dest_path, "rootfs"))
+os.chmod(dest_path, 0o770)
+
+# Setup the new container's configuration
+dest = lxc.Container(os.path.basename(dest_path), args.lxcpath)
+dest.load_config(orig.config_file_name)
+dest.set_config_item("lxc.utsname", dest.name)
+dest.set_config_item("lxc.rootfs", os.path.join(dest_path, "rootfs"))
+print("setting rootfs to .%s.", os.path.join(dest_path, "rootfs"))
+for nic in dest.network:
+ if hasattr(nic, 'hwaddr'):
+ nic.hwaddr = randomMAC()
+
+overlay_dirs = [(orig.get_config_item("lxc.rootfs"), "%s/rootfs/" % dest_path)]
+
+# Generate a new fstab
+if orig.get_config_item("lxc.mount"):
+ dest.set_config_item("lxc.mount", os.path.join(dest_path, "fstab"))
+ with open(orig.get_config_item("lxc.mount"), "r") as orig_fd:
+ with open(dest.get_config_item("lxc.mount"), "w+") as dest_fd:
+ for line in orig_fd.read().split("\n"):
+ # Start by replacing any reference to the container rootfs
+ line.replace(orig.get_config_item("lxc.rootfs"),
+ dest.get_config_item("lxc.rootfs"))
+
+ fields = line.split()
+
+ # Skip invalid entries
+ if len(fields) < 4:
+ continue
+
+ # Non-bind mounts are kept as-is
+ if "bind" not in fields[3]:
+ dest_fd.write("%s\n" % line)
+ continue
+
+ # Bind mounts of virtual filesystems are also kept as-is
+ src_path = fields[0].split("/")
+ if len(src_path) > 1 and src_path[1] in ("proc", "sys"):
+ dest_fd.write("%s\n" % line)
+ continue
+
+ # Skip invalid mount points
+ dest_mount = os.path.abspath(os.path.join("%s/rootfs/" % (
+ dest_path), fields[1]))
+
+ if "%s/rootfs/" % dest_path not in dest_mount:
+ print(_("Skipping mount entry '%s' as it's outside "
+ "of the container rootfs.") % line)
+
+ # Setup an overlay for anything remaining
+ overlay_dirs += [(fields[0], dest_mount)]
+
+for entry in args.cdir:
+ if not os.path.exists(entry):
+ print(_("Path '%s' doesn't exist, won't be cow-mounted.") %
+ entry)
+ else:
+ src_path = os.path.abspath(entry)
+ dst_path = "%s/rootfs/%s" % (dest_path, src_path)
+ overlay_dirs += [(src_path, dst_path)]
+
+# do we have the new overlay fs which requires workdir, or the older
+# overlayfs which does not?
+have_new_overlay = False
+with open("/proc/filesystems", "r") as fd:
+ for line in fd:
+ if line == "nodev\toverlay\n":
+ have_new_overlay = True
+
+# Generate pre-mount script
+with open(os.path.join(dest_path, "pre-mount"), "w+") as fd:
+ os.fchmod(fd.fileno(), 0o755)
+ fd.write("""#!/bin/sh
+LXC_DIR="%s"
+LXC_BASE="%s"
+LXC_NAME="%s"
+""" % (dest_path, orig.name, dest.name))
+
+ count = 0
+ for entry in overlay_dirs:
+ tmpdir = "%s/tmpfs" % dest_path
+ fd.write("mkdir -p %s\n" % (tmpdir))
+ if args.storage_type == "tmpfs":
+ fd.write("mount -n -t tmpfs -o mode=0755 none %s\n" % (tmpdir))
+ deltdir = "%s/delta%s" % (tmpdir, count)
+ workdir = "%s/work%s" % (tmpdir, count)
+ fd.write("mkdir -p %s %s\n" % (deltdir, entry[1]))
+ if have_new_overlay:
+ fd.write("mkdir -p %s\n" % workdir)
+
+ fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" %
+ (entry[0], deltdir))
+ fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" %
+ (entry[0], entry[1]))
+
+ if args.union_type == "overlayfs":
+ if have_new_overlay:
+ fd.write("mount -n -t overlay"
+ " -oupperdir=%s,lowerdir=%s,workdir=%s none %s\n" % (
+ deltdir,
+ entry[0],
+ workdir,
+ entry[1]))
+ else:
+ fd.write("mount -n -t overlayfs"
+ " -oupperdir=%s,lowerdir=%s none %s\n" % (
+ deltdir,
+ entry[0],
+ entry[1]))
+ elif args.union_type == "aufs":
+ xino_path = "/dev/shm/aufs.xino"
+ if not os.path.exists(os.path.basename(xino_path)):
+ os.makedirs(os.path.basename(xino_path))
+
+ fd.write("mount -n -t aufs "
+ "-o br=%s=rw:%s=ro,noplink,xino=%s none %s\n" % (
+ deltdir,
+ entry[0],
+ xino_path,
+ entry[1]))
+ count += 1
+
+ for entry in args.bdir:
+ if ':' in entry:
+ src_path, dst_path = entry.split(":")
+ else:
+ src_path = entry
+ dst_path = os.path.abspath(entry)
+
+ if not os.path.exists(src_path):
+ print(_("Path '%s' doesn't exist, won't be bind-mounted.") %
+ src_path)
+ else:
+ src_path = os.path.abspath(src_path)
+ dst_path = "%s/rootfs/%s" % (dest_path, dst_path)
+ fd.write("mkdir -p %s\nmount -n --bind %s %s\n" % (
+ dst_path, src_path, dst_path))
+
+ fd.write("""
+[ -e $LXC_DIR/configured ] && exit 0
+for file in $LXC_DIR/rootfs/etc/hostname \\
+ $LXC_DIR/rootfs/etc/hosts \\
+ $LXC_DIR/rootfs/etc/sysconfig/network \\
+ $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do
+ [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file
+done
+touch $LXC_DIR/configured
+""")
+
+dest.set_config_item("lxc.hook.pre-mount",
+ os.path.join(dest_path, "pre-mount"))
+
+if not args.keep_data:
+ dest.set_config_item("lxc.ephemeral", "1")
+
+dest.save_config()
+
+# Start the container
+if not dest.start() or not dest.wait("RUNNING", timeout=5):
+ print(_("The container '%s' failed to start.") % dest.name)
+ dest.stop()
+ if dest.defined:
+ dest.destroy()
+ sys.exit(1)
+
+# Deal with the case where we just attach to the container's console
+if not args.command and not args.daemon:
+ dest.console()
+ if not dest.shutdown(timeout=5):
+ dest.stop()
+ sys.exit(0)
+
+# Try to get the IP addresses
+ips = dest.get_ips(timeout=10)
+
+# Deal with the case where we just print info about the container
+if args.daemon:
+ print(_("""The ephemeral container is now started.
+
+You can enter it from the command line with: lxc-console -n %s
+The following IP addresses have be found in the container:
+%s""") % (dest.name,
+ "\n".join([" - %s" % entry for entry in ips]
+ or [" - %s" % _("No address could be found")])))
+ sys.exit(0)
+
+# Now deal with the case where we want to run a command in the container
+if not ips:
+ print(_("Failed to get an IP for container '%s'.") % dest.name)
+ dest.stop()
+ if dest.defined:
+ dest.destroy()
+ sys.exit(1)
+
+if os.path.exists("/proc/self/ns/pid"):
+ def attach_as_user(command):
+ try:
+ username = "root"
+ if args.user:
+ username = args.user
+
+ line = subprocess.check_output(
+ ["getent", "passwd", username],
+ universal_newlines=True).rstrip("\n")
+ _, _, pw_uid, pw_gid, _, pw_dir, _ = line.split(":", 6)
+ pw_uid = int(pw_uid)
+ pw_gid = int(pw_gid)
+ os.setgid(pw_gid)
+ os.initgroups(username, pw_gid)
+ os.setuid(pw_uid)
+ os.chdir(pw_dir)
+ os.environ['HOME'] = pw_dir
+ except:
+ print(_("Unable to switch to user: %s" % username))
+ sys.exit(1)
+
+ return lxc.attach_run_command(command)
+
+ retval = dest.attach_wait(attach_as_user, args.command,
+ env_policy=lxc.LXC_ATTACH_CLEAR_ENV)
+
+else:
+ cmd = ["ssh",
+ "-o", "StrictHostKeyChecking=no",
+ "-o", "UserKnownHostsFile=/dev/null"]
+
+ if args.user:
+ cmd += ["-l", args.user]
+
+ if args.key:
+ cmd += ["-i", args.key]
+
+ for ip in ips:
+ ssh_cmd = cmd + [ip] + args.command
+ retval = subprocess.call(ssh_cmd, universal_newlines=True)
+ if retval == 255:
+ print(_("SSH failed to connect, trying next IP address."))
+ continue
+
+ if retval != 0:
+ print(_("Command returned with non-zero return code: %s") % retval)
+ break
+
+# Shutdown the container
+if not dest.shutdown(timeout=5):
+ dest.stop()
+
+sys.exit(retval)
diff --git a/src/lxc/tools/lxc-top.lua b/src/lxc/tools/lxc-top.lua
new file mode 100755
index 0000000..b5b3a69
--- /dev/null
+++ b/src/lxc/tools/lxc-top.lua
@@ -0,0 +1,243 @@
+#!/usr/bin/env lua
+--
+-- top(1) like monitor for lxc containers
+--
+-- Copyright © 2012 Oracle.
+--
+-- Authors:
+-- Dwight Engen <dwight.engen at oracle.com>
+--
+-- This library is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License version 2, as
+-- published by the Free Software Foundation.
+--
+-- This program 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 General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along
+-- with this program; if not, write to the Free Software Foundation, Inc.,
+-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+--
+
+local lxc = require("lxc")
+local core = require("lxc.core")
+local getopt = require("alt_getopt")
+
+local USER_HZ = 100
+local ESC = string.format("%c", 27)
+local TERMCLEAR = ESC.."[H"..ESC.."[J"
+local TERMNORM = ESC.."[0m"
+local TERMBOLD = ESC.."[1m"
+local TERMRVRS = ESC.."[7m"
+
+local containers = {}
+local stats = {}
+local stats_total = {}
+local max_containers
+
+function printf(...)
+ local function wrapper(...) io.write(string.format(...)) end
+ local status, result = pcall(wrapper, ...)
+ if not status then
+ error(result, 2)
+ end
+end
+
+function string:split(delim, max_cols)
+ local cols = {}
+ local start = 1
+ local nextc
+ repeat
+ nextc = string.find(self, delim, start)
+ if (nextc and #cols ~= max_cols - 1) then
+ table.insert(cols, string.sub(self, start, nextc-1))
+ start = nextc + #delim
+ else
+ table.insert(cols, string.sub(self, start, string.len(self)))
+ nextc = nil
+ end
+ until nextc == nil or start > #self
+ return cols
+end
+
+function strsisize(size, width)
+ local KiB = 1024
+ local MiB = 1048576
+ local GiB = 1073741824
+ local TiB = 1099511627776
+ local PiB = 1125899906842624
+ local EiB = 1152921504606846976
+ local ZiB = 1180591620717411303424
+
+ if (size >= ZiB) then
+ return string.format("%d.%2.2d ZB", size / ZiB, (math.floor(size % ZiB) * 100) / ZiB)
+ end
+ if (size >= EiB) then
+ return string.format("%d.%2.2d EB", size / EiB, (math.floor(size % EiB) * 100) / EiB)
+ end
+ if (size >= PiB) then
+ return string.format("%d.%2.2d PB", size / PiB, (math.floor(size % PiB) * 100) / PiB)
+ end
+ if (size >= TiB) then
+ return string.format("%d.%2.2d TB", size / TiB, (math.floor(size % TiB) * 100) / TiB)
+ end
+ if (size >= GiB) then
+ return string.format("%d.%2.2d GB", size / GiB, (math.floor(size % GiB) * 100) / GiB)
+ end
+ if (size >= MiB) then
+ return string.format("%d.%2.2d MB", size / MiB, (math.floor(size % MiB) * 1000) / (MiB * 10))
+ end
+ if (size >= KiB) then
+ return string.format("%d.%2.2d KB", size / KiB, (math.floor(size % KiB) * 1000) / (KiB * 10))
+ end
+ return string.format("%3d.00 ", size)
+end
+
+function tty_lines()
+ local rows = 25
+ local f = assert(io.popen("stty -a | head -n 1"))
+ for line in f:lines() do
+ local stty_rows
+ _,_,stty_rows = string.find(line, "rows (%d+)")
+ if (stty_rows ~= nil) then
+ rows = stty_rows
+ break
+ end
+ end
+ f:close()
+ return rows
+end
+
+function container_sort(a, b)
+ if (optarg["r"]) then
+ if (optarg["s"] == "n") then return (a > b)
+ elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos < stats[b].cpu_use_nanos)
+ elseif (optarg["s"] == "d") then return (stats[a].blkio < stats[b].blkio)
+ elseif (optarg["s"] == "m") then return (stats[a].mem_used < stats[b].mem_used)
+ elseif (optarg["s"] == "k") then return (stats[a].kmem_used < stats[b].kmem_used)
+ end
+ else
+ if (optarg["s"] == "n") then return (a < b)
+ elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos > stats[b].cpu_use_nanos)
+ elseif (optarg["s"] == "d") then return (stats[a].blkio > stats[b].blkio)
+ elseif (optarg["s"] == "m") then return (stats[a].mem_used > stats[b].mem_used)
+ elseif (optarg["s"] == "k") then return (stats[a].kmem_used > stats[b].kmem_used)
+ end
+ end
+end
+
+function container_list_update()
+ local now_running
+
+ now_running = lxc.containers_running(true)
+
+ -- check for newly started containers
+ for _,v in ipairs(now_running) do
+ if (containers[v] == nil) then
+ local ct = lxc.container:new(v)
+ -- note, this is a "mixed" table, ie both dictionary and list
+ containers[v] = ct
+ table.insert(containers, v)
+ end
+ end
+
+ -- check for newly stopped containers
+ local indx = 1
+ while (indx <= #containers) do
+ local ctname = containers[indx]
+ if (now_running[ctname] == nil) then
+ containers[ctname] = nil
+ stats[ctname] = nil
+ table.remove(containers, indx)
+ else
+ indx = indx + 1
+ end
+ end
+
+ -- get stats for all current containers and resort the list
+ lxc.stats_clear(stats_total)
+ for _,ctname in ipairs(containers) do
+ stats[ctname] = containers[ctname]:stats_get(stats_total)
+ end
+ table.sort(containers, container_sort)
+end
+
+function stats_print_header(stats_total)
+ printf(TERMRVRS .. TERMBOLD)
+ printf("%-15s %8s %8s %8s %10s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem")
+ if (stats_total.kmem_used > 0) then printf(" %10s", "KMem") end
+ printf("\n")
+
+ printf("%-15s %8s %8s %8s %10s %10s", "Name", "Used", "Sys", "User", "Total", "Used")
+ if (stats_total.kmem_used > 0) then printf(" %10s", "Used") end
+ printf("\n")
+ printf(TERMNORM)
+end
+
+function stats_print(name, stats, stats_total)
+ printf("%-15s %8.2f %8.2f %8.2f %10s %10s",
+ name,
+ stats.cpu_use_nanos / 1000000000,
+ stats.cpu_use_sys / USER_HZ,
+ stats.cpu_use_user / USER_HZ,
+ strsisize(stats.blkio),
+ strsisize(stats.mem_used))
+ if (stats_total.kmem_used > 0) then
+ printf(" %10s", strsisize(stats.kmem_used))
+ end
+end
+
+function usage()
+ printf("Usage: lxc-top [options]\n" ..
+ " -h|--help print this help message\n" ..
+ " -m|--max display maximum number of containers\n" ..
+ " -d|--delay delay in seconds between refreshes (default: 3.0)\n" ..
+ " -s|--sort sort by [n,c,d,m] (default: n) where\n" ..
+ " n = Name\n" ..
+ " c = CPU use\n" ..
+ " d = Disk I/O use\n" ..
+ " m = Memory use\n" ..
+ " k = Kernel memory use\n" ..
+ " -r|--reverse sort in reverse (descending) order\n"
+ )
+ os.exit(1)
+end
+
+local long_opts = {
+ help = "h",
+ delay = "d",
+ max = "m",
+ reverse = "r",
+ sort = "s",
+}
+
+optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts)
+optarg["d"] = tonumber(optarg["d"]) or 3.0
+optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3)
+optarg["r"] = optarg["r"] or false
+optarg["s"] = optarg["s"] or "n"
+if (optarg["h"] ~= nil) then
+ usage()
+end
+
+while true
+do
+ container_list_update()
+ -- if some terminal we care about doesn't support the simple escapes, we
+ -- may fall back to this, or ncurses. ug.
+ --os.execute("tput clear")
+ printf(TERMCLEAR)
+ stats_print_header(stats_total)
+ for index,ctname in ipairs(containers) do
+ stats_print(ctname, stats[ctname], stats_total)
+ printf("\n")
+ if (index >= optarg["m"]) then
+ break
+ end
+ end
+ stats_print(string.format("TOTAL (%-2d)", #containers), stats_total, stats_total)
+ io.flush()
+ core.usleep(optarg["d"] * 1000000)
+end
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
new file mode 100644
index 0000000..58f658b
--- /dev/null
+++ b/src/lxc/tools/lxc_attach.c
@@ -0,0 +1,440 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2010
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "attach.h"
+#include "arguments.h"
+#include "caps.h"
+#include "confile.h"
+#include "console.h"
+#include "log.h"
+#include "list.h"
+#include "mainloop.h"
+#include "utils.h"
+
+#if HAVE_PTY_H
+#include <pty.h>
+#else
+#include <../include/openpty.h>
+#endif
+
+lxc_log_define(lxc_attach_ui, lxc);
+
+static const struct option my_longopts[] = {
+ {"elevated-privileges", optional_argument, 0, 'e'},
+ {"arch", required_argument, 0, 'a'},
+ {"namespaces", required_argument, 0, 's'},
+ {"remount-sys-proc", no_argument, 0, 'R'},
+ /* TODO: decide upon short option names */
+ {"clear-env", no_argument, 0, 500},
+ {"keep-env", no_argument, 0, 501},
+ {"keep-var", required_argument, 0, 502},
+ {"set-var", required_argument, 0, 'v'},
+ {"pty-log", required_argument, 0, 'L'},
+ LXC_COMMON_OPTIONS
+};
+
+static int elevated_privileges = 0;
+static signed long new_personality = -1;
+static int namespace_flags = -1;
+static int remount_sys_proc = 0;
+static lxc_attach_env_policy_t env_policy = LXC_ATTACH_KEEP_ENV;
+static char **extra_env = NULL;
+static ssize_t extra_env_size = 0;
+static char **extra_keep = NULL;
+static ssize_t extra_keep_size = 0;
+
+static int add_to_simple_array(char ***array, ssize_t *capacity, char *value)
+{
+ ssize_t count = 0;
+
+ assert(array);
+
+ if (*array)
+ for (; (*array)[count]; count++);
+
+ /* we have to reallocate */
+ if (count >= *capacity - 1) {
+ ssize_t new_capacity = ((count + 1) / 32 + 1) * 32;
+ char **new_array = realloc((void*)*array, sizeof(char *) * new_capacity);
+ if (!new_array)
+ return -1;
+ memset(&new_array[count], 0, sizeof(char*)*(new_capacity - count));
+ *array = new_array;
+ *capacity = new_capacity;
+ }
+
+ assert(*array);
+
+ (*array)[count] = value;
+ return 0;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ int ret;
+
+ switch (c) {
+ case 'e':
+ ret = lxc_fill_elevated_privileges(arg, &elevated_privileges);
+ if (ret)
+ return -1;
+ break;
+ case 'R': remount_sys_proc = 1; break;
+ case 'a':
+ new_personality = lxc_config_parse_arch(arg);
+ if (new_personality < 0) {
+ lxc_error(args, "invalid architecture specified: %s", arg);
+ return -1;
+ }
+ break;
+ case 's':
+ namespace_flags = 0;
+ ret = lxc_fill_namespace_flags(arg, &namespace_flags);
+ if (ret)
+ return -1;
+ /* -s implies -e */
+ lxc_fill_elevated_privileges(NULL, &elevated_privileges);
+ break;
+ case 500: /* clear-env */
+ env_policy = LXC_ATTACH_CLEAR_ENV;
+ break;
+ case 501: /* keep-env */
+ env_policy = LXC_ATTACH_KEEP_ENV;
+ break;
+ case 502: /* keep-var */
+ ret = add_to_simple_array(&extra_keep, &extra_keep_size, arg);
+ if (ret < 0) {
+ lxc_error(args, "memory allocation error");
+ return -1;
+ }
+ break;
+ case 'v':
+ ret = add_to_simple_array(&extra_env, &extra_env_size, arg);
+ if (ret < 0) {
+ lxc_error(args, "memory allocation error");
+ return -1;
+ }
+ break;
+ case 'L':
+ args->console_log = arg;
+ break;
+ }
+
+ return 0;
+}
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-attach",
+ .help = "\
+--name=NAME [-- COMMAND]\n\
+\n\
+Execute the specified COMMAND - enter the container NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -e, --elevated-privileges=PRIVILEGES\n\
+ Use elevated privileges instead of those of the\n\
+ container. If you don't specify privileges to be\n\
+ elevated as OR'd list: CAP, CGROUP and LSM (capabilities,\n\
+ cgroup and restrictions, respectively) then all of them\n\
+ will be elevated.\n\
+ WARNING: This may leak privileges into the container.\n\
+ Use with care.\n\
+ -a, --arch=ARCH Use ARCH for program instead of container's own\n\
+ architecture.\n\
+ -s, --namespaces=FLAGS\n\
+ Don't attach to all the namespaces of the container\n\
+ but just to the following OR'd list of flags:\n\
+ MOUNT, PID, UTSNAME, IPC, USER or NETWORK.\n\
+ WARNING: Using -s implies -e with all privileges\n\
+ elevated, it may therefore leak privileges into the\n\
+ container. Use with care.\n\
+ -R, --remount-sys-proc\n\
+ Remount /sys and /proc if not attaching to the\n\
+ mount namespace when using -s in order to properly\n\
+ reflect the correct namespace context. See the\n\
+ lxc-attach(1) manual page for details.\n\
+ --clear-env Clear all environment variables before attaching.\n\
+ The attached shell/program will start with only\n\
+ container=lxc set.\n\
+ --keep-env Keep all current environment variables. This\n\
+ is the current default behaviour, but is likely to\n\
+ change in the future.\n\
+ -L, --pty-log=FILE\n\
+ Log pty output to FILE\n\
+ -v, --set-var Set an additional variable that is seen by the\n\
+ attached program in the container. May be specified\n\
+ multiple times.\n\
+ --keep-var Keep an additional environment variable. Only\n\
+ applicable if --clear-env is specified. May be used\n\
+ multiple times.\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+};
+
+struct wrapargs {
+ lxc_attach_options_t *options;
+ lxc_attach_command_t *command;
+ struct lxc_console *console;
+ int ptyfd;
+};
+
+/* Minimalistic login_tty() implementation. */
+static int login_pty(int fd)
+{
+ setsid();
+ if (ioctl(fd, TIOCSCTTY, NULL) < 0)
+ return -1;
+ if (lxc_console_set_stdfds(fd) < 0)
+ return -1;
+ if (fd > STDERR_FILENO)
+ close(fd);
+ return 0;
+}
+
+static int get_pty_on_host_callback(void *p)
+{
+ struct wrapargs *wrap = p;
+
+ close(wrap->console->master);
+ if (login_pty(wrap->console->slave) < 0)
+ return -1;
+
+ if (wrap->command->program)
+ lxc_attach_run_command(wrap->command);
+ else
+ lxc_attach_run_shell(NULL);
+ return -1;
+}
+
+static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *pid)
+{
+ int ret = -1;
+ struct wrapargs *args = wrap;
+ struct lxc_epoll_descr descr;
+ struct lxc_conf *conf;
+ struct lxc_tty_state *ts;
+
+ INFO("Trying to allocate a pty on the host");
+
+ if (!isatty(args->ptyfd)) {
+ ERROR("Standard file descriptor does not refer to a pty\n.");
+ return -1;
+ }
+
+ conf = c->lxc_conf;
+ free(conf->console.log_path);
+ if (my_args.console_log)
+ conf->console.log_path = strdup(my_args.console_log);
+ else
+ conf->console.log_path = NULL;
+
+ /* In the case of lxc-attach our peer pty will always be the current
+ * controlling terminal. We clear whatever was set by the user for
+ * lxc.console.path here and set it to "/dev/tty". Doing this will (a)
+ * prevent segfaults when the container has been setup with
+ * lxc.console = none and (b) provide an easy way to ensure that we
+ * always do the correct thing. strdup() must be used since console.path
+ * is free()ed when we call lxc_container_put(). */
+ free(conf->console.path);
+ conf->console.path = strdup("/dev/tty");
+ if (!conf->console.path)
+ return -1;
+
+ /* Create pty on the host. */
+ if (lxc_console_create(conf) < 0)
+ return -1;
+ ts = conf->console.tty_state;
+ conf->console.descr = &descr;
+
+ /* Shift ttys to container. */
+ if (ttys_shift_ids(conf) < 0) {
+ ERROR("Failed to shift tty into container");
+ goto err1;
+ }
+
+ /* Send wrapper function on its way. */
+ wrap->console = &conf->console;
+ if (c->attach(c, get_pty_on_host_callback, wrap, wrap->options, pid) < 0)
+ goto err1;
+ close(conf->console.slave); /* Close slave side. */
+
+ ret = lxc_mainloop_open(&descr);
+ if (ret) {
+ ERROR("failed to create mainloop");
+ goto err2;
+ }
+
+ if (lxc_console_mainloop_add(&descr, conf) < 0) {
+ ERROR("Failed to add handlers to lxc mainloop.");
+ goto err3;
+ }
+
+ ret = lxc_mainloop(&descr, -1);
+ if (ret) {
+ ERROR("mainloop returned an error");
+ goto err3;
+ }
+ ret = 0;
+
+err3:
+ lxc_mainloop_close(&descr);
+err2:
+ if (ts->sigfd != -1)
+ lxc_console_sigwinch_fini(ts);
+err1:
+ lxc_console_delete(&conf->console);
+
+ return ret;
+}
+
+static int stdfd_is_pty(void)
+{
+ if (isatty(STDIN_FILENO))
+ return STDIN_FILENO;
+ if (isatty(STDOUT_FILENO))
+ return STDOUT_FILENO;
+ if (isatty(STDERR_FILENO))
+ return STDERR_FILENO;
+
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = -1, r;
+ int wexit = 0;
+ pid_t pid;
+ lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
+ lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL};
+
+ r = lxc_caps_init();
+ if (r)
+ exit(EXIT_FAILURE);
+
+ r = lxc_arguments_parse(&my_args, argc, argv);
+ if (r)
+ exit(EXIT_FAILURE);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ r = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]);
+ if (r)
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+
+ if (geteuid()) {
+ if (access(my_args.lxcpath[0], O_RDWR) < 0) {
+ if (!my_args.quiet)
+ fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ struct lxc_container *c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c)
+ exit(EXIT_FAILURE);
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->is_defined(c)) {
+ fprintf(stderr, "Error: container %s is not defined\n", c->name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ if (remount_sys_proc)
+ attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS;
+ if (elevated_privileges)
+ attach_options.attach_flags &= ~(elevated_privileges);
+ attach_options.namespaces = namespace_flags;
+ attach_options.personality = new_personality;
+ attach_options.env_policy = env_policy;
+ attach_options.extra_env_vars = extra_env;
+ attach_options.extra_keep_env = extra_keep;
+
+ if (my_args.argc > 0) {
+ command.program = my_args.argv[0];
+ command.argv = (char**)my_args.argv;
+ }
+
+ struct wrapargs wrap = (struct wrapargs){
+ .command = &command,
+ .options = &attach_options
+ };
+
+ wrap.ptyfd = stdfd_is_pty();
+ if (wrap.ptyfd >= 0) {
+ if ((!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) && my_args.console_log) {
+ fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
+ goto out;
+ }
+ ret = get_pty_on_host(c, &wrap, &pid);
+ } else {
+ if (my_args.console_log) {
+ fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
+ goto out;
+ }
+ if (command.program)
+ ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
+ else
+ ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
+ }
+
+ if (ret < 0)
+ goto out;
+
+ ret = lxc_wait_for_pid_status(pid);
+ if (ret < 0)
+ goto out;
+
+ if (WIFEXITED(ret))
+ wexit = WEXITSTATUS(ret);
+out:
+ lxc_container_put(c);
+ if (ret >= 0)
+ exit(wexit);
+ exit(EXIT_FAILURE);
+}
diff --git a/src/lxc/tools/lxc_autostart.c b/src/lxc/tools/lxc_autostart.c
new file mode 100644
index 0000000..eed0f5f
--- /dev/null
+++ b/src/lxc/tools/lxc_autostart.c
@@ -0,0 +1,526 @@
+/* lxc_autostart
+ *
+ * Copyright © 2013 Stéphane Graber <stgraber at ubuntu.com>
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * 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 <string.h>
+#include <unistd.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "arguments.h"
+#include "list.h"
+#include "log.h"
+
+lxc_log_define(lxc_autostart_ui, lxc);
+static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list);
+
+struct lxc_list *cmd_groups_list = NULL;
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'k': args->hardstop = 1; break;
+ case 'L': args->list = 1; break;
+ case 'r': args->reboot = 1; break;
+ case 's': args->shutdown = 1; break;
+ case 'a': args->all = 1; break;
+ case 'A': args->ignore_auto = 1; break;
+ case 'g': cmd_groups_list = accumulate_list( arg, ",", cmd_groups_list); break;
+ case 't': args->timeout = atoi(arg); break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"kill", no_argument, 0, 'k'},
+ {"list", no_argument, 0, 'L'},
+ {"reboot", no_argument, 0, 'r'},
+ {"shutdown", no_argument, 0, 's'},
+ {"all", no_argument, 0, 'a'},
+ {"ignore-auto", no_argument, 0, 'A'},
+ {"groups", required_argument, 0, 'g'},
+ {"timeout", required_argument, 0, 't'},
+ {"help", no_argument, 0, 'h'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-autostart",
+ .help = "\
+\n\
+lxc-autostart managed auto-started containers\n\
+\n\
+Options:\n\
+ -k, --kill kill the containers instead of starting them\n\
+ -L, --list list all affected containers and wait delay\n\
+ -r, --reboot reboot the containers instead of starting them\n\
+ -s, --shutdown shutdown the containers instead of starting them\n\
+\n\
+ -a, --all list all auto-started containers (ignore groups)\n\
+ -A, --ignore-auto ignore lxc.start.auto and select all matching containers\n\
+ -g, --groups list of groups (comma separated) to select\n\
+ -t, --timeout=T wait T seconds before hard-stopping\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .timeout = 60,
+};
+
+int list_contains_entry( char *str_ptr, struct lxc_list *p1 ) {
+ struct lxc_list *it1;
+
+ /*
+ * If the entry is NULL or the empty string and the list
+ * is NULL, we have a match
+ */
+ if (! p1 && ! str_ptr)
+ return 1;
+ if (! p1 && ! *str_ptr)
+ return 1;
+
+ if (!p1)
+ return 0;
+
+ lxc_list_for_each(it1, p1) {
+ if (strcmp(it1->elem, str_ptr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) {
+ struct lxc_list *it1;
+ struct lxc_list *it2;
+
+ if (!p1 && !p2)
+ return 1;
+
+ if (!p1)
+ return 0;
+
+ if (!p2)
+ return 0;
+
+ lxc_list_for_each(it1, p1) {
+ lxc_list_for_each(it2, p2) {
+ if (strcmp(it1->elem, it2->elem) == 0)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This is a variation of get_list below it.
+ * This version allows two additional features.
+ * If a list is passed to it, it adds to it.
+ * It allows for empty entries (i.e. "group1,,group2") generating
+ * and empty list entry.
+ */
+static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list) {
+ char *workstr = NULL;
+ char *workptr = NULL;
+ char *next_ptr = NULL;
+ struct lxc_list *worklist;
+ struct lxc_list *workstr_list;
+
+ workstr = strdup(input);
+ if (!workstr) {
+ return NULL;
+ }
+
+ workstr_list = str_list;
+ if ( ! workstr_list ) {
+ workstr_list = malloc(sizeof(*workstr_list));
+ lxc_list_init(workstr_list);
+ }
+
+ for (workptr = workstr; workptr; workptr = next_ptr) {
+ /*
+ * We can't use strtok_r here because it collapses
+ * multiple delimiters into 1 making empty fields
+ * impossible...
+ */
+ /* token = strtok_r(workptr, delimiter, &sptr); */
+ next_ptr = strchr( workptr, *delimiter );
+
+ if( next_ptr ) {
+ *next_ptr++ = '\0';
+ }
+
+ /*
+ * At this point, we'd like to check to see if this
+ * group is already contained in the list and ignore
+ * it if it is... This also helps us with any
+ * corner cases where a string begins or ends with a
+ * delimiter.
+ */
+
+ if ( list_contains_entry( workptr, workstr_list ) ) {
+ if ( *workptr ) {
+ fprintf(stderr, "Duplicate group \"%s\" in list - ignoring\n", workptr );
+ fflush(stderr);
+ } else {
+ fprintf(stderr, "Duplicate NULL group in list - ignoring\n" );
+ fflush(stderr);
+ }
+ } else {
+ worklist = malloc(sizeof(*worklist));
+ if (!worklist)
+ break;
+
+ worklist->elem = strdup(workptr);
+ if (!worklist->elem) {
+ free(worklist);
+ break;
+ }
+
+ lxc_list_add_tail(workstr_list, worklist);
+ }
+ }
+
+ free(workstr);
+
+ return workstr_list;
+}
+
+static struct lxc_list *get_list(char *input, char *delimiter) {
+ char *workstr = NULL;
+ char *workptr = NULL;
+ char *sptr = NULL;
+ char *token = NULL;
+ struct lxc_list *worklist;
+ struct lxc_list *workstr_list;
+
+ workstr_list = malloc(sizeof(*workstr_list));
+ lxc_list_init(workstr_list);
+
+ workstr = strdup(input);
+ if (!workstr) {
+ free(workstr_list);
+ return NULL;
+ }
+
+ for (workptr = workstr;;workptr = NULL) {
+ token = strtok_r(workptr, delimiter, &sptr);
+ if (!token) {
+ break;
+ }
+
+ worklist = malloc(sizeof(*worklist));
+ if (!worklist)
+ break;
+
+ worklist->elem = strdup(token);
+ if (!worklist->elem) {
+ free(worklist);
+ break;
+ }
+
+ lxc_list_add_tail(workstr_list, worklist);
+ }
+
+ free(workstr);
+
+ return workstr_list;
+}
+
+static struct lxc_list *get_config_list(struct lxc_container *c, char *key) {
+ int len = 0;
+ char* value = NULL;
+ struct lxc_list *config_list = NULL;
+
+ len = c->get_config_item(c, key, NULL, 0);
+ if (len < 0)
+ return NULL;
+
+ value = (char*) malloc(sizeof(char)*len + 1);
+ if (value == NULL)
+ return NULL;
+
+ if (c->get_config_item(c, key, value, len + 1) != len) {
+ free(value);
+ return NULL;
+ }
+
+ if (strlen(value) == 0) {
+ free(value);
+ return NULL;
+ }
+
+ config_list = get_list(value, "\n");
+ free(value);
+
+ return config_list;
+}
+
+static int get_config_integer(struct lxc_container *c, char *key) {
+ int len = 0;
+ int ret = 0;
+ char* value = NULL;
+
+ len = c->get_config_item(c, key, NULL, 0);
+ if (len < 0)
+ return 0;
+
+ value = (char*) malloc(sizeof(char)*len + 1);
+ if (value == NULL)
+ return 0;
+
+ if (c->get_config_item(c, key, value, len + 1) != len) {
+ free(value);
+ return 0;
+ }
+
+ ret = atoi(value);
+ free(value);
+
+ return ret;
+}
+
+static int cmporder(const void *p1, const void *p2) {
+ struct lxc_container *c1 = *(struct lxc_container **)p1;
+ struct lxc_container *c2 = *(struct lxc_container **)p2;
+
+ int c1_order = get_config_integer(c1, "lxc.start.order");
+ int c2_order = get_config_integer(c2, "lxc.start.order");
+
+ if (c1_order == c2_order)
+ return strcmp(c1->name, c2->name);
+ else
+ return (c1_order - c2_order);
+}
+
+static int toss_list( struct lxc_list *c_groups_list ) {
+ struct lxc_list *it, *next;
+
+ if (c_groups_list) {
+ lxc_list_for_each_safe(it, c_groups_list, next) {
+ lxc_list_del(it);
+ free(it->elem);
+ free(it);
+ }
+ free(c_groups_list);
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int count = 0;
+ int i = 0;
+ int ret = 0;
+ struct lxc_container **containers = NULL;
+ struct lxc_list **c_groups_lists = NULL;
+ struct lxc_list *cmd_group;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ count = list_defined_containers(my_args.lxcpath[0], NULL, &containers);
+
+ if (count < 0)
+ return 1;
+
+ if (!my_args.all) {
+ /* Allocate an array for our container group lists */
+ c_groups_lists = calloc( count, sizeof( struct lxc_list * ) );
+ }
+
+ qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
+
+ if (cmd_groups_list && my_args.all) {
+ fprintf(stderr, "Specifying -a (all) with -g (groups) doesn't make sense. All option overrides.\n");
+ fflush(stderr);
+ }
+
+ if (!cmd_groups_list) {
+ /*
+ * We need a default cmd_groups_list even for the -a
+ * case in order to force a pass through the loop for
+ * the NULL group. This, someday, could be taken from
+ * a config file somewhere...
+ */
+ cmd_groups_list = accumulate_list( "" , ",", NULL );
+ }
+
+ lxc_list_for_each(cmd_group, cmd_groups_list) {
+
+ /*
+ * Prograpmmers Note:
+ * Because we may take several passes through the container list
+ * We'll switch on if the container pointer is NULL and if we process a
+ * container (run it or decide to ignore it) and call lxc_container_put
+ * then we'll NULL it out and not check it again.
+ */
+ for (i = 0; i < count; i++) {
+ struct lxc_container *c = containers[i];
+
+ if (!c)
+ /* Skip - must have been already processed */
+ continue;
+
+ /*
+ * We haven't loaded the container groups yet so
+ * these next two checks don't need to free them
+ * if they fail. They'll fail on the first pass.
+ */
+ if (!c->may_control(c)) {
+ /* We're done with this container */
+ if ( lxc_container_put(c) > 0 )
+ containers[i] = NULL;
+ continue;
+ }
+
+ if (!my_args.ignore_auto &&
+ get_config_integer(c, "lxc.start.auto") != 1) {
+ /* We're done with this container */
+ if ( lxc_container_put(c) > 0 )
+ containers[i] = NULL;
+ continue;
+ }
+
+ if (!my_args.all) {
+ /* Filter by group */
+ if( ! c_groups_lists[i] ) {
+ /* Now we're loading up a container's groups */
+ c_groups_lists[i] = get_config_list(c, "lxc.group");
+ }
+
+ ret = list_contains_entry(cmd_group->elem, c_groups_lists[i]);
+
+ if ( ret == 0 ) {
+ /* Not in the target group this pass */
+ /* Leave in the list for subsequent passes */
+ continue;
+ }
+ }
+
+ /* We have a candidate continer to process */
+ c->want_daemonize(c, 1);
+
+ if (my_args.shutdown) {
+ /* Shutdown the container */
+ if (c->is_running(c)) {
+ if (my_args.list) {
+ printf("%s\n", c->name);
+ fflush(stdout);
+ }
+ else {
+ if (!c->shutdown(c, my_args.timeout)) {
+ if (!c->stop(c)) {
+ fprintf(stderr, "Error shutting down container: %s\n", c->name);
+ fflush(stderr);
+ }
+ }
+ }
+ }
+ }
+ else if (my_args.hardstop) {
+ /* Kill the container */
+ if (c->is_running(c)) {
+ if (my_args.list) {
+ printf("%s\n", c->name);
+ fflush(stdout);
+ }
+ else {
+ if (!c->stop(c)) {
+ fprintf(stderr, "Error killing container: %s\n", c->name);
+ fflush(stderr);
+ }
+ }
+ }
+ }
+ else if (my_args.reboot) {
+ /* Reboot the container */
+ if (c->is_running(c)) {
+ if (my_args.list) {
+ printf("%s %d\n", c->name,
+ get_config_integer(c, "lxc.start.delay"));
+ fflush(stdout);
+ }
+ else {
+ if (!c->reboot(c)) {
+ fprintf(stderr, "Error rebooting container: %s\n", c->name);
+ fflush(stderr);
+ }
+ else
+ sleep(get_config_integer(c, "lxc.start.delay"));
+ }
+ }
+ }
+ else {
+ /* Start the container */
+ if (!c->is_running(c)) {
+ if (my_args.list) {
+ printf("%s %d\n", c->name,
+ get_config_integer(c, "lxc.start.delay"));
+ fflush(stdout);
+ }
+ else {
+ if (!c->start(c, 0, NULL)) {
+ fprintf(stderr, "Error starting container: %s\n", c->name);
+ fflush(stderr);
+ }
+ else
+ sleep(get_config_integer(c, "lxc.start.delay"));
+ }
+ }
+ }
+
+ /*
+ * If we get this far and we haven't hit any skip "continue"
+ * then we're done with this container... We can dump any
+ * c_groups_list and the container itself.
+ */
+ if ( lxc_container_put(c) > 0 ) {
+ containers[i] = NULL;
+ }
+ if ( c_groups_lists ) {
+ toss_list(c_groups_lists[i]);
+ c_groups_lists[i] = NULL;
+ }
+ }
+
+ }
+
+ /* clean up any lingering detritus */
+ for (i = 0; i < count; i++) {
+ if ( containers[i] ) {
+ lxc_container_put(containers[i]);
+ }
+ if ( c_groups_lists && c_groups_lists[i] ) {
+ toss_list(c_groups_lists[i]);
+ }
+ }
+
+ free(c_groups_lists);
+ toss_list( cmd_groups_list );
+ free(containers);
+
+ return 0;
+}
diff --git a/src/lxc/tools/lxc_cgroup.c b/src/lxc/tools/lxc_cgroup.c
new file mode 100644
index 0000000..dd60fd1
--- /dev/null
+++ b/src/lxc/tools/lxc_cgroup.c
@@ -0,0 +1,122 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "arguments.h"
+
+lxc_log_define(lxc_cgroup_ui, lxc);
+
+static int my_checker(const struct lxc_arguments* args)
+{
+ if (!args->argc) {
+ lxc_error(args, "missing state object");
+ return -1;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-cgroup",
+ .help = "\
+--name=NAME state-object [value]\n\
+\n\
+Get or set the value of a state object (for example, 'cpuset.cpus')\n\
+in the container's cgroup for the corresponding subsystem.\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container",
+ .options = my_longopts,
+ .parser = NULL,
+ .checker = my_checker,
+};
+
+int main(int argc, char *argv[])
+{
+ char *state_object = NULL, *value = NULL;
+ struct lxc_container *c;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ state_object = my_args.argv[0];
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c)
+ return 1;
+
+ if (!c->may_control(c)) {
+ ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ return 1;
+ }
+
+ if (!c->is_running(c)) {
+ ERROR("'%s:%s' is not running", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ return 1;
+ }
+
+ if ((my_args.argc) > 1) {
+ value = my_args.argv[1];
+ if (!c->set_cgroup_item(c, state_object, value)) {
+ ERROR("failed to assign '%s' value to '%s' for '%s'",
+ value, state_object, my_args.name);
+ lxc_container_put(c);
+ return 1;
+ }
+ } else {
+ int len = 4096;
+ char buffer[len];
+ int ret = c->get_cgroup_item(c, state_object, buffer, len);
+ if (ret < 0) {
+ ERROR("failed to retrieve value of '%s' for '%s:%s'",
+ state_object, my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ return 1;
+ }
+ printf("%*s", ret, buffer);
+ }
+
+ lxc_container_put(c);
+ return 0;
+}
diff --git a/src/lxc/tools/lxc_checkpoint.c b/src/lxc/tools/lxc_checkpoint.c
new file mode 100644
index 0000000..7130245
--- /dev/null
+++ b/src/lxc/tools/lxc_checkpoint.c
@@ -0,0 +1,236 @@
+/*
+ *
+ * Copyright © 2014 Tycho Andersen <tycho.andersen at canonical.com>.
+ * Copyright © 2014 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "log.h"
+#include "config.h"
+#include "lxc.h"
+#include "arguments.h"
+#include "utils.h"
+
+static char *checkpoint_dir = NULL;
+static bool stop = false;
+static bool verbose = false;
+static bool do_restore = false;
+static bool daemonize_set = false;
+
+static const struct option my_longopts[] = {
+ {"checkpoint-dir", required_argument, 0, 'D'},
+ {"stop", no_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"restore", no_argument, 0, 'r'},
+ {"daemon", no_argument, 0, 'd'},
+ {"foreground", no_argument, 0, 'F'},
+ LXC_COMMON_OPTIONS
+};
+
+static int my_checker(const struct lxc_arguments *args)
+{
+ if (do_restore && stop) {
+ lxc_error(args, "-s not compatible with -r.");
+ return -1;
+
+ } else if (!do_restore && daemonize_set) {
+ lxc_error(args, "-d/-F not compatible with -r.");
+ return -1;
+ }
+
+ if (checkpoint_dir == NULL) {
+ lxc_error(args, "-D is required.");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ switch (c) {
+ case 'D':
+ checkpoint_dir = strdup(arg);
+ if (!checkpoint_dir)
+ return -1;
+ break;
+ case 's':
+ stop = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'r':
+ do_restore = true;
+ break;
+ case 'd':
+ args->daemonize = 1;
+ daemonize_set = true;
+ break;
+ case 'F':
+ args->daemonize = 0;
+ daemonize_set = true;
+ break;
+ }
+ return 0;
+}
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-checkpoint",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-checkpoint checkpoints and restores a container\n\
+ Serializes a container's running state to disk to allow restoring it in\n\
+ its running state at a later time.\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -r, --restore Restore container\n\
+ -D, --checkpoint-dir=DIR directory to save the checkpoint in\n\
+ -v, --verbose Enable verbose criu logs\n\
+ Checkpoint options:\n\
+ -s, --stop Stop the container after checkpointing.\n\
+ Restore options:\n\
+ -d, --daemon Daemonize the container (default)\n\
+ -F, --foreground Start with the current tty attached to /dev/console\n\
+",
+ .options = my_longopts,
+ .parser = my_parser,
+ .daemonize = 1,
+ .checker = my_checker,
+};
+
+static bool checkpoint(struct lxc_container *c)
+{
+ bool ret;
+
+ if (!c->is_running(c)) {
+ fprintf(stderr, "%s not running, not checkpointing.\n", my_args.name);
+ lxc_container_put(c);
+ return false;
+ }
+
+ ret = c->checkpoint(c, checkpoint_dir, stop, verbose);
+ lxc_container_put(c);
+
+ if (!ret) {
+ fprintf(stderr, "Checkpointing %s failed.\n", my_args.name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool restore_finalize(struct lxc_container *c)
+{
+ bool ret = c->restore(c, checkpoint_dir, verbose);
+ if (!ret) {
+ fprintf(stderr, "Restoring %s failed.\n", my_args.name);
+ }
+
+ lxc_container_put(c);
+ return ret;
+}
+
+static bool restore(struct lxc_container *c)
+{
+ if (c->is_running(c)) {
+ fprintf(stderr, "%s is running, not restoring.\n", my_args.name);
+ lxc_container_put(c);
+ return false;
+ }
+
+ if (my_args.daemonize) {
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return false;
+ }
+
+ if (pid == 0) {
+ close(0);
+ close(1);
+
+ exit(!restore_finalize(c));
+ } else {
+ return wait_for_pid(pid) == 0;
+ }
+ } else {
+ int status;
+
+ if (!restore_finalize(c))
+ return false;
+
+ if (waitpid(-1, &status, 0) < 0)
+ return false;
+
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ bool ret;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(1);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(1);
+
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ fprintf(stderr, "System error loading %s\n", my_args.name);
+ exit(1);
+ }
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+ if (!c->is_defined(c)) {
+ fprintf(stderr, "%s is not defined\n", my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+
+ if (do_restore)
+ ret = restore(c);
+ else
+ ret = checkpoint(c);
+
+ return !ret;
+}
diff --git a/src/lxc/tools/lxc_clone.c b/src/lxc/tools/lxc_clone.c
new file mode 100644
index 0000000..6bd2226
--- /dev/null
+++ b/src/lxc/tools/lxc_clone.c
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "log.h"
+#include "config.h"
+#include "lxc.h"
+#include "conf.h"
+#include "state.h"
+
+lxc_log_define(lxc_clone_ui, lxc);
+
+/* we pass fssize in bytes */
+static uint64_t get_fssize(char *s)
+{
+ uint64_t ret;
+ char *end;
+
+ ret = strtoull(s, &end, 0);
+ if (end == s)
+ {
+ fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
+ return 0;
+ }
+ while (isblank(*end))
+ end++;
+ if (*end == '\0')
+ ret *= 1024ULL * 1024ULL; // MB by default
+ else if (*end == 'b' || *end == 'B')
+ ret *= 1ULL;
+ else if (*end == 'k' || *end == 'K')
+ ret *= 1024ULL;
+ else if (*end == 'm' || *end == 'M')
+ ret *= 1024ULL * 1024ULL;
+ else if (*end == 'g' || *end == 'G')
+ ret *= 1024ULL * 1024ULL * 1024ULL;
+ else if (*end == 't' || *end == 'T')
+ ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
+ else
+ {
+ fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s);
+ return 0;
+ }
+ return ret;
+}
+
+static void usage(const char *me)
+{
+ printf("Usage: %s [-s] [-B backingstore] [-L size[unit]] [-K] [-M] [-H]\n", me);
+ printf(" [-p lxcpath] [-P newlxcpath] orig new\n");
+ printf("\n");
+ printf(" -s: snapshot rather than copy\n");
+ printf(" -B: use specified new backingstore. Default is the same as\n");
+ printf(" the original. Options include aufs, btrfs, lvm, overlayfs, \n");
+ printf(" dir and loop\n");
+ printf(" -L: for blockdev-backed backingstore, use specified size * specified\n");
+ printf(" unit. Default size is the size of the source blockdev, default\n");
+ printf(" unit is MB\n");
+ printf(" -K: Keep name - do not change the container name\n");
+ printf(" -M: Keep macaddr - do not choose a random new mac address\n");
+ printf(" -p: use container orig from custom lxcpath\n");
+ printf(" -P: create container new in custom lxcpath\n");
+ printf(" -R: rename existing container\n");
+ exit(1);
+}
+
+static struct option options[] = {
+ { "snapshot", no_argument, 0, 's'},
+ { "backingstore", required_argument, 0, 'B'},
+ { "size", required_argument, 0, 'L'},
+ { "orig", required_argument, 0, 'o'},
+ { "new", required_argument, 0, 'n'},
+ { "vgname", required_argument, 0, 'v'},
+ { "rename", no_argument, 0, 'R'},
+ { "keepname", no_argument, 0, 'K'},
+ { "keepmac", no_argument, 0, 'M'},
+ { "lxcpath", required_argument, 0, 'p'},
+ { "newpath", required_argument, 0, 'P'},
+ { "fstype", required_argument, 0, 't'},
+ { "help", no_argument, 0, 'h'},
+ { 0, 0, 0, 0 },
+};
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c1 = NULL, *c2 = NULL;
+ int snapshot = 0, keepname = 0, keepmac = 0, rename = 0;
+ int flags = 0, option_index;
+ uint64_t newsize = 0;
+ char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL;
+ char *orig = NULL, *new = NULL, *vgname = NULL;
+ char **args = NULL;
+ int c;
+ bool ret;
+
+ fprintf(stderr, "lxc-clone is deprecated in favor of lxc-copy.\n\n");
+
+ if (argc < 3)
+ usage(argv[0]);
+
+ while (1) {
+ c = getopt_long(argc, argv, "sB:L:o:n:v:KMHp:P:Rt:h", options, &option_index);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 's': snapshot = 1; break;
+ case 'B': bdevtype = optarg; break;
+ case 'L': newsize = get_fssize(optarg); break;
+ case 'o': orig = optarg; break;
+ case 'n': new = optarg; break;
+ case 'v': vgname = optarg; break;
+ case 'K': keepname = 1; break;
+ case 'M': keepmac = 1; break;
+ case 'p': lxcpath = optarg; break;
+ case 'P': newpath = optarg; break;
+ case 'R': rename = 1; break;
+ case 't': fstype = optarg; break;
+ case 'h': usage(argv[0]);
+ default: break;
+ }
+ }
+ if (optind < argc && !orig)
+ orig = argv[optind++];
+ if (optind < argc && !new)
+ new = argv[optind++];
+ if (optind < argc)
+ /* arguments for the clone hook */
+ args = &argv[optind];
+ if (!new || !orig) {
+ printf("Error: you must provide orig and new names\n");
+ usage(argv[0]);
+ }
+
+ if (snapshot) flags |= LXC_CLONE_SNAPSHOT;
+ if (keepname) flags |= LXC_CLONE_KEEPNAME;
+ if (keepmac) flags |= LXC_CLONE_KEEPMACADDR;
+
+ // vgname and fstype could be supported by sending them through the
+ // bdevdata. However, they currently are not yet. I'm not convinced
+ // they are worthwhile.
+ if (vgname) {
+ printf("Error: vgname not supported\n");
+ usage(argv[0]);
+ }
+ if (fstype) {
+ printf("Error: fstype not supported\n");
+ usage(argv[0]);
+ }
+
+ c1 = lxc_container_new(orig, lxcpath);
+ if (!c1)
+ exit(EXIT_FAILURE);
+
+ if (!c1->may_control(c1)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", orig);
+ lxc_container_put(c1);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c1->is_defined(c1)) {
+ fprintf(stderr, "Error: container %s is not defined\n", orig);
+ lxc_container_put(c1);
+ exit(EXIT_FAILURE);
+ }
+ if (rename) {
+ ret = c1->rename(c1, new);
+ if (!ret) {
+ fprintf(stderr,
+ "Error: Renaming container %s to %s failed\n",
+ c1->name, new);
+ lxc_container_put(c1);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize,
+ args);
+ if (c2 == NULL) {
+ lxc_container_put(c1);
+ fprintf(stderr, "clone failed\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("Created container %s as %s of %s\n", new,
+ snapshot ? "snapshot" : "copy", orig);
+ lxc_container_put(c2);
+ }
+ lxc_container_put(c1);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/lxc/tools/lxc_config.c b/src/lxc/tools/lxc_config.c
new file mode 100644
index 0000000..d146ad8
--- /dev/null
+++ b/src/lxc/tools/lxc_config.c
@@ -0,0 +1,81 @@
+/* lxc_config
+ *
+ * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2012 Canonical Ltd.
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "config.h"
+
+struct lxc_config_items {
+ char *name;
+};
+
+static struct lxc_config_items items[] =
+{
+ { .name = "lxc.default_config", },
+ { .name = "lxc.lxcpath", },
+ { .name = "lxc.bdev.lvm.vg", },
+ { .name = "lxc.bdev.lvm.thin_pool", },
+ { .name = "lxc.bdev.zfs.root", },
+ { .name = "lxc.cgroup.use", },
+ { .name = "lxc.cgroup.pattern", },
+ { .name = NULL, },
+};
+
+static void usage(char *me)
+{
+ printf("Usage: %s -l: list all available configuration items\n", me);
+ printf(" %s item: print configuration item\n", me);
+ exit(1);
+}
+
+static void list_config_items(void)
+{
+ struct lxc_config_items *i;
+
+ for (i = &items[0]; i->name; i++)
+ printf("%s\n", i->name);
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_config_items *i;
+ const char *value;
+
+ if (argc < 2)
+ usage(argv[0]);
+ if (strcmp(argv[1], "-l") == 0)
+ list_config_items();
+ for (i = &items[0]; i->name; i++) {
+ if (strcmp(argv[1], i->name) == 0) {
+ value = lxc_get_global_config_item(i->name);
+ if (value)
+ printf("%s\n", value);
+ else
+ printf("%s is not set.\n", argv[1]);
+ exit(0);
+ }
+ }
+ printf("Unknown configuration item: %s\n", argv[1]);
+ exit(1);
+}
diff --git a/src/lxc/tools/lxc_console.c b/src/lxc/tools/lxc_console.c
new file mode 100644
index 0000000..adbd7e0
--- /dev/null
+++ b/src/lxc/tools/lxc_console.c
@@ -0,0 +1,134 @@
+/*
+ * 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
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <libgen.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "error.h"
+#include "lxc.h"
+#include "log.h"
+#include "mainloop.h"
+#include "arguments.h"
+#include "commands.h"
+
+lxc_log_define(lxc_console_ui, lxc);
+
+static char etoc(const char *expr)
+{
+ /* returns "control code" of given expression */
+ char c = expr[0] == '^' ? expr[1] : expr[0];
+ return 1 + ((c > 'Z') ? (c - 'a') : (c - 'Z'));
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 't': args->ttynum = atoi(arg); break;
+ case 'e': args->escape = etoc(arg); break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"tty", required_argument, 0, 't'},
+ {"escape", required_argument, 0, 'e'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-console",
+ .help = "\
+--name=NAME [--tty NUMBER]\n\
+\n\
+lxc-console logs on the container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -t, --tty=NUMBER console tty number\n\
+ -e, --escape=PREFIX prefix for escape command\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .ttynum = -1,
+ .escape = 1,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ struct lxc_container *c;
+
+ ret = lxc_arguments_parse(&my_args, argc, argv);
+ if (ret)
+ return EXIT_FAILURE;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]);
+ if (ret)
+ return EXIT_FAILURE;
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ fprintf(stderr, "System error loading container\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->is_running(c)) {
+ fprintf(stderr, "%s is not running\n", my_args.name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = c->console(c, my_args.ttynum, 0, 1, 2, my_args.escape);
+ if (ret < 0) {
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+ lxc_container_put(c);
+ return EXIT_SUCCESS;
+}
diff --git a/src/lxc/tools/lxc_copy.c b/src/lxc/tools/lxc_copy.c
new file mode 100644
index 0000000..9f653e3
--- /dev/null
+++ b/src/lxc/tools/lxc_copy.c
@@ -0,0 +1,877 @@
+/*
+ *
+ * Copyright © 2015 Christian Brauner <christian.brauner at mailbox.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdbool.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "attach.h"
+#include "bdev.h"
+#include "log.h"
+#include "confile.h"
+#include "arguments.h"
+#include "lxc.h"
+#include "conf.h"
+#include "state.h"
+#include "utils.h"
+
+#ifndef HAVE_GETSUBOPT
+#include <../include/getsubopt.h>
+#endif
+
+lxc_log_define(lxc_copy_ui, lxc);
+
+enum mnttype {
+ LXC_MNT_BIND,
+ LXC_MNT_AUFS,
+ LXC_MNT_OVL,
+};
+
+struct mnts {
+ enum mnttype mnt_type;
+ char *src;
+ char *dest;
+ char *options;
+ char *upper;
+ char *workdir;
+ char *lower;
+};
+
+static unsigned int mnt_table_size = 0;
+static struct mnts *mnt_table = NULL;
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg);
+
+static const struct option my_longopts[] = {
+ { "newname", required_argument, 0, 'N'},
+ { "newpath", required_argument, 0, 'p'},
+ { "rename", no_argument, 0, 'R'},
+ { "snapshot", no_argument, 0, 's'},
+ { "foreground", no_argument, 0, 'F'},
+ { "daemon", no_argument, 0, 'd'},
+ { "ephemeral", no_argument, 0, 'e'},
+ { "mount", required_argument, 0, 'm'},
+ { "backingstore", required_argument, 0, 'B'},
+ { "fssize", required_argument, 0, 'L'},
+ { "keepdata", no_argument, 0, 'D'},
+ { "keepname", no_argument, 0, 'K'},
+ { "keepmac", no_argument, 0, 'M'},
+ { "tmpfs", no_argument, 0, 't'},
+ LXC_COMMON_OPTIONS
+};
+
+/* mount keys */
+static char *const keys[] = {
+ [LXC_MNT_BIND] = "bind",
+ [LXC_MNT_AUFS] = "aufs",
+ [LXC_MNT_OVL] = "overlay",
+ NULL
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-copy",
+ .help = "\n\
+--name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\
+--name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,aufs,overlay}=/src:/dest] -- hook options\n\
+--name=NAME [-P lxcpath] -N newname -R\n\
+\n\
+lxc-copy clone a container\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -N, --newname=NEWNAME NEWNAME for the restored container\n\
+ -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
+ -R, --rename rename container\n\
+ -s, --snapshot create snapshot instead of clone\n\
+ -F, --foreground start with current tty attached to /dev/console\n\
+ -d, --daemon daemonize the container (default)\n\
+ -e, --ephemeral start ephemeral container\n\
+ -m, --mount directory to mount into container, either \n\
+ {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\
+ -B, --backingstorage=TYPE backingstorage type for the container\n\
+ -t, --tmpfs place ephemeral container on a tmpfs\n\
+ (WARNING: On reboot all changes made to the container will be lost.)\n\
+ -L, --fssize size of the new block device for block device containers\n\
+ -D, --keedata pass together with -e start a persistent snapshot \n\
+ -K, --keepname keep the hostname of the original container\n\
+ -- hook options arguments passed to the hook program\n\
+ -M, --keepmac keep the MAC address of the original container\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .task = CLONE,
+ .daemonize = 1,
+ .quiet = false,
+ .tmpfs = false,
+};
+
+static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num,
+ enum mnttype type);
+static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num,
+ struct lxc_arguments *arg);
+static char *construct_path(char *path, bool as_prefix);
+static char *set_mnt_entry(struct mnts *m);
+static int do_clone(struct lxc_container *c, char *newname, char *newpath,
+ int flags, char *bdevtype, uint64_t fssize, enum task task,
+ char **args);
+static int do_clone_ephemeral(struct lxc_container *c,
+ struct lxc_arguments *arg, char **args,
+ int flags);
+static int do_clone_rename(struct lxc_container *c, char *newname);
+static int do_clone_task(struct lxc_container *c, enum task task, int flags,
+ char **args);
+static void free_mnts(void);
+static uint64_t get_fssize(char *s);
+
+/* Place an ephemeral container started with -e flag on a tmpfs. Restrictions
+ * are that you cannot request the data to be kept while placing the container
+ * on a tmpfs and that either overlay or aufs backing storage must be used.
+ */
+static char *mount_tmpfs(const char *oldname, const char *newname,
+ const char *path, struct lxc_arguments *arg);
+static int parse_mntsubopts(char *subopts, char *const *keys,
+ char *mntparameters);
+static int parse_aufs_mnt(char *mntstring, enum mnttype type);
+static int parse_bind_mnt(char *mntstring, enum mnttype type);
+static int parse_ovl_mnt(char *mntstring, enum mnttype type);
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ int flags = 0;
+ int ret = EXIT_FAILURE;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(ret);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(ret);
+ lxc_log_options_no_override();
+
+ if (geteuid()) {
+ if (access(my_args.lxcpath[0], O_RDWR) < 0) {
+ if (!my_args.quiet)
+ fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
+ exit(ret);
+ }
+ }
+
+ if (!my_args.newname && !(my_args.task == DESTROY)) {
+ if (!my_args.quiet)
+ printf("Error: You must provide a NEWNAME for the clone.\n");
+ exit(ret);
+ }
+
+ if (my_args.task == SNAP || my_args.task == DESTROY)
+ flags |= LXC_CLONE_SNAPSHOT;
+ if (my_args.keepname)
+ flags |= LXC_CLONE_KEEPNAME;
+ if (my_args.keepmac)
+ flags |= LXC_CLONE_KEEPMACADDR;
+
+ if (!my_args.newpath)
+ my_args.newpath = (char *)my_args.lxcpath[0];
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c)
+ exit(ret);
+
+ if (!c->may_control(c)) {
+ if (!my_args.quiet)
+ fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ goto out;
+ }
+
+ if (!c->is_defined(c)) {
+ if (!my_args.quiet)
+ fprintf(stderr, "Error: container %s is not defined\n", c->name);
+ goto out;
+ }
+
+ ret = do_clone_task(c, my_args.task, flags, &argv[optind]);
+
+out:
+ lxc_container_put(c);
+
+ if (ret == 0)
+ exit(EXIT_SUCCESS);
+ exit(EXIT_FAILURE);
+}
+
+static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, enum mnttype type)
+{
+ struct mnts *m, *n;
+
+ n = realloc(*mnts, (*num + 1) * sizeof(struct mnts));
+ if (!n)
+ return NULL;
+
+ *mnts = n;
+ m = *mnts + *num;
+ (*num)++;
+
+ *m = (struct mnts) {.mnt_type = type};
+
+ return m;
+}
+
+static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_arguments *arg)
+{
+ char upperdir[MAXPATHLEN];
+ char workdir[MAXPATHLEN];
+ unsigned int i;
+ int ret;
+ struct mnts *m = NULL;
+
+ for (i = 0, m = mnts; i < num; i++, m++) {
+ if ((m->mnt_type == LXC_MNT_OVL) || (m->mnt_type == LXC_MNT_AUFS)) {
+ ret = snprintf(upperdir, MAXPATHLEN, "%s/%s/delta#XXXXXX",
+ arg->newpath, arg->newname);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return -1;
+ if (!mkdtemp(upperdir))
+ return -1;
+ m->upper = strdup(upperdir);
+ if (!m->upper)
+ return -1;
+ }
+
+ if (m->mnt_type == LXC_MNT_OVL) {
+ ret = snprintf(workdir, MAXPATHLEN, "%s/%s/work#XXXXXX",
+ arg->newpath, arg->newname);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return -1;
+ if (!mkdtemp(workdir))
+ return -1;
+ m->workdir = strdup(workdir);
+ if (!m->workdir)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static char *construct_path(char *path, bool as_prefix)
+{
+ char **components = NULL;
+ char *cleanpath = NULL;
+
+ components = lxc_normalize_path(path);
+ if (!components)
+ return NULL;
+
+ cleanpath = lxc_string_join("/", (const char **)components, as_prefix);
+ lxc_free_array((void **)components, free);
+
+ return cleanpath;
+}
+
+static char *set_mnt_entry(struct mnts *m)
+{
+ char *mntentry = NULL;
+ int ret = 0;
+ size_t len = 0;
+
+ if (m->mnt_type == LXC_MNT_AUFS) {
+ len = strlen(" aufs br==rw:=ro,xino=,create=dir") +
+ 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
+ strlen(m->workdir) + 1;
+
+ mntentry = malloc(len);
+ if (!mntentry)
+ goto err;
+
+ ret = snprintf(mntentry, len, "%s %s aufs br=%s=rw:%s=ro,xino=%s,create=dir",
+ m->src, m->dest, m->upper, m->src, m->workdir);
+ if (ret < 0 || (size_t)ret >= len)
+ goto err;
+ } else if (m->mnt_type == LXC_MNT_OVL) {
+ len = strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") +
+ 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
+ strlen(m->workdir) + 1;
+
+ mntentry = malloc(len);
+ if (!mntentry)
+ goto err;
+
+ ret = snprintf(mntentry, len, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
+ m->src, m->dest, m->src, m->upper, m->workdir);
+ if (ret < 0 || (size_t)ret >= len)
+ goto err;
+ } else if (m->mnt_type == LXC_MNT_BIND) {
+ len = strlen(" none bind,optional,, 0 0") +
+ strlen(is_dir(m->src) ? "create=dir" : "create=file") +
+ strlen(m->src) + strlen(m->dest) + strlen(m->options) + 1;
+
+ mntentry = malloc(len);
+ if (!mntentry)
+ goto err;
+
+ ret = snprintf(mntentry, len, "%s %s none bind,optional,%s,%s 0 0",
+ m->src, m->dest, m->options,
+ is_dir(m->src) ? "create=dir" : "create=file");
+ if (ret < 0 || (size_t)ret >= len)
+ goto err;
+ }
+
+ return mntentry;
+
+err:
+ free(mntentry);
+ return NULL;
+}
+
+static int do_clone(struct lxc_container *c, char *newname, char *newpath,
+ int flags, char *bdevtype, uint64_t fssize, enum task task,
+ char **args)
+{
+ struct lxc_container *clone;
+
+ clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize,
+ args);
+ if (!clone) {
+ if (!my_args.quiet)
+ fprintf(stderr, "clone failed\n");
+ return -1;
+ }
+
+ INFO("Created %s as %s of %s\n", newname, task ? "snapshot" : "copy", c->name);
+
+ lxc_container_put(clone);
+
+ return 0;
+}
+
+static int do_clone_ephemeral(struct lxc_container *c,
+ struct lxc_arguments *arg, char **args, int flags)
+{
+ char *bdev;
+ char *premount;
+ char randname[MAXPATHLEN];
+ unsigned int i;
+ int ret = 0;
+ bool bret = true, started = false;
+ struct lxc_container *clone;
+ lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
+ attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
+
+ if (!arg->newname) {
+ ret = snprintf(randname, MAXPATHLEN, "%s/%s_XXXXXX", arg->newpath, arg->name);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return -1;
+ if (!mkdtemp(randname))
+ return -1;
+ if (chmod(randname, 0770) < 0) {
+ remove(randname);
+ return -1;
+ }
+ arg->newname = randname + strlen(arg->newpath) + 1;
+ }
+
+ clone = c->clone(c, arg->newname, arg->newpath, flags,
+ arg->bdevtype, NULL, arg->fssize, args);
+ if (!clone)
+ return -1;
+
+ if (arg->tmpfs) {
+ bdev = c->lxc_conf->rootfs.bdev_type;
+ if (bdev && strcmp(bdev, "dir")) {
+ fprintf(stderr, "Cannot currently use tmpfs with %s storage backend.\n", bdev);
+ goto destroy_and_put;
+ }
+
+ premount = mount_tmpfs(arg->name, arg->newname, arg->newpath, arg);
+ if (!premount)
+ goto destroy_and_put;
+
+ bret = clone->set_config_item(clone, "lxc.hook.pre-mount", premount);
+ free(premount);
+ if (!bret)
+ goto destroy_and_put;
+ }
+
+ if (!arg->keepdata)
+ if (!clone->set_config_item(clone, "lxc.ephemeral", "1"))
+ goto destroy_and_put;
+
+ /* allocate and create random upper- and workdirs for overlay mounts */
+ if (mk_rand_ovl_dirs(mnt_table, mnt_table_size, arg) < 0)
+ goto destroy_and_put;
+
+ /* allocate and set mount entries */
+ struct mnts *n = NULL;
+ for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) {
+ char *mntentry = NULL;
+ mntentry = set_mnt_entry(n);
+ if (!mntentry)
+ goto destroy_and_put;
+ bret = clone->set_config_item(clone, "lxc.mount.entry", mntentry);
+ free(mntentry);
+ if (!bret)
+ goto destroy_and_put;
+ }
+
+ if (!clone->save_config(clone, NULL))
+ goto destroy_and_put;
+
+ if (!my_args.quiet)
+ printf("Created %s as clone of %s\n", arg->newname, arg->name);
+
+ if (arg->tmpfs && !my_args.quiet)
+ printf("Container is placed on tmpfs.\nRebooting will cause "
+ "all changes made to it to be lost!");
+
+ if (!arg->daemonize && arg->argc) {
+ clone->want_daemonize(clone, true);
+ arg->daemonize = 1;
+ } else if (!arg->daemonize) {
+ clone->want_daemonize(clone, false);
+ }
+
+ started = clone->start(clone, 0, NULL);
+ if (!started)
+ goto destroy_and_put;
+
+ if (arg->daemonize && arg->argc) {
+ ret = clone->attach_run_wait(clone, &attach_options, arg->argv[0], (const char *const *)arg->argv);
+ if (ret < 0)
+ goto destroy_and_put;
+ clone->shutdown(clone, -1);
+ }
+
+ free_mnts();
+ lxc_container_put(clone);
+ return 0;
+
+destroy_and_put:
+ if (started)
+ clone->shutdown(clone, -1);
+ if (!started || clone->lxc_conf->ephemeral != 1)
+ clone->destroy(clone);
+ free_mnts();
+ lxc_container_put(clone);
+ return -1;
+}
+
+static int do_clone_rename(struct lxc_container *c, char *newname)
+{
+ if (!c->rename(c, newname)) {
+ ERROR("Error: Renaming container %s to %s failed\n", c->name, newname);
+ return -1;
+ }
+
+ INFO("Renamed container %s to %s\n", c->name, newname);
+
+ return 0;
+}
+
+static int do_clone_task(struct lxc_container *c, enum task task, int flags,
+ char **args)
+{
+ int ret = 0;
+
+ switch (task) {
+ case DESTROY:
+ ret = do_clone_ephemeral(c, &my_args, args, flags);
+ break;
+ case RENAME:
+ ret = do_clone_rename(c, my_args.newname);
+ break;
+ default:
+ ret = do_clone(c, my_args.newname, my_args.newpath, flags,
+ my_args.bdevtype, my_args.fssize, my_args.task,
+ args);
+ break;
+ }
+
+ return ret;
+}
+
+static void free_mnts()
+{
+ unsigned int i;
+ struct mnts *n = NULL;
+
+ for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) {
+ free(n->src);
+ free(n->dest);
+ free(n->options);
+ free(n->upper);
+ free(n->workdir);
+ }
+ free(mnt_table);
+ mnt_table = NULL;
+ mnt_table_size = 0;
+}
+
+/* we pass fssize in bytes */
+static uint64_t get_fssize(char *s)
+{
+ uint64_t ret;
+ char *end;
+
+ ret = strtoull(s, &end, 0);
+ if (end == s) {
+ if (!my_args.quiet)
+ fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
+ return 0;
+ }
+ while (isblank(*end))
+ end++;
+ if (*end == '\0') {
+ ret *= 1024ULL * 1024ULL; // MB by default
+ } else if (*end == 'b' || *end == 'B') {
+ ret *= 1ULL;
+ } else if (*end == 'k' || *end == 'K') {
+ ret *= 1024ULL;
+ } else if (*end == 'm' || *end == 'M') {
+ ret *= 1024ULL * 1024ULL;
+ } else if (*end == 'g' || *end == 'G') {
+ ret *= 1024ULL * 1024ULL * 1024ULL;
+ } else if (*end == 't' || *end == 'T') {
+ ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
+ } else {
+ if (!my_args.quiet)
+ fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end, s);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ char *subopts = NULL;
+ char *mntparameters = NULL;
+ switch (c) {
+ case 'N':
+ args->newname = arg;
+ break;
+ case 'p':
+ args->newpath = arg;
+ break;
+ case 'R':
+ args->task = RENAME;
+ break;
+ case 's':
+ args->task = SNAP;
+ break;
+ case 'F':
+ args->daemonize = 0;
+ break;
+ case 'd':
+ args->daemonize = 1;
+ break;
+ case 'e':
+ args->task = DESTROY;
+ break;
+ case 'm':
+ subopts = optarg;
+ if (parse_mntsubopts(subopts, keys, mntparameters) < 0)
+ return -1;
+ break;
+ case 'B':
+ args->bdevtype = arg;
+ break;
+ case 't':
+ args->tmpfs = true;
+ break;
+ case 'L':
+ args->fssize = get_fssize(optarg);
+ break;
+ case 'D':
+ args->keepdata = 1;
+ break;
+ case 'K':
+ args->keepname = 1;
+ break;
+ case 'M':
+ args->keepmac = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static int parse_aufs_mnt(char *mntstring, enum mnttype type)
+{
+ int len = 0;
+ const char *xinopath = "/dev/shm/aufs.xino";
+ char **mntarray = NULL;
+ struct mnts *m = NULL;
+
+ m = add_mnt(&mnt_table, &mnt_table_size, type);
+ if (!m)
+ goto err;
+
+ mntarray = lxc_string_split(mntstring, ':');
+ if (!mntarray)
+ goto err;
+
+ m->src = construct_path(mntarray[0], true);
+ if (!m->src)
+ goto err;
+
+ len = lxc_array_len((void **)mntarray);
+ if (len == 1) /* aufs=src */
+ m->dest = construct_path(mntarray[0], false);
+ else if (len == 2) /* aufs=src:dest */
+ m->dest = construct_path(mntarray[1], false);
+ else
+ INFO("Excess elements in mount specification");
+
+ if (!m->dest)
+ goto err;
+
+ m->workdir = strdup(xinopath);
+ if (!m->workdir)
+ goto err;
+
+ lxc_free_array((void **)mntarray, free);
+ return 0;
+
+err:
+ free_mnts();
+ lxc_free_array((void **)mntarray, free);
+ return -1;
+}
+
+static int parse_bind_mnt(char *mntstring, enum mnttype type)
+{
+ int len = 0;
+ char **mntarray = NULL;
+ struct mnts *m = NULL;
+
+ m = add_mnt(&mnt_table, &mnt_table_size, type);
+ if (!m)
+ goto err;
+
+ mntarray = lxc_string_split(mntstring, ':');
+ if (!mntarray)
+ goto err;
+
+ m->src = construct_path(mntarray[0], true);
+ if (!m->src)
+ goto err;
+
+ len = lxc_array_len((void **)mntarray);
+ if (len == 1) { /* bind=src */
+ m->dest = construct_path(mntarray[0], false);
+ } else if (len == 2) { /* bind=src:option or bind=src:dest */
+ if (strncmp(mntarray[1], "rw", strlen(mntarray[1])) == 0)
+ m->options = strdup("rw");
+
+ if (strncmp(mntarray[1], "ro", strlen(mntarray[1])) == 0)
+ m->options = strdup("ro");
+
+ if (m->options)
+ m->dest = construct_path(mntarray[0], false);
+ else
+ m->dest = construct_path(mntarray[1], false);
+ } else if (len == 3) { /* bind=src:dest:option */
+ m->dest = construct_path(mntarray[1], false);
+ m->options = strdup(mntarray[2]);
+ } else {
+ INFO("Excess elements in mount specification");
+ }
+
+ if (!m->dest)
+ goto err;
+
+ if (!m->options)
+ m->options = strdup("rw");
+
+ if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) &&
+ strncmp(m->options, "ro", strlen(m->options))))
+ goto err;
+
+ lxc_free_array((void **)mntarray, free);
+ return 0;
+
+err:
+ free_mnts();
+ lxc_free_array((void **)mntarray, free);
+ return -1;
+}
+
+static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters)
+{
+ while (*subopts != '\0') {
+ switch (getsubopt(&subopts, keys, &mntparameters)) {
+ case LXC_MNT_BIND:
+ if (parse_bind_mnt(mntparameters, LXC_MNT_BIND) < 0)
+ return -1;
+ break;
+ case LXC_MNT_OVL:
+ if (parse_ovl_mnt(mntparameters, LXC_MNT_OVL) < 0)
+ return -1;
+ break;
+ case LXC_MNT_AUFS:
+ if (parse_aufs_mnt(mntparameters, LXC_MNT_AUFS) < 0)
+ return -1;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int parse_ovl_mnt(char *mntstring, enum mnttype type)
+{
+ int len = 0;
+ char **mntarray = NULL;
+ struct mnts *m;
+
+ m = add_mnt(&mnt_table, &mnt_table_size, type);
+ if (!m)
+ goto err;
+
+ mntarray = lxc_string_split(mntstring, ':');
+ if (!mntarray)
+ goto err;
+
+ m->src = construct_path(mntarray[0], true);
+ if (!m->src)
+ goto err;
+
+ len = lxc_array_len((void **)mntarray);
+ if (len == 1) /* overlay=src */
+ m->dest = construct_path(mntarray[0], false);
+ else if (len == 2) /* overlay=src:dest */
+ m->dest = construct_path(mntarray[1], false);
+ else
+ INFO("Excess elements in mount specification");
+
+ if (!m->dest)
+ goto err;
+
+ lxc_free_array((void **)mntarray, free);
+ return 0;
+
+err:
+ free_mnts();
+ lxc_free_array((void **)mntarray, free);
+ return -1;
+}
+
+/* For ephemeral snapshots backed by overlay or aufs filesystems, this function
+ * mounts a fresh tmpfs over the containers directory if the user requests it.
+ * Because we mount a fresh tmpfs over the directory of the container the
+ * updated /etc/hostname file created during the clone residing in the upperdir
+ * (currently named "delta0" by default) will be hidden. Hence, if the user
+ * requests that the old name is not to be kept for the clone, we recreate this
+ * file on the tmpfs. This should be all that is required to restore the exact
+ * behaviour we would get with a normal clone.
+ */
+static char *mount_tmpfs(const char *oldname, const char *newname,
+ const char *path, struct lxc_arguments *arg)
+{
+ int ret, fd;
+ size_t len;
+ char *premount = NULL;
+ FILE *fp;
+
+ if (arg->tmpfs && arg->keepdata) {
+ fprintf(stderr, "%s\n", "A container can only be placed on a "
+ "tmpfs when storage backend is overlay "
+ "or aufs.");
+ goto err_free;
+ }
+
+ if (arg->tmpfs && !arg->bdevtype) {
+ arg->bdevtype = "overlayfs";
+ } else if (arg->tmpfs && arg->bdevtype && strcmp(arg->bdevtype, "overlayfs") && strcmp(arg->bdevtype, "aufs")) {
+ fprintf(stderr, "%s\n", "A container can only be placed on a "
+ "tmpfs when storage backend is overlay "
+ "or aufs.");
+ goto err_free;
+ }
+
+ len = strlen(path) + strlen(newname) + strlen("pre-start-XXXXXX") + /* //\0 */ 3;
+ premount = malloc(len);
+ if (!premount)
+ goto err_free;
+
+ ret = snprintf(premount, len, "%s/%s/pre-start-XXXXXX", path, newname);
+ if (ret < 0 || (size_t)ret >= len)
+ goto err_free;
+
+ fd = mkstemp(premount);
+ if (fd < 0)
+ goto err_free;
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+ SYSERROR("Failed to set close-on-exec on file descriptor.");
+ goto err_close;
+ }
+
+ if (chmod(premount, 0755) < 0)
+ goto err_close;
+
+ fp = fdopen(fd, "r+");
+ if (!fp)
+ goto err_close;
+ fd = -1;
+
+ ret = fprintf(fp, "#! /bin/sh\n"
+ "mount -n -t tmpfs -o mode=0755 none %s/%s\n",
+ path, newname);
+ if (ret < 0)
+ goto err_close;
+
+ if (!arg->keepname) {
+ ret = fprintf(fp, "mkdir -p %s/%s/delta0/etc\n"
+ "echo %s > %s/%s/delta0/etc/hostname\n",
+ path, newname, newname, path, newname);
+ if (ret < 0)
+ goto err_close;
+ }
+
+ close(fd);
+ return premount;
+
+err_close:
+ if (fd > 0)
+ close(fd);
+ else
+ fclose(fp);
+err_free:
+ free(premount);
+ return NULL;
+}
diff --git a/src/lxc/tools/lxc_create.c b/src/lxc/tools/lxc_create.c
new file mode 100644
index 0000000..4b0e9d7
--- /dev/null
+++ b/src/lxc/tools/lxc_create.c
@@ -0,0 +1,326 @@
+/*
+ *
+ * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <lxc/lxccontainer.h>
+#include <sys/types.h>
+
+#include "arguments.h"
+#include "bdev.h"
+#include "log.h"
+#include "lxc.h"
+#include "utils.h"
+
+lxc_log_define(lxc_create_ui, lxc);
+
+static uint64_t get_fssize(char *s)
+{
+ uint64_t ret;
+ char *end;
+
+ ret = strtoull(s, &end, 0);
+ if (end == s)
+ {
+ fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
+ return 0;
+ }
+ while (isblank(*end))
+ end++;
+ if (*end == '\0')
+ ret *= 1024ULL * 1024ULL; // MB by default
+ else if (*end == 'b' || *end == 'B')
+ ret *= 1ULL;
+ else if (*end == 'k' || *end == 'K')
+ ret *= 1024ULL;
+ else if (*end == 'm' || *end == 'M')
+ ret *= 1024ULL * 1024ULL;
+ else if (*end == 'g' || *end == 'G')
+ ret *= 1024ULL * 1024ULL * 1024ULL;
+ else if (*end == 't' || *end == 'T')
+ ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
+ else
+ {
+ fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s);
+ return 0;
+ }
+ return ret;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'B': args->bdevtype = arg; break;
+ case 'f': args->configfile = arg; break;
+ case 't': args->template = arg; break;
+ case '0': args->lvname = arg; break;
+ case '1': args->vgname = arg; break;
+ case '2': args->thinpool = arg; break;
+ case '3': args->fstype = arg; break;
+ case '4': args->fssize = get_fssize(arg); break;
+ case '5': args->zfsroot = arg; break;
+ case '6': args->dir = arg; break;
+ case '7': args->rbdname = arg; break;
+ case '8': args->rbdpool = arg; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"bdev", required_argument, 0, 'B'},
+ {"config", required_argument, 0, 'f'},
+ {"template", required_argument, 0, 't'},
+ {"lvname", required_argument, 0, '0'},
+ {"vgname", required_argument, 0, '1'},
+ {"thinpool", required_argument, 0, '2'},
+ {"fstype", required_argument, 0, '3'},
+ {"fssize", required_argument, 0, '4'},
+ {"zfsroot", required_argument, 0, '5'},
+ {"dir", required_argument, 0, '6'},
+ {"rbdname", required_argument, 0, '7'},
+ {"rbdpool", required_argument, 0, '8'},
+ LXC_COMMON_OPTIONS
+};
+
+static void create_helpfn(const struct lxc_arguments *args)
+{
+ char *argv[3], *path;
+ pid_t pid;
+
+ if (!args->template)
+ return;
+
+ pid = fork();
+ if (pid) {
+ wait_for_pid(pid);
+ return;
+ }
+
+ path = get_template_path(args->template);
+
+ argv[0] = path;
+ argv[1] = "-h";
+ argv[2] = NULL;
+
+ execv(path, argv);
+ ERROR("Error executing %s -h", path);
+ exit(EXIT_FAILURE);
+}
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-create",
+ .helpfn = create_helpfn,
+ .help = "\
+--name=NAME --template=TEMPLATE [OPTION...]\n\
+\n\
+lxc-create creates a container\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -f, --config=CONFIG Initial configuration file\n\
+ -t, --template=TEMPLATE Template to use to setup container\n\
+ -B, --bdev=BDEV Backing store type to use\n\
+ --dir=DIR Place rootfs directory under DIR\n\
+\n\
+ BDEV options for LVM (with -B/--bdev lvm):\n\
+ --lvname=LVNAME Use LVM lv name LVNAME\n\
+ (Default: container name)\n\
+ --vgname=VG Use LVM vg called VG\n\
+ (Default: lxc)\n\
+ --thinpool=TP Use LVM thin pool called TP\n\
+ (Default: lxc)\n\
+\n\
+ BDEV options for Ceph RBD (with -B/--bdev rbd) :\n\
+ --rbdname=RBDNAME Use Ceph RBD name RBDNAME\n\
+ (Default: container name)\n\
+ --rbdpool=POOL Use Ceph RBD pool name POOL\n\
+ (Default: lxc)\n\
+\n\
+ BDEV option for ZFS (with -B/--bdev zfs) :\n\
+ --zfsroot=PATH Create zfs under given zfsroot\n\
+ (Default: tank/lxc)\n\
+\n\
+ BDEV options for LVM or Loop (with -B/--bdev lvm/loop) :\n\
+ --fstype=TYPE Create fstype TYPE\n\
+ (Default: ext3)\n\
+ --fssize=SIZE[U] Create filesystem of\n\
+ size SIZE * unit U (bBkKmMgGtT)\n\
+ (Default: 1G, default unit: M)\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+};
+
+static bool validate_bdev_args(struct lxc_arguments *a)
+{
+ if (strcmp(a->bdevtype, "best") != 0) {
+ if (a->fstype || a->fssize) {
+ if (strcmp(a->bdevtype, "lvm") != 0 &&
+ strcmp(a->bdevtype, "loop") != 0 &&
+ strcmp(a->bdevtype, "rbd") != 0) {
+ fprintf(stderr, "filesystem type and size are only valid with block devices\n");
+ return false;
+ }
+ }
+ if (strcmp(a->bdevtype, "lvm") != 0) {
+ if (a->lvname || a->vgname || a->thinpool) {
+ fprintf(stderr, "--lvname, --vgname and --thinpool are only valid with -B lvm\n");
+ return false;
+ }
+ }
+ if (strcmp(a->bdevtype, "rbd") != 0) {
+ if (a->rbdname || a->rbdpool) {
+ fprintf(stderr, "--rbdname and --rbdpool are only valid with -B rbd\n");
+ return false;
+ }
+ }
+ if (strcmp(a->bdevtype, "zfs") != 0) {
+ if (a->zfsroot) {
+ fprintf(stderr, "zfsroot is only valid with -B zfs\n");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ struct bdev_specs spec;
+ int flags = 0;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(EXIT_FAILURE);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+
+ if (!my_args.template) {
+ fprintf(stderr, "A template must be specified.\n");
+ fprintf(stderr, "Use \"none\" if you really want a container without a rootfs.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (strcmp(my_args.template, "none") == 0)
+ my_args.template = NULL;
+
+ memset(&spec, 0, sizeof(spec));
+ if (!my_args.bdevtype)
+ my_args.bdevtype = "_unset";
+
+ if (!validate_bdev_args(&my_args))
+ exit(EXIT_FAILURE);
+
+ if (strcmp(my_args.bdevtype, "none") == 0)
+ my_args.bdevtype = "dir";
+
+ // Final check whether the user gave use a valid bdev type.
+ if (strcmp(my_args.bdevtype, "best") &&
+ strcmp(my_args.bdevtype, "_unset") &&
+ !is_valid_bdev_type(my_args.bdevtype)) {
+ fprintf(stderr, "%s is not a valid backing storage type.\n", my_args.bdevtype);
+ exit(EXIT_FAILURE);
+ }
+
+ if (geteuid()) {
+ if (mkdir_p(my_args.lxcpath[0], 0755)) {
+ exit(EXIT_FAILURE);
+ }
+ if (access(my_args.lxcpath[0], O_RDWR) < 0) {
+ fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (strcmp(my_args.bdevtype, "dir") && strcmp(my_args.bdevtype, "_unset") &&
+ strcmp(my_args.bdevtype, "btrfs")) {
+ fprintf(stderr, "Unprivileged users cannot create %s containers", my_args.bdevtype);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ fprintf(stderr, "Failed to create lxc container.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (c->is_defined(c)) {
+ lxc_container_put(c);
+ fprintf(stderr, "Container already exists\n");
+ exit(EXIT_FAILURE);
+ }
+ if (my_args.configfile)
+ c->load_config(c, my_args.configfile);
+ else
+ c->load_config(c, lxc_global_config_value("lxc.default_config"));
+
+ if (my_args.fstype)
+ spec.fstype = my_args.fstype;
+ if (my_args.fssize)
+ spec.fssize = my_args.fssize;
+
+ if ((strcmp(my_args.bdevtype, "zfs") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
+ if (my_args.zfsroot)
+ spec.zfs.zfsroot = my_args.zfsroot;
+ }
+
+ if ((strcmp(my_args.bdevtype, "lvm") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
+ if (my_args.lvname)
+ spec.lvm.lv = my_args.lvname;
+ if (my_args.vgname)
+ spec.lvm.vg = my_args.vgname;
+ if (my_args.thinpool)
+ spec.lvm.thinpool = my_args.thinpool;
+ }
+
+ if ((strcmp(my_args.bdevtype, "rbd") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) {
+ if (my_args.rbdname)
+ spec.rbd.rbdname = my_args.rbdname;
+ if (my_args.rbdpool)
+ spec.rbd.rbdpool = my_args.rbdpool;
+ }
+
+ if (my_args.dir)
+ spec.dir = my_args.dir;
+
+ if (strcmp(my_args.bdevtype, "_unset") == 0)
+ my_args.bdevtype = NULL;
+
+ if (my_args.quiet)
+ flags = LXC_CREATE_QUIET;
+
+ if (!c->create(c, my_args.template, my_args.bdevtype, &spec, flags, &argv[optind])) {
+ ERROR("Error creating container %s", c->name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ lxc_container_put(c);
+ INFO("container %s created", c->name);
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/lxc/tools/lxc_destroy.c b/src/lxc/tools/lxc_destroy.c
new file mode 100644
index 0000000..b521739
--- /dev/null
+++ b/src/lxc/tools/lxc_destroy.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+
+#include <libgen.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "arguments.h"
+#include "log.h"
+#include "lxc.h"
+#include "utils.h"
+
+lxc_log_define(lxc_destroy_ui, lxc);
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg);
+static bool quiet;
+
+static const struct option my_longopts[] = {
+ {"force", no_argument, 0, 'f'},
+ {"snapshots", no_argument, 0, 's'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-destroy",
+ .help = "\
+--name=NAME [-f] [-P lxcpath]\n\
+\n\
+lxc-destroy destroys a container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -s, --snapshots destroy including all snapshots\n\
+ -f, --force wait for the container to shut down\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .task = DESTROY,
+};
+
+static bool do_destroy(struct lxc_container *c);
+static bool do_destroy_with_snapshots(struct lxc_container *c);
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ bool bret;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(EXIT_FAILURE);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+ if (my_args.quiet)
+ quiet = true;
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ if (!quiet)
+ fprintf(stderr, "System error loading container\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->may_control(c)) {
+ if (!quiet)
+ fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->is_defined(c)) {
+ if (!quiet)
+ fprintf(stderr, "Container is not defined\n");
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ if (my_args.task == SNAP) {
+ bret = do_destroy_with_snapshots(c);
+ if (bret && !quiet)
+ printf("Destroyed container %s including snapshots \n", my_args.name);
+ } else {
+ bret = do_destroy(c);
+ if (bret && !quiet)
+ printf("Destroyed container %s\n", my_args.name);
+ }
+
+ lxc_container_put(c);
+
+ if (bret)
+ exit(EXIT_SUCCESS);
+ exit(EXIT_FAILURE);
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ switch (c) {
+ case 'f': args->force = 1; break;
+ case 's': args->task = SNAP; break;
+ }
+ return 0;
+}
+
+static bool do_destroy(struct lxc_container *c)
+{
+ bool bret = true;
+ char path[MAXPATHLEN];
+
+ /* First check whether the container has dependent clones or snapshots. */
+ int ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return false;
+
+ if (file_exists(path)) {
+ if (!quiet)
+ fprintf(stdout, "Destroying %s failed: %s has clones.\n", c->name, c->name);
+ return false;
+ }
+
+ ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return false;
+
+ if (dir_exists(path)) {
+ if (!quiet)
+ fprintf(stdout, "Destroying %s failed: %s has snapshots.\n", c->name, c->name);
+ return false;
+ }
+
+ if (c->is_running(c)) {
+ if (!my_args.force && !quiet) {
+ fprintf(stderr, "%s is running\n", my_args.name);
+ return false;
+ }
+ /* If the container was ephemeral it will be removed on shutdown. */
+ c->stop(c);
+ }
+
+ /* If the container was ephemeral we have already removed it when we
+ * stopped it. */
+ if (c->is_defined(c) && !c->lxc_conf->ephemeral)
+ bret = c->destroy(c);
+
+ if (!bret) {
+ if (!quiet)
+ fprintf(stderr, "Destroying %s failed\n", my_args.name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool do_destroy_with_snapshots(struct lxc_container *c)
+{
+ struct lxc_container *c1;
+ struct stat fbuf;
+ bool bret = false;
+ char path[MAXPATHLEN];
+ char *buf = NULL;
+ char *lxcpath = NULL;
+ char *lxcname = NULL;
+ char *scratch = NULL;
+ int fd;
+ int ret;
+ int counter = 0;
+
+ /* Destroy clones. */
+ ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return false;
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ ret = fstat(fd, &fbuf);
+ if (ret < 0) {
+ close(fd);
+ return false;
+ }
+
+ /* Make sure that the string is \0 terminated. */
+ buf = calloc(fbuf.st_size + 1, sizeof(char));
+ if (!buf) {
+ SYSERROR("failed to allocate memory");
+ close(fd);
+ return false;
+ }
+
+ ret = read(fd, buf, fbuf.st_size);
+ if (ret < 0) {
+ ERROR("could not read %s", path);
+ close(fd);
+ free(buf);
+ return false;
+ }
+ close(fd);
+
+ while ((lxcpath = strtok_r(!counter ? buf : NULL, "\n", &scratch))) {
+ if (!(lxcname = strtok_r(NULL, "\n", &scratch)))
+ break;
+ c1 = lxc_container_new(lxcname, lxcpath);
+ if (!c1) {
+ counter++;
+ continue;
+ }
+ /* We do not destroy recursively. If a clone of a clone
+ * has clones or snapshots the user should remove it
+ * explicitly. */
+ if (!do_destroy(c1)) {
+ lxc_container_put(c1);
+ free(buf);
+ return false;
+ }
+ lxc_container_put(c1);
+ counter++;
+ }
+ free(buf);
+ }
+
+ /* Destroy snapshots located in the containers snap/ folder. */
+ ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return false;
+
+ if (dir_exists(path))
+ bret = c->destroy_with_snapshots(c);
+ else
+ bret = do_destroy(c);
+
+ return bret;
+}
+
diff --git a/src/lxc/tools/lxc_device.c b/src/lxc/tools/lxc_device.c
new file mode 100644
index 0000000..0c9e066
--- /dev/null
+++ b/src/lxc/tools/lxc_device.c
@@ -0,0 +1,178 @@
+/*
+ * lxc: linux Container library
+ *
+ * Authors:
+ * Dongsheng Yang <yangds.fnst at cn.fujitsu.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <libgen.h>
+#include <string.h>
+#include <limits.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "utils.h"
+#include "lxc.h"
+#include "log.h"
+
+#include "arguments.h"
+
+#if HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#else
+#include <../include/ifaddrs.h>
+#endif
+
+lxc_log_define(lxc_device, lxc);
+
+static const struct option my_longopts[] = {
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-device",
+ .help = "\
+--name=NAME -- add|del DEV\n\
+\n\
+lxc-device attach or detach DEV to or from container.\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container",
+ .options = my_longopts,
+ .parser = NULL,
+ .checker = NULL,
+};
+
+static bool is_interface(const char* dev_name, pid_t pid)
+{
+ pid_t p = fork();
+
+ if (p < 0) {
+ SYSERROR("failed to fork task.");
+ exit(1);
+ }
+
+ if (p == 0) {
+ struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+
+ if (!switch_to_ns(pid, "net")) {
+ ERROR("failed to enter netns of container.");
+ exit(-1);
+ }
+
+ /* Grab the list of interfaces */
+ if (getifaddrs(&interfaceArray)) {
+ ERROR("failed to get interfaces list");
+ exit(-1);
+ }
+
+ /* Iterate through the interfaces */
+ for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+ if (strcmp(tempIfAddr->ifa_name, dev_name) == 0) {
+ exit(0);
+ }
+ }
+ exit(1);
+ }
+
+ if (wait_for_pid(p) == 0) {
+ return true;
+ }
+ return false;
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ char *cmd, *dev_name, *dst_name;
+ int ret = 1;
+
+ if (geteuid() != 0) {
+ ERROR("%s must be run as root", argv[0]);
+ exit(1);
+ }
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ goto err;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ goto err;
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ ERROR("%s doesn't exist", my_args.name);
+ goto err;
+ }
+
+ if (!c->is_running(c)) {
+ ERROR("Container %s is not running.", c->name);
+ goto err1;
+ }
+
+ if (my_args.argc < 2) {
+ ERROR("Error: no command given (Please see --help output)");
+ goto err1;
+ }
+
+ cmd = my_args.argv[0];
+ dev_name = my_args.argv[1];
+ if (my_args.argc < 3)
+ dst_name = dev_name;
+ else
+ dst_name = my_args.argv[2];
+
+ if (strcmp(cmd, "add") == 0) {
+ if (is_interface(dev_name, 1)) {
+ ret = c->attach_interface(c, dev_name, dst_name);
+ } else {
+ ret = c->add_device_node(c, dev_name, dst_name);
+ }
+ if (ret != true) {
+ ERROR("Failed to add %s to %s.", dev_name, c->name);
+ ret = 1;
+ goto err1;
+ }
+ INFO("Add %s to %s.", dev_name, c->name);
+ } else if (strcmp(cmd, "del") == 0) {
+ if (is_interface(dev_name, c->init_pid(c))) {
+ ret = c->detach_interface(c, dev_name, dst_name);
+ } else {
+ ret = c->remove_device_node(c, dev_name, dst_name);
+ }
+ if (ret != true) {
+ ERROR("Failed to del %s from %s.", dev_name, c->name);
+ ret = 1;
+ goto err1;
+ }
+ INFO("Delete %s from %s.", dev_name, c->name);
+ } else {
+ ERROR("Error: Please use add or del (Please see --help output)");
+ goto err1;
+ }
+ exit(0);
+err1:
+ lxc_container_put(c);
+err:
+ exit(ret);
+}
diff --git a/src/lxc/tools/lxc_execute.c b/src/lxc/tools/lxc_execute.c
new file mode 100644
index 0000000..50d481f
--- /dev/null
+++ b/src/lxc/tools/lxc_execute.c
@@ -0,0 +1,161 @@
+/*
+ * 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
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include "caps.h"
+#include "lxc.h"
+#include "log.h"
+#include "conf.h"
+#include "confile.h"
+#include "arguments.h"
+#include "config.h"
+#include "start.h"
+#include "utils.h"
+
+lxc_log_define(lxc_execute_ui, lxc);
+
+static struct lxc_list defines;
+
+static int my_checker(const struct lxc_arguments* args)
+{
+ if (!args->argc) {
+ lxc_error(args, "missing command to execute !");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'f': args->rcfile = arg; break;
+ case 's': return lxc_config_define_add(&defines, arg); break;
+ case 'u': args->uid = atoi(arg); break;
+ case 'g': args->gid = atoi(arg);
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"rcfile", required_argument, 0, 'f'},
+ {"define", required_argument, 0, 's'},
+ {"uid", required_argument, 0, 'u'},
+ {"gid", required_argument, 0, 'g'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-execute",
+ .help = "\
+--name=NAME -- COMMAND\n\
+\n\
+lxc-execute creates a container with the identifier NAME\n\
+and execs COMMAND into this container.\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -f, --rcfile=FILE Load configuration file FILE\n\
+ -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\
+ -u, --uid=UID Execute COMMAND with UID inside the container\n\
+ -g, --gid=GID Execute COMMAND with GID inside the container\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = my_checker,
+};
+
+int main(int argc, char *argv[])
+{
+ char *rcfile;
+ struct lxc_conf *conf;
+ int ret;
+
+ lxc_list_init(&defines);
+
+ if (lxc_caps_init())
+ return 1;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ /* rcfile is specified in the cli option */
+ if (my_args.rcfile)
+ rcfile = (char *)my_args.rcfile;
+ else {
+ int rc;
+
+ rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name);
+ if (rc == -1) {
+ SYSERROR("failed to allocate memory");
+ return 1;
+ }
+
+ /* container configuration does not exist */
+ if (access(rcfile, F_OK)) {
+ free(rcfile);
+ rcfile = NULL;
+ }
+ }
+
+ conf = lxc_conf_init();
+ if (!conf) {
+ ERROR("failed to initialize configuration");
+ return 1;
+ }
+
+ if (rcfile && lxc_config_read(rcfile, conf, NULL)) {
+ ERROR("failed to read configuration file");
+ return 1;
+ }
+
+ if (lxc_config_define_load(&defines, conf))
+ return 1;
+
+ if (my_args.uid)
+ conf->init_uid = my_args.uid;
+
+ if (my_args.gid)
+ conf->init_gid = my_args.gid;
+
+ ret = lxc_execute(my_args.name, my_args.argv, my_args.quiet, conf, my_args.lxcpath[0], false);
+
+ lxc_conf_free(conf);
+
+ if (ret < 0)
+ return 1;
+ return ret;
+}
diff --git a/src/lxc/tools/lxc_freeze.c b/src/lxc/tools/lxc_freeze.c
new file mode 100644
index 0000000..ea8bd3e
--- /dev/null
+++ b/src/lxc/tools/lxc_freeze.c
@@ -0,0 +1,92 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <libgen.h>
+#include <string.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+
+#include "arguments.h"
+
+lxc_log_define(lxc_freeze_ui, lxc);
+
+static const struct option my_longopts[] = {
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-freeze",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-freeze freezes a container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container",
+ .options = my_longopts,
+ .parser = NULL,
+ .checker = NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(1);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(1);
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name);
+ exit(1);
+ }
+
+ if (!c->may_control(c)) {
+ ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+ if (!c->freeze(c)) {
+ ERROR("Failed to freeze %s:%s", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+ lxc_container_put(c);
+
+ exit(0);
+}
diff --git a/src/lxc/tools/lxc_info.c b/src/lxc/tools/lxc_info.c
new file mode 100644
index 0000000..58ff619
--- /dev/null
+++ b/src/lxc/tools/lxc_info.c
@@ -0,0 +1,397 @@
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <libgen.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "utils.h"
+#include "commands.h"
+#include "arguments.h"
+
+lxc_log_define(lxc_info_ui, lxc);
+
+static bool ips;
+static bool state;
+static bool pid;
+static bool stats;
+static bool humanize = true;
+static char **key = NULL;
+static int keys = 0;
+static int filter_count = 0;
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ char **newk;
+ switch (c) {
+ case 'c':
+ newk = realloc(key, (keys + 1) * sizeof(key[0]));
+ if (!newk)
+ return -1;
+ key = newk;
+ key[keys] = arg;
+ keys++;
+ break;
+ case 'i': ips = true; filter_count += 1; break;
+ case 's': state = true; filter_count += 1; break;
+ case 'p': pid = true; filter_count += 1; break;
+ case 'S': stats = true; filter_count += 5; break;
+ case 'H': humanize = false; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"config", required_argument, 0, 'c'},
+ {"ips", no_argument, 0, 'i'},
+ {"state", no_argument, 0, 's'},
+ {"pid", no_argument, 0, 'p'},
+ {"stats", no_argument, 0, 'S'},
+ {"no-humanize", no_argument, 0, 'H'},
+ LXC_COMMON_OPTIONS,
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-info",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-info display some information about a container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -c, --config=KEY show configuration variable KEY from running container\n\
+ -i, --ips shows the IP addresses\n\
+ -p, --pid shows the process id of the init container\n\
+ -S, --stats shows usage stats\n\
+ -H, --no-humanize shows stats as raw numbers, not humanized\n\
+ -s, --state shows the state of the container\n",
+ .name = NULL,
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+};
+
+static void str_chomp(char *buf)
+{
+ char *ch;
+
+ /* remove trailing whitespace from buf */
+ for(ch = &buf[strlen(buf)-1];
+ ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' ');
+ ch--)
+ *ch = '\0';
+}
+
+static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
+{
+ if (val > 1 << 30) {
+ snprintf(buf, bufsz, "%u.%2.2u GiB",
+ (int)(val >> 30),
+ (int)(val & ((1 << 30) - 1)) / 10737419);
+ } else if (val > 1 << 20) {
+ int x = val + 5243; /* for rounding */
+ snprintf(buf, bufsz, "%u.%2.2u MiB",
+ x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
+ } else if (val > 1 << 10) {
+ int x = val + 5; /* for rounding */
+ snprintf(buf, bufsz, "%u.%2.2u KiB",
+ x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
+ } else {
+ snprintf(buf, bufsz, "%u bytes", (int)val);
+ }
+}
+
+static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
+{
+ unsigned long long val;
+ char *end = NULL;
+
+ val = strtoull(iobuf, &end, 0);
+ if (humanize) {
+ if (*end == '\0' || *end == '\n')
+ size_humanize(val, iobuf, iobufsz);
+ else
+ *iobuf = '\0';
+ }
+ return val;
+}
+
+static void print_net_stats(struct lxc_container *c)
+{
+ int rc,netnr;
+ unsigned long long rx_bytes = 0, tx_bytes = 0;
+ char *ifname, *type;
+ char path[PATH_MAX];
+ char buf[256];
+
+ for(netnr = 0; ;netnr++) {
+ sprintf(buf, "lxc.network.%d.type", netnr);
+ type = c->get_running_config_item(c, buf);
+ if (!type)
+ break;
+
+ if (!strcmp(type, "veth")) {
+ sprintf(buf, "lxc.network.%d.veth.pair", netnr);
+ } else {
+ sprintf(buf, "lxc.network.%d.link", netnr);
+ }
+ free(type);
+ ifname = c->get_running_config_item(c, buf);
+ if (!ifname)
+ return;
+ printf("%-15s %s\n", "Link:", ifname);
+ fflush(stdout);
+
+ /* XXX: tx and rx are reversed from the host vs container
+ * perspective, print them from the container perspective
+ */
+ snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname);
+ rc = lxc_read_from_file(path, buf, sizeof(buf));
+ if (rc > 0) {
+ str_chomp(buf);
+ rx_bytes = str_size_humanize(buf, sizeof(buf));
+ printf("%-15s %s\n", " TX bytes:", buf);
+ fflush(stdout);
+ }
+
+ snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname);
+ rc = lxc_read_from_file(path, buf, sizeof(buf));
+ if (rc > 0) {
+ str_chomp(buf);
+ tx_bytes = str_size_humanize(buf, sizeof(buf));
+ printf("%-15s %s\n", " RX bytes:", buf);
+ fflush(stdout);
+ }
+
+ sprintf(buf, "%llu", rx_bytes + tx_bytes);
+ str_size_humanize(buf, sizeof(buf));
+ printf("%-15s %s\n", " Total bytes:", buf);
+ fflush(stdout);
+ free(ifname);
+ }
+}
+
+static void print_stats(struct lxc_container *c)
+{
+ int i, ret;
+ char buf[256];
+
+ ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
+ if (ret > 0 && ret < sizeof(buf)) {
+ str_chomp(buf);
+ if (humanize) {
+ float seconds = strtof(buf, NULL) / 1000000000.0;
+ printf("%-15s %.2f seconds\n", "CPU use:", seconds);
+ } else {
+ printf("%-15s %s\n", "CPU use:", buf);
+ }
+ fflush(stdout);
+ }
+
+ ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
+ if (ret > 0 && ret < sizeof(buf)) {
+ char *ch;
+
+ /* put ch on last "Total" line */
+ str_chomp(buf);
+ for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--)
+ ;
+ if (*ch == '\n')
+ ch++;
+
+ if (strncmp(ch, "Total", 5) == 0) {
+ ch += 6;
+ memmove(buf, ch, strlen(ch)+1);
+ str_size_humanize(buf, sizeof(buf));
+ printf("%-15s %s\n", "BlkIO use:", buf);
+ }
+ fflush(stdout);
+ }
+
+ static const struct {
+ const char *name;
+ const char *file;
+ } lxstat[] = {
+ { "Memory use:", "memory.usage_in_bytes" },
+ { "KMem use:", "memory.kmem.usage_in_bytes" },
+ { NULL, NULL },
+ };
+
+ for (i = 0; lxstat[i].name; i++) {
+ ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
+ if (ret > 0 && ret < sizeof(buf)) {
+ str_chomp(buf);
+ str_size_humanize(buf, sizeof(buf));
+ printf("%-15s %s\n", lxstat[i].name, buf);
+ fflush(stdout);
+ }
+ }
+}
+
+static void print_info_msg_int(const char *key, int value)
+{
+ if (humanize)
+ printf("%-15s %d\n", key, value);
+ else {
+ if (filter_count == 1)
+ printf("%d\n", value);
+ else
+ printf("%-15s %d\n", key, value);
+ }
+ fflush(stdout);
+}
+
+static void print_info_msg_str(const char *key, const char *value)
+{
+ if (humanize)
+ printf("%-15s %s\n", key, value);
+ else {
+ if (filter_count == 1)
+ printf("%s\n", value);
+ else
+ printf("%-15s %s\n", key, value);
+ }
+ fflush(stdout);
+}
+
+static int print_info(const char *name, const char *lxcpath)
+{
+ int i;
+ struct lxc_container *c;
+
+ c = lxc_container_new(name, lxcpath);
+ if (!c) {
+ fprintf(stderr, "Failure to retrieve information on %s:%s\n", lxcpath ? lxcpath : "null",
+ name ? name : "null");
+ return -1;
+ }
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ lxc_container_put(c);
+ return -1;
+ }
+
+ if (!c->is_running(c) && !c->is_defined(c)) {
+ fprintf(stderr, "%s doesn't exist\n", c->name);
+ lxc_container_put(c);
+ return -1;
+ }
+
+ if (!state && !pid && !ips && !stats && keys <= 0) {
+ state = pid = ips = stats = true;
+ print_info_msg_str("Name:", c->name);
+ }
+
+ if (state) {
+ print_info_msg_str("State:", c->state(c));
+ }
+
+ if (c->is_running(c)) {
+ if (pid) {
+ pid_t initpid;
+
+ initpid = c->init_pid(c);
+ if (initpid >= 0)
+ print_info_msg_int("PID:", initpid);
+ }
+
+ if (ips) {
+ fflush(stdout);
+ char **addresses = c->get_ips(c, NULL, NULL, 0);
+ if (addresses) {
+ char *address;
+ i = 0;
+ while (addresses[i]) {
+ address = addresses[i];
+ print_info_msg_str("IP:", address);
+ i++;
+ }
+ }
+ }
+ }
+
+ if (stats) {
+ print_stats(c);
+ print_net_stats(c);
+ }
+
+ for(i = 0; i < keys; i++) {
+ int len = c->get_config_item(c, key[i], NULL, 0);
+
+ if (len > 0) {
+ char *val = (char*) malloc(sizeof(char)*len + 1);
+
+ if (c->get_config_item(c, key[i], val, len + 1) != len) {
+ fprintf(stderr, "unable to read %s from configuration\n", key[i]);
+ } else {
+ if (!humanize && keys == 1)
+ printf("%s\n", val);
+ else
+ printf("%s = %s\n", key[i], val);
+ }
+ free(val);
+ } else if (len == 0) {
+ if (!humanize && keys == 1)
+ printf("\n");
+ else
+ printf("%s =\n", key[i]);
+ } else {
+ fprintf(stderr, "%s invalid\n", key[i]);
+ }
+ fflush(stdout);
+ }
+
+ lxc_container_put(c);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return ret;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return ret;
+ lxc_log_options_no_override();
+
+ if (print_info(my_args.name, my_args.lxcpath[0]) == 0)
+ ret = EXIT_SUCCESS;
+
+ return ret;
+}
diff --git a/src/lxc/tools/lxc_init.c b/src/lxc/tools/lxc_init.c
new file mode 100644
index 0000000..5dd29af
--- /dev/null
+++ b/src/lxc/tools/lxc_init.c
@@ -0,0 +1,261 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include "log.h"
+#include "caps.h"
+#include "error.h"
+#include "initutils.h"
+
+lxc_log_define(lxc_init, lxc);
+
+static int quiet;
+
+static const struct option options[] = {
+ { "name", required_argument, NULL, 'n' },
+ { "logpriority", required_argument, NULL, 'l' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "lxcpath", required_argument, NULL, 'P' },
+ { 0, 0, 0, 0 },
+};
+
+static sig_atomic_t was_interrupted = 0;
+
+static void interrupt_handler(int sig)
+{
+ if (!was_interrupted)
+ was_interrupted = sig;
+}
+
+static void usage(void) {
+ fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n"
+ "Common options :\n"
+ " -n, --name=NAME NAME of the container\n"
+ " -l, --logpriority=LEVEL Set log priority to LEVEL\n"
+ " -q, --quiet Don't produce any output\n"
+ " -P, --lxcpath=PATH Use specified container path\n"
+ " -?, --help Give this help list\n"
+ "\n"
+ "Mandatory or optional arguments to long options are also mandatory or optional\n"
+ "for any corresponding short options.\n"
+ "\n"
+ "NOTE: lxc-init is intended for use by lxc internally\n"
+ " and does not need to be run by hand\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ pid_t pid;
+ int err;
+ char **aargv;
+ sigset_t mask, omask;
+ int i, have_status = 0, shutdown = 0;
+ int opt;
+ char *lxcpath = NULL, *name = NULL, *logpriority = NULL;
+
+ while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) {
+ switch(opt) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'l':
+ logpriority = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'P':
+ lxcpath = optarg;
+ break;
+ default: /* '?' */
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ err = lxc_log_init(name, name ? NULL : "none", logpriority,
+ basename(argv[0]), quiet, lxcpath);
+ if (err < 0)
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+
+ if (!argv[optind]) {
+ ERROR("missing command to launch");
+ exit(EXIT_FAILURE);
+ }
+
+ aargv = &argv[optind];
+
+ /*
+ * mask all the signals so we are safe to install a
+ * signal handler and to fork
+ */
+ if (sigfillset(&mask) ||
+ sigdelset(&mask, SIGILL) ||
+ sigdelset(&mask, SIGSEGV) ||
+ sigdelset(&mask, SIGBUS) ||
+ sigprocmask(SIG_SETMASK, &mask, &omask)) {
+ SYSERROR("failed to set signal mask");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 1; i < NSIG; i++) {
+ struct sigaction act;
+
+ /* Exclude some signals: ILL, SEGV and BUS are likely to
+ * reveal a bug and we want a core. STOP and KILL cannot be
+ * handled anyway: they're here for documentation.
+ */
+ if (i == SIGILL ||
+ i == SIGSEGV ||
+ i == SIGBUS ||
+ i == SIGSTOP ||
+ i == SIGKILL ||
+ i == 32 || i == 33)
+ continue;
+
+ if (sigfillset(&act.sa_mask) ||
+ sigdelset(&act.sa_mask, SIGILL) ||
+ sigdelset(&act.sa_mask, SIGSEGV) ||
+ sigdelset(&act.sa_mask, SIGBUS) ||
+ sigdelset(&act.sa_mask, SIGSTOP) ||
+ sigdelset(&act.sa_mask, SIGKILL)) {
+ ERROR("failed to set signal");
+ exit(EXIT_FAILURE);
+ }
+
+ act.sa_flags = 0;
+ act.sa_handler = interrupt_handler;
+ if (sigaction(i, &act, NULL) && errno != EINVAL) {
+ SYSERROR("failed to sigaction");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ lxc_setup_fs();
+
+ pid = fork();
+
+ if (pid < 0)
+ exit(EXIT_FAILURE);
+
+ if (!pid) {
+
+ /* restore default signal handlers */
+ for (i = 1; i < NSIG; i++)
+ signal(i, SIG_DFL);
+
+ if (sigprocmask(SIG_SETMASK, &omask, NULL)) {
+ SYSERROR("failed to set signal mask");
+ exit(EXIT_FAILURE);
+ }
+
+ NOTICE("about to exec '%s'", aargv[0]);
+
+ execvp(aargv[0], aargv);
+ ERROR("failed to exec: '%s' : %m", aargv[0]);
+ exit(err);
+ }
+
+ /* let's process the signals now */
+ if (sigdelset(&omask, SIGALRM) ||
+ sigprocmask(SIG_SETMASK, &omask, NULL)) {
+ SYSERROR("failed to set signal mask");
+ exit(EXIT_FAILURE);
+ }
+
+ /* no need of other inherited fds but stderr */
+ close(fileno(stdin));
+ close(fileno(stdout));
+
+ err = EXIT_SUCCESS;
+ for (;;) {
+ int status;
+ pid_t waited_pid;
+
+ switch (was_interrupted) {
+
+ case 0:
+ break;
+
+ case SIGPWR:
+ case SIGTERM:
+ if (!shutdown) {
+ shutdown = 1;
+ kill(-1, SIGTERM);
+ alarm(1);
+ }
+ break;
+
+ case SIGALRM:
+ kill(-1, SIGKILL);
+ break;
+
+ default:
+ kill(pid, was_interrupted);
+ break;
+ }
+
+ was_interrupted = 0;
+ waited_pid = wait(&status);
+ if (waited_pid < 0) {
+ if (errno == ECHILD)
+ goto out;
+ if (errno == EINTR)
+ continue;
+
+ ERROR("failed to wait child : %s",
+ strerror(errno));
+ goto out;
+ }
+
+ /* reset timer each time a process exited */
+ if (shutdown)
+ alarm(1);
+
+ /*
+ * keep the exit code of started application
+ * (not wrapped pid) and continue to wait for
+ * the end of the orphan group.
+ */
+ if (waited_pid == pid && !have_status) {
+ err = lxc_error_set_and_log(waited_pid, status);
+ have_status = 1;
+ }
+ }
+out:
+ if (err < 0)
+ exit(EXIT_FAILURE);
+ exit(err);
+}
diff --git a/src/lxc/tools/lxc_ls.c b/src/lxc/tools/lxc_ls.c
new file mode 100644
index 0000000..e2a4c34
--- /dev/null
+++ b/src/lxc/tools/lxc_ls.c
@@ -0,0 +1,1213 @@
+/*
+ *
+ * Copyright © 2016 Christian Brauner <christian.brauner at mailbox.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <getopt.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "arguments.h"
+#include "conf.h"
+#include "confile.h"
+#include "log.h"
+#include "lxc.h"
+#include "utils.h"
+
+lxc_log_define(lxc_ls, lxc);
+
+#define LINELEN 1024
+/* Per default we only allow five levels of recursion to protect the stack at
+ * least a little bit. */
+#define MAX_NESTLVL 5
+
+#define LS_FROZEN 1
+#define LS_STOPPED 2
+#define LS_ACTIVE 3
+#define LS_RUNNING 4
+#define LS_NESTING 5
+#define LS_FILTER 6
+
+#ifndef SOCK_CLOEXEC
+# define SOCK_CLOEXEC 02000000
+#endif
+
+/* Store container info. */
+struct ls {
+ char *name;
+ char *state;
+ char *groups;
+ char *interface;
+ char *ipv4;
+ char *ipv6;
+ unsigned int nestlvl;
+ pid_t init;
+ double ram;
+ double swap;
+ bool autostart;
+ bool running;
+};
+
+/* Keep track of field widths for printing. */
+struct lengths {
+ unsigned int name_length;
+ unsigned int state_length;
+ unsigned int groups_length;
+ unsigned int interface_length;
+ unsigned int ipv4_length;
+ unsigned int ipv6_length;
+ unsigned int init_length;
+ unsigned int ram_length;
+ unsigned int swap_length;
+ unsigned int autostart_length;
+};
+
+static int ls_deserialize(int rpipefd, struct ls **m, size_t *len);
+static void ls_field_width(const struct ls *l, const size_t size,
+ struct lengths *lht);
+static void ls_free(struct ls *l, size_t size);
+static void ls_free_arr(char **arr, size_t size);
+static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args,
+ const char *basepath, const char *parent, unsigned int lvl,
+ char **lockpath, size_t len_lockpath, char **grps_must,
+ size_t grps_must_len);
+static char *ls_get_cgroup_item(struct lxc_container *c, const char *item);
+static char *ls_get_config_item(struct lxc_container *c, const char *item,
+ bool running);
+static char *ls_get_groups(struct lxc_container *c, bool running);
+static char *ls_get_ips(struct lxc_container *c, const char *inet);
+static int ls_recv_str(int fd, char **buf);
+static int ls_send_str(int fd, const char *buf);
+
+struct wrapargs {
+ const struct lxc_arguments *args;
+ char **grps_must;
+ size_t grps_must_len;
+ int pipefd[2];
+ size_t *size;
+ const char *parent;
+ unsigned int nestlvl;
+};
+
+/*
+ * Takes struct wrapargs as argument.
+ */
+static int ls_get_wrapper(void *wrap);
+
+/*
+ * To calculate swap usage we should not simply check memory.usage_in_bytes and
+ * memory.memsw.usage_in_bytes and then do:
+ * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes;
+ * because we might receive an incorrect/negative value.
+ * Instead we check memory.stat and check the "swap" value.
+ */
+static double ls_get_swap(struct lxc_container *c);
+static unsigned int ls_get_term_width(void);
+static char *ls_get_interface(struct lxc_container *c);
+static bool ls_has_all_grps(const char *has, char **must, size_t must_len);
+static struct ls *ls_new(struct ls **ls, size_t *size);
+
+/*
+ * Print user-specified fancy format.
+ */
+static void ls_print_fancy_format(struct ls *l, struct lengths *lht,
+ size_t size, const char *fancy_fmt);
+
+/*
+ * Only print names of containers.
+ */
+static void ls_print_names(struct ls *l, struct lengths *lht,
+ size_t ls_arr, size_t termwidth);
+
+/*
+ * Print default fancy format.
+ */
+static void ls_print_table(struct ls *l, struct lengths *lht,
+ size_t size);
+
+/*
+ * id can only be 79 + \0 chars long.
+ */
+static int ls_remove_lock(const char *path, const char *name,
+ char **lockpath, size_t *len_lockpath, bool recalc);
+static int ls_serialize(int wpipefd, struct ls *n);
+static int my_parser(struct lxc_arguments *args, int c, char *arg);
+
+static const struct option my_longopts[] = {
+ {"line", no_argument, 0, '1'},
+ {"fancy", no_argument, 0, 'f'},
+ {"fancy-format", required_argument, 0, 'F'},
+ {"active", no_argument, 0, LS_ACTIVE},
+ {"running", no_argument, 0, LS_RUNNING},
+ {"frozen", no_argument, 0, LS_FROZEN},
+ {"stopped", no_argument, 0, LS_STOPPED},
+ {"nesting", optional_argument, 0, LS_NESTING},
+ {"groups", required_argument, 0, 'g'},
+ {"filter", required_argument, 0, LS_FILTER},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-ls",
+ .help = "\n\
+[-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
+[-1] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
+[-f] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\
+\n\
+lxc-ls list containers\n\
+\n\
+Options :\n\
+ -1, --line show one entry per line\n\
+ -f, --fancy column-based output\n\
+ -F, --fancy-format column-based output\n\
+ --active list only active containers\n\
+ --running list only running containers\n\
+ --frozen list only frozen containers\n\
+ --stopped list only stopped containers\n\
+ --nesting=NUM list nested containers up to NUM (default is 5) levels of nesting\n\
+ --filter=REGEX filter container names by regular expression\n\
+ -g --groups comma separated list of groups a container must have to be displayed\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .ls_nesting = 0,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+ /*
+ * The lxc parser requires that my_args.name is set. So let's satisfy
+ * that condition by setting a dummy name which is never used.
+ */
+ my_args.name = "";
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(EXIT_FAILURE);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ /*
+ * We set the first argument that usually takes my_args.name to NULL so
+ * that the log is only used when the user specifies a file.
+ */
+ if (lxc_log_init(NULL, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+
+ struct lengths max_len = {
+ /* default header length */
+ .name_length = 4, /* NAME */
+ .state_length = 5, /* STATE */
+ .groups_length = 6, /* GROUPS */
+ .interface_length = 9, /* INTERFACE */
+ .ipv4_length = 4, /* IPV4 */
+ .ipv6_length = 4, /* IPV6 */
+ .init_length = 3, /* PID */
+ .ram_length = 3, /* RAM */
+ .swap_length = 4, /* SWAP */
+ .autostart_length = 9, /* AUTOSTART */
+ };
+
+ char **grps = NULL;
+ size_t ngrps = 0;
+ if (my_args.groups) {
+ grps = lxc_string_split_and_trim(my_args.groups, ',');
+ ngrps = lxc_array_len((void **)grps);
+ }
+
+ struct ls *ls_arr = NULL;
+ size_t ls_size = 0;
+ /* &(char *){NULL} is no magic. It's just a compound literal which
+ * avoids having a pointless variable in main() that serves no purpose
+ * here. */
+ int status = ls_get(&ls_arr, &ls_size, &my_args, "", NULL, 0, &(char *){NULL}, 0, grps, ngrps);
+ if (!ls_arr && status == 0)
+ /* We did not fail. There was just nothing to do. */
+ exit(EXIT_SUCCESS);
+ else if (!ls_arr || status == -1)
+ goto out;
+
+ ls_field_width(ls_arr, ls_size, &max_len);
+ if (my_args.ls_fancy && !my_args.ls_fancy_format) {
+ ls_print_table(ls_arr, &max_len, ls_size);
+ } else if (my_args.ls_fancy && my_args.ls_fancy_format) {
+ ls_print_fancy_format(ls_arr, &max_len, ls_size, my_args.ls_fancy_format);
+ } else {
+ unsigned int cols = 0;
+ if (!my_args.ls_line)
+ cols = ls_get_term_width();
+ ls_print_names(ls_arr, &max_len, ls_size, cols);
+ }
+
+ ret = EXIT_SUCCESS;
+
+out:
+ ls_free(ls_arr, ls_size);
+ lxc_free_array((void **)grps, free);
+
+ exit(ret);
+}
+
+static void ls_free(struct ls *l, size_t size)
+{
+ size_t i;
+ struct ls *m = NULL;
+ for (i = 0, m = l; i < size; i++, m++) {
+ free(m->groups);
+ free(m->interface);
+ free(m->ipv4);
+ free(m->ipv6);
+ free(m->name);
+ free(m->state);
+ }
+ free(l);
+}
+
+static char *ls_get_config_item(struct lxc_container *c, const char *item,
+ bool running)
+{
+ if (running)
+ return c->get_running_config_item(c, item);
+
+ size_t len = c->get_config_item(c, item, NULL, 0);
+ if (len <= 0)
+ return NULL;
+
+ char *val = malloc((len + 1) * sizeof(*val));
+ if (!val)
+ return NULL;
+
+ if ((size_t)c->get_config_item(c, item, val, len + 1) != len) {
+ free(val);
+ val = NULL;
+ }
+
+ return val;
+}
+
+static void ls_free_arr(char **arr, size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; i++)
+ free(arr[i]);
+ free(arr);
+}
+
+static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args,
+ const char *basepath, const char *parent, unsigned int lvl,
+ char **lockpath, size_t len_lockpath, char **grps_must,
+ size_t grps_must_len)
+{
+ /* As ls_get() is non-tail recursive we face the inherent danger of
+ * blowing up the stack at some level of nesting. To have at least some
+ * security we define MAX_NESTLVL to be 5. That should be sufficient for
+ * most users. The argument lvl can be used to keep track of the level
+ * of nesting we are at. If lvl is greater than the allowed default
+ * level or the level the user specified on the command line we return
+ * and unwind the stack. */
+ if (lvl > args->ls_nesting)
+ return 0;
+
+ int num = 0, ret = -1;
+ char **containers = NULL;
+ /* If we, at some level of nesting, encounter a stopped container but
+ * want to retrieve nested containers we need to build an absolute path
+ * beginning from it. Initially, at nesting level 0, basepath will
+ * simply be the empty string and path will simply be whatever the
+ * default lxcpath or the path the user gave us is. Basepath will also
+ * be the empty string in case we encounter a running container since we
+ * can simply attach to its namespace to retrieve nested containers. */
+ char *path = lxc_append_paths(basepath, args->lxcpath[0]);
+ if (!path)
+ goto out;
+
+ if (!dir_exists(path)) {
+ ret = 0;
+ goto out;
+ }
+
+ /* Do not do more work than is necessary right from the start. */
+ if (args->ls_active || (args->ls_active && args->ls_frozen))
+ num = list_active_containers(path, &containers, NULL);
+ else
+ num = list_all_containers(path, &containers, NULL);
+ if (num == -1) {
+ num = 0;
+ goto out;
+ }
+
+ char *tmp = NULL;
+ int check;
+ struct ls *l = NULL;
+ struct lxc_container *c = NULL;
+ size_t i;
+ for (i = 0; i < (size_t)num; i++) {
+ char *name = containers[i];
+
+ /* Filter container names by regex the user gave us. */
+ if (args->ls_filter || args->argc == 1) {
+ regex_t preg;
+ tmp = args->ls_filter ? args->ls_filter : args->argv[0];
+ check = regcomp(&preg, tmp, REG_NOSUB | REG_EXTENDED);
+ if (check == REG_ESPACE) /* we're out of memory */
+ goto out;
+ else if (check != 0)
+ continue;
+ check = regexec(&preg, name, 0, NULL, 0);
+ regfree(&preg);
+ if (check != 0)
+ continue;
+ }
+
+ errno = 0;
+ c = lxc_container_new(name, path);
+ if ((errno == ENOMEM) && !c)
+ goto out;
+ else if (!c)
+ continue;
+
+ if (!c->is_defined(c))
+ goto put_and_next;
+
+ /* This does not allocate memory so no worries about freeing it
+ * when we goto next or out. */
+ const char *state_tmp = c->state(c);
+ if (!state_tmp)
+ state_tmp = "UNKNOWN";
+
+ if (args->ls_running && !c->is_running(c))
+ goto put_and_next;
+
+ if (args->ls_frozen && !args->ls_active && strcmp(state_tmp, "FROZEN"))
+ goto put_and_next;
+
+ if (args->ls_stopped && strcmp(state_tmp, "STOPPED"))
+ goto put_and_next;
+
+ bool running = c->is_running(c);
+
+ char *grp_tmp = ls_get_groups(c, running);
+ if (!ls_has_all_grps(grp_tmp, grps_must, grps_must_len)) {
+ free(grp_tmp);
+ goto put_and_next;
+ }
+
+ /* Now it makes sense to allocate memory. */
+ l = ls_new(m, size);
+ if (!l) {
+ free(grp_tmp);
+ goto put_and_next;
+ }
+
+ /* How deeply nested are we? */
+ l->nestlvl = lvl;
+
+ l->groups = grp_tmp;
+
+ l->running = running;
+
+ if (parent && args->ls_nesting && (args->ls_line || !args->ls_fancy))
+ /* Prepend the name of the container with all its parents when
+ * the user requests it. */
+ l->name = lxc_append_paths(parent, name);
+ else
+ /* Otherwise simply record the name. */
+ l->name = strdup(name);
+ if (!l->name)
+ goto put_and_next;
+
+ /* Do not record stuff the user did not explictly request. */
+ if (args->ls_fancy) {
+ /* Maybe we should even consider the name sensitive and
+ * hide it when you're not allowed to control the
+ * container. */
+ if (!c->may_control(c))
+ goto put_and_next;
+
+ l->state = strdup(state_tmp);
+ if (!l->state)
+ goto put_and_next;
+
+ tmp = ls_get_config_item(c, "lxc.start.auto", running);
+ if (tmp)
+ l->autostart = atoi(tmp);
+ free(tmp);
+
+ if (running) {
+ l->init = c->init_pid(c);
+
+ l->interface = ls_get_interface(c);
+
+ l->ipv4 = ls_get_ips(c, "inet");
+
+ l->ipv6 = ls_get_ips(c, "inet6");
+
+ tmp = ls_get_cgroup_item(c, "memory.usage_in_bytes");
+ if (tmp) {
+ l->ram = strtoull(tmp, NULL, 0);
+ l->ram = l->ram / 1024 /1024;
+ free(tmp);
+ }
+
+ l->swap = ls_get_swap(c);
+ }
+ }
+
+ /* Get nested containers: Only do this after we have gathered
+ * all other information we need. */
+ if (args->ls_nesting && running) {
+ struct wrapargs wargs = (struct wrapargs){.args = NULL};
+ /* Open a socket so that the child can communicate with us. */
+ check = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wargs.pipefd);
+ if (check == -1)
+ goto put_and_next;
+
+ /* Set the next nesting level. */
+ wargs.nestlvl = lvl + 1;
+ /* Send in the parent for the next nesting level. */
+ wargs.parent = l->name;
+ wargs.args = args;
+ wargs.grps_must = grps_must;
+ wargs.grps_must_len = grps_must_len;
+
+ pid_t out;
+
+ lxc_attach_options_t aopt = LXC_ATTACH_OPTIONS_DEFAULT;
+ aopt.env_policy = LXC_ATTACH_CLEAR_ENV;
+
+ /* fork(): Attach to the namespace of the container and
+ * run ls_get() in it which is called in ls_get_wrapper(). */
+ check = c->attach(c, ls_get_wrapper, &wargs, &aopt, &out);
+ /* close the socket */
+ close(wargs.pipefd[1]);
+
+ /* Retrieve all information we want from the child. */
+ if (check == 0)
+ if (ls_deserialize(wargs.pipefd[0], m, size) == -1)
+ goto put_and_next;
+
+ /* Wait for the child to finish. */
+ wait_for_pid(out);
+
+ /* We've done all the communication we need so shutdown
+ * the socket and close it. */
+ shutdown(wargs.pipefd[0], SHUT_RDWR);
+ close(wargs.pipefd[0]);
+ } else if (args->ls_nesting && !running) {
+ /* This way of extracting the rootfs is not safe since
+ * it will return very different things depending on the
+ * storage backend that is used for the container. We
+ * need a path-extractor function. We face the same
+ * problem with the ovl_mkdir() function in
+ * lxcoverlay.{c,h}. */
+ char *curr_path = ls_get_config_item(c, "lxc.rootfs", running);
+ if (!curr_path)
+ goto put_and_next;
+
+ /* Since the container is not running and we cannot
+ * attach to it we need another strategy to retrieve
+ * nested containers. What we do is simply create a
+ * growing path which will lead us into the rootfs of
+ * the next container where it stores its containers. */
+ char *newpath = lxc_append_paths(basepath, curr_path);
+ free(curr_path);
+ if (!newpath)
+ goto put_and_next;
+
+ /* We want to remove all locks we create under
+ * /run/lxc/lock so we create a string pointing us to
+ * the lock path for the current container. */
+ if (ls_remove_lock(path, name, lockpath, &len_lockpath, true) == -1)
+ goto put_and_next;
+
+ ls_get(m, size, args, newpath, l->name, lvl + 1, lockpath, len_lockpath, grps_must, grps_must_len);
+ free(newpath);
+
+ /* Remove the lock. No need to check for failure here. */
+ ls_remove_lock(path, name, lockpath, &len_lockpath, false);
+ }
+
+put_and_next:
+ lxc_container_put(c);
+ }
+ ret = 0;
+
+out:
+ ls_free_arr(containers, num);
+ free(path);
+ /* lockpath is shared amongst all non-fork()ing recursive calls to
+ * ls_get() so only free it on the uppermost level. */
+ if (lvl == 0)
+ free(*lockpath);
+
+ return ret;
+}
+
+static char *ls_get_cgroup_item(struct lxc_container *c, const char *item)
+{
+ size_t len = c->get_cgroup_item(c, item, NULL, 0);
+ if (len <= 0)
+ return NULL;
+
+ char *val = malloc((len + 1) * sizeof(*val));
+ if (!val)
+ return NULL;
+
+ if ((size_t)c->get_cgroup_item(c, item, val, len + 1) != len) {
+ free(val);
+ val = NULL;
+ }
+
+ return val;
+}
+
+static char *ls_get_groups(struct lxc_container *c, bool running)
+{
+ size_t len = 0;
+ char *val = NULL;
+
+ if (running)
+ val = c->get_running_config_item(c, "lxc.group");
+ else
+ len = c->get_config_item(c, "lxc.group", NULL, 0);
+
+ if (!val && (len > 0)) {
+ val = malloc((len + 1) * sizeof(*val));
+ if ((size_t)c->get_config_item(c, "lxc.group", val, len + 1) != len) {
+ free(val);
+ return NULL;
+ }
+ }
+
+ if (val) {
+ char *tmp;
+ if ((tmp = strrchr(val, '\n')))
+ *tmp = '\0';
+
+ tmp = lxc_string_replace("\n", ", ", val);
+ free(val);
+ val = tmp;
+ }
+
+ return val;
+}
+
+static char *ls_get_ips(struct lxc_container *c, const char *inet)
+{
+ char *ips = NULL;
+ char **iptmp = c->get_ips(c, NULL, inet, 0);
+ if (iptmp)
+ ips = lxc_string_join(", ", (const char **)iptmp, false);
+
+ lxc_free_array((void **)iptmp, free);
+
+ return ips;
+}
+
+static char *ls_get_interface(struct lxc_container *c)
+{
+ char **interfaces = c->get_interfaces(c);
+ if (!interfaces)
+ return NULL;
+
+ char *interface = lxc_string_join(", ", (const char **)interfaces, false);
+
+ lxc_free_array((void **)interfaces, free);
+
+ return interface;
+}
+
+/*
+ * To calculate swap usage we should not simply check memory.usage_in_bytes and
+ * memory.memsw.usage_in_bytes and then do:
+ * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes;
+ * because we might receive an incorrect/negative value.
+ * Instead we check memory.stat and check the "swap" value.
+ */
+static double ls_get_swap(struct lxc_container *c)
+{
+ unsigned long long int num = 0;
+ char *stat = ls_get_cgroup_item(c, "memory.stat");
+ if (!stat)
+ goto out;
+
+ char *swap = strstr(stat, "\nswap");
+ if (!swap)
+ goto out;
+
+ swap = 1 + swap + 4 + 1; // start_of_swap_value = '\n' + strlen(swap) + ' '
+
+ char *tmp = strchr(swap, '\n'); // find end of swap value
+ if (!tmp)
+ goto out;
+
+ *tmp = '\0';
+
+ num = strtoull(swap, NULL, 0);
+ num = num / 1024 / 1024;
+
+out:
+ free(stat);
+
+ return num;
+}
+
+static unsigned int ls_get_term_width(void)
+{
+ struct winsize ws;
+ if (((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) &&
+ (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1) &&
+ (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)) ||
+ (ws.ws_col == 0))
+ return 0;
+
+ return ws.ws_col;
+}
+
+static bool ls_has_all_grps(const char *has, char **must, size_t must_len)
+{
+ bool bret = false;
+
+ if (!has && must)
+ return false;
+ else if (!must)
+ return true;
+
+ char **tmp_has = lxc_string_split_and_trim(has, ',');
+ size_t tmp_has_len = lxc_array_len((void **)tmp_has);
+
+ /* Don't do any unnecessary work. */
+ if (must_len > tmp_has_len)
+ goto out;
+
+ size_t i, j;
+ for (i = 0; i < must_len; i++) {
+ for (j = 0; j < tmp_has_len; j++)
+ if (strcmp(must[i], tmp_has[j]) == 0)
+ break;
+ if (j == tmp_has_len)
+ break;
+ }
+ if (i == must_len)
+ bret = true;
+
+out:
+ lxc_free_array((void **)tmp_has, free);
+
+ return bret;
+}
+
+static struct ls *ls_new(struct ls **ls, size_t *size)
+{
+ struct ls *m, *n;
+
+ n = realloc(*ls, (*size + 1) * sizeof(struct ls));
+ if (!n)
+ return NULL;
+
+ *ls = n;
+ m = *ls + *size;
+ (*size)++;
+
+ *m = (struct ls){.name = NULL, .init = -1};
+
+ return m;
+}
+
+static void ls_print_names(struct ls *l, struct lengths *lht,
+ size_t size, size_t termwidth)
+{
+ /* If list is empty do nothing. */
+ if (size == 0)
+ return;
+
+ size_t i, len = 0;
+ struct ls *m = NULL;
+ for (i = 0, m = l; i < size; i++, m++) {
+ printf("%-*s", lht->name_length, m->name ? m->name : "-");
+ len += lht->name_length;
+ if ((len + lht->name_length) >= termwidth) {
+ printf("\n");
+ len = 0;
+ } else {
+ printf(" ");
+ len++;
+ }
+ }
+ if (len > 0)
+ printf("\n");
+}
+
+static void ls_print_fancy_format(struct ls *l, struct lengths *lht,
+ size_t size, const char *fancy_fmt)
+{
+ /* If list is empty do nothing. */
+ if (size == 0)
+ return;
+
+ char **tmp = lxc_string_split_and_trim(fancy_fmt, ',');
+ if (!tmp)
+ return;
+
+ char **s;
+ /* Check for invalid keys. */
+ for (s = tmp; s && *s; s++) {
+ if (strcasecmp(*s, "NAME") && strcasecmp(*s, "STATE") &&
+ strcasecmp(*s, "PID") && strcasecmp(*s, "RAM") &&
+ strcasecmp(*s, "SWAP") && strcasecmp(*s, "AUTOSTART") &&
+ strcasecmp(*s, "GROUPS") && strcasecmp(*s, "INTERFACE") &&
+ strcasecmp(*s, "IPV4") && strcasecmp(*s, "IPV6")) {
+ fprintf(stderr, "Invalid key: %s\n", *s);
+ return;
+ }
+ }
+
+ /* print header */
+ for (s = tmp; s && *s; s++) {
+ if (strcasecmp(*s, "NAME") == 0)
+ printf("%-*s ", lht->name_length, "NAME");
+ else if (strcasecmp(*s, "STATE") == 0)
+ printf("%-*s ", lht->state_length, "STATE");
+ else if (strcasecmp(*s, "PID") == 0)
+ printf("%-*s ", lht->init_length, "PID");
+ else if (strcasecmp(*s, "RAM") == 0)
+ printf("%-*s ", lht->ram_length + 2, "RAM");
+ else if (strcasecmp(*s, "SWAP") == 0)
+ printf("%-*s ", lht->swap_length + 2, "SWAP");
+ else if (strcasecmp(*s, "AUTOSTART") == 0)
+ printf("%-*s ", lht->autostart_length, "AUTOSTART");
+ else if (strcasecmp(*s, "GROUPS") == 0)
+ printf("%-*s ", lht->groups_length, "GROUPS");
+ else if (strcasecmp(*s, "INTERFACE") == 0)
+ printf("%-*s ", lht->interface_length, "INTERFACE");
+ else if (strcasecmp(*s, "IPV4") == 0)
+ printf("%-*s ", lht->ipv4_length, "IPV4");
+ else if (strcasecmp(*s, "IPV6") == 0)
+ printf("%-*s ", lht->ipv6_length, "IPV6");
+ }
+ printf("\n");
+
+ struct ls *m = NULL;
+ size_t i;
+ for (i = 0, m = l; i < size; i++, m++) {
+ for (s = tmp; s && *s; s++) {
+ if (strcasecmp(*s, "NAME") == 0) {
+ if (m->nestlvl > 0) {
+ printf("%*s", m->nestlvl, "\\");
+ printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-");
+ } else {
+ printf("%-*s ", lht->name_length, m->name ? m->name : "-");
+ }
+ } else if (strcasecmp(*s, "STATE") == 0) {
+ printf("%-*s ", lht->state_length, m->state ? m->state : "-");
+ } else if (strcasecmp(*s, "PID") == 0) {
+ if (m->init > 0)
+ printf("%-*d ", lht->init_length, m->init);
+ else
+ printf("%-*s ", lht->init_length, "-");
+ } else if (strcasecmp(*s, "RAM") == 0) {
+ if ((m->ram >= 0) && m->running)
+ printf("%*.2fMB ", lht->ram_length, m->ram);
+ else
+ printf("%-*s ", lht->ram_length, "-");
+ } else if (strcasecmp(*s, "SWAP") == 0) {
+ if ((m->swap >= 0) && m->running)
+ printf("%*.2fMB ", lht->swap_length, m->swap);
+ else
+ printf("%-*s ", lht->swap_length, "-");
+ } else if (strcasecmp(*s, "AUTOSTART") == 0) {
+ printf("%-*d ", lht->autostart_length, m->autostart);
+ } else if (strcasecmp(*s, "GROUPS") == 0) {
+ printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-");
+ } else if (strcasecmp(*s, "INTERFACE") == 0) {
+ printf("%-*s ", lht->interface_length, m->interface ? m->interface : "-");
+ } else if (strcasecmp(*s, "IPV4") == 0) {
+ printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-");
+ } else if (strcasecmp(*s, "IPV6") == 0) {
+ printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-");
+ }
+ }
+ printf("\n");
+ }
+}
+
+static void ls_print_table(struct ls *l, struct lengths *lht,
+ size_t size)
+{
+ /* If list is empty do nothing. */
+ if (size == 0)
+ return;
+
+ struct ls *m = NULL;
+
+ /* print header */
+ printf("%-*s ", lht->name_length, "NAME");
+ printf("%-*s ", lht->state_length, "STATE");
+ printf("%-*s ", lht->autostart_length, "AUTOSTART");
+ printf("%-*s ", lht->groups_length, "GROUPS");
+ printf("%-*s ", lht->ipv4_length, "IPV4");
+ printf("%-*s ", lht->ipv6_length, "IPV6");
+ printf("\n");
+
+ size_t i;
+ for (i = 0, m = l; i < size; i++, m++) {
+ if (m->nestlvl > 0) {
+ printf("%*s", m->nestlvl, "\\");
+ printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-");
+ } else {
+ printf("%-*s ", lht->name_length, m->name ? m->name : "-");
+ }
+ printf("%-*s ", lht->state_length, m->state ? m->state : "-");
+ printf("%-*d ", lht->autostart_length, m->autostart);
+ printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-");
+ printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-");
+ printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-");
+ printf("\n");
+ }
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ char *invalid;
+ unsigned long int m, n = MAX_NESTLVL;
+ switch (c) {
+ case '1':
+ args->ls_line = true;
+ break;
+ case 'f':
+ args->ls_fancy = true;
+ break;
+ case LS_ACTIVE:
+ args->ls_active = true;
+ break;
+ case LS_FROZEN:
+ args->ls_frozen = true;
+ break;
+ case LS_RUNNING:
+ args->ls_running = true;
+ break;
+ case LS_STOPPED:
+ args->ls_stopped = true;
+ break;
+ case LS_NESTING:
+ /* In case strtoul() receives a string that represents a
+ * negative number it will return ULONG_MAX - the number that
+ * the string represents if the number the string represents is
+ * < ULONG_MAX and ULONG_MAX otherwise. But it will consider
+ * this valid input and not set errno. So we check manually if
+ * the first character of num_string == '-'. Otherwise the
+ * default level remains set. */
+ if (arg && !(*arg == '-')) {
+ errno = 0;
+ m = strtoul(arg, &invalid, 0);
+ /* ls_nesting has type unsigned int. */
+ if (!errno && (*invalid == '\0') && (m <= UINT_MAX))
+ n = m;
+ }
+ args->ls_nesting = n;
+ break;
+ case 'g':
+ args->groups = arg;
+ break;
+ case LS_FILTER:
+ args->ls_filter = arg;
+ break;
+ case 'F':
+ args->ls_fancy_format = arg;
+ break;
+ }
+
+ return 0;
+}
+
+static int ls_get_wrapper(void *wrap)
+{
+ int ret = -1;
+ size_t len = 0;
+ struct wrapargs *wargs = (struct wrapargs *)wrap;
+ struct ls *m = NULL, *n = NULL;
+
+ /* close pipe */
+ close(wargs->pipefd[0]);
+
+ /* &(char *){NULL} is no magic. It's just a compound literal which
+ * allows us to avoid keeping a pointless variable around. */
+ ls_get(&m, &len, wargs->args, "", wargs->parent, wargs->nestlvl, &(char *){NULL}, 0, wargs->grps_must, wargs->grps_must_len);
+ if (!m)
+ goto out;
+
+ /* send length */
+ if (lxc_write_nointr(wargs->pipefd[1], &len, sizeof(len)) <= 0)
+ goto out;
+
+ size_t i;
+ for (i = 0, n = m; i < len; i++, n++) {
+ if (ls_serialize(wargs->pipefd[1], n) == -1)
+ goto out;
+ }
+ ret = 0;
+
+out:
+ shutdown(wargs->pipefd[1], SHUT_RDWR);
+ close(wargs->pipefd[1]);
+ ls_free(m, len);
+
+ return ret;
+}
+
+static int ls_remove_lock(const char *path, const char *name,
+ char **lockpath, size_t *len_lockpath, bool recalc)
+{
+ /* Avoid doing unnecessary work if we can. */
+ if (recalc) {
+ size_t newlen = strlen(path) + strlen(name) + strlen(RUNTIME_PATH) + /* / + lxc + / + lock + / + / = */ 11 + 1;
+ if (newlen > *len_lockpath) {
+ char *tmp = realloc(*lockpath, newlen * 2);
+ if (!tmp)
+ return -1;
+ *lockpath = tmp;
+ *len_lockpath = newlen * 2;
+ }
+ }
+
+ int check = snprintf(*lockpath, *len_lockpath, "%s/lxc/lock/%s/%s", RUNTIME_PATH, path, name);
+ if (check < 0 || (size_t)check >= *len_lockpath)
+ return -1;
+
+ lxc_rmdir_onedev(*lockpath, NULL);
+
+ return 0;
+}
+
+static int ls_send_str(int fd, const char *buf)
+{
+ size_t slen = 0;
+ if (buf)
+ slen = strlen(buf);
+ if (lxc_write_nointr(fd, &slen, sizeof(slen)) != sizeof(slen))
+ return -1;
+ if (slen > 0) {
+ if (lxc_write_nointr(fd, buf, slen) != (ssize_t)slen)
+ return -1;
+ }
+ return 0;
+}
+
+static int ls_serialize(int wpipefd, struct ls *n)
+{
+ ssize_t nbytes = sizeof(n->ram);
+ if (lxc_write_nointr(wpipefd, &n->ram, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->swap);
+ if (lxc_write_nointr(wpipefd, &n->swap, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->init);
+ if (lxc_write_nointr(wpipefd, &n->init, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->autostart);
+ if (lxc_write_nointr(wpipefd, &n->autostart, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->running);
+ if (lxc_write_nointr(wpipefd, &n->running, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->nestlvl);
+ if (lxc_write_nointr(wpipefd, &n->nestlvl, (size_t)nbytes) != nbytes)
+ return -1;
+
+ /* NAME */
+ if (ls_send_str(wpipefd, n->name) < 0)
+ return -1;
+
+ /* STATE */
+ if (ls_send_str(wpipefd, n->state) < 0)
+ return -1;
+
+ /* GROUPS */
+ if (ls_send_str(wpipefd, n->groups) < 0)
+ return -1;
+
+ /* INTERFACE */
+ if (ls_send_str(wpipefd, n->interface) < 0)
+ return -1;
+
+ /* IPV4 */
+ if (ls_send_str(wpipefd, n->ipv4) < 0)
+ return -1;
+
+ /* IPV6 */
+ if (ls_send_str(wpipefd, n->ipv6) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int ls_recv_str(int fd, char **buf)
+{
+ size_t slen = 0;
+ if (lxc_read_nointr(fd, &slen, sizeof(slen)) != sizeof(slen))
+ return -1;
+ if (slen > 0) {
+ *buf = malloc(sizeof(char) * (slen + 1));
+ if (!*buf)
+ return -1;
+ if (lxc_read_nointr(fd, *buf, slen) != (ssize_t)slen)
+ return -1;
+ (*buf)[slen] = '\0';
+ }
+ return 0;
+}
+
+static int ls_deserialize(int rpipefd, struct ls **m, size_t *len)
+{
+ struct ls *n;
+ size_t sublen = 0;
+ ssize_t nbytes = 0;
+
+ /* get length */
+ nbytes = sizeof(sublen);
+ if (lxc_read_nointr(rpipefd, &sublen, (size_t)nbytes) != nbytes)
+ return -1;
+
+ while (sublen-- > 0) {
+ n = ls_new(m, len);
+ if (!n)
+ return -1;
+
+ nbytes = sizeof(n->ram);
+ if (lxc_read_nointr(rpipefd, &n->ram, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->swap);
+ if (lxc_read_nointr(rpipefd, &n->swap, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->init);
+ if (lxc_read_nointr(rpipefd, &n->init, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->autostart);
+ if (lxc_read_nointr(rpipefd, &n->autostart, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->running);
+ if (lxc_read_nointr(rpipefd, &n->running, (size_t)nbytes) != nbytes)
+ return -1;
+
+ nbytes = sizeof(n->nestlvl);
+ if (lxc_read_nointr(rpipefd, &n->nestlvl, (size_t)nbytes) != nbytes)
+ return -1;
+
+ /* NAME */
+ if (ls_recv_str(rpipefd, &n->name) < 0)
+ return -1;
+
+ /* STATE */
+ if (ls_recv_str(rpipefd, &n->state) < 0)
+ return -1;
+
+ /* GROUPS */
+ if (ls_recv_str(rpipefd, &n->groups) < 0)
+ return -1;
+
+ /* INTERFACE */
+ if (ls_recv_str(rpipefd, &n->interface) < 0)
+ return -1;
+
+ /* IPV4 */
+ if (ls_recv_str(rpipefd, &n->ipv4) < 0)
+ return -1;
+
+ /* IPV6 */
+ if (ls_recv_str(rpipefd, &n->ipv6) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ls_field_width(const struct ls *l, const size_t size,
+ struct lengths *lht)
+{
+ const struct ls *m;
+ size_t i, len = 0;
+ for (i = 0, m = l; i < size; i++, m++) {
+ if (m->name) {
+ len = strlen(m->name) + m->nestlvl;
+ if (len > lht->name_length)
+ lht->name_length = len;
+ }
+
+ if (m->state) {
+ len = strlen(m->state);
+ if (len > lht->state_length)
+ lht->state_length = len;
+ }
+
+ if (m->interface) {
+ len = strlen(m->interface);
+ if (len > lht->interface_length)
+ lht->interface_length = len;
+ }
+
+ if (m->groups) {
+ len = strlen(m->groups);
+ if (len > lht->groups_length)
+ lht->groups_length = len;
+ }
+ if (m->ipv4) {
+ len = strlen(m->ipv4);
+ if (len > lht->ipv4_length)
+ lht->ipv4_length = len;
+ }
+
+ if (m->ipv6) {
+ len = strlen(m->ipv6);
+ if (len > lht->ipv6_length)
+ lht->ipv6_length = len;
+ }
+
+ if ((len = snprintf(NULL, 0, "%.2f", m->ram)) > lht->ram_length)
+ lht->ram_length = len;
+
+ if ((len = snprintf(NULL, 0, "%.2f", m->swap)) > lht->swap_length)
+ lht->swap_length = len;
+
+ if (m->init != -1) {
+ if ((len = snprintf(NULL, 0, "%d", m->init)) > lht->init_length)
+ lht->init_length = len;
+ }
+ }
+}
diff --git a/src/lxc/tools/lxc_monitor.c b/src/lxc/tools/lxc_monitor.c
new file mode 100644
index 0000000..797ae8b
--- /dev/null
+++ b/src/lxc/tools/lxc_monitor.c
@@ -0,0 +1,211 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "monitor.h"
+#include "arguments.h"
+
+lxc_log_define(lxc_monitor_ui, lxc);
+
+static bool quit_monitord;
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'Q': quit_monitord = true; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"quit", no_argument, 0, 'Q'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-monitor",
+ .help = "\
+[--name=NAME]\n\
+\n\
+lxc-monitor monitors the state of the NAME container\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ NAME may be a regular expression\n\
+ -Q, --quit tell lxc-monitord to quit\n",
+ .name = ".*",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .lxcpath_additional = -1,
+};
+
+static void close_fds(struct pollfd *fds, nfds_t nfds)
+{
+ nfds_t i;
+
+ if (nfds < 1)
+ return;
+
+ for (i = 0; i < nfds; ++i) {
+ close(fds[i].fd);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char *regexp;
+ struct lxc_msg msg;
+ regex_t preg;
+ struct pollfd *fds;
+ nfds_t nfds;
+ int len, rc_main, rc_snp, i;
+
+ rc_main = 0;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ if (quit_monitord) {
+ int ret = EXIT_SUCCESS;
+ for (i = 0; i < my_args.lxcpath_cnt; i++) {
+ int fd;
+
+ fd = lxc_monitor_open(my_args.lxcpath[i]);
+ if (fd < 0) {
+ ERROR("Unable to open monitor on path: %s", my_args.lxcpath[i]);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ if (write(fd, "quit", 4) < 0) {
+ SYSERROR("Unable to close monitor on path: %s", my_args.lxcpath[i]);
+ ret = EXIT_FAILURE;
+ close(fd);
+ continue;
+ }
+ close(fd);
+ }
+ return ret;
+ }
+
+ len = strlen(my_args.name) + 3;
+ regexp = malloc(len + 3);
+ if (!regexp) {
+ ERROR("failed to allocate memory");
+ return 1;
+ }
+ rc_snp = snprintf(regexp, len, "^%s$", my_args.name);
+ if (rc_snp < 0 || rc_snp >= len) {
+ ERROR("Name too long");
+ rc_main = 1;
+ goto error;
+ }
+
+ if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) {
+ ERROR("failed to compile the regex '%s'", my_args.name);
+ rc_main = 1;
+ goto error;
+ }
+
+ fds = malloc(my_args.lxcpath_cnt * sizeof(struct pollfd));
+ if (!fds) {
+ SYSERROR("out of memory");
+ rc_main = -1;
+ goto cleanup;
+ }
+
+ nfds = my_args.lxcpath_cnt;
+ for (i = 0; i < nfds; i++) {
+ int fd;
+
+ lxc_monitord_spawn(my_args.lxcpath[i]);
+
+ fd = lxc_monitor_open(my_args.lxcpath[i]);
+ if (fd < 0) {
+ close_fds(fds, i);
+ rc_main = 1;
+ goto cleanup;
+ }
+ fds[i].fd = fd;
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ }
+
+ setlinebuf(stdout);
+
+ for (;;) {
+ if (lxc_monitor_read_fdset(fds, nfds, &msg, -1) < 0) {
+ rc_main = 1;
+ goto close_and_clean;
+ }
+
+ msg.name[sizeof(msg.name)-1] = '\0';
+ if (regexec(&preg, msg.name, 0, NULL, 0))
+ continue;
+
+ switch (msg.type) {
+ case lxc_msg_state:
+ printf("'%s' changed state to [%s]\n",
+ msg.name, lxc_state2str(msg.value));
+ break;
+ case lxc_msg_exit_code:
+ printf("'%s' exited with status [%d]\n",
+ msg.name, WEXITSTATUS(msg.value));
+ break;
+ default:
+ /* ignore garbage */
+ break;
+ }
+ }
+
+close_and_clean:
+ close_fds(fds, nfds);
+
+cleanup:
+ regfree(&preg);
+ free(fds);
+
+error:
+ free(regexp);
+
+ return rc_main;
+}
diff --git a/src/lxc/tools/lxc_snapshot.c b/src/lxc/tools/lxc_snapshot.c
new file mode 100644
index 0000000..8f44891
--- /dev/null
+++ b/src/lxc/tools/lxc_snapshot.c
@@ -0,0 +1,284 @@
+/*
+ *
+ * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "confile.h"
+#include <stdio.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "bdev.h"
+#include "lxc.h"
+#include "log.h"
+#include "arguments.h"
+#include "utils.h"
+
+lxc_log_define(lxc_snapshot_ui, lxc);
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg);
+
+static const struct option my_longopts[] = {
+ {"list", no_argument, 0, 'L'},
+ {"restore", required_argument, 0, 'r'},
+ {"newname", required_argument, 0, 'N'},
+ {"destroy", required_argument, 0, 'd'},
+ {"comment", required_argument, 0, 'c'},
+ {"showcomments", no_argument, 0, 'C'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-snapshot",
+ .help = "\
+--name=NAME [-P lxcpath] [-L [-C]] [-c commentfile] [-r snapname [-N newname]]\n\
+\n\
+lxc-snapshot snapshots a container\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -L, --list list all snapshots\n\
+ -r, --restore=NAME restore snapshot NAME, e.g. 'snap0'\n\
+ -N, --newname=NEWNAME NEWNAME for the restored container\n\
+ -d, --destroy=NAME destroy snapshot NAME, e.g. 'snap0'\n\
+ use ALL to destroy all snapshots\n\
+ -c, --comment=FILE add FILE as a comment\n\
+ -C, --showcomments show snapshot comments\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .task = SNAP,
+};
+
+static int do_snapshot(struct lxc_container *c, char *commentfile);
+static int do_snapshot_destroy(struct lxc_container *c, char *snapname);
+static int do_snapshot_list(struct lxc_container *c, int print_comments);
+static int do_snapshot_restore(struct lxc_container *c,
+ struct lxc_arguments *args);
+static int do_snapshot_task(struct lxc_container *c, enum task task);
+static void print_file(char *path);
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ int ret;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(EXIT_FAILURE);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(EXIT_FAILURE);
+ lxc_log_options_no_override();
+
+ if (geteuid()) {
+ if (access(my_args.lxcpath[0], O_RDWR) < 0) {
+ fprintf(stderr, "You lack access to %s\n",
+ my_args.lxcpath[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ fprintf(stderr, "System error loading container\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n",
+ my_args.name);
+ lxc_container_put(c);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = do_snapshot_task(c, my_args.task);
+
+ lxc_container_put(c);
+
+ if (ret == 0)
+ exit(EXIT_SUCCESS);
+ exit(EXIT_FAILURE);
+}
+
+static int do_snapshot_task(struct lxc_container *c, enum task task)
+{
+ int ret = 0;
+
+ switch (task) {
+ case DESTROY:
+ ret = do_snapshot_destroy(c, my_args.snapname);
+ break;
+ case LIST:
+ ret = do_snapshot_list(c, my_args.print_comments);
+ break;
+ case RESTORE:
+ ret = do_snapshot_restore(c, &my_args);
+ break;
+ case SNAP:
+ ret = do_snapshot(c, my_args.commentfile);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ switch (c) {
+ case 'L':
+ args->task = LIST;
+ break;
+ case 'r':
+ args->task = RESTORE;
+ args->snapname = arg;
+ break;
+ case 'N':
+ args->newname = arg;
+ break;
+ case 'd':
+ args->task = DESTROY;
+ args->snapname = arg;
+ break;
+ case 'c':
+ args->commentfile = arg;
+ break;
+ case 'C':
+ args->print_comments = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static int do_snapshot(struct lxc_container *c, char *commentfile)
+{
+ int ret;
+
+ ret = c->snapshot(c, commentfile);
+ if (ret < 0) {
+ ERROR("Error creating a snapshot");
+ return -1;
+ }
+
+ INFO("Created snapshot snap%d", ret);
+
+ return 0;
+}
+
+static int do_snapshot_destroy(struct lxc_container *c, char *snapname)
+{
+ bool ret;
+
+ if (strcmp(snapname, "ALL") == 0)
+ ret = c->snapshot_destroy_all(c);
+ else
+ ret = c->snapshot_destroy(c, snapname);
+
+ if (!ret) {
+ ERROR("Error destroying snapshot %s", snapname);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_snapshot_list(struct lxc_container *c, int print_comments)
+{
+ struct lxc_snapshot *s;
+ int i, n;
+
+ n = c->snapshot_list(c, &s);
+ if (n < 0) {
+ ERROR("Error listing snapshots");
+ return -1;
+ }
+ if (n == 0) {
+ printf("No snapshots\n");
+ return 0;
+ }
+
+ for (i = 0; i < n; i++) {
+ printf("%s (%s) %s\n", s[i].name, s[i].lxcpath, s[i].timestamp);
+ if (print_comments)
+ print_file(s[i].comment_pathname);
+ s[i].free(&s[i]);
+ }
+
+ free(s);
+
+ return 0;
+}
+
+static int do_snapshot_restore(struct lxc_container *c,
+ struct lxc_arguments *args)
+{
+ int bret;
+
+ /* When restoring a snapshot, the last optional argument if not given
+ * explicitly via the corresponding command line option is the name to
+ * use for the restored container. If no name is given, then the
+ * original container will be destroyed and the restored container will
+ * take its place. */
+ if ((!args->newname) && (args->argc > 1)) {
+ lxc_error(args, "Too many arguments");
+ return -1;
+ }
+
+ if ((!args->newname) && (args->argc == 1))
+ args->newname = args->argv[0];
+
+ bret = c->snapshot_restore(c, args->snapname, args->newname);
+ if (!bret) {
+ ERROR("Error restoring snapshot %s", args->snapname);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_file(char *path)
+{
+ if (!path)
+ return;
+
+ FILE *f = fopen(path, "r");
+ char *line = NULL;
+ size_t sz = 0;
+
+ if (!f)
+ return;
+
+ while (getline(&line, &sz, f) != -1) {
+ printf("%s", line);
+ }
+
+ free(line);
+ fclose(f);
+}
+
diff --git a/src/lxc/tools/lxc_start.c b/src/lxc/tools/lxc_start.c
new file mode 100644
index 0000000..6b942ac
--- /dev/null
+++ b/src/lxc/tools/lxc_start.c
@@ -0,0 +1,357 @@
+/*
+ * 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 <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "log.h"
+#include "caps.h"
+#include "lxc.h"
+#include "conf.h"
+#include "cgroup.h"
+#include "utils.h"
+#include "confile.h"
+#include "arguments.h"
+
+#define OPT_SHARE_NET OPT_USAGE+1
+#define OPT_SHARE_IPC OPT_USAGE+2
+#define OPT_SHARE_UTS OPT_USAGE+3
+
+lxc_log_define(lxc_start_ui, lxc);
+
+static struct lxc_list defines;
+
+static int ensure_path(char **confpath, const char *path)
+{
+ int err = -1, fd;
+ char *fullpath = NULL;
+
+ if (path) {
+ if (access(path, W_OK)) {
+ fd = creat(path, 0600);
+ if (fd < 0 && errno != EEXIST) {
+ SYSERROR("failed to create '%s'", path);
+ goto err;
+ }
+ if (fd >= 0)
+ close(fd);
+ }
+
+ fullpath = realpath(path, NULL);
+ if (!fullpath) {
+ SYSERROR("failed to get the real path of '%s'", path);
+ goto err;
+ }
+
+ *confpath = strdup(fullpath);
+ if (!*confpath) {
+ ERROR("failed to dup string '%s'", fullpath);
+ goto err;
+ }
+ }
+ err = 0;
+
+err:
+ free(fullpath);
+ return err;
+}
+
+static int pid_from_lxcname(const char *lxcname_or_pid, const char *lxcpath) {
+ char *eptr;
+ int pid = strtol(lxcname_or_pid, &eptr, 10);
+ if (*eptr != '\0' || pid < 1) {
+ struct lxc_container *s;
+ s = lxc_container_new(lxcname_or_pid, lxcpath);
+ if (!s) {
+ SYSERROR("'%s' is not a valid pid nor a container name", lxcname_or_pid);
+ return -1;
+ }
+
+ if (!s->may_control(s)) {
+ SYSERROR("Insufficient privileges to control container '%s'", s->name);
+ lxc_container_put(s);
+ return -1;
+ }
+
+ pid = s->init_pid(s);
+ if (pid < 1) {
+ SYSERROR("Is container '%s' running?", s->name);
+ lxc_container_put(s);
+ return -1;
+ }
+
+ lxc_container_put(s);
+ }
+ if (kill(pid, 0) < 0) {
+ SYSERROR("Can't send signal to pid %d", pid);
+ return -1;
+ }
+
+ return pid;
+}
+
+static int open_ns(int pid, const char *ns_proc_name) {
+ int fd;
+ char path[MAXPATHLEN];
+ snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns_proc_name);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ SYSERROR("failed to open %s", path);
+ return -1;
+ }
+ return fd;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'c': args->console = arg; break;
+ case 'L': args->console_log = arg; break;
+ case 'd': args->daemonize = 1; break;
+ case 'F': args->daemonize = 0; break;
+ case 'f': args->rcfile = arg; break;
+ case 'C': args->close_all_fds = 1; break;
+ case 's': return lxc_config_define_add(&defines, arg);
+ case 'p': args->pidfile = arg; break;
+ case OPT_SHARE_NET: args->share_ns[LXC_NS_NET] = arg; break;
+ case OPT_SHARE_IPC: args->share_ns[LXC_NS_IPC] = arg; break;
+ case OPT_SHARE_UTS: args->share_ns[LXC_NS_UTS] = arg; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"daemon", no_argument, 0, 'd'},
+ {"foreground", no_argument, 0, 'F'},
+ {"rcfile", required_argument, 0, 'f'},
+ {"define", required_argument, 0, 's'},
+ {"console", required_argument, 0, 'c'},
+ {"console-log", required_argument, 0, 'L'},
+ {"close-all-fds", no_argument, 0, 'C'},
+ {"pidfile", required_argument, 0, 'p'},
+ {"share-net", required_argument, 0, OPT_SHARE_NET},
+ {"share-ipc", required_argument, 0, OPT_SHARE_IPC},
+ {"share-uts", required_argument, 0, OPT_SHARE_UTS},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-start",
+ .help = "\
+--name=NAME -- COMMAND\n\
+\n\
+lxc-start start COMMAND in specified container NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -d, --daemon Daemonize the container (default)\n\
+ -F, --foreground Start with the current tty attached to /dev/console\n\
+ -p, --pidfile=FILE Create a file with the process id\n\
+ -f, --rcfile=FILE Load configuration file FILE\n\
+ -c, --console=FILE Use specified FILE for the container console\n\
+ -L, --console-log=FILE Log container console output to FILE\n\
+ -C, --close-all-fds If any fds are inherited, close them\n\
+ If not specified, exit with failure instead\n\
+ Note: --daemon implies --close-all-fds\n\
+ -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\
+ --share-[net|ipc|uts]=NAME Share a namespace with another container or pid\n\
+",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .daemonize = 1,
+ .pidfile = NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int err = 1;
+ struct lxc_conf *conf;
+ char *const *args;
+ char *rcfile = NULL;
+ char *const default_args[] = {
+ "/sbin/init",
+ NULL,
+ };
+ struct lxc_container *c;
+
+ lxc_list_init(&defines);
+
+ if (lxc_caps_init())
+ return err;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return err;
+
+ if (!my_args.argc)
+ args = default_args;
+ else
+ args = my_args.argv;
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return err;
+ lxc_log_options_no_override();
+
+ const char *lxcpath = my_args.lxcpath[0];
+
+ /*
+ * rcfile possibilities:
+ * 1. rcfile from random path specified in cli option
+ * 2. rcfile not specified, use $lxcpath/$lxcname/config
+ * 3. rcfile not specified and does not exist.
+ */
+ /* rcfile is specified in the cli option */
+ if (my_args.rcfile) {
+ rcfile = (char *)my_args.rcfile;
+ c = lxc_container_new(my_args.name, lxcpath);
+ if (!c) {
+ ERROR("Failed to create lxc_container");
+ return err;
+ }
+ c->clear_config(c);
+ if (!c->load_config(c, rcfile)) {
+ ERROR("Failed to load rcfile");
+ lxc_container_put(c);
+ return err;
+ }
+ } else {
+ int rc;
+
+ rc = asprintf(&rcfile, "%s/%s/config", lxcpath, my_args.name);
+ if (rc == -1) {
+ SYSERROR("failed to allocate memory");
+ return err;
+ }
+ INFO("using rcfile %s", rcfile);
+
+ /* container configuration does not exist */
+ if (access(rcfile, F_OK)) {
+ free(rcfile);
+ rcfile = NULL;
+ }
+ c = lxc_container_new(my_args.name, lxcpath);
+ if (!c) {
+ ERROR("Failed to create lxc_container");
+ return err;
+ }
+ }
+
+ if (c->is_running(c)) {
+ ERROR("Container is already running.");
+ err = 0;
+ goto out;
+ }
+ /*
+ * We should use set_config_item() over &defines, which would handle
+ * unset c->lxc_conf for us and let us not use lxc_config_define_load()
+ */
+ if (!c->lxc_conf)
+ c->lxc_conf = lxc_conf_init();
+ conf = c->lxc_conf;
+
+ if (lxc_config_define_load(&defines, conf))
+ goto out;
+
+ if (!rcfile && !strcmp("/sbin/init", args[0])) {
+ ERROR("Executing '/sbin/init' with no configuration file may crash the host");
+ goto out;
+ }
+
+ if (ensure_path(&conf->console.path, my_args.console) < 0) {
+ ERROR("failed to ensure console path '%s'", my_args.console);
+ goto out;
+ }
+
+ if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) {
+ ERROR("failed to ensure console log '%s'", my_args.console_log);
+ goto out;
+ }
+
+ if (my_args.pidfile != NULL) {
+ if (ensure_path(&c->pidfile, my_args.pidfile) < 0) {
+ ERROR("failed to ensure pidfile '%s'", my_args.pidfile);
+ goto out;
+ }
+ }
+
+ int i;
+ for (i = 0; i < LXC_NS_MAX; i++) {
+ if (my_args.share_ns[i] == NULL)
+ continue;
+
+ int pid = pid_from_lxcname(my_args.share_ns[i], lxcpath);
+ if (pid < 1)
+ goto out;
+
+ int fd = open_ns(pid, ns_info[i].proc_name);
+ if (fd < 0)
+ goto out;
+ conf->inherit_ns_fd[i] = fd;
+ }
+
+ if (!my_args.daemonize) {
+ c->want_daemonize(c, false);
+ }
+
+ if (my_args.close_all_fds)
+ c->want_close_all_fds(c, true);
+
+ if (args == default_args)
+ err = c->start(c, 0, NULL) ? 0 : 1;
+ else
+ err = c->start(c, 0, args) ? 0 : 1;
+
+ if (err) {
+ ERROR("The container failed to start.");
+ if (my_args.daemonize)
+ ERROR("To get more details, run the container in foreground mode.");
+ ERROR("Additional information can be obtained by setting the "
+ "--logfile and --logpriority options.");
+ err = c->error_num;
+ lxc_container_put(c);
+ return err;
+ }
+
+out:
+ lxc_container_put(c);
+ return err;
+}
diff --git a/src/lxc/tools/lxc_stop.c b/src/lxc/tools/lxc_stop.c
new file mode 100644
index 0000000..10ddce6
--- /dev/null
+++ b/src/lxc/tools/lxc_stop.c
@@ -0,0 +1,246 @@
+/*
+ * 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 <stdio.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "arguments.h"
+#include "commands.h"
+#include "utils.h"
+
+#define OPT_NO_LOCK OPT_USAGE+1
+#define OPT_NO_KILL OPT_USAGE+2
+
+lxc_log_define(lxc_stop_ui, lxc);
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'r': args->reboot = 1; break;
+ case 'W': args->nowait = 1; break;
+ case 't': args->timeout = atoi(arg); break;
+ case 'k': args->hardstop = 1; break;
+ case OPT_NO_LOCK: args->nolock = 1; break;
+ case OPT_NO_KILL: args->nokill = 1; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"reboot", no_argument, 0, 'r'},
+ {"nowait", no_argument, 0, 'W'},
+ {"timeout", required_argument, 0, 't'},
+ {"kill", no_argument, 0, 'k'},
+ {"nokill", no_argument, 0, OPT_NO_KILL},
+ {"nolock", no_argument, 0, OPT_NO_LOCK},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-stop",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-stop stops a container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -r, --reboot reboot the container\n\
+ -W, --nowait don't wait for shutdown or reboot to complete\n\
+ -t, --timeout=T wait T seconds before hard-stopping\n\
+ -k, --kill kill container rather than request clean shutdown\n\
+ --nolock Avoid using API locks\n\
+ --nokill Only request clean shutdown, don't force kill after timeout\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .timeout = -2,
+};
+
+/* returns -1 on failure, 0 on success */
+static int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c)
+{
+ int ret;
+ pid_t pid;
+ pid_t newpid;
+ int timeout = a->timeout;
+
+ pid = c->init_pid(c);
+ if (pid == -1)
+ return -1;
+ if (!c->reboot(c))
+ return -1;
+ if (a->nowait)
+ return 0;
+ if (timeout == 0)
+ goto out;
+
+ for (;;) {
+ /* can we use c-> wait for this, assuming it will
+ * re-enter RUNNING? For now just sleep */
+ int elapsed_time, curtime = 0;
+ struct timeval tv;
+
+ newpid = c->init_pid(c);
+ if (newpid != -1 && newpid != pid)
+ return 0;
+
+ if (timeout != -1) {
+ ret = gettimeofday(&tv, NULL);
+ if (ret)
+ break;
+ curtime = tv.tv_sec;
+ }
+
+ sleep(1);
+ if (timeout != -1) {
+ ret = gettimeofday(&tv, NULL);
+ if (ret)
+ break;
+ elapsed_time = tv.tv_sec - curtime;
+ if (timeout - elapsed_time <= 0)
+ break;
+ timeout -= elapsed_time;
+ }
+ }
+
+out:
+ newpid = c->init_pid(c);
+ if (newpid == -1 || newpid == pid) {
+ printf("Reboot did not complete before timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ bool s;
+ int ret = 1;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ /* Set default timeout */
+ if (my_args.timeout == -2) {
+ if (my_args.hardstop) {
+ my_args.timeout = 0;
+ }
+ else {
+ my_args.timeout = 60;
+ }
+ }
+
+ if (my_args.nowait) {
+ my_args.timeout = 0;
+ }
+
+ /* some checks */
+ if (!my_args.hardstop && my_args.timeout < -1) {
+ fprintf(stderr, "invalid timeout\n");
+ return 1;
+ }
+
+ if (my_args.hardstop && my_args.nokill) {
+ fprintf(stderr, "-k can't be used with --nokill\n");
+ return 1;
+ }
+
+ if (my_args.hardstop && my_args.reboot) {
+ fprintf(stderr, "-k can't be used with -r\n");
+ return 1;
+ }
+
+ if (my_args.hardstop && my_args.timeout) {
+ fprintf(stderr, "-k doesn't allow timeouts\n");
+ return 1;
+ }
+
+ if (my_args.nolock && !my_args.hardstop) {
+ fprintf(stderr, "--nolock may only be used with -k\n");
+ return 1;
+ }
+
+ /* shortcut - if locking is bogus, we should be able to kill
+ * containers at least */
+ if (my_args.nolock)
+ return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]);
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ fprintf(stderr, "Error opening container\n");
+ goto out;
+ }
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ goto out;
+ }
+
+ if (!c->is_running(c)) {
+ fprintf(stderr, "%s is not running\n", c->name);
+ ret = 2;
+ goto out;
+ }
+
+ /* kill */
+ if (my_args.hardstop) {
+ ret = c->stop(c) ? 0 : 1;
+ goto out;
+ }
+
+ /* reboot */
+ if (my_args.reboot) {
+ ret = do_reboot_and_check(&my_args, c);
+ goto out;
+ }
+
+ /* shutdown */
+ s = c->shutdown(c, my_args.timeout);
+ if (!s) {
+ if (my_args.timeout == 0)
+ ret = 0;
+ else if (my_args.nokill)
+ ret = 1;
+ else
+ ret = c->stop(c) ? 0 : 1;
+ } else
+ ret = 0;
+
+out:
+ lxc_container_put(c);
+ if (ret < 0)
+ return 1;
+ return ret;
+}
diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
new file mode 100644
index 0000000..c4cb871
--- /dev/null
+++ b/src/lxc/tools/lxc_top.c
@@ -0,0 +1,510 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2014 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen at oracle.com>
+ *
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <lxc/lxccontainer.h>
+
+#include "arguments.h"
+#include "log.h"
+#include "lxc.h"
+#include "mainloop.h"
+#include "utils.h"
+
+lxc_log_define(lxc_top_ui, lxc);
+
+#define USER_HZ 100
+#define ESC "\033"
+#define TERMCLEAR ESC "[H" ESC "[J"
+#define TERMNORM ESC "[0m"
+#define TERMBOLD ESC "[1m"
+#define TERMRVRS ESC "[7m"
+
+struct stats {
+ uint64_t mem_used;
+ uint64_t mem_limit;
+ uint64_t kmem_used;
+ uint64_t kmem_limit;
+ uint64_t cpu_use_nanos;
+ uint64_t cpu_use_user;
+ uint64_t cpu_use_sys;
+ uint64_t blkio;
+};
+
+struct ct {
+ struct lxc_container *c;
+ struct stats *stats;
+};
+
+static int delay = 3;
+static char sort_by = 'n';
+static int sort_reverse = 0;
+
+static struct termios oldtios;
+static struct ct *ct = NULL;
+static int ct_alloc_cnt = 0;
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 'd': delay = atoi(arg); break;
+ case 's': sort_by = arg[0]; break;
+ case 'r': sort_reverse = 1; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"delay", required_argument, 0, 'd'},
+ {"sort", required_argument, 0, 's'},
+ {"reverse", no_argument, 0, 'r'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-top",
+ .help = "\
+[--name=NAME]\n\
+\n\
+lxc-top monitors the state of the active containers\n\
+\n\
+Options :\n\
+ -d, --delay delay in seconds between refreshes (default: 3.0)\n\
+ -s, --sort sort by [n,c,b,m] (default: n) where\n\
+ n = Name\n\
+ c = CPU use\n\
+ b = Block I/O use\n\
+ m = Memory use\n\
+ k = Kernel memory use\n\
+ -r, --reverse sort in reverse (descending) order\n",
+ .name = ".*",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+ .lxcpath_additional = -1,
+};
+
+static void stdin_tios_restore(void)
+{
+ tcsetattr(0, TCSAFLUSH, &oldtios);
+ fprintf(stderr, "\n");
+}
+
+static int stdin_tios_setup(void)
+{
+ struct termios newtios;
+
+ if (!isatty(0)) {
+ ERROR("stdin is not a tty");
+ return -1;
+ }
+
+ if (tcgetattr(0, &oldtios)) {
+ SYSERROR("failed to get current terminal settings");
+ return -1;
+ }
+
+ newtios = oldtios;
+
+ /* turn off echo and line buffering */
+ newtios.c_iflag &= ~IGNBRK;
+ newtios.c_iflag &= BRKINT;
+ newtios.c_lflag &= ~(ECHO|ICANON);
+ newtios.c_cc[VMIN] = 1;
+ newtios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(0, TCSAFLUSH, &newtios)) {
+ ERROR("failed to set new terminal settings");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int stdin_tios_rows(void)
+{
+ struct winsize wsz;
+ if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0)
+ return wsz.ws_row;
+ return 25;
+}
+
+static int stdin_handler(int fd, uint32_t events, void *data,
+ struct lxc_epoll_descr *descr)
+{
+ char *in_char = data;
+
+ if (events & EPOLLIN) {
+ int rc;
+
+ rc = read(fd, in_char, sizeof(*in_char));
+ if (rc <= 0)
+ *in_char = '\0';
+ }
+
+ if (events & EPOLLHUP)
+ *in_char = 'q';
+ return 1;
+}
+
+static void sig_handler(int sig)
+{
+ exit(EXIT_SUCCESS);
+}
+
+static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
+{
+ if (val > 1 << 30) {
+ snprintf(buf, bufsz, "%u.%2.2u GB",
+ (int)(val >> 30),
+ (int)(val & ((1 << 30) - 1)) / 10737419);
+ } else if (val > 1 << 20) {
+ int x = val + 5243; /* for rounding */
+ snprintf(buf, bufsz, "%u.%2.2u MB",
+ x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
+ } else if (val > 1 << 10) {
+ int x = val + 5; /* for rounding */
+ snprintf(buf, bufsz, "%u.%2.2u KB",
+ x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
+ } else {
+ snprintf(buf, bufsz, "%3u.00 ", (int)val);
+ }
+}
+
+static uint64_t stat_get_int(struct lxc_container *c, const char *item)
+{
+ char buf[80];
+ int len;
+ uint64_t val;
+
+ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
+ if (len <= 0) {
+ ERROR("unable to read cgroup item %s", item);
+ return 0;
+ }
+
+ val = strtoull(buf, NULL, 0);
+ return val;
+}
+
+static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
+ const char *match, int column)
+{
+ char buf[4096];
+ int i,j,len;
+ uint64_t val = 0;
+ char **lines, **cols;
+ size_t matchlen;
+
+ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
+ if (len <= 0) {
+ ERROR("unable to read cgroup item %s", item);
+ goto out;
+ }
+
+ lines = lxc_string_split_and_trim(buf, '\n');
+ if (!lines)
+ goto out;
+
+ matchlen = strlen(match);
+ for (i = 0; lines[i]; i++) {
+ if (strncmp(lines[i], match, matchlen) == 0) {
+ cols = lxc_string_split_and_trim(lines[i], ' ');
+ if (!cols)
+ goto err1;
+ for (j = 0; cols[j]; j++) {
+ if (j == column) {
+ val = strtoull(cols[j], NULL, 0);
+ break;
+ }
+ }
+ lxc_free_array((void **)cols, free);
+ break;
+ }
+ }
+err1:
+ lxc_free_array((void **)lines, free);
+out:
+ return val;
+}
+
+static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total)
+{
+ ct->c = c;
+ ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
+ ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
+ ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
+ ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
+ ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
+ ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
+ ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
+ ct->stats->blkio = stat_match_get_int(c, "blkio.throttle.io_service_bytes", "Total", 1);
+
+ if (total) {
+ total->mem_used = total->mem_used + ct->stats->mem_used;
+ total->mem_limit = total->mem_limit + ct->stats->mem_limit;
+ total->kmem_used = total->kmem_used + ct->stats->kmem_used;
+ total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
+ total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
+ total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
+ total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
+ total->blkio = total->blkio + ct->stats->blkio;
+ }
+}
+
+static void stats_print_header(struct stats *stats)
+{
+ printf(TERMRVRS TERMBOLD);
+ printf("%-18s %12s %12s %12s %14s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
+ if (stats->kmem_used > 0)
+ printf(" %10s", "KMem");
+ printf("\n");
+
+ printf("%-18s %12s %12s %12s %14s %10s", "Name", "Used", "Sys", "User", "Total", "Used");
+ if (stats->kmem_used > 0)
+ printf(" %10s", "Used");
+ printf("\n");
+ printf(TERMNORM);
+}
+
+static void stats_print(const char *name, const struct stats *stats,
+ const struct stats *total)
+{
+ char blkio_str[20];
+ char mem_used_str[20];
+ char kmem_used_str[20];
+
+ size_humanize(stats->blkio, blkio_str, sizeof(blkio_str));
+ size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
+
+ printf("%-18.18s %12.2f %12.2f %12.2f %14s %10s",
+ name,
+ (float)stats->cpu_use_nanos / 1000000000,
+ (float)stats->cpu_use_sys / USER_HZ,
+ (float)stats->cpu_use_user / USER_HZ,
+ blkio_str,
+ mem_used_str);
+ if (total->kmem_used > 0) {
+ size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
+ printf(" %10s", kmem_used_str);
+ }
+}
+
+static int cmp_name(const void *sct1, const void *sct2)
+{
+ const struct ct *ct1 = sct1;
+ const struct ct *ct2 = sct2;
+
+ if (sort_reverse)
+ return strcmp(ct2->c->name, ct1->c->name);
+ return strcmp(ct1->c->name, ct2->c->name);
+}
+
+static int cmp_cpuuse(const void *sct1, const void *sct2)
+{
+ const struct ct *ct1 = sct1;
+ const struct ct *ct2 = sct2;
+
+ if (sort_reverse)
+ return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
+ return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
+}
+
+static int cmp_blkio(const void *sct1, const void *sct2)
+{
+ const struct ct *ct1 = sct1;
+ const struct ct *ct2 = sct2;
+
+ if (sort_reverse)
+ return ct2->stats->blkio < ct1->stats->blkio;
+ return ct1->stats->blkio < ct2->stats->blkio;
+}
+
+static int cmp_memory(const void *sct1, const void *sct2)
+{
+ const struct ct *ct1 = sct1;
+ const struct ct *ct2 = sct2;
+
+ if (sort_reverse)
+ return ct2->stats->mem_used < ct1->stats->mem_used;
+ return ct1->stats->mem_used < ct2->stats->mem_used;
+}
+
+static int cmp_kmemory(const void *sct1, const void *sct2)
+{
+ const struct ct *ct1 = sct1;
+ const struct ct *ct2 = sct2;
+
+ if (sort_reverse)
+ return ct2->stats->kmem_used < ct1->stats->kmem_used;
+ return ct1->stats->kmem_used < ct2->stats->kmem_used;
+}
+
+static void ct_sort(int active)
+{
+ int (*cmp_func)(const void *, const void *);
+
+ switch(sort_by) {
+ default:
+ case 'n': cmp_func = cmp_name; break;
+ case 'c': cmp_func = cmp_cpuuse; break;
+ case 'b': cmp_func = cmp_blkio; break;
+ case 'm': cmp_func = cmp_memory; break;
+ case 'k': cmp_func = cmp_kmemory; break;
+ }
+ qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func);
+}
+
+static void ct_free(void)
+{
+ int i;
+
+ for (i = 0; i < ct_alloc_cnt; i++) {
+ if (ct[i].c) {
+ lxc_container_put(ct[i].c);
+ ct[i].c = NULL;
+ }
+ free(ct[i].stats);
+ ct[i].stats = NULL;
+ }
+}
+
+static void ct_realloc(int active_cnt)
+{
+ int i;
+
+ if (active_cnt > ct_alloc_cnt) {
+ ct_free();
+ ct = realloc(ct, sizeof(*ct) * active_cnt);
+ if (!ct) {
+ ERROR("cannot alloc mem");
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < active_cnt; i++) {
+ ct[i].stats = malloc(sizeof(*ct[0].stats));
+ if (!ct[i].stats) {
+ ERROR("cannot alloc mem");
+ exit(EXIT_FAILURE);
+ }
+ }
+ ct_alloc_cnt = active_cnt;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_epoll_descr descr;
+ int ret, ct_print_cnt;
+ char in_char;
+
+ ret = EXIT_FAILURE;
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ goto out;
+
+ ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */
+ if (stdin_tios_setup() < 0) {
+ ERROR("failed to setup terminal");
+ goto out;
+ }
+
+ /* ensure the terminal gets restored */
+ atexit(stdin_tios_restore);
+ signal(SIGINT, sig_handler);
+ signal(SIGQUIT, sig_handler);
+
+ if (lxc_mainloop_open(&descr)) {
+ ERROR("failed to create mainloop");
+ goto out;
+ }
+
+ ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char);
+ if (ret) {
+ ERROR("failed to add stdin handler");
+ ret = EXIT_FAILURE;
+ goto err1;
+ }
+
+ for(;;) {
+ struct lxc_container **active;
+ int i, active_cnt;
+ struct stats total;
+ char total_name[30];
+
+ active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active);
+ ct_realloc(active_cnt);
+
+ memset(&total, 0, sizeof(total));
+ for (i = 0; i < active_cnt; i++)
+ stats_get(active[i], &ct[i], &total);
+
+ ct_sort(active_cnt);
+
+ printf(TERMCLEAR);
+ stats_print_header(&total);
+ for (i = 0; i < active_cnt && i < ct_print_cnt; i++) {
+ stats_print(ct[i].c->name, ct[i].stats, &total);
+ printf("\n");
+ }
+ sprintf(total_name, "TOTAL %d of %d", i, active_cnt);
+ stats_print(total_name, &total, &total);
+ fflush(stdout);
+
+ for (i = 0; i < active_cnt; i++) {
+ lxc_container_put(ct[i].c);
+ ct[i].c = NULL;
+ }
+
+ in_char = '\0';
+ ret = lxc_mainloop(&descr, 1000 * delay);
+ if (ret != 0 || in_char == 'q')
+ break;
+ switch(in_char) {
+ case 'r':
+ sort_reverse ^= 1;
+ break;
+ case 'n':
+ case 'c':
+ case 'b':
+ case 'm':
+ case 'k':
+ if (sort_by == in_char)
+ sort_reverse ^= 1;
+ else
+ sort_reverse = 0;
+ sort_by = in_char;
+ }
+ }
+ ret = EXIT_SUCCESS;
+
+err1:
+ lxc_mainloop_close(&descr);
+out:
+ return ret;
+}
diff --git a/src/lxc/tools/lxc_unfreeze.c b/src/lxc/tools/lxc_unfreeze.c
new file mode 100644
index 0000000..3a13d37
--- /dev/null
+++ b/src/lxc/tools/lxc_unfreeze.c
@@ -0,0 +1,90 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "arguments.h"
+
+lxc_log_define(lxc_unfreeze_ui, lxc);
+
+static const struct option my_longopts[] = {
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-unfreeze",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-unfreeze unfreezes a container with the identifier NAME\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n",
+ .options = my_longopts,
+ .parser = NULL,
+ .checker = NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ exit(1);
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ exit(1);
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c) {
+ ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name);
+ exit(1);
+ }
+
+ if (!c->may_control(c)) {
+ ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+ if (!c->unfreeze(c)) {
+ ERROR("Failed to unfreeze %s:%s", my_args.lxcpath[0], my_args.name);
+ lxc_container_put(c);
+ exit(1);
+ }
+
+ lxc_container_put(c);
+
+ exit(0);
+}
diff --git a/src/lxc/tools/lxc_unshare.c b/src/lxc/tools/lxc_unshare.c
new file mode 100644
index 0000000..e629525
--- /dev/null
+++ b/src/lxc/tools/lxc_unshare.c
@@ -0,0 +1,257 @@
+/*
+ * 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 <errno.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "caps.h"
+#include "cgroup.h"
+#include "error.h"
+#include "log.h"
+#include "namespace.h"
+#include "network.h"
+#include "utils.h"
+
+/* Define sethostname() if missing from the C library */
+#ifndef HAVE_SETHOSTNAME
+static int sethostname(const char * name, size_t len)
+{
+#ifdef __NR_sethostname
+return syscall(__NR_sethostname, name, len);
+#else
+errno = ENOSYS;
+return -1;
+#endif
+}
+#endif
+
+lxc_log_define(lxc_unshare_ui, lxc);
+
+struct my_iflist
+{
+ char *mi_ifname;
+ struct my_iflist *mi_next;
+};
+
+static void usage(char *cmd)
+{
+ fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
+ fprintf(stderr, "Options are:\n");
+ fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \
+ "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n");
+ fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
+ fprintf(stderr, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
+ fprintf(stderr, "\t -H <hostname>: Set the hostname in the container\n");
+ fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n");
+ fprintf(stderr, "\t -M : reMount default fs inside container (/proc /dev/shm /dev/mqueue)\n");
+ _exit(1);
+}
+
+static bool lookup_user(const char *optarg, uid_t *uid)
+{
+ char name[sysconf(_SC_LOGIN_NAME_MAX)];
+ struct passwd *pwent = NULL;
+
+ if (!optarg || (optarg[0] == '\0'))
+ return false;
+
+ if (sscanf(optarg, "%u", uid) < 1) {
+ /* not a uid -- perhaps a username */
+ if (sscanf(optarg, "%s", name) < 1)
+ return false;
+
+ pwent = getpwnam(name);
+ if (!pwent) {
+ ERROR("invalid username %s", name);
+ return false;
+ }
+ *uid = pwent->pw_uid;
+ } else {
+ pwent = getpwuid(*uid);
+ if (!pwent) {
+ ERROR("invalid uid %u", *uid);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+struct start_arg {
+ char ***args;
+ int *flags;
+ uid_t *uid;
+ bool setuid;
+ int want_default_mounts;
+ const char *want_hostname;
+};
+
+static int do_start(void *arg)
+{
+ struct start_arg *start_arg = arg;
+ char **args = *start_arg->args;
+ int flags = *start_arg->flags;
+ uid_t uid = *start_arg->uid;
+ int want_default_mounts = start_arg->want_default_mounts;
+ const char *want_hostname = start_arg->want_hostname;
+
+ if ((flags & CLONE_NEWNS) && want_default_mounts)
+ lxc_setup_fs();
+
+ if ((flags & CLONE_NEWUTS) && want_hostname)
+ if (sethostname(want_hostname, strlen(want_hostname)) < 0) {
+ ERROR("failed to set hostname %s: %s", want_hostname, strerror(errno));
+ exit(1);
+ }
+
+ // Setuid is useful even without a new user id space
+ if (start_arg->setuid && setuid(uid)) {
+ ERROR("failed to set uid %d: %s", uid, strerror(errno));
+ exit(1);
+ }
+
+ execvp(args[0], args);
+
+ ERROR("failed to exec: '%s': %s", args[0], strerror(errno));
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int opt, status;
+ int ret;
+ char *namespaces = NULL;
+ char **args;
+ int flags = 0;
+ int daemonize = 0;
+ uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */
+ pid_t pid;
+ struct my_iflist *tmpif, *my_iflist = NULL;
+ struct start_arg start_arg = {
+ .args = &args,
+ .uid = &uid,
+ .setuid = false,
+ .flags = &flags,
+ .want_hostname = NULL,
+ .want_default_mounts = 0,
+ };
+
+ while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) {
+ switch (opt) {
+ case 's':
+ namespaces = optarg;
+ break;
+ case 'i':
+ if (!(tmpif = malloc(sizeof(*tmpif)))) {
+ perror("malloc");
+ exit(1);
+ }
+ tmpif->mi_ifname = optarg;
+ tmpif->mi_next = my_iflist;
+ my_iflist = tmpif;
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'M':
+ start_arg.want_default_mounts = 1;
+ break;
+ case 'H':
+ start_arg.want_hostname = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'u':
+ if (!lookup_user(optarg, &uid))
+ return 1;
+ start_arg.setuid = true;
+ }
+ }
+
+ if (argv[optind] == NULL) {
+ ERROR("a command to execute in the new namespace is required");
+ return 1;
+ }
+
+ args = &argv[optind];
+
+ ret = lxc_caps_init();
+ if (ret)
+ return 1;
+
+ ret = lxc_fill_namespace_flags(namespaces, &flags);
+ if (ret)
+ usage(argv[0]);
+
+ if (!(flags & CLONE_NEWNET) && my_iflist) {
+ ERROR("-i <interfacename> needs -s NETWORK option");
+ return 1;
+ }
+
+ if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) {
+ ERROR("-H <hostname> needs -s UTSNAME option");
+ return 1;
+ }
+
+ if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) {
+ ERROR("-M needs -s MOUNT option");
+ return 1;
+ }
+
+ pid = lxc_clone(do_start, &start_arg, flags);
+ if (pid < 0) {
+ ERROR("failed to clone");
+ return 1;
+ }
+
+ if (my_iflist) {
+ for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) {
+ if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid, NULL) < 0)
+ fprintf(stderr,"Could not move interface %s into container %d: %s\n", tmpif->mi_ifname, pid, strerror(errno));
+ }
+ }
+
+ if (daemonize)
+ exit(0);
+
+ if (waitpid(pid, &status, 0) < 0) {
+ ERROR("failed to wait for '%d'", pid);
+ return 1;
+ }
+
+ return lxc_error_set_and_log(pid, status);
+}
diff --git a/src/lxc/tools/lxc_usernsexec.c b/src/lxc/tools/lxc_usernsexec.c
new file mode 100644
index 0000000..6745ac3
--- /dev/null
+++ b/src/lxc/tools/lxc_usernsexec.c
@@ -0,0 +1,385 @@
+/*
+ * (C) Copyright IBM Corp. 2008
+ * (C) Copyright Canonical, Inc 2010-2013
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at ubuntu.com>
+ * (Once upon a time, this was based on nsexec from the IBM
+ * container tools)
+ *
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <sched.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "conf.h"
+#include "namespace.h"
+#include "utils.h"
+
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19)
+#endif
+
+int unshare(int flags);
+
+static void usage(const char *name)
+{
+ printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
+ printf("\n");
+ printf(" -h this message\n");
+ printf("\n");
+ printf(" -m <uid-maps> uid maps to use\n");
+ printf("\n");
+ printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
+ printf(" [u|g|b]: map user id, group id, or both\n");
+ printf(" ns_id: the base id in the new namespace\n");
+ printf(" host_id: the base id in the parent namespace\n");
+ printf(" range: how many ids to map\n");
+ printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n");
+ printf(" As such, /etc/subuid and /etc/subgid must grant the\n");
+ printf(" calling user permission to use the mapped ranges\n");
+ exit(1);
+}
+
+static void opentty(const char * tty, int which) {
+ int fd, flags;
+
+ if (tty[0] == '\0')
+ return;
+
+ fd = open(tty, O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ printf("WARN: could not reopen tty: %s\n", strerror(errno));
+ return;
+ }
+
+ flags = fcntl(fd, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ printf("WARN: could not set fd flags: %s\n", strerror(errno));
+ return;
+ }
+
+ close(which);
+ if (fd != which) {
+ dup2(fd, which);
+ close(fd);
+ }
+}
+// Code copy end
+
+static int do_child(void *vargv)
+{
+ char **argv = (char **)vargv;
+
+ // Assume we want to become root
+ if (setgid(0) < 0) {
+ perror("setgid");
+ return -1;
+ }
+ if (setuid(0) < 0) {
+ perror("setuid");
+ return -1;
+ }
+ if (setgroups(0, NULL) < 0) {
+ perror("setgroups");
+ return -1;
+ }
+ if (unshare(CLONE_NEWNS) < 0) {
+ perror("unshare CLONE_NEWNS");
+ return -1;
+ }
+ if (detect_shared_rootfs()) {
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
+ printf("Failed to make / rslave");
+ return -1;
+ }
+ }
+ execvp(argv[0], argv);
+ perror("execvpe");
+ return -1;
+}
+
+static struct lxc_list active_map;
+
+/*
+ * given a string like "b:0:100000:10", map both uids and gids
+ * 0-10 to 100000 to 100010
+ */
+static int parse_map(char *map)
+{
+ struct id_map *newmap;
+ struct lxc_list *tmp = NULL;
+ int ret;
+ int i;
+ char types[2] = {'u', 'g'};
+ char which;
+ long host_id, ns_id, range;
+
+ if (!map)
+ return -1;
+
+ ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
+ if (ret != 4)
+ return -1;
+
+ if (which != 'b' && which != 'u' && which != 'g')
+ return -1;
+
+ for (i = 0; i < 2; i++) {
+ if (which != types[i] && which != 'b')
+ continue;
+
+ newmap = malloc(sizeof(*newmap));
+ if (!newmap)
+ return -1;
+
+ newmap->hostid = host_id;
+ newmap->nsid = ns_id;
+ newmap->range = range;
+
+ if (types[i] == 'u')
+ newmap->idtype = ID_TYPE_UID;
+ else
+ newmap->idtype = ID_TYPE_GID;
+
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp) {
+ free(newmap);
+ return -1;
+ }
+
+ tmp->elem = newmap;
+ lxc_list_add_tail(&active_map, tmp);
+ }
+
+ return 0;
+}
+
+/*
+ * This is called if the user did not pass any uid ranges in
+ * through -m flags. It's called once to get the default uid
+ * map, and once for the default gid map.
+ * Go through /etc/subuids and /etc/subgids to find this user's
+ * allowed map. We only use the first one for each of uid and
+ * gid, because otherwise we're not sure which entries the user
+ * wanted.
+ */
+static int read_default_map(char *fnam, int which, char *username)
+{
+ FILE *fin;
+ char *line = NULL;
+ size_t sz = 0;
+ struct id_map *newmap;
+ struct lxc_list *tmp = NULL;
+ char *p1, *p2;
+
+ fin = fopen(fnam, "r");
+ if (!fin)
+ return -1;
+ while (getline(&line, &sz, fin) != -1) {
+ if (sz <= strlen(username) ||
+ strncmp(line, username, strlen(username)) != 0 ||
+ line[strlen(username)] != ':')
+ continue;
+ p1 = strchr(line, ':');
+ if (!p1)
+ continue;
+ p2 = strchr(p1+1, ':');
+ if (!p2)
+ continue;
+ newmap = malloc(sizeof(*newmap));
+ if (!newmap) {
+ fclose(fin);
+ free(line);
+ return -1;
+ }
+ newmap->hostid = atol(p1+1);
+ newmap->range = atol(p2+1);
+ newmap->nsid = 0;
+ newmap->idtype = which;
+
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp) {
+ fclose(fin);
+ free(line);
+ free(newmap);
+ return -1;
+ }
+
+ tmp->elem = newmap;
+ lxc_list_add_tail(&active_map, tmp);
+ break;
+ }
+
+ free(line);
+ fclose(fin);
+ return 0;
+}
+
+static int find_default_map(void)
+{
+ struct passwd *p = getpwuid(getuid());
+ if (!p)
+ return -1;
+ if (read_default_map(subuidfile, ID_TYPE_UID, p->pw_name) < 0)
+ return -1;
+ if (read_default_map(subgidfile, ID_TYPE_GID, p->pw_name) < 0)
+ return -1;
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
+ char ttyname0[256], ttyname1[256], ttyname2[256];
+ int status;
+ int ret;
+ int pid;
+ char *default_args[] = {"/bin/sh", NULL};
+ char buf[1];
+ int pipe1[2], // child tells parent it has unshared
+ pipe2[2]; // parent tells child it is mapped and may proceed
+
+ memset(ttyname0, '\0', sizeof(ttyname0));
+ memset(ttyname1, '\0', sizeof(ttyname1));
+ memset(ttyname2, '\0', sizeof(ttyname2));
+ if (isatty(0)) {
+ ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
+ if (ret < 0) {
+ perror("unable to open stdin.");
+ exit(1);
+ }
+ ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
+ if (ret < 0) {
+ printf("Warning: unable to open stdout, continuing.");
+ memset(ttyname1, '\0', sizeof(ttyname1));
+ }
+ ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
+ if (ret < 0) {
+ printf("Warning: unable to open stderr, continuing.");
+ memset(ttyname2, '\0', sizeof(ttyname2));
+ }
+ }
+
+ lxc_list_init(&active_map);
+
+ while ((c = getopt(argc, argv, "m:h")) != EOF) {
+ switch (c) {
+ case 'm': if (parse_map(optarg)) usage(argv[0]); break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ }
+ };
+
+ if (lxc_list_empty(&active_map)) {
+ if (find_default_map()) {
+ fprintf(stderr, "You have no allocated subuids or subgids\n");
+ exit(1);
+ }
+ }
+
+ argv = &argv[optind];
+ argc = argc - optind;
+ if (argc < 1) {
+ argv = default_args;
+ argc = 1;
+ }
+
+ if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+ if ((pid = fork()) == 0) {
+ // Child.
+
+ close(pipe1[0]);
+ close(pipe2[1]);
+ opentty(ttyname0, 0);
+ opentty(ttyname1, 1);
+ opentty(ttyname2, 2);
+
+ ret = unshare(flags);
+ if (ret < 0) {
+ perror("unshare");
+ return 1;
+ }
+ buf[0] = '1';
+ if (write(pipe1[1], buf, 1) < 1) {
+ perror("write pipe");
+ exit(1);
+ }
+ if (read(pipe2[0], buf, 1) < 1) {
+ perror("read pipe");
+ exit(1);
+ }
+ if (buf[0] != '1') {
+ fprintf(stderr, "parent had an error, child exiting\n");
+ exit(1);
+ }
+
+ close(pipe1[1]);
+ close(pipe2[0]);
+ return do_child((void*)argv);
+ }
+
+ close(pipe1[1]);
+ close(pipe2[0]);
+ if (read(pipe1[0], buf, 1) < 1) {
+ perror("read pipe");
+ exit(1);
+ }
+
+ buf[0] = '1';
+
+ if (lxc_map_ids(&active_map, pid)) {
+ fprintf(stderr, "error mapping child\n");
+ ret = 0;
+ }
+ if (write(pipe2[1], buf, 1) < 0) {
+ perror("write to pipe");
+ exit(1);
+ }
+
+ if ((ret = waitpid(pid, &status, __WALL)) < 0) {
+ printf("waitpid() returns %d, errno %d\n", ret, errno);
+ exit(1);
+ }
+
+ exit(WEXITSTATUS(status));
+}
diff --git a/src/lxc/tools/lxc_wait.c b/src/lxc/tools/lxc_wait.c
new file mode 100644
index 0000000..712ba52
--- /dev/null
+++ b/src/lxc/tools/lxc_wait.c
@@ -0,0 +1,112 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "arguments.h"
+
+lxc_log_define(lxc_wait_ui, lxc);
+
+static int my_checker(const struct lxc_arguments* args)
+{
+ if (!args->states) {
+ lxc_error(args, "missing state option to wait for.");
+ return -1;
+ }
+ return 0;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+ switch (c) {
+ case 's': args->states = optarg; break;
+ case 't': args->timeout = atol(optarg); break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"state", required_argument, 0, 's'},
+ {"timeout", required_argument, 0, 't'},
+ LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-wait",
+ .help = "\
+--name=NAME --state=STATE\n\
+\n\
+lxc-wait waits for NAME container state to reach STATE\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME of the container\n\
+ -s, --state=STATE ORed states to wait for\n\
+ STOPPED, STARTING, RUNNING, STOPPING,\n\
+ ABORTING, FREEZING, FROZEN, THAWED\n\
+ -t, --timeout=TMO Seconds to wait for state changes\n",
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = my_checker,
+ .timeout = -1,
+};
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ return 1;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ return 1;
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c)
+ return 1;
+
+ if (!c->may_control(c)) {
+ fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ lxc_container_put(c);
+ return 1;
+ }
+
+ if (!c->wait(c, my_args.states, my_args.timeout)) {
+ lxc_container_put(c);
+ return 1;
+ }
+ return 0;
+}
More information about the lxc-devel
mailing list