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.
33 #include <sys/types.h>
36 #include <lxc/lxccontainer.h>
38 #include "arguments.h"
39 #include "tool_utils.h"
41 #ifndef HAVE_GETSUBOPT
42 #include "include/getsubopt.h"
52 enum mnttype mnt_type
;
61 static unsigned int mnt_table_size
= 0;
62 static struct mnts
*mnt_table
= NULL
;
64 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
);
66 static const struct option my_longopts
[] = {
67 { "newname", required_argument
, 0, 'N'},
68 { "newpath", required_argument
, 0, 'p'},
69 { "rename", no_argument
, 0, 'R'},
70 { "snapshot", no_argument
, 0, 's'},
71 { "foreground", no_argument
, 0, 'F'},
72 { "daemon", no_argument
, 0, 'd'},
73 { "ephemeral", no_argument
, 0, 'e'},
74 { "mount", required_argument
, 0, 'm'},
75 { "backingstorage", required_argument
, 0, 'B'},
76 { "fssize", required_argument
, 0, 'L'},
77 { "keepdata", no_argument
, 0, 'D'},
78 { "keepname", no_argument
, 0, 'K'},
79 { "keepmac", no_argument
, 0, 'M'},
80 { "tmpfs", no_argument
, 0, 't'},
85 static char *const keys
[] = {
86 [LXC_MNT_BIND
] = "bind",
87 [LXC_MNT_AUFS
] = "aufs",
88 [LXC_MNT_OVL
] = "overlay",
92 static struct lxc_arguments my_args
= {
93 .progname
= "lxc-copy",
95 --name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\
96 --name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,aufs,overlay}=/src:/dest] -- hook options\n\
97 --name=NAME [-P lxcpath] -N newname -R\n\
99 lxc-copy clone a container\n\
102 -n, --name=NAME NAME of the container\n\
103 -N, --newname=NEWNAME NEWNAME for the restored container\n\
104 -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
105 -R, --rename rename container\n\
106 -s, --snapshot create snapshot instead of clone\n\
107 -F, --foreground start with current tty attached to /dev/console\n\
108 -d, --daemon daemonize the container (default)\n\
109 -e, --ephemeral start ephemeral container\n\
110 -m, --mount directory to mount into container, either \n\
111 {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\
112 -B, --backingstorage=TYPE backingstorage type for the container\n\
113 -t, --tmpfs place ephemeral container on a tmpfs\n\
114 (WARNING: On reboot all changes made to the container will be lost.)\n\
115 -L, --fssize size of the new block device for block device containers\n\
116 -D, --keedata pass together with -e start a persistent snapshot \n\
117 -K, --keepname keep the hostname of the original container\n\
118 -- hook options arguments passed to the hook program\n\
119 -M, --keepmac keep the MAC address of the original container\n\
120 --rcfile=FILE Load configuration file FILE\n",
121 .options
= my_longopts
,
129 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
,
131 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
,
132 struct lxc_arguments
*arg
);
133 static char *construct_path(char *path
, bool as_prefix
);
134 static char *set_mnt_entry(struct mnts
*m
);
135 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
136 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
138 static int do_clone_ephemeral(struct lxc_container
*c
,
139 struct lxc_arguments
*arg
, char **args
,
141 static int do_clone_rename(struct lxc_container
*c
, char *newname
);
142 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
144 static void free_mnts(void);
145 static uint64_t get_fssize(char *s
);
147 /* Place an ephemeral container started with -e flag on a tmpfs. Restrictions
148 * are that you cannot request the data to be kept while placing the container
149 * on a tmpfs and that either overlay or aufs backing storage must be used.
151 static char *mount_tmpfs(const char *oldname
, const char *newname
,
152 const char *path
, struct lxc_arguments
*arg
);
153 static int parse_mntsubopts(char *subopts
, char *const *keys
,
154 char *mntparameters
);
155 static int parse_aufs_mnt(char *mntstring
, enum mnttype type
);
156 static int parse_bind_mnt(char *mntstring
, enum mnttype type
);
157 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
);
159 int main(int argc
, char *argv
[])
161 struct lxc_container
*c
;
164 int ret
= EXIT_FAILURE
;
166 if (lxc_arguments_parse(&my_args
, argc
, argv
))
169 if (!my_args
.log_file
)
170 my_args
.log_file
= "none";
172 log
.name
= my_args
.name
;
173 log
.file
= my_args
.log_file
;
174 log
.level
= my_args
.log_priority
;
175 log
.prefix
= my_args
.progname
;
176 log
.quiet
= my_args
.quiet
;
177 log
.lxcpath
= my_args
.lxcpath
[0];
179 if (lxc_log_init(&log
))
183 if (access(my_args
.lxcpath
[0], O_RDONLY
) < 0) {
185 fprintf(stderr
, "You lack access to %s\n", my_args
.lxcpath
[0]);
190 if (!my_args
.newname
&& !(my_args
.task
== DESTROY
)) {
192 printf("Error: You must provide a NEWNAME for the clone.\n");
196 if (my_args
.task
== SNAP
|| my_args
.task
== DESTROY
)
197 flags
|= LXC_CLONE_SNAPSHOT
;
198 if (my_args
.keepname
)
199 flags
|= LXC_CLONE_KEEPNAME
;
201 flags
|= LXC_CLONE_KEEPMACADDR
;
203 if (!my_args
.newpath
)
204 my_args
.newpath
= (char *)my_args
.lxcpath
[0];
206 c
= lxc_container_new(my_args
.name
, my_args
.lxcpath
[0]);
210 if (my_args
.rcfile
) {
212 if (!c
->load_config(c
, my_args
.rcfile
)) {
213 fprintf(stderr
, "Failed to load rcfile\n");
216 c
->configfile
= strdup(my_args
.rcfile
);
217 if (!c
->configfile
) {
218 fprintf(stderr
, "Out of memory setting new config filename\n");
223 if (!c
->may_control(c
)) {
225 fprintf(stderr
, "Insufficent privileges to control %s\n", c
->name
);
229 if (!c
->is_defined(c
)) {
231 fprintf(stderr
, "Error: container %s is not defined\n", c
->name
);
235 ret
= do_clone_task(c
, my_args
.task
, flags
, &argv
[optind
]);
238 lxc_container_put(c
);
245 static struct mnts
*add_mnt(struct mnts
**mnts
, unsigned int *num
, enum mnttype type
)
249 n
= realloc(*mnts
, (*num
+ 1) * sizeof(struct mnts
));
257 *m
= (struct mnts
) {.mnt_type
= type
};
262 static int mk_rand_ovl_dirs(struct mnts
*mnts
, unsigned int num
, struct lxc_arguments
*arg
)
264 char upperdir
[TOOL_MAXPATHLEN
];
265 char workdir
[TOOL_MAXPATHLEN
];
268 struct mnts
*m
= NULL
;
270 for (i
= 0, m
= mnts
; i
< num
; i
++, m
++) {
271 if ((m
->mnt_type
== LXC_MNT_OVL
) || (m
->mnt_type
== LXC_MNT_AUFS
)) {
272 ret
= snprintf(upperdir
, TOOL_MAXPATHLEN
, "%s/%s/delta#XXXXXX",
273 arg
->newpath
, arg
->newname
);
274 if (ret
< 0 || ret
>= TOOL_MAXPATHLEN
)
276 if (!mkdtemp(upperdir
))
278 m
->upper
= strdup(upperdir
);
283 if (m
->mnt_type
== LXC_MNT_OVL
) {
284 ret
= snprintf(workdir
, TOOL_MAXPATHLEN
, "%s/%s/work#XXXXXX",
285 arg
->newpath
, arg
->newname
);
286 if (ret
< 0 || ret
>= TOOL_MAXPATHLEN
)
288 if (!mkdtemp(workdir
))
290 m
->workdir
= strdup(workdir
);
299 static char *construct_path(char *path
, bool as_prefix
)
301 char **components
= NULL
;
302 char *cleanpath
= NULL
;
304 components
= lxc_normalize_path(path
);
308 cleanpath
= lxc_string_join("/", (const char **)components
, as_prefix
);
309 lxc_free_array((void **)components
, free
);
314 static char *set_mnt_entry(struct mnts
*m
)
316 char *mntentry
= NULL
;
320 if (m
->mnt_type
== LXC_MNT_AUFS
) {
321 len
= strlen(" aufs br==rw:=ro,xino=,create=dir") +
322 2 * strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->upper
) +
323 strlen(m
->workdir
) + 1;
325 mntentry
= malloc(len
);
329 ret
= snprintf(mntentry
, len
, "%s %s aufs br=%s=rw:%s=ro,xino=%s,create=dir",
330 m
->src
, m
->dest
, m
->upper
, m
->src
, m
->workdir
);
331 if (ret
< 0 || (size_t)ret
>= len
)
333 } else if (m
->mnt_type
== LXC_MNT_OVL
) {
334 len
= strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") +
335 2 * strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->upper
) +
336 strlen(m
->workdir
) + 1;
338 mntentry
= malloc(len
);
342 ret
= snprintf(mntentry
, len
, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
343 m
->src
, m
->dest
, m
->src
, m
->upper
, m
->workdir
);
344 if (ret
< 0 || (size_t)ret
>= len
)
346 } else if (m
->mnt_type
== LXC_MNT_BIND
) {
347 len
= strlen(" none bind,optional,, 0 0") +
348 strlen(is_dir(m
->src
) ? "create=dir" : "create=file") +
349 strlen(m
->src
) + strlen(m
->dest
) + strlen(m
->options
) + 1;
351 mntentry
= malloc(len
);
355 ret
= snprintf(mntentry
, len
, "%s %s none bind,optional,%s,%s 0 0",
356 m
->src
, m
->dest
, m
->options
,
357 is_dir(m
->src
) ? "create=dir" : "create=file");
358 if (ret
< 0 || (size_t)ret
>= len
)
369 static int do_clone(struct lxc_container
*c
, char *newname
, char *newpath
,
370 int flags
, char *bdevtype
, uint64_t fssize
, enum task task
,
373 struct lxc_container
*clone
;
375 clone
= c
->clone(c
, newname
, newpath
, flags
, bdevtype
, NULL
, fssize
,
379 fprintf(stderr
, "clone failed\n");
383 lxc_container_put(clone
);
388 static int do_clone_ephemeral(struct lxc_container
*c
,
389 struct lxc_arguments
*arg
, char **args
, int flags
)
392 char randname
[TOOL_MAXPATHLEN
];
395 bool bret
= true, started
= false;
396 char *tmp_buf
= randname
;
397 struct lxc_container
*clone
;
398 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
399 attach_options
.env_policy
= LXC_ATTACH_CLEAR_ENV
;
402 ret
= snprintf(randname
, TOOL_MAXPATHLEN
, "%s/%s_XXXXXX", arg
->newpath
, arg
->name
);
403 if (ret
< 0 || ret
>= TOOL_MAXPATHLEN
)
405 if (!mkdtemp(randname
))
407 if (chmod(randname
, 0770) < 0) {
408 (void)remove(randname
);
411 arg
->newname
= randname
+ strlen(arg
->newpath
) + 1;
414 clone
= c
->clone(c
, arg
->newname
, arg
->newpath
, flags
,
415 arg
->bdevtype
, NULL
, arg
->fssize
, args
);
420 premount
= mount_tmpfs(arg
->name
, arg
->newname
, arg
->newpath
, arg
);
422 goto destroy_and_put
;
424 bret
= clone
->set_config_item(clone
, "lxc.hook.pre-mount", premount
);
427 goto destroy_and_put
;
431 if (!clone
->set_config_item(clone
, "lxc.ephemeral", "1"))
432 goto destroy_and_put
;
434 /* allocate and create random upper- and workdirs for overlay mounts */
435 if (mk_rand_ovl_dirs(mnt_table
, mnt_table_size
, arg
) < 0)
436 goto destroy_and_put
;
438 /* allocate and set mount entries */
439 struct mnts
*n
= NULL
;
440 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
441 char *mntentry
= NULL
;
442 mntentry
= set_mnt_entry(n
);
444 goto destroy_and_put
;
445 bret
= clone
->set_config_item(clone
, "lxc.mount.entry", mntentry
);
448 goto destroy_and_put
;
451 if (!clone
->save_config(clone
, NULL
))
452 goto destroy_and_put
;
455 printf("Created %s as clone of %s\n", arg
->newname
, arg
->name
);
457 if (arg
->tmpfs
&& !my_args
.quiet
)
458 printf("Container is placed on tmpfs.\nRebooting will cause "
459 "all changes made to it to be lost!\n");
461 if (!arg
->daemonize
&& arg
->argc
) {
462 clone
->want_daemonize(clone
, true);
464 } else if (!arg
->daemonize
) {
465 clone
->want_daemonize(clone
, false);
468 started
= clone
->start(clone
, 0, NULL
);
470 goto destroy_and_put
;
472 if (arg
->daemonize
&& arg
->argc
) {
473 ret
= clone
->attach_run_wait(clone
, &attach_options
, arg
->argv
[0], (const char *const *)arg
->argv
);
475 goto destroy_and_put
;
476 clone
->shutdown(clone
, -1);
480 lxc_container_put(clone
);
485 clone
->shutdown(clone
, -1);
486 ret
= clone
->get_config_item(clone
, "lxc.ephemeral", tmp_buf
, TOOL_MAXPATHLEN
);
487 if (ret
> 0 && strcmp(tmp_buf
, "0"))
488 clone
->destroy(clone
);
490 lxc_container_put(clone
);
494 static int do_clone_rename(struct lxc_container
*c
, char *newname
)
496 if (!c
->rename(c
, newname
)) {
497 fprintf(stderr
, "Error: Renaming container %s to %s failed\n", c
->name
, newname
);
504 static int do_clone_task(struct lxc_container
*c
, enum task task
, int flags
,
511 ret
= do_clone_ephemeral(c
, &my_args
, args
, flags
);
514 ret
= do_clone_rename(c
, my_args
.newname
);
517 ret
= do_clone(c
, my_args
.newname
, my_args
.newpath
, flags
,
518 my_args
.bdevtype
, my_args
.fssize
, my_args
.task
,
526 static void free_mnts()
529 struct mnts
*n
= NULL
;
531 for (i
= 0, n
= mnt_table
; i
< mnt_table_size
; i
++, n
++) {
543 /* we pass fssize in bytes */
544 static uint64_t get_fssize(char *s
)
549 ret
= strtoull(s
, &end
, 0);
552 fprintf(stderr
, "Invalid blockdev size '%s', using default size\n", s
);
555 while (isblank(*end
))
558 ret
*= 1024ULL * 1024ULL; /* MB by default */
559 } else if (*end
== 'b' || *end
== 'B') {
561 } else if (*end
== 'k' || *end
== 'K') {
563 } else if (*end
== 'm' || *end
== 'M') {
564 ret
*= 1024ULL * 1024ULL;
565 } else if (*end
== 'g' || *end
== 'G') {
566 ret
*= 1024ULL * 1024ULL * 1024ULL;
567 } else if (*end
== 't' || *end
== 'T') {
568 ret
*= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
571 fprintf(stderr
, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end
, s
);
578 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
)
580 char *subopts
= NULL
;
581 char *mntparameters
= NULL
;
602 args
->task
= DESTROY
;
606 if (parse_mntsubopts(subopts
, keys
, mntparameters
) < 0)
610 if (strcmp(arg
, "overlay") == 0)
612 args
->bdevtype
= arg
;
618 args
->fssize
= get_fssize(optarg
);
634 static int parse_aufs_mnt(char *mntstring
, enum mnttype type
)
637 const char *xinopath
= "/dev/shm/aufs.xino";
638 char **mntarray
= NULL
;
639 struct mnts
*m
= NULL
;
641 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
645 mntarray
= lxc_string_split(mntstring
, ':');
649 m
->src
= construct_path(mntarray
[0], true);
653 len
= lxc_array_len((void **)mntarray
);
654 if (len
== 1) /* aufs=src */
655 m
->dest
= construct_path(mntarray
[0], false);
656 else if (len
== 2) /* aufs=src:dest */
657 m
->dest
= construct_path(mntarray
[1], false);
659 printf("Excess elements in mount specification\n");
664 m
->workdir
= strdup(xinopath
);
668 lxc_free_array((void **)mntarray
, free
);
673 lxc_free_array((void **)mntarray
, free
);
677 static int parse_bind_mnt(char *mntstring
, enum mnttype type
)
680 char **mntarray
= NULL
;
681 struct mnts
*m
= 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) { /* bind=src */
697 m
->dest
= construct_path(mntarray
[0], false);
698 } else if (len
== 2) { /* bind=src:option or bind=src:dest */
699 if (strncmp(mntarray
[1], "rw", strlen(mntarray
[1])) == 0)
700 m
->options
= strdup("rw");
702 if (strncmp(mntarray
[1], "ro", strlen(mntarray
[1])) == 0)
703 m
->options
= strdup("ro");
706 m
->dest
= construct_path(mntarray
[0], false);
708 m
->dest
= construct_path(mntarray
[1], false);
709 } else if (len
== 3) { /* bind=src:dest:option */
710 m
->dest
= construct_path(mntarray
[1], false);
711 m
->options
= strdup(mntarray
[2]);
713 printf("Excess elements in mount specification\n");
720 m
->options
= strdup("rw");
722 if (!m
->options
|| (strncmp(m
->options
, "rw", strlen(m
->options
)) &&
723 strncmp(m
->options
, "ro", strlen(m
->options
))))
726 lxc_free_array((void **)mntarray
, free
);
731 lxc_free_array((void **)mntarray
, free
);
735 static int parse_mntsubopts(char *subopts
, char *const *keys
, char *mntparameters
)
737 while (*subopts
!= '\0') {
738 switch (getsubopt(&subopts
, keys
, &mntparameters
)) {
740 if (parse_bind_mnt(mntparameters
, LXC_MNT_BIND
) < 0)
744 if (parse_ovl_mnt(mntparameters
, LXC_MNT_OVL
) < 0)
748 if (parse_aufs_mnt(mntparameters
, LXC_MNT_AUFS
) < 0)
758 static int parse_ovl_mnt(char *mntstring
, enum mnttype type
)
761 char **mntarray
= NULL
;
764 m
= add_mnt(&mnt_table
, &mnt_table_size
, type
);
768 mntarray
= lxc_string_split(mntstring
, ':');
772 m
->src
= construct_path(mntarray
[0], true);
776 len
= lxc_array_len((void **)mntarray
);
777 if (len
== 1) /* overlay=src */
778 m
->dest
= construct_path(mntarray
[0], false);
779 else if (len
== 2) /* overlay=src:dest */
780 m
->dest
= construct_path(mntarray
[1], false);
782 printf("Excess elements in mount specification\n");
787 lxc_free_array((void **)mntarray
, free
);
792 lxc_free_array((void **)mntarray
, free
);
796 /* For ephemeral snapshots backed by overlay or aufs filesystems, this function
797 * mounts a fresh tmpfs over the containers directory if the user requests it.
798 * Because we mount a fresh tmpfs over the directory of the container the
799 * updated /etc/hostname file created during the clone residing in the upperdir
800 * (currently named "delta0" by default) will be hidden. Hence, if the user
801 * requests that the old name is not to be kept for the clone, we recreate this
802 * file on the tmpfs. This should be all that is required to restore the exact
803 * behaviour we would get with a normal clone.
805 static char *mount_tmpfs(const char *oldname
, const char *newname
,
806 const char *path
, struct lxc_arguments
*arg
)
810 char *premount
= NULL
;
813 if (arg
->tmpfs
&& arg
->keepdata
) {
814 fprintf(stderr
, "%s\n", "A container can only be placed on a "
815 "tmpfs when storage backend is overlay "
820 if (arg
->tmpfs
&& !arg
->bdevtype
) {
821 arg
->bdevtype
= "overlayfs";
822 } else if (arg
->tmpfs
&& arg
->bdevtype
&& strcmp(arg
->bdevtype
, "overlayfs") && strcmp(arg
->bdevtype
, "aufs")) {
823 fprintf(stderr
, "%s\n", "A container can only be placed on a "
824 "tmpfs when storage backend is overlay "
829 len
= strlen(path
) + strlen(newname
) + strlen("pre-start-XXXXXX") + /* //\0 */ 3;
830 premount
= malloc(len
);
834 ret
= snprintf(premount
, len
, "%s/%s/pre-start-XXXXXX", path
, newname
);
835 if (ret
< 0 || (size_t)ret
>= len
)
838 fd
= mkstemp(premount
);
842 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
)) {
843 fprintf(stderr
, "Failed to set close-on-exec on file descriptor.\n");
847 if (chmod(premount
, 0755) < 0)
850 fp
= fdopen(fd
, "r+");
855 ret
= fprintf(fp
, "#! /bin/sh\n"
856 "mount -n -t tmpfs -o mode=0755 none %s/%s\n",
861 if (!arg
->keepname
) {
862 ret
= fprintf(fp
, "mkdir -p %s/%s/delta0/etc\n"
863 "echo %s > %s/%s/delta0/etc/hostname\n",
864 path
, newname
, newname
, path
, newname
);