3 * Copyright © 2015 Christian Brauner <christian.brauner@mailbox.org>.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2, as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <sys/types.h>
37 #include <lxc/lxccontainer.h>
39 #include "arguments.h"
42 #include "storage_utils.h"
45 #ifndef HAVE_GETSUBOPT
46 #include "include/getsubopt.h"
49 lxc_log_define(lxc_copy
, lxc
);
57 enum mnttype mnt_type
;
66 static unsigned int mnt_table_size
= 0;
67 static struct mnts
*mnt_table
= NULL
;
69 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
);
71 static const struct option my_longopts
[] = {
72 { "newname", required_argument
, 0, 'N'},
73 { "newpath", required_argument
, 0, 'p'},
74 { "rename", no_argument
, 0, 'R'},
75 { "snapshot", no_argument
, 0, 's'},
76 { "foreground", no_argument
, 0, 'F'},
77 { "daemon", no_argument
, 0, 'd'},
78 { "ephemeral", no_argument
, 0, 'e'},
79 { "mount", required_argument
, 0, 'm'},
80 { "backingstorage", required_argument
, 0, 'B'},
81 { "fssize", required_argument
, 0, 'L'},
82 { "keepdata", no_argument
, 0, 'D'},
83 { "keepname", no_argument
, 0, 'K'},
84 { "keepmac", no_argument
, 0, 'M'},
85 { "tmpfs", no_argument
, 0, 't'},
90 static char *const mount_keys
[] = {
91 [LXC_MNT_BIND
] = "bind",
92 [LXC_MNT_OVL
] = "overlay",
96 static struct lxc_arguments my_args
= {
97 .progname
= "lxc-copy",
99 --name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\
100 --name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,overlay}=/src:/dest] -- hook options\n\
101 --name=NAME [-P lxcpath] -N newname -R\n\
103 lxc-copy clone a container\n\
106 -n, --name=NAME NAME of the container\n\
107 -N, --newname=NEWNAME NEWNAME for the restored container\n\
108 -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
109 -R, --rename rename container\n\
110 -s, --snapshot create snapshot instead of clone\n\
111 -F, --foreground start with current tty attached to /dev/console\n\
112 -d, --daemon daemonize the container (default)\n\
113 -e, --ephemeral start ephemeral container\n\
114 -m, --mount directory to mount into container, either \n\
115 {bind,overlay}=/src-path or {bind,overlay}=/src-path:/dst-path\n\
116 -B, --backingstorage=TYPE backingstorage type for the container\n\
117 -t, --tmpfs place ephemeral container on a tmpfs\n\
118 (WARNING: On reboot all changes made to the container will be lost.)\n\
119 -L, --fssize size of the new block device for block device containers\n\
120 -D, --keepdata pass together with -e start a persistent snapshot \n\
121 -K, --keepname keep the hostname of the original container\n\
122 -- hook options arguments passed to the hook program\n\
123 -M, --keepmac keep the MAC address of the original container\n\
124 --rcfile=FILE Load configuration file FILE\n",
125 .options
= my_longopts
,
133 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
,
135 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
,
136 struct lxc_arguments
*arg
);
137 static char *construct_path(char *path
, bool as_prefix
);
138 static char *set_mnt_entry(struct mnts
*m
);
139 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
140 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
142 static int do_clone_ephemeral(struct lxc_container
*c
,
143 struct lxc_arguments
*arg
, char **args
,
145 static int do_clone_rename(struct lxc_container
*c
, char *newname
);
146 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
148 static void free_mnts(void);
150 /* Place an ephemeral container started with -e flag on a tmpfs. Restrictions
151 * are that you cannot request the data to be kept while placing the container
152 * on a tmpfs and that either overlay storage driver must be used.
154 static char *mount_tmpfs(const char *oldname
, const char *newname
,
155 const char *path
, struct lxc_arguments
*arg
);
156 static int parse_mntsubopts(char *subopts
, char *const *keys
,
157 char *mntparameters
);
158 static int parse_bind_mnt(char *mntstring
, enum mnttype type
);
159 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
);
161 int main(int argc
, char *argv
[])
163 struct lxc_container
*c
;
166 int ret
= EXIT_FAILURE
;
168 if (lxc_arguments_parse(&my_args
, argc
, argv
))
171 /* Only create log if explicitly instructed */
172 if (my_args
.log_file
|| my_args
.log_priority
) {
173 log
.name
= my_args
.name
;
174 log
.file
= my_args
.log_file
;
175 log
.level
= my_args
.log_priority
;
176 log
.prefix
= my_args
.progname
;
177 log
.quiet
= my_args
.quiet
;
178 log
.lxcpath
= my_args
.lxcpath
[0];
180 if (lxc_log_init(&log
))
185 if (access(my_args
.lxcpath
[0], O_RDONLY
) < 0) {
186 ERROR("You lack access to %s", my_args
.lxcpath
[0]);
191 if (!my_args
.newname
&& !(my_args
.task
== DESTROY
)) {
192 ERROR("You must provide a NEWNAME for the clone");
196 if (my_args
.task
== SNAP
|| my_args
.task
== DESTROY
)
197 flags
|= LXC_CLONE_SNAPSHOT
;
199 if (my_args
.keepname
)
200 flags
|= LXC_CLONE_KEEPNAME
;
203 flags
|= LXC_CLONE_KEEPMACADDR
;
205 if (!my_args
.newpath
)
206 my_args
.newpath
= (char *)my_args
.lxcpath
[0];
208 c
= lxc_container_new(my_args
.name
, my_args
.lxcpath
[0]);
212 if (my_args
.rcfile
) {
215 if (!c
->load_config(c
, my_args
.rcfile
)) {
216 ERROR("Failed to load rcfile");
220 c
->configfile
= strdup(my_args
.rcfile
);
221 if (!c
->configfile
) {
222 ERROR("Out of memory setting new config filename");
227 if (!c
->may_control(c
)) {
228 ERROR("Insufficent privileges to control %s", c
->name
);
232 if (!c
->is_defined(c
)) {
233 ERROR("Container %s is not defined", c
->name
);
237 ret
= do_clone_task(c
, my_args
.task
, flags
, &argv
[optind
]);
240 lxc_container_put(c
);
247 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
, enum mnttype type
)
251 n
= realloc(*mnts
, (*num
+ 1) * sizeof(struct mnts
));
259 *m
= (struct mnts
) {.mnt_type
= type
};
264 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
, struct lxc_arguments
*arg
)
266 char upperdir
[PATH_MAX
];
267 char workdir
[PATH_MAX
];
270 struct mnts
*m
= NULL
;
272 for (i
= 0, m
= mnts
; i
< num
; i
++, m
++) {
273 if (m
->mnt_type
== LXC_MNT_OVL
) {
274 ret
= snprintf(upperdir
, PATH_MAX
, "%s/%s/delta#XXXXXX",
275 arg
->newpath
, arg
->newname
);
276 if (ret
< 0 || ret
>= PATH_MAX
)
279 if (!mkdtemp(upperdir
))
282 m
->upper
= strdup(upperdir
);
287 if (m
->mnt_type
== LXC_MNT_OVL
) {
288 ret
= snprintf(workdir
, PATH_MAX
, "%s/%s/work#XXXXXX",
289 arg
->newpath
, arg
->newname
);
290 if (ret
< 0 || ret
>= PATH_MAX
)
293 if (!mkdtemp(workdir
))
296 m
->workdir
= strdup(workdir
);
305 static char *construct_path(char *path
, bool as_prefix
)
307 char **components
= NULL
;
308 char *cleanpath
= NULL
;
310 components
= lxc_normalize_path(path
);
314 cleanpath
= lxc_string_join("/", (const char **)components
, as_prefix
);
315 lxc_free_array((void **)components
, free
);
320 static char *set_mnt_entry(struct mnts
*m
)
322 char *mntentry
= NULL
;
326 if (m
->mnt_type
== LXC_MNT_OVL
) {
327 len
= strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") +
328 2 * strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->upper
) +
329 strlen(m
->workdir
) + 1;
331 mntentry
= malloc(len
);
335 ret
= snprintf(mntentry
, len
, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
336 m
->src
, m
->dest
, m
->src
, m
->upper
, m
->workdir
);
337 if (ret
< 0 || (size_t)ret
>= len
)
339 } else if (m
->mnt_type
== LXC_MNT_BIND
) {
340 len
= strlen(" none bind,optional,, 0 0") +
341 strlen(is_dir(m
->src
) ? "create=dir" : "create=file") +
342 strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->options
) + 1;
344 mntentry
= malloc(len
);
348 ret
= snprintf(mntentry
, len
, "%s %s none bind,optional,%s,%s 0 0",
349 m
->src
, m
->dest
, m
->options
,
350 is_dir(m
->src
) ? "create=dir" : "create=file");
351 if (ret
< 0 || (size_t)ret
>= len
)
362 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
363 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
366 struct lxc_container
*clone
;
368 clone
= c
->clone(c
, newname
, newpath
, flags
, bdevtype
, NULL
, fssize
,
371 ERROR("Failed to clone");
375 lxc_container_put(clone
);
380 static int do_clone_ephemeral(struct lxc_container
*c
,
381 struct lxc_arguments
*arg
, char **args
, int flags
)
384 char randname
[PATH_MAX
];
387 bool bret
= true, started
= false;
388 char *tmp_buf
= randname
;
389 struct lxc_container
*clone
;
390 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
391 attach_options
.env_policy
= LXC_ATTACH_CLEAR_ENV
;
394 ret
= snprintf(randname
, PATH_MAX
, "%s/%s_XXXXXX", arg
->newpath
, arg
->name
);
395 if (ret
< 0 || ret
>= PATH_MAX
)
398 if (!mkdtemp(randname
))
401 if (chmod(randname
, 0770) < 0) {
402 (void)remove(randname
);
406 arg
->newname
= randname
+ strlen(arg
->newpath
) + 1;
409 clone
= c
->clone(c
, arg
->newname
, arg
->newpath
, flags
,
410 arg
->bdevtype
, NULL
, arg
->fssize
, args
);
415 premount
= mount_tmpfs(arg
->name
, arg
->newname
, arg
->newpath
, arg
);
417 goto destroy_and_put
;
419 bret
= clone
->set_config_item(clone
, "lxc.hook.pre-mount", premount
);
422 goto destroy_and_put
;
426 if (!clone
->set_config_item(clone
, "lxc.ephemeral", "1"))
427 goto destroy_and_put
;
429 /* allocate and create random upper- and workdirs for overlay mounts */
430 if (mk_rand_ovl_dirs(mnt_table
, mnt_table_size
, arg
) < 0)
431 goto destroy_and_put
;
433 /* allocate and set mount entries */
434 struct mnts
*n
= NULL
;
435 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
436 char *mntentry
= NULL
;
438 mntentry
= set_mnt_entry(n
);
440 goto destroy_and_put
;
442 bret
= clone
->set_config_item(clone
, "lxc.mount.entry", mntentry
);
445 goto destroy_and_put
;
448 if (!clone
->save_config(clone
, NULL
))
449 goto destroy_and_put
;
452 printf("Created %s as clone of %s\n", arg
->newname
, arg
->name
);
454 if (arg
->tmpfs
&& !my_args
.quiet
)
455 printf("Container is placed on tmpfs.\nRebooting will cause "
456 "all changes made to it to be lost!\n");
458 if (!arg
->daemonize
&& arg
->argc
) {
459 clone
->want_daemonize(clone
, true);
461 } else if (!arg
->daemonize
) {
462 clone
->want_daemonize(clone
, false);
465 started
= clone
->start(clone
, 0, NULL
);
467 goto destroy_and_put
;
469 if (arg
->daemonize
&& arg
->argc
) {
470 ret
= clone
->attach_run_wait(clone
, &attach_options
, arg
->argv
[0], (const char *const *)arg
->argv
);
472 goto destroy_and_put
;
473 clone
->shutdown(clone
, -1);
477 lxc_container_put(clone
);
482 clone
->shutdown(clone
, -1);
484 ret
= clone
->get_config_item(clone
, "lxc.ephemeral", tmp_buf
, PATH_MAX
);
485 if (ret
> 0 && strcmp(tmp_buf
, "0"))
486 clone
->destroy(clone
);
489 lxc_container_put(clone
);
493 static int do_clone_rename(struct lxc_container
*c
, char *newname
)
495 if (!c
->rename(c
, newname
)) {
496 ERROR("Renaming container %s to %s failed", c
->name
, newname
);
503 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
510 ret
= do_clone_ephemeral(c
, &my_args
, args
, flags
);
513 ret
= do_clone_rename(c
, my_args
.newname
);
516 ret
= do_clone(c
, my_args
.newname
, my_args
.newpath
, flags
,
517 my_args
.bdevtype
, my_args
.fssize
, my_args
.task
,
525 static void free_mnts()
528 struct mnts
*n
= NULL
;
530 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
543 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
)
545 char *subopts
= NULL
;
546 char *mntparameters
= NULL
;
567 args
->task
= DESTROY
;
571 if (parse_mntsubopts(subopts
, mount_keys
, mntparameters
) < 0)
575 if (strncmp(arg
, "overlay", strlen(arg
)) == 0)
577 args
->bdevtype
= arg
;
583 args
->fssize
= get_fssize(optarg
);
599 static int parse_bind_mnt(char *mntstring
, enum mnttype type
)
602 char **mntarray
= NULL
;
603 struct mnts
*m
= NULL
;
605 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
609 mntarray
= lxc_string_split(mntstring
, ':');
613 m
->src
= construct_path(mntarray
[0], true);
617 len
= lxc_array_len((void **)mntarray
);
618 if (len
== 1) { /* bind=src */
619 m
->dest
= construct_path(mntarray
[0], false);
620 } else if (len
== 2) { /* bind=src:option or bind=src:dest */
621 if (strncmp(mntarray
[1], "rw", strlen(mntarray
[1])) == 0)
622 m
->options
= strdup("rw");
624 if (strncmp(mntarray
[1], "ro", strlen(mntarray
[1])) == 0)
625 m
->options
= strdup("ro");
628 m
->dest
= construct_path(mntarray
[0], false);
630 m
->dest
= construct_path(mntarray
[1], false);
631 } else if (len
== 3) { /* bind=src:dest:option */
632 m
->dest
= construct_path(mntarray
[1], false);
633 m
->options
= strdup(mntarray
[2]);
635 printf("Excess elements in mount specification\n");
642 m
->options
= strdup("rw");
644 if (!m
->options
|| (strncmp(m
->options
, "rw", strlen(m
->options
)) != 0 &&
645 strncmp(m
->options
, "ro", strlen(m
->options
)) != 0))
648 lxc_free_array((void **)mntarray
, free
);
653 lxc_free_array((void **)mntarray
, free
);
657 static int parse_mntsubopts(char *subopts
, char *const *keys
, char *mntparameters
)
659 while (*subopts
!= '\0') {
660 switch (getsubopt(&subopts
, keys
, &mntparameters
)) {
662 if (parse_bind_mnt(mntparameters
, LXC_MNT_BIND
) < 0)
666 if (parse_ovl_mnt(mntparameters
, LXC_MNT_OVL
) < 0)
677 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
)
680 char **mntarray
= NULL
;
683 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
687 mntarray
= lxc_string_split(mntstring
, ':');
691 m
->src
= construct_path(mntarray
[0], true);
695 len
= lxc_array_len((void **)mntarray
);
696 if (len
== 1) /* overlay=src */
697 m
->dest
= construct_path(mntarray
[0], false);
698 else if (len
== 2) /* overlay=src:dest */
699 m
->dest
= construct_path(mntarray
[1], false);
701 printf("Excess elements in mount specification\n");
706 lxc_free_array((void **)mntarray
, free
);
711 lxc_free_array((void **)mntarray
, free
);
715 /* For ephemeral snapshots backed by the overlay filesystem, this function
716 * mounts a fresh tmpfs over the containers directory if the user requests it.
717 * Because we mount a fresh tmpfs over the directory of the container the
718 * updated /etc/hostname file created during the clone residing in the upperdir
719 * (currently named "delta0" by default) will be hidden. Hence, if the user
720 * requests that the old name is not to be kept for the clone, we recreate this
721 * file on the tmpfs. This should be all that is required to restore the exact
722 * behaviour we would get with a normal clone.
724 static char *mount_tmpfs(const char *oldname
, const char *newname
,
725 const char *path
, struct lxc_arguments
*arg
)
730 char *premount
= NULL
;
733 if (arg
->tmpfs
&& arg
->keepdata
) {
735 "A container can only be placed on a tmpfs when the "
736 "overlay storage driver is used");
740 if (arg
->tmpfs
&& !arg
->bdevtype
) {
741 arg
->bdevtype
= "overlayfs";
742 } else if (arg
->tmpfs
&& arg
->bdevtype
&&
743 strncmp(arg
->bdevtype
, "overlayfs", strlen(arg
->bdevtype
)) != 0) {
745 "A container can only be placed on a tmpfs when the "
746 "overlay storage driver is used");
750 len
= strlen(path
) + strlen(newname
) + strlen("pre-start-XXXXXX") + /* //\0 */ 3;
751 premount
= malloc(len
);
755 ret
= snprintf(premount
, len
, "%s/%s/pre-start-XXXXXX", path
, newname
);
756 if (ret
< 0 || (size_t)ret
>= len
)
760 fd
= mkstemp(premount
);
765 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
)) {
766 ERROR("Failed to set close-on-exec on file descriptor");
770 if (chmod(premount
, 0755) < 0)
773 fp
= fdopen(fd
, "r+");
778 ret
= fprintf(fp
, "#! /bin/sh\n"
779 "mount -n -t tmpfs -o mode=0755 none %s/%s\n",
784 if (!arg
->keepname
) {
785 ret
= fprintf(fp
, "mkdir -p %s/%s/delta0/etc\n"
786 "echo %s > %s/%s/delta0/etc/hostname\n",
787 path
, newname
, newname
, path
, newname
);