* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#define _GNU_SOURCE
-#include "config.h"
-
-#include <unistd.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
-#include <stdio.h>
-#include <sys/types.h>
+#include <stdbool.h>
#include <stdint.h>
-#include <sys/wait.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
-#include <ctype.h>
+#include <string.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <time.h>
-#include <stdbool.h>
+#include <unistd.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 "config.h"
+#include "log.h"
+#include "storage_utils.h"
#include "utils.h"
#ifndef HAVE_GETSUBOPT
-#include <../include/getsubopt.h>
+#include "include/getsubopt.h"
#endif
-lxc_log_define(lxc_copy_ui, lxc);
+lxc_log_define(lxc_copy, lxc);
enum mnttype {
LXC_MNT_BIND,
- LXC_MNT_AUFS,
LXC_MNT_OVL,
};
{ "newpath", required_argument, 0, 'p'},
{ "rename", no_argument, 0, 'R'},
{ "snapshot", no_argument, 0, 's'},
+ { "allowrunning", no_argument, 0, 'a'},
{ "foreground", no_argument, 0, 'F'},
{ "daemon", no_argument, 0, 'd'},
{ "ephemeral", no_argument, 0, 'e'},
/* mount keys */
static char *const keys[] = {
[LXC_MNT_BIND] = "bind",
- [LXC_MNT_AUFS] = "aufs",
[LXC_MNT_OVL] = "overlay",
NULL
};
.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] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,overlay}=/src:/dest] -- hook options\n\
--name=NAME [-P lxcpath] -N newname -R\n\
\n\
lxc-copy clone a 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\
+ -a, --allowrunning allow snapshot creation even if source container is running\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\
+ {bind,overlay}=/src-path or {bind,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\
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.
+ * on a tmpfs and that either overlay storage driver 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);
if (lxc_arguments_parse(&my_args, argc, argv))
exit(ret);
- if (!my_args.log_file)
- my_args.log_file = "none";
+ /* Only create log if explicitly instructed */
+ if (my_args.log_file || my_args.log_priority) {
+ log.name = my_args.name;
+ log.file = my_args.log_file;
+ log.level = my_args.log_priority;
+ log.prefix = my_args.progname;
+ log.quiet = my_args.quiet;
+ log.lxcpath = my_args.lxcpath[0];
- log.name = my_args.name;
- log.file = my_args.log_file;
- log.priority = my_args.log_priority;
- log.prefix = my_args.progname;
- log.quiet = my_args.quiet;
- log.lxcpath = my_args.lxcpath[0];
-
- if (lxc_log_init(&log))
- exit(ret);
- lxc_log_options_no_override();
+ if (lxc_log_init(&log))
+ exit(ret);
+ }
if (geteuid()) {
if (access(my_args.lxcpath[0], O_RDONLY) < 0) {
- if (!my_args.quiet)
- fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
+ ERROR("You lack access to %s", 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");
+ ERROR("You must provide a NEWNAME for the clone");
exit(ret);
}
if (my_args.task == SNAP || my_args.task == DESTROY)
flags |= LXC_CLONE_SNAPSHOT;
+ if (my_args.allowrunning)
+ flags |= LXC_CLONE_ALLOW_RUNNING;
if (my_args.keepname)
flags |= LXC_CLONE_KEEPNAME;
+
if (my_args.keepmac)
flags |= LXC_CLONE_KEEPMACADDR;
if (my_args.rcfile) {
c->clear_config(c);
+
if (!c->load_config(c, my_args.rcfile)) {
- fprintf(stderr, "Failed to load rcfile\n");
+ ERROR("Failed to load rcfile");
goto out;
}
+
c->configfile = strdup(my_args.rcfile);
if (!c->configfile) {
- fprintf(stderr, "Out of memory setting new config filename\n");
+ ERROR("Out of memory setting new config filename");
goto out;
}
}
if (!c->may_control(c)) {
- if (!my_args.quiet)
- fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
+ ERROR("Insufficent privileges to control %s", c->name);
goto out;
}
if (!c->is_defined(c)) {
- if (!my_args.quiet)
- fprintf(stderr, "Error: container %s is not defined\n", c->name);
+ ERROR("Container %s is not defined", c->name);
goto out;
}
static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_arguments *arg)
{
- char upperdir[MAXPATHLEN];
- char workdir[MAXPATHLEN];
+ char upperdir[PATH_MAX];
+ char workdir[PATH_MAX];
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",
+ if (m->mnt_type == LXC_MNT_OVL) {
+ ret = snprintf(upperdir, PATH_MAX, "%s/%s/delta#XXXXXX",
arg->newpath, arg->newname);
- if (ret < 0 || ret >= MAXPATHLEN)
+ if (ret < 0 || ret >= PATH_MAX)
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",
+ ret = snprintf(workdir, PATH_MAX, "%s/%s/work#XXXXXX",
arg->newpath, arg->newname);
- if (ret < 0 || ret >= MAXPATHLEN)
+ if (ret < 0 || ret >= PATH_MAX)
return -1;
+
if (!mkdtemp(workdir))
return -1;
+
m->workdir = strdup(workdir);
if (!m->workdir)
return -1;
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) {
+ 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;
clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize,
args);
if (!clone) {
- if (!my_args.quiet)
- fprintf(stderr, "clone failed\n");
+ ERROR("Failed to clone");
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];
+ char randname[PATH_MAX];
unsigned int i;
int ret = 0;
bool bret = true, started = false;
+ char *tmp_buf = randname;
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)
+ ret = snprintf(randname, PATH_MAX, "%s/%s_XXXXXX", arg->newpath, arg->name);
+ if (ret < 0 || ret >= PATH_MAX)
return -1;
+
if (!mkdtemp(randname))
return -1;
+
if (chmod(randname, 0770) < 0) {
- remove(randname);
+ (void)remove(randname);
return -1;
}
+
arg->newname = randname + strlen(arg->newpath) + 1;
}
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;
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)
destroy_and_put:
if (started)
clone->shutdown(clone, -1);
- if (!started || clone->lxc_conf->ephemeral != 1)
+
+ ret = clone->get_config_item(clone, "lxc.ephemeral", tmp_buf, PATH_MAX);
+ if (ret > 0 && strcmp(tmp_buf, "0"))
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);
+ ERROR("Renaming container %s to %s failed", c->name, newname);
return -1;
}
- INFO("Renamed container %s to %s\n", c->name, newname);
-
return 0;
}
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;
case 's':
args->task = SNAP;
break;
+ case 'a':
+ args->allowrunning = 1;
+ break;
case 'F':
args->daemonize = 0;
break;
return -1;
break;
case 'B':
- if (strcmp(arg, "overlay") == 0)
+ if (strncmp(arg, "overlay", strlen(arg)) == 0)
arg = "overlayfs";
args->bdevtype = arg;
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;
m->dest = construct_path(mntarray[1], false);
m->options = strdup(mntarray[2]);
} else {
- INFO("Excess elements in mount specification");
+ printf("Excess elements in mount specification\n");
}
if (!m->dest)
if (!m->options)
m->options = strdup("rw");
- if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) &&
- strncmp(m->options, "ro", strlen(m->options))))
+ if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) != 0 &&
+ strncmp(m->options, "ro", strlen(m->options)) != 0))
goto err;
lxc_free_array((void **)mntarray, free);
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;
}
else if (len == 2) /* overlay=src:dest */
m->dest = construct_path(mntarray[1], false);
else
- INFO("Excess elements in mount specification");
+ printf("Excess elements in mount specification\n");
if (!m->dest)
goto err;
return -1;
}
-/* For ephemeral snapshots backed by overlay or aufs filesystems, this function
+/* For ephemeral snapshots backed by the overlay filesystem, 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
{
int ret, fd;
size_t len;
+ mode_t msk;
char *premount = NULL;
- FILE *fp;
+ FILE *fp = NULL;
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.");
+ ERROR("%s",
+ "A container can only be placed on a tmpfs when the "
+ "overlay storage driver is used");
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.");
+ } else if (arg->tmpfs && arg->bdevtype &&
+ strncmp(arg->bdevtype, "overlayfs", strlen(arg->bdevtype)) != 0) {
+ ERROR("%s",
+ "A container can only be placed on a tmpfs when the "
+ "overlay storage driver is used");
goto err_free;
}
if (ret < 0 || (size_t)ret >= len)
goto err_free;
+ msk = umask(0022);
fd = mkstemp(premount);
+ umask(msk);
if (fd < 0)
goto err_free;
if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
- SYSERROR("Failed to set close-on-exec on file descriptor.");
+ ERROR("Failed to set close-on-exec on file descriptor");
goto err_close;
}
err_close:
if (fd > 0)
close(fd);
- else
+ else if (fp)
fclose(fp);
+
err_free:
free(premount);
return NULL;