+ 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(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
+
+ if (arg_legend)
+ printf("%-*s %-*s %-*s %-*s %-*s\n",
+ (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
+ (int) 7, "PERCENT",
+ (int) max_type, "TYPE",
+ (int) max_local, "LOCAL",
+ (int) max_remote, "REMOTE");
+
+ for (j = 0; j < n_transfers; j++)
+ printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
+ (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
+ (int) 6, (unsigned) (transfers[j].progress * 100),
+ (int) max_type, transfers[j].type,
+ (int) max_local, transfers[j].local,
+ (int) max_remote, transfers[j].remote);
+
+ if (arg_legend)
+ printf("\n%zu transfers listed.\n", n_transfers);
+
+ return 0;
+}
+
+static int cancel_transfer(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int r, i;
+
+ assert(bus);
+
+ polkit_agent_open_if_enabled();
+
+ for (i = 1; i < argc; i++) {
+ uint32_t id;
+
+ r = safe_atou32(argv[i], &id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "CancelTransfer",
+ &error,
+ NULL,
+ "u", id);
+ if (r < 0) {
+ log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+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"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " --no-ask-password Do not ask for system passwords\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " -p --property=NAME Show only properties by this name\n"
+ " -q --quiet Suppress output\n"
+ " -a --all Show all properties, including empty ones\n"
+ " -l --full Do not ellipsize output\n"
+ " --kill-who=WHO Who to send signal to\n"
+ " -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 --lines=INTEGER Number of journal entries to show\n"
+ " -o --output=STRING Change journal output mode (short,\n"
+ " short-monotonic, verbose, export, json,\n"
+ " json-pretty, json-sse, cat)\n"
+ " --verify=MODE Verification mode for downloaded images (no,\n"
+ " checksum, signature)\n"
+ " --force Download image even if already exists\n"
+ " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
+ " downloads\n\n"
+ "Machine Commands:\n"
+ " list List running VMs and containers\n"
+ " status NAME... Show VM/container details\n"
+ " show NAME... Show properties of one or more VMs/containers\n"
+ " start NAME... Start container as a service\n"
+ " login NAME Get a login prompt on a container\n"
+ " enable NAME... Enable automatic container start at boot\n"
+ " disable NAME... Disable automatic container start at boot\n"
+ " poweroff NAME... Power off one or more containers\n"
+ " reboot NAME... Reboot one or more containers\n"
+ " terminate NAME... Terminate one or more VMs/containers\n"
+ " kill NAME... Send signal to processes of a VM/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"
+ " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
+ "Image Commands:\n"
+ " list-images Show available container and VM images\n"
+ " image-status NAME... Show image details\n"
+ " show-image NAME... Show properties of image\n"
+ " clone NAME NAME Clone an image\n"
+ " rename NAME NAME Rename an image\n"
+ " read-only NAME [BOOL] Mark or unmark image read-only\n"
+ " remove NAME... Remove an image\n\n"
+ "Image Transfer Commands:\n"
+ " pull-tar URL [NAME] Download a TAR container image\n"
+ " pull-raw URL [NAME] Download a RAW container or VM image\n"
+ " pull-dkr REMOTE [NAME] Download a DKR container image\n"
+ " list-transfers Show list of downloads in progress\n"
+ " cancel-transfer Cancel a download\n"
+ , program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_NO_LEGEND,
+ ARG_KILL_WHO,
+ ARG_READ_ONLY,
+ ARG_MKDIR,
+ ARG_NO_ASK_PASSWORD,
+ ARG_VERIFY,
+ ARG_FORCE,
+ ARG_DKR_INDEX_URL,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "property", required_argument, NULL, 'p' },
+ { "all", no_argument, NULL, 'a' },
+ { "full", no_argument, NULL, 'l' },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "kill-who", required_argument, NULL, ARG_KILL_WHO },
+ { "signal", required_argument, NULL, 's' },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "mkdir", no_argument, NULL, ARG_MKDIR },
+ { "quiet", no_argument, NULL, 'q' },
+ { "lines", required_argument, NULL, 'n' },
+ { "output", required_argument, NULL, 'o' },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "verify", required_argument, NULL, ARG_VERIFY },
+ { "force", no_argument, NULL, ARG_FORCE },
+ { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case 'p':
+ r = strv_extend(&arg_property, optarg);
+ if (r < 0)
+ return log_oom();
+
+ /* If the user asked for a particular
+ * property, show it to him, even if it is
+ * empty. */
+ arg_all = true;
+ break;
+
+ case 'a':
+ arg_all = true;
+ break;
+
+ case 'l':
+ arg_full = true;
+ break;
+
+ case 'n':
+ if (safe_atou(optarg, &arg_lines) < 0) {
+ log_error("Failed to parse lines '%s'", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case 'o':
+ arg_output = output_mode_from_string(optarg);
+ if (arg_output < 0) {
+ log_error("Unknown output '%s'.", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_legend = false;
+ break;
+
+ case ARG_KILL_WHO:
+ arg_kill_who = optarg;
+ break;
+
+ case 's':
+ arg_signal = signal_from_string_try_harder(optarg);
+ if (arg_signal < 0) {
+ log_error("Failed to parse signal string %s.", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case 'H':
+ arg_transport = BUS_TRANSPORT_REMOTE;
+ arg_host = optarg;
+ break;
+
+ case 'M':
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case ARG_READ_ONLY:
+ arg_read_only = true;
+ break;
+
+ case ARG_MKDIR:
+ arg_mkdir = true;
+ break;
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
+ case ARG_VERIFY:
+ arg_verify = import_verify_from_string(optarg);
+ if (arg_verify < 0) {
+ log_error("Failed to parse --verify= setting: %s", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case ARG_FORCE:
+ arg_force = true;
+ break;
+
+ case ARG_DKR_INDEX_URL:
+ if (!http_url_is_valid(optarg)) {
+ log_error("Index URL is invalid: %s", optarg);
+ return -EINVAL;
+ }
+
+ arg_dkr_index_url = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 1;
+}
+
+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_machine },
+ { "image-status", 2, VERB_ANY, 0, show_image },
+ { "show", VERB_ANY, VERB_ANY, 0, show_machine },
+ { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
+ { "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 },
+ { "remove", 2, VERB_ANY, 0, remove_image },
+ { "rename", 3, 3, 0, rename_image },
+ { "clone", 3, 3, 0, clone_image },
+ { "read-only", 2, 3, 0, read_only_image },
+ { "start", 2, VERB_ANY, 0, start_machine },
+ { "enable", 2, VERB_ANY, 0, enable_machine },
+ { "disable", 2, VERB_ANY, 0, enable_machine },
+ { "pull-tar", 2, 3, 0, pull_tar },
+ { "pull-raw", 2, 3, 0, pull_raw },
+ { "pull-dkr", 2, 3, 0, pull_dkr },
+ { "list-transfers", VERB_ANY, 1, 0, list_transfers },
+ { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
+ {}
+ };