chiark / gitweb /
shared: utf8 - support ucs4 -> utf8
[elogind.git] / src / machine / machinectl.c
index a62ffe30ac385f0ea70e3c7be8ff67248de1e8c2..cde0c6af137a848b24d1b4ec184b5802bc67e8b6 100644 (file)
@@ -50,6 +50,7 @@
 #include "path-util.h"
 #include "mkdir.h"
 #include "copy.h"
+#include "verbs.h"
 
 static char **arg_property = NULL;
 static bool arg_all = false;
@@ -72,13 +73,17 @@ static void pager_open_if_enabled(void) {
         pager_open(false);
 }
 
-static int list_machines(sd_bus *bus, char **args, unsigned n) {
+static int list_machines(int argc, char *argv[], void *userdata) {
+
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         const char *name, *class, *service, *object;
+        sd_bus *bus = userdata;
         unsigned k = 0;
         int r;
 
+        assert(bus);
+
         pager_open_if_enabled();
 
         r = sd_bus_call_method(
@@ -120,6 +125,101 @@ static int list_machines(sd_bus *bus, char **args, unsigned n) {
         return 0;
 }
 
+typedef struct ImageInfo {
+        const char *name;
+        const char *type;
+        bool read_only;
+} ImageInfo;
+
+static int compare_image_info(const void *a, const void *b) {
+        const ImageInfo *x = a, *y = b;
+
+        return strcmp(x->name, y->name);
+}
+
+static int list_images(int argc, char *argv[], void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        size_t max_name = strlen("NAME"), max_type = strlen("TYPE");
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_free_ ImageInfo *images = NULL;
+        size_t n_images = 0, n_allocated = 0, j;
+        const char *name, *type, *object;
+        sd_bus *bus = userdata;
+        int read_only;
+        int r;
+
+        assert(bus);
+
+        pager_open_if_enabled();
+
+        r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.machine1",
+                                "/org/freedesktop/machine1",
+                                "org.freedesktop.machine1.Manager",
+                                "ListImages",
+                                &error,
+                                &reply,
+                                "");
+        if (r < 0) {
+                log_error("Could not get images: %s", bus_error_message(&error, -r));
+                return r;
+        }
+
+        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbo)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        while ((r = sd_bus_message_read(reply, "(ssbo)", &name, &type, &read_only, &object)) > 0) {
+
+                if (name[0] == '.' && !arg_all)
+                        continue;
+
+                if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
+                        return log_oom();
+
+                images[n_images].name = name;
+                images[n_images].type = type;
+                images[n_images].read_only = read_only;
+
+                if (strlen(name) > max_name)
+                        max_name = strlen(name);
+
+                if (strlen(type) > max_type)
+                        max_type = strlen(type);
+
+                n_images++;
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
+
+        if (arg_legend)
+                printf("%-*s %-*s %-3s\n", (int) max_name, "NAME", (int) max_type, "TYPE", "RO");
+
+        for (j = 0; j < n_images; j++) {
+                printf("%-*s %-*s %-3s\n",
+                       (int) max_name, images[j].name,
+                       (int) max_type, images[j].type,
+                       yes_no(images[j].read_only));
+        }
+
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+
+        if (arg_legend)
+                printf("\n%zu images listed.\n", n_images);
+
+        return 0;
+}
+
 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -297,6 +397,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
         char since2[FORMAT_TIMESTAMP_MAX], *s2;
         int ifi = -1;
 
+        assert(bus);
         assert(i);
 
         fputs(strna(i->name), stdout);
@@ -413,6 +514,8 @@ static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_
         MachineStatusInfo info = {};
         int r;
 
+        assert(verb);
+        assert(bus);
         assert(path);
         assert(new_line);
 
@@ -443,6 +546,10 @@ static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_
 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
         int r;
 
+        assert(bus);
+        assert(path);
+        assert(new_line);
+
         if (*new_line)
                 printf("\n");
 
@@ -455,21 +562,21 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
         return r;
 }
 
-static int show(sd_bus *bus, char **args, unsigned n) {
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int show(int argc, char *argv[], void *userdata) {
+
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        int r = 0;
-        unsigned i;
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         bool properties, new_line = false;
+        sd_bus *bus = userdata;
+        int r = 0, i;
 
         assert(bus);
-        assert(args);
 
-        properties = !strstr(args[0], "status");
+        properties = !strstr(argv[0], "status");
 
         pager_open_if_enabled();
 
-        if (properties && n <= 1) {
+        if (properties && argc <= 1) {
 
                 /* If no argument is specified, inspect the manager
                  * itself */
@@ -478,7 +585,7 @@ static int show(sd_bus *bus, char **args, unsigned n) {
                         return r;
         }
 
-        for (i = 1; i < n; i++) {
+        for (i = 1; i < argc; i++) {
                 const char *path = NULL;
 
                 r = sd_bus_call_method(
@@ -489,7 +596,7 @@ static int show(sd_bus *bus, char **args, unsigned n) {
                                         "GetMachine",
                                         &error,
                                         &reply,
-                                        "s", args[i]);
+                                        "s", argv[i]);
                 if (r < 0) {
                         log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
                         return r;
@@ -502,33 +609,34 @@ static int show(sd_bus *bus, char **args, unsigned n) {
                 if (properties)
                         r = show_properties(bus, path, &new_line);
                 else
-                        r = show_info(args[0], bus, path, &new_line);
+                        r = show_info(argv[0], bus, path, &new_line);
         }
 
         return r;
 }
 
-static int kill_machine(sd_bus *bus, char **args, unsigned n) {
+static int kill_machine(int argc, char *argv[], void *userdata) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        unsigned i;
+        sd_bus *bus = userdata;
+        int i;
 
-        assert(args);
+        assert(bus);
 
         if (!arg_kill_who)
                 arg_kill_who = "all";
 
-        for (i = 1; i < n; i++) {
+        for (i = 1; i < argc; i++) {
                 int r;
 
                 r = sd_bus_call_method(
-                                        bus,
-                                        "org.freedesktop.machine1",
-                                        "/org/freedesktop/machine1",
-                                        "org.freedesktop.machine1.Manager",
-                                        "KillMachine",
-                                        &error,
-                                        NULL,
-                                        "ssi", args[i], arg_kill_who, arg_signal);
+                                bus,
+                                "org.freedesktop.machine1",
+                                "/org/freedesktop/machine1",
+                                "org.freedesktop.machine1.Manager",
+                                "KillMachine",
+                                &error,
+                                NULL,
+                                "ssi", argv[i], arg_kill_who, arg_signal);
                 if (r < 0) {
                         log_error("Could not kill machine: %s", bus_error_message(&error, -r));
                         return r;
@@ -538,27 +646,28 @@ static int kill_machine(sd_bus *bus, char **args, unsigned n) {
         return 0;
 }
 
-static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
+static int reboot_machine(int argc, char *argv[], void *userdata) {
         arg_kill_who = "leader";
         arg_signal = SIGINT; /* sysvinit + systemd */
 
-        return kill_machine(bus, args, n);
+        return kill_machine(argc, argv, userdata);
 }
 
-static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
+static int poweroff_machine(int argc, char *argv[], void *userdata) {
         arg_kill_who = "leader";
         arg_signal = SIGRTMIN+4; /* only systemd */
 
-        return kill_machine(bus, args, n);
+        return kill_machine(argc, argv, userdata);
 }
 
-static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
+static int terminate_machine(int argc, char *argv[], void *userdata) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        unsigned i;
+        sd_bus *bus = userdata;
+        int i;
 
-        assert(args);
+        assert(bus);
 
-        for (i = 1; i < n; i++) {
+        for (i = 1; i < argc; i++) {
                 int r;
 
                 r = sd_bus_call_method(
@@ -569,7 +678,7 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
                                 "TerminateMachine",
                                 &error,
                                 NULL,
-                                "s", args[i]);
+                                "s", argv[i]);
                 if (r < 0) {
                         log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
                         return r;
@@ -628,23 +737,21 @@ static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
         return 0;
 }
 
-static int copy_files(sd_bus *bus, char **args, unsigned n) {
+static int copy_files(int argc, char *argv[], void *userdata) {
         char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
         _cleanup_close_ int hostfd = -1;
+        sd_bus *bus = userdata;
         pid_t child, leader;
         bool copy_from;
         siginfo_t si;
         int r;
 
-        if (n > 4) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
+        assert(bus);
 
-        copy_from = streq(args[0], "copy-from");
-        dest = args[3] ?: args[2];
-        host_path = strdupa(copy_from ? dest : args[2]);
-        container_path = strdupa(copy_from ? args[2] : dest);
+        copy_from = streq(argv[0], "copy-from");
+        dest = argv[3] ?: argv[2];
+        host_path = strdupa(copy_from ? dest : argv[2]);
+        container_path = strdupa(copy_from ? argv[2] : dest);
 
         if (!path_is_absolute(container_path)) {
                 log_error("Container path not absolute.");
@@ -659,7 +766,7 @@ static int copy_files(sd_bus *bus, char **args, unsigned n) {
         container_basename = basename(t);
         container_dirname = dirname(container_path);
 
-        r = machine_get_leader(bus, args[1], &leader);
+        r = machine_get_leader(bus, argv[1], &leader);
         if (r < 0)
                 return r;
 
@@ -719,8 +826,9 @@ static int copy_files(sd_bus *bus, char **args, unsigned n) {
         return 0;
 }
 
-static int bind_mount(sd_bus *bus, char **args, unsigned n) {
+static int bind_mount(int argc, char *argv[], void *userdata) {
         char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+        sd_bus *bus = userdata;
         pid_t child, leader;
         const char *dest;
         siginfo_t si;
@@ -729,28 +837,25 @@ static int bind_mount(sd_bus *bus, char **args, unsigned n) {
                 mount_outside_created = false, mount_outside_mounted = false;
         int r;
 
+        assert(bus);
+
         /* One day, when bind mounting /proc/self/fd/n works across
          * namespace boundaries we should rework this logic to make
          * use of it... */
 
-        if (n > 4) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
-
-        dest = args[3] ?: args[2];
+        dest = argv[3] ?: argv[2];
         if (!path_is_absolute(dest)) {
                 log_error("Destination path not absolute.");
                 return -EINVAL;
         }
 
-        p = strappenda("/run/systemd/nspawn/propagate/", args[1], "/");
+        p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
         if (access(p, F_OK) < 0) {
                 log_error("Container does not allow propagation of mount points.");
                 return -ENOTSUP;
         }
 
-        r = machine_get_leader(bus, args[1], &leader);
+        r = machine_get_leader(bus, argv[1], &leader);
         if (r < 0)
                 return r;
 
@@ -790,7 +895,7 @@ static int bind_mount(sd_bus *bus, char **args, unsigned n) {
 
         mount_tmp_created = true;
 
-        if (mount(args[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
+        if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
                 r = log_error_errno(errno, "Failed to overmount: %m");
                 goto finish;
         }
@@ -808,7 +913,7 @@ static int bind_mount(sd_bus *bus, char **args, unsigned n) {
          * directory. This way it will appear there read-only
          * right-away. */
 
-        mount_outside = strappenda("/run/systemd/nspawn/propagate/", args[1], "/XXXXXX");
+        mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
         if (!mkdtemp(mount_outside)) {
                 r = log_error_errno(errno, "Cannot create propagation directory: %m");
                 goto finish;
@@ -921,6 +1026,8 @@ static int openpt_in_namespace(pid_t pid, int flags) {
         pid_t child;
         siginfo_t si;
 
+        assert(pid > 0);
+
         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
         if (r < 0)
                 return r;
@@ -990,7 +1097,7 @@ static int openpt_in_namespace(pid_t pid, int flags) {
         return master;
 }
 
-static int login_machine(sd_bus *bus, char **args, unsigned n) {
+static int login_machine(int argc, char *argv[], void *userdata) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
@@ -998,13 +1105,13 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
         _cleanup_event_unref_ sd_event *event = NULL;
         _cleanup_close_ int master = -1;
         _cleanup_free_ char *getty = NULL;
+        sd_bus *bus = userdata;
         const char *pty, *p;
         pid_t leader;
         sigset_t mask;
         int r, ret = 0;
 
         assert(bus);
-        assert(args);
 
         if (arg_transport != BUS_TRANSPORT_LOCAL) {
                 log_error("Login only supported on local machines.");
@@ -1019,7 +1126,7 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
         if (r < 0)
                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
 
-        r = machine_get_leader(bus, args[1], &leader);
+        r = machine_get_leader(bus, argv[1], &leader);
         if (r < 0)
                 return r;
 
@@ -1037,7 +1144,7 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
                 return -EIO;
         }
 
-        r = sd_bus_open_system_container(&container_bus, args[1]);
+        r = sd_bus_open_system_container(&container_bus, argv[1]);
         if (r < 0)
                 return log_error_errno(r, "Failed to get container bus: %m");
 
@@ -1066,7 +1173,7 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
         sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
         assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
 
-        log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
+        log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
 
         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
         sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
@@ -1083,13 +1190,14 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
 
         fputc('\n', stdout);
 
-        log_info("Connection to container %s terminated.", args[1]);
+        log_info("Connection to container %s terminated.", argv[1]);
 
         sd_event_get_exit_code(event, &ret);
         return ret;
 }
 
-static void help(void) {
+static int help(int argc, char *argv[], void *userdata) {
+
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
                "Send control commands to or query the virtual machine and container\n"
                "registration manager.\n\n"
@@ -1106,7 +1214,7 @@ static void help(void) {
                "  -s --signal=SIGNAL          Which signal to send\n"
                "     --read-only              Create read-only bind mount\n"
                "     --mkdir                  Create directory before bind mounting, if missing\n\n"
-               "Commands:\n"
+               "Machine Commands:\n"
                "  list                        List running VMs and containers\n"
                "  status NAME...              Show VM/container status\n"
                "  show NAME...                Show properties of one or more VMs/containers\n"
@@ -1117,8 +1225,12 @@ static void help(void) {
                "  terminate NAME...           Terminate one or more VMs/containers\n"
                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n"
                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
-               "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n",
+               "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n\n"
+               "Image commands:\n"
+               "  list-images                 Show available images\n",
                program_invocation_short_name);
+
+        return 0;
 }
 
 static int parse_argv(int argc, char *argv[]) {
@@ -1159,8 +1271,7 @@ static int parse_argv(int argc, char *argv[]) {
                 switch (c) {
 
                 case 'h':
-                        help();
-                        return 0;
+                        return help(0, NULL, NULL);
 
                 case ARG_VERSION:
                         puts(PACKAGE_STRING);
@@ -1234,89 +1345,26 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
-
-        static const struct {
-                const char* verb;
-                const enum {
-                        MORE,
-                        LESS,
-                        EQUAL
-                } argc_cmp;
-                const int argc;
-                int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
-        } verbs[] = {
-                { "list",                  LESS,   1, list_machines     },
-                { "status",                MORE,   2, show              },
-                { "show",                  MORE,   1, show              },
-                { "terminate",             MORE,   2, terminate_machine },
-                { "reboot",                MORE,   2, reboot_machine    },
-                { "poweroff",              MORE,   2, poweroff_machine  },
-                { "kill",                  MORE,   2, kill_machine      },
-                { "login",                 MORE,   2, login_machine     },
-                { "bind",                  MORE,   3, bind_mount        },
-                { "copy-to",               MORE,   3, copy_files        },
-                { "copy-from",             MORE,   3, copy_files        },
+static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
+
+        static const Verb verbs[] = {
+                { "help",        VERB_ANY, VERB_ANY, 0,            help              },
+                { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
+                { "list-images", VERB_ANY, 1,        0,            list_images       },
+                { "status",      2,        VERB_ANY, 0,            show              },
+                { "show",        VERB_ANY, VERB_ANY, 0,            show              },
+                { "terminate",   2,        VERB_ANY, 0,            terminate_machine },
+                { "reboot",      2,        VERB_ANY, 0,            reboot_machine    },
+                { "poweroff",    2,        VERB_ANY, 0,            poweroff_machine  },
+                { "kill",        2,        VERB_ANY, 0,            kill_machine      },
+                { "login",       2,        2,        0,            login_machine     },
+                { "bind",        3,        4,        0,            bind_mount        },
+                { "copy-to",     3,        4,        0,            copy_files        },
+                { "copy-from",   3,        4,        0,            copy_files        },
+                {}
         };
 
-        int left;
-        unsigned i;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        left = argc - optind;
-
-        if (left <= 0)
-                /* Special rule: no arguments means "list" */
-                i = 0;
-        else {
-                if (streq(argv[optind], "help")) {
-                        help();
-                        return 0;
-                }
-
-                for (i = 0; i < ELEMENTSOF(verbs); i++)
-                        if (streq(argv[optind], verbs[i].verb))
-                                break;
-
-                if (i >= ELEMENTSOF(verbs)) {
-                        log_error("Unknown operation %s", argv[optind]);
-                        return -EINVAL;
-                }
-        }
-
-        switch (verbs[i].argc_cmp) {
-
-        case EQUAL:
-                if (left != verbs[i].argc) {
-                        log_error("Invalid number of arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        case MORE:
-                if (left < verbs[i].argc) {
-                        log_error("Too few arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        case LESS:
-                if (left > verbs[i].argc) {
-                        log_error("Too many arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Unknown comparison operator.");
-        }
-
-        return verbs[i].dispatch(bus, argv + optind, left);
+        return dispatch_verb(argc, argv, verbs, bus);
 }
 
 int main(int argc, char*argv[]) {
@@ -1337,7 +1385,7 @@ int main(int argc, char*argv[]) {
                 goto finish;
         }
 
-        r = machinectl_main(bus, argc, argv);
+        r = machinectl_main(argc, argv, bus);
 
 finish:
         pager_close();