From: 2xsec Date: Sat, 14 Jul 2018 11:00:38 +0000 (+0900) Subject: tools: lxc-unshare: apply argument parser of lxc and log system of lxc X-Git-Tag: lxc-3.1.0~207^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=727b9b165986fb5b180932dbcada8ad5b7af9131;p=thirdparty%2Flxc.git tools: lxc-unshare: apply argument parser of lxc and log system of lxc Signed-off-by: 2xsec --- diff --git a/src/lxc/namespace.c b/src/lxc/namespace.c index b6e3938b1..9512bfe5e 100644 --- a/src/lxc/namespace.c +++ b/src/lxc/namespace.c @@ -115,6 +115,7 @@ pid_t lxc_raw_clone(unsigned long flags) : "=r"(in_child), "=r"(child_pid) : "i"(__NR_clone), "r"(flags | SIGCHLD) : "%o1", "%o0", "%g1"); + if (in_child) return 0; else @@ -174,6 +175,7 @@ const struct ns_info ns_info[LXC_NS_MAX] = { int lxc_namespace_2_cloneflag(const char *namespace) { int i; + for (i = 0; i < LXC_NS_MAX; i++) if (!strcasecmp(ns_info[i].proc_name, namespace)) return ns_info[i].clone_flag; @@ -185,6 +187,7 @@ int lxc_namespace_2_cloneflag(const char *namespace) int lxc_namespace_2_ns_idx(const char *namespace) { int i; + for (i = 0; i < LXC_NS_MAX; i++) if (!strcmp(ns_info[i].proc_name, namespace)) return i; @@ -239,7 +242,6 @@ int lxc_fill_namespace_flags(char *flaglist, int *flags) token = strtok_r(flaglist, "|", &saveptr); while (token) { - aflag = lxc_namespace_2_cloneflag(token); if (aflag < 0) return -1; @@ -248,5 +250,6 @@ int lxc_fill_namespace_flags(char *flaglist, int *flags) token = strtok_r(NULL, "|", &saveptr); } + return 0; } diff --git a/src/lxc/tools/arguments.c b/src/lxc/tools/arguments.c index daff4d816..30bb5dbb2 100644 --- a/src/lxc/tools/arguments.c +++ b/src/lxc/tools/arguments.c @@ -50,7 +50,6 @@ static int build_shortopts(const struct option *a_options, char *a_shortopts, return -1; for (opt = a_options; opt->name; opt++) { - if (!isascii(opt->val)) continue; @@ -163,6 +162,7 @@ See the %s man page for further information.\n\n", if (args->helpfn) args->helpfn(args); + exit(code); } @@ -183,6 +183,7 @@ static int lxc_arguments_lxcpath_add(struct lxc_arguments *args, lxc_error(args, "no memory"); return -ENOMEM; } + args->lxcpath[args->lxcpath_cnt++] = lxcpath; return 0; } @@ -207,6 +208,7 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, c = getopt_long(argc, argv, shortopts, args->options, &index); if (c == -1) break; + switch (c) { case 'n': args->name = optarg; @@ -261,7 +263,8 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, } /* Check the command options */ - if (!args->name && strcmp(args->progname, "lxc-autostart") != 0) { + if (!args->name && strncmp(args->progname, "lxc-autostart", strlen(args->progname)) != 0 + && strncmp(args->progname, "lxc-unshare", strlen(args->progname)) != 0) { if (args->argv) { args->name = argv[optind]; optind++; @@ -277,9 +280,11 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, if (args->checker) ret = args->checker(args); + error: if (ret) lxc_error(args, "could not parse command line"); + return ret; } @@ -333,4 +338,4 @@ bool lxc_setup_shared_ns(struct lxc_arguments *args, struct lxc_container *c) } return true; -} +} \ No newline at end of file diff --git a/src/lxc/tools/arguments.h b/src/lxc/tools/arguments.h index 8acebc55c..43b3a86e2 100644 --- a/src/lxc/tools/arguments.h +++ b/src/lxc/tools/arguments.h @@ -94,7 +94,7 @@ struct lxc_arguments { char *rbdname, *rbdpool; char *zfsroot, *lowerdir, *dir; - /* lxc-execute */ + /* lxc-execute and lxc-unshare */ uid_t uid; gid_t gid; @@ -137,6 +137,12 @@ struct lxc_arguments { /* lxc-copy */ bool tmpfs; + /* lxc-unshare */ + int flags; + int want_default_mounts; + const char *want_hostname; + bool setuid; + /* remaining arguments */ char *const *argv; int argc; diff --git a/src/lxc/tools/lxc_unshare.c b/src/lxc/tools/lxc_unshare.c index 85b9d4c02..c1ec99611 100644 --- a/src/lxc/tools/lxc_unshare.c +++ b/src/lxc/tools/lxc_unshare.c @@ -41,10 +41,112 @@ #include "arguments.h" #include "caps.h" -#include "conf.h" +#include "log.h" #include "namespace.h" #include "utils.h" +lxc_log_define(lxc_unshare, lxc); + +struct start_arg { + char *const *args; + int flags; + uid_t uid; + bool setuid; + int want_default_mounts; + int wait_fd; + const char *want_hostname; +}; + +struct my_iflist +{ + char *mi_ifname; + struct my_iflist *mi_next; +}; + +static int my_parser(struct lxc_arguments *args, int c, char *arg); +static inline int sethostname_including_android(const char *name, size_t len); +static int get_namespace_flags(char *namespaces); +static bool lookup_user(const char *optarg, uid_t *uid); +static int mount_fs(const char *source, const char *target, const char *type); +static void lxc_setup_fs(void); +static int do_start(void *arg); + +static struct my_iflist *tmpif, *my_iflist = NULL; + +static const struct option my_longopts[] = { + {"namespaces", required_argument, 0, 's'}, + {"user", required_argument, 0, 'u'}, + {"hostname", required_argument, 0, 'H'}, + {"ifname", required_argument, 0, 'i'}, + {"daemon", no_argument, 0, 'd'}, + {"remount", no_argument, 0, 'M'}, + LXC_COMMON_OPTIONS +}; + +static struct lxc_arguments my_args = { + .progname = "lxc-unshare", + .help = "\ +-s NAMESPACES COMMAND\n\ +\n\ +lxc-unshare run a COMMAND in a new set of NAMESPACES\n\ +\n\ +Options :\n\ + -s, --namespaces=FLAGS\n\ + ORed list of flags to unshare:\n\ + MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n\ + -u, --user=USERID\n\ + new id to be set if -s USER is specified\n\ + -H, --hostname=HOSTNAME\n\ + Set the hostname in the container\n\ + -i, --ifname=IFNAME\n\ + Interface name to be moved into container (presumably with NETWORK unsharing set)\n\ + -d, --daemon Daemonize (do not wait for container to exit)\n\ + -M, --remount Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n\ +", + .options = my_longopts, + .parser = my_parser, + .checker = NULL, + .daemonize = 0, + .pidfile = NULL, +}; + +static int my_parser(struct lxc_arguments *args, int c, char *arg) +{ + switch (c) { + case 's': + args->flags = get_namespace_flags(arg); + if (args->flags < 0) + return -1; + break; + case 'u': + if (!lookup_user(arg, &args->uid)) + return -1; + + args->setuid = true; + break; + case 'H': + args->want_hostname = arg; + break; + case 'i': + if (!(tmpif = malloc(sizeof(*tmpif)))) { + SYSERROR("Failed to malloc()"); + return -1; + } + + tmpif->mi_ifname = arg; + tmpif->mi_next = my_iflist; + my_iflist = tmpif; + break; + case 'd': + args->daemonize = 1; + break; + case 'M': + args->want_default_mounts = 1; + break; + } + return 0; +} + /* Define sethostname() if missing from the C library also workaround some * quirky with having this defined in multiple places. */ @@ -62,25 +164,17 @@ static inline int sethostname_including_android(const char *name, size_t len) #endif } -struct my_iflist +static int get_namespace_flags(char *namespaces) { - char *mi_ifname; - struct my_iflist *mi_next; -}; + int flags = 0; -static void usage(char *cmd) -{ - fprintf(stderr, "%s 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 : new id to be set if -s USER is specified\n"); - fprintf(stderr, "\t -i : Interface name to be moved into container (presumably with NETWORK unsharing set)\n"); - fprintf(stderr, "\t -H : 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(EXIT_SUCCESS); + if (lxc_namespace_2_std_identifiers(namespaces) < 0) + return -1; + + if (lxc_fill_namespace_flags(namespaces, &flags) < 0) + return -1; + + return flags; } static bool lookup_user(const char *optarg, uid_t *uid) @@ -113,9 +207,9 @@ static bool lookup_user(const char *optarg, uid_t *uid) ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) - fprintf(stderr, "Could not find matched password record\n"); + SYSERROR("Could not find matched password record"); - fprintf(stderr, "Invalid username %s\n", name); + SYSERROR("Invalid username \"%s\"", name); free(buf); return false; } @@ -125,9 +219,9 @@ static bool lookup_user(const char *optarg, uid_t *uid) ret = getpwuid_r(*uid, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) - fprintf(stderr, "Could not find matched password record\n"); + SYSERROR("Could not find matched password record"); - fprintf(stderr, "Invalid uid %u\n", *uid); + SYSERROR("Invalid uid : %u", *uid); free(buf); return false; } @@ -137,16 +231,6 @@ static bool lookup_user(const char *optarg, uid_t *uid) return true; } -struct start_arg { - char ***args; - int *flags; - uid_t *uid; - bool setuid; - int want_default_mounts; - int wait_fd; - const char *want_hostname; -}; - static int mount_fs(const char *source, const char *target, const char *type) { /* the umount may fail */ @@ -183,170 +267,140 @@ static int do_start(void *arg) int ret; uint64_t wait_val; 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; + char *const *args = start_arg->args; const char *want_hostname = start_arg->want_hostname; - int wait_fd = start_arg->wait_fd; if (start_arg->setuid) { /* waiting until uid maps is set */ - ret = read(wait_fd, &wait_val, sizeof(wait_val)); + ret = read(start_arg->wait_fd, &wait_val, sizeof(wait_val)); if (ret == -1) { - close(wait_fd); - fprintf(stderr, "Failed to read eventfd\n"); + SYSERROR("Failed to read eventfd"); + close(start_arg->wait_fd); _exit(EXIT_FAILURE); } } - if ((flags & CLONE_NEWNS) && want_default_mounts) + if ((start_arg->flags & CLONE_NEWNS) && start_arg->want_default_mounts) lxc_setup_fs(); - if ((flags & CLONE_NEWUTS) && want_hostname) + if ((start_arg->flags & CLONE_NEWUTS) && want_hostname) if (sethostname_including_android(want_hostname, strlen(want_hostname)) < 0) { - fprintf(stderr, "Failed to set hostname %s: %s\n", want_hostname, strerror(errno)); + SYSERROR("Failed to set hostname %s", want_hostname); _exit(EXIT_FAILURE); } /* Setuid is useful even without a new user id space. */ - if (start_arg->setuid && setuid(uid)) { - fprintf(stderr, "Failed to set uid %d: %s\n", uid, strerror(errno)); + if (start_arg->setuid && setuid(start_arg->uid)) { + SYSERROR("Failed to set uid %d", start_arg->uid); _exit(EXIT_FAILURE); } execvp(args[0], args); - fprintf(stderr, "Failed to exec: '%s': %s\n", args[0], strerror(errno)); + SYSERROR("Failed to exec: '%s'", args[0]); return 1; } int main(int argc, char *argv[]) { - char **args; - int opt; int ret; - char *namespaces = NULL; - int flags = 0, daemonize = 0; - uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */ pid_t pid; - uint64_t wait_val = 1; - 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(EXIT_FAILURE); - } + struct lxc_log log; + struct start_arg start_arg; - 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)) - exit(EXIT_FAILURE); - start_arg.setuid = true; - } - } + if (lxc_caps_init()) + exit(EXIT_FAILURE); - if (argv[optind] == NULL) { - fprintf(stderr, "A command to execute in the new namespace is required\n"); + if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); - } - args = &argv[optind]; + /* 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]; - ret = lxc_caps_init(); - if (ret) - exit(EXIT_FAILURE); + if (lxc_log_init(&log)) + exit(EXIT_FAILURE); + } - if (lxc_namespace_2_std_identifiers(namespaces) < 0) - usage(argv[0]); + if (*my_args.argv == NULL) { + ERROR("A command to execute in the new namespace is required"); + exit(EXIT_FAILURE); + } - ret = lxc_fill_namespace_flags(namespaces, &flags); - if (ret) - usage(argv[0]); + if (my_args.flags == 0) { + ERROR("A namespace to execute command is required"); + exit(EXIT_FAILURE); + } - if (!(flags & CLONE_NEWNET) && my_iflist) { - fprintf(stderr, "-i needs -s NETWORK option\n"); + if (!(my_args.flags & CLONE_NEWNET) && my_iflist) { + ERROR("-i needs -s NETWORK option"); exit(EXIT_FAILURE); } - if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) { - fprintf(stderr, "-H needs -s UTSNAME option\n"); + if (!(my_args.flags & CLONE_NEWUTS) && my_args.want_hostname) { + ERROR("-H needs -s UTSNAME option"); exit(EXIT_FAILURE); } - if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) { - fprintf(stderr, "-M needs -s MOUNT option\n"); + if (!(my_args.flags & CLONE_NEWNS) && my_args.want_default_mounts) { + ERROR("-M needs -s MOUNT option"); exit(EXIT_FAILURE); } - if (start_arg.setuid) { + if (my_args.setuid) { start_arg.wait_fd = eventfd(0, EFD_CLOEXEC); if (start_arg.wait_fd < 0) { - fprintf(stderr, "Failed to create eventfd\n"); + SYSERROR("Failed to create eventfd"); exit(EXIT_FAILURE); } } - pid = lxc_clone(do_start, &start_arg, flags); + /* set start arguments for lxc_clone from lxc_arguments */ + start_arg.args = my_args.argv; + start_arg.uid = my_args.uid; /* valid only if (flags & CLONE_NEWUSER) */ + start_arg.setuid = my_args.setuid; + start_arg.flags = my_args.flags; + start_arg.want_hostname = my_args.want_hostname; + start_arg.want_default_mounts = my_args.want_default_mounts; + start_arg.wait_fd = -1; + + pid = lxc_clone(do_start, &start_arg, my_args.flags); if (pid < 0) { - fprintf(stderr, "Failed to clone\n"); + ERROR("Failed to clone"); exit(EXIT_FAILURE); } - if (start_arg.setuid) { + if (my_args.setuid) { + uint64_t wait_val = 1; /* enough space to accommodate uids */ char *umap = (char *)alloca(100); /* create new uid mapping using current UID and the one * specified as parameter */ - ret = snprintf(umap, 100, "%d %d 1\n" , *(start_arg.uid), getuid()); + ret = snprintf(umap, 100, "%d %d 1\n" , my_args.uid, getuid()); if (ret < 0 || ret >= 100) { + ERROR("snprintf is failed"); close(start_arg.wait_fd); - fprintf(stderr, "snprintf is failed\n"); exit(EXIT_FAILURE); } ret = write_id_mapping(ID_TYPE_UID, pid, umap, strlen(umap)); if (ret < 0) { + ERROR("Failed to map uid"); close(start_arg.wait_fd); - fprintf(stderr, "uid mapping failed\n"); exit(EXIT_FAILURE); } ret = write(start_arg.wait_fd, &wait_val, sizeof(wait_val)); if (ret < 0) { + SYSERROR("Failed to write eventfd"); close(start_arg.wait_fd); - fprintf(stderr, "Failed to write eventfd\n"); exit(EXIT_FAILURE); } } @@ -356,10 +410,11 @@ int main(int argc, char *argv[]) pid_t pid; pid = fork(); - if (pid < 0) - fprintf(stderr, "Failed to move network device " - "\"%s\" to network namespace\n", - tmpif->mi_ifname); + if (pid < 0) { + SYSERROR("Failed to move network device \"%s\" to network namespace", + tmpif->mi_ifname); + continue; + } if (pid == 0) { char buf[256]; @@ -373,20 +428,19 @@ int main(int argc, char *argv[]) } if (wait_for_pid(pid) != 0) - fprintf(stderr, "Could not move interface %s " - "into container %d: %s\n", - tmpif->mi_ifname, pid, strerror(errno)); + SYSERROR("Could not move interface \"%s\" into container %d", + tmpif->mi_ifname, pid); } } - if (daemonize) + if (my_args.daemonize) exit(EXIT_SUCCESS); if (wait_for_pid(pid) != 0) { - fprintf(stderr, "Failed to wait for '%d'\n", pid); + SYSERROR("Failed to wait for '%d'", pid); exit(EXIT_FAILURE); } /* Call exit() directly on this function because it retuns an exit code. */ exit(EXIT_SUCCESS); -} +} \ No newline at end of file