LCOV - code coverage report
Current view: top level - machine - machinectl.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 33 1513 2.2 %
Date: 2019-08-22 15:41:25 Functions: 6 67 9.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <arpa/inet.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <getopt.h>
       7             : #include <locale.h>
       8             : #include <math.h>
       9             : #include <net/if.h>
      10             : #include <netinet/in.h>
      11             : #include <string.h>
      12             : #include <sys/mount.h>
      13             : #include <sys/socket.h>
      14             : #include <unistd.h>
      15             : 
      16             : #include "sd-bus.h"
      17             : 
      18             : #include "alloc-util.h"
      19             : #include "bus-common-errors.h"
      20             : #include "bus-error.h"
      21             : #include "bus-unit-procs.h"
      22             : #include "bus-unit-util.h"
      23             : #include "bus-util.h"
      24             : #include "bus-wait-for-jobs.h"
      25             : #include "cgroup-show.h"
      26             : #include "cgroup-util.h"
      27             : #include "copy.h"
      28             : #include "def.h"
      29             : #include "env-util.h"
      30             : #include "fd-util.h"
      31             : #include "format-table.h"
      32             : #include "hostname-util.h"
      33             : #include "import-util.h"
      34             : #include "locale-util.h"
      35             : #include "log.h"
      36             : #include "logs-show.h"
      37             : #include "macro.h"
      38             : #include "main-func.h"
      39             : #include "mkdir.h"
      40             : #include "nulstr-util.h"
      41             : #include "pager.h"
      42             : #include "parse-util.h"
      43             : #include "path-util.h"
      44             : #include "pretty-print.h"
      45             : #include "process-util.h"
      46             : #include "ptyfwd.h"
      47             : #include "rlimit-util.h"
      48             : #include "sigbus.h"
      49             : #include "signal-util.h"
      50             : #include "sort-util.h"
      51             : #include "spawn-polkit-agent.h"
      52             : #include "stdio-util.h"
      53             : #include "string-table.h"
      54             : #include "strv.h"
      55             : #include "terminal-util.h"
      56             : #include "unit-name.h"
      57             : #include "verbs.h"
      58             : #include "web-util.h"
      59             : 
      60             : #define ALL_IP_ADDRESSES -1
      61             : 
      62             : static char **arg_property = NULL;
      63             : static bool arg_all = false;
      64             : static bool arg_value = false;
      65             : static bool arg_full = false;
      66             : static PagerFlags arg_pager_flags = 0;
      67             : static bool arg_legend = true;
      68             : static const char *arg_kill_who = NULL;
      69             : static int arg_signal = SIGTERM;
      70             : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
      71             : static const char *arg_host = NULL;
      72             : static bool arg_read_only = false;
      73             : static bool arg_mkdir = false;
      74             : static bool arg_quiet = false;
      75             : static bool arg_ask_password = true;
      76             : static unsigned arg_lines = 10;
      77             : static OutputMode arg_output = OUTPUT_SHORT;
      78             : static bool arg_force = false;
      79             : static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
      80             : static const char* arg_format = NULL;
      81             : static const char *arg_uid = NULL;
      82             : static char **arg_setenv = NULL;
      83             : static int arg_addrs = 1;
      84             : 
      85           4 : STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
      86           4 : STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
      87             : 
      88           0 : static OutputFlags get_output_flags(void) {
      89             :         return
      90           0 :                 arg_all * OUTPUT_SHOW_ALL |
      91           0 :                 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
      92           0 :                 colors_enabled() * OUTPUT_COLOR |
      93           0 :                 !arg_quiet * OUTPUT_WARN_CUTOFF;
      94             : }
      95             : 
      96           0 : static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
      97           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
      98           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
      99           0 :         const char *k, *v, *iter, **query_res = NULL;
     100           0 :         size_t count = 0, awaited_args = 0;
     101             :         va_list ap;
     102             :         int r;
     103             : 
     104           0 :         assert(bus);
     105           0 :         assert(name);
     106           0 :         assert(query);
     107             : 
     108           0 :         NULSTR_FOREACH(iter, query)
     109           0 :                 awaited_args++;
     110           0 :         query_res = newa0(const char *, awaited_args);
     111             : 
     112           0 :         r = sd_bus_call_method(
     113             :                         bus,
     114             :                         "org.freedesktop.machine1",
     115             :                         "/org/freedesktop/machine1",
     116             :                         "org.freedesktop.machine1.Manager",
     117             :                         method,
     118             :                         &error,
     119             :                         &reply, "s", name);
     120           0 :         if (r < 0)
     121           0 :                 return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
     122             : 
     123           0 :         r = sd_bus_message_enter_container(reply, 'a', "{ss}");
     124           0 :         if (r < 0)
     125           0 :                 return bus_log_parse_error(r);
     126             : 
     127           0 :         while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
     128           0 :                 count = 0;
     129           0 :                 NULSTR_FOREACH(iter, query) {
     130           0 :                         if (streq(k, iter)) {
     131           0 :                                 query_res[count] = v;
     132           0 :                                 break;
     133             :                         }
     134           0 :                         count++;
     135             :                 }
     136             :         }
     137           0 :         if (r < 0)
     138           0 :                 return bus_log_parse_error(r);
     139             : 
     140           0 :         r = sd_bus_message_exit_container(reply);
     141           0 :         if (r < 0)
     142           0 :                 return bus_log_parse_error(r);
     143             : 
     144           0 :         va_start(ap, query);
     145           0 :         for (count = 0; count < awaited_args; count++) {
     146             :                 char *val, **out;
     147             : 
     148           0 :                 out = va_arg(ap, char **);
     149           0 :                 assert(out);
     150           0 :                 if (query_res[count]) {
     151           0 :                         val = strdup(query_res[count]);
     152           0 :                         if (!val) {
     153           0 :                                 va_end(ap);
     154           0 :                                 return -ENOMEM;
     155             :                         }
     156           0 :                         *out = val;
     157             :                 }
     158             :         }
     159           0 :         va_end(ap);
     160             : 
     161           0 :         return 0;
     162             : }
     163             : 
     164           0 : static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr, char **ret) {
     165             : 
     166           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     167           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     168           0 :         _cleanup_free_ char *addresses = NULL;
     169           0 :         bool truncate = false;
     170           0 :         unsigned n = 0;
     171             :         int r;
     172             : 
     173           0 :         assert(bus);
     174           0 :         assert(name);
     175           0 :         assert(prefix);
     176           0 :         assert(prefix2);
     177             : 
     178           0 :         r = sd_bus_call_method(bus,
     179             :                                "org.freedesktop.machine1",
     180             :                                "/org/freedesktop/machine1",
     181             :                                "org.freedesktop.machine1.Manager",
     182             :                                "GetMachineAddresses",
     183             :                                NULL,
     184             :                                &reply,
     185             :                                "s", name);
     186           0 :         if (r < 0)
     187           0 :                 return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r));
     188             : 
     189           0 :         addresses = strdup(prefix);
     190           0 :         if (!addresses)
     191           0 :                 return log_oom();
     192           0 :         prefix = "";
     193             : 
     194           0 :         r = sd_bus_message_enter_container(reply, 'a', "(iay)");
     195           0 :         if (r < 0)
     196           0 :                 return bus_log_parse_error(r);
     197             : 
     198           0 :         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
     199             :                 int family;
     200             :                 const void *a;
     201             :                 size_t sz;
     202           0 :                 char buf_ifi[DECIMAL_STR_MAX(int) + 2], buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
     203             : 
     204           0 :                 r = sd_bus_message_read(reply, "i", &family);
     205           0 :                 if (r < 0)
     206           0 :                         return bus_log_parse_error(r);
     207             : 
     208           0 :                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
     209           0 :                 if (r < 0)
     210           0 :                         return bus_log_parse_error(r);
     211             : 
     212           0 :                 if (n_addr != 0) {
     213           0 :                         if (family == AF_INET6 && ifi > 0)
     214           0 :                                 xsprintf(buf_ifi, "%%%i", ifi);
     215             :                         else
     216           0 :                                 strcpy(buf_ifi, "");
     217             : 
     218           0 :                         if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
     219           0 :                                 return log_oom();
     220             :                 } else
     221           0 :                         truncate = true;
     222             : 
     223           0 :                 r = sd_bus_message_exit_container(reply);
     224           0 :                 if (r < 0)
     225           0 :                         return bus_log_parse_error(r);
     226             : 
     227           0 :                 prefix = prefix2;
     228             : 
     229           0 :                 if (n_addr > 0)
     230           0 :                         n_addr --;
     231             : 
     232           0 :                 n++;
     233             :         }
     234           0 :         if (r < 0)
     235           0 :                 return bus_log_parse_error(r);
     236             : 
     237           0 :         r = sd_bus_message_exit_container(reply);
     238           0 :         if (r < 0)
     239           0 :                 return bus_log_parse_error(r);
     240             : 
     241           0 :         if (truncate) {
     242             : 
     243           0 :                 if (!strextend(&addresses, special_glyph(SPECIAL_GLYPH_ELLIPSIS), NULL))
     244           0 :                         return -ENOMEM;
     245             : 
     246             :         }
     247             : 
     248           0 :         *ret = TAKE_PTR(addresses);
     249           0 :         return (int) n;
     250             : }
     251             : 
     252           0 : static int show_table(Table *table, const char *word) {
     253             :         int r;
     254             : 
     255           0 :         assert(table);
     256           0 :         assert(word);
     257             : 
     258           0 :         if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) {
     259           0 :                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
     260           0 :                 if (r < 0)
     261           0 :                         return log_error_errno(r, "Failed to sort table: %m");
     262             : 
     263           0 :                 table_set_header(table, arg_legend);
     264             : 
     265           0 :                 if (OUTPUT_MODE_IS_JSON(arg_output))
     266           0 :                         r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
     267             :                 else
     268           0 :                         r = table_print(table, NULL);
     269           0 :                 if (r < 0)
     270           0 :                         return log_error_errno(r, "Failed to show table: %m");
     271             :         }
     272             : 
     273           0 :         if (arg_legend) {
     274           0 :                 if (table_get_rows(table) > 1)
     275           0 :                         printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
     276             :                 else
     277           0 :                         printf("No %s.\n", word);
     278             :         }
     279             : 
     280           0 :         return 0;
     281             : }
     282             : 
     283           0 : static int list_machines(int argc, char *argv[], void *userdata) {
     284             : 
     285           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     286           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     287           0 :         _cleanup_(table_unrefp) Table *table = NULL;
     288           0 :         sd_bus *bus = userdata;
     289             :         int r;
     290             : 
     291           0 :         assert(bus);
     292             : 
     293           0 :         (void) pager_open(arg_pager_flags);
     294             : 
     295           0 :         r = sd_bus_call_method(bus,
     296             :                                "org.freedesktop.machine1",
     297             :                                "/org/freedesktop/machine1",
     298             :                                "org.freedesktop.machine1.Manager",
     299             :                                "ListMachines",
     300             :                                &error,
     301             :                                &reply,
     302             :                                NULL);
     303           0 :         if (r < 0)
     304           0 :                 return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
     305             : 
     306           0 :         table = table_new("machine", "class", "service", "os", "version", "addresses");
     307           0 :         if (!table)
     308           0 :                 return log_oom();
     309             : 
     310           0 :         r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
     311           0 :         if (r < 0)
     312           0 :                 return bus_log_parse_error(r);
     313             : 
     314           0 :         for (;;) {
     315           0 :                 _cleanup_free_ char *os = NULL, *version_id = NULL, *addresses = NULL;
     316             :                 const char *name, *class, *service;
     317             : 
     318           0 :                 r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, NULL);
     319           0 :                 if (r < 0)
     320           0 :                         return bus_log_parse_error(r);
     321           0 :                 if (r == 0)
     322           0 :                         break;
     323             : 
     324           0 :                 if (name[0] == '.' && !arg_all)
     325           0 :                         continue;
     326             : 
     327           0 :                 (void) call_get_os_release(
     328             :                                 bus,
     329             :                                 "GetMachineOSRelease",
     330             :                                 name,
     331             :                                 "ID\0"
     332             :                                 "VERSION_ID\0",
     333             :                                 &os,
     334             :                                 &version_id);
     335             : 
     336           0 :                 (void) call_get_addresses(
     337             :                                 bus,
     338             :                                 name,
     339             :                                 0,
     340             :                                 "",
     341             :                                 " ",
     342             :                                 arg_addrs,
     343             :                                 &addresses);
     344             : 
     345           0 :                 r = table_add_many(table,
     346             :                                    TABLE_STRING, name,
     347             :                                    TABLE_STRING, class,
     348             :                                    TABLE_STRING, empty_to_dash(service),
     349             :                                    TABLE_STRING, empty_to_dash(os),
     350             :                                    TABLE_STRING, empty_to_dash(version_id),
     351             :                                    TABLE_STRING, empty_to_dash(addresses));
     352           0 :                 if (r < 0)
     353           0 :                         return log_error_errno(r, "Failed to add table row: %m");
     354             :         }
     355             : 
     356           0 :         r = sd_bus_message_exit_container(reply);
     357           0 :         if (r < 0)
     358           0 :                 return bus_log_parse_error(r);
     359             : 
     360           0 :         return show_table(table, "machines");
     361             : }
     362             : 
     363           0 : static int list_images(int argc, char *argv[], void *userdata) {
     364             : 
     365           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     366           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     367           0 :         _cleanup_(table_unrefp) Table *table = NULL;
     368           0 :         sd_bus *bus = userdata;
     369             :         int r;
     370             : 
     371           0 :         assert(bus);
     372             : 
     373           0 :         (void) pager_open(arg_pager_flags);
     374             : 
     375           0 :         r = sd_bus_call_method(bus,
     376             :                                "org.freedesktop.machine1",
     377             :                                "/org/freedesktop/machine1",
     378             :                                "org.freedesktop.machine1.Manager",
     379             :                                "ListImages",
     380             :                                &error,
     381             :                                &reply,
     382             :                                NULL);
     383           0 :         if (r < 0)
     384           0 :                 return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
     385             : 
     386           0 :         table = table_new("name", "type", "ro", "usage", "created", "modified");
     387           0 :         if (!table)
     388           0 :                 return log_oom();
     389             : 
     390           0 :         (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100);
     391             : 
     392           0 :         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
     393           0 :         if (r < 0)
     394           0 :                 return bus_log_parse_error(r);
     395             : 
     396           0 :         for (;;) {
     397             :                 uint64_t crtime, mtime, size;
     398             :                 const char *name, *type;
     399             :                 TableCell *cell;
     400             :                 bool ro_bool;
     401             :                 int ro_int;
     402             : 
     403           0 :                 r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &ro_int, &crtime, &mtime, &size, NULL);
     404           0 :                 if (r < 0)
     405           0 :                         return bus_log_parse_error(r);
     406           0 :                 if (r == 0)
     407           0 :                         break;
     408             : 
     409           0 :                 if (name[0] == '.' && !arg_all)
     410           0 :                         continue;
     411             : 
     412           0 :                 r = table_add_many(table,
     413             :                                    TABLE_STRING, name,
     414             :                                    TABLE_STRING, type);
     415           0 :                 if (r < 0)
     416           0 :                         return log_error_errno(r, "Failed to add table row: %m");
     417             : 
     418           0 :                 ro_bool = ro_int;
     419           0 :                 r = table_add_cell(table, &cell, TABLE_BOOLEAN, &ro_bool);
     420           0 :                 if (r < 0)
     421           0 :                         return log_error_errno(r, "Failed to add table cell: %m");
     422             : 
     423           0 :                 if (ro_bool) {
     424           0 :                         r = table_set_color(table, cell, ansi_highlight_red());
     425           0 :                         if (r < 0)
     426           0 :                                 return log_error_errno(r, "Failed to set table cell color: %m");
     427             :                 }
     428             : 
     429           0 :                 r = table_add_many(table,
     430             :                                    TABLE_SIZE, size,
     431             :                                    TABLE_TIMESTAMP, crtime,
     432             :                                    TABLE_TIMESTAMP, mtime);
     433           0 :                 if (r < 0)
     434           0 :                         return log_error_errno(r, "Failed to add table row: %m");
     435             :         }
     436             : 
     437           0 :         r = sd_bus_message_exit_container(reply);
     438           0 :         if (r < 0)
     439           0 :                 return bus_log_parse_error(r);
     440             : 
     441           0 :         return show_table(table, "images");
     442             : }
     443             : 
     444           0 : static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
     445           0 :         _cleanup_free_ char *cgroup = NULL;
     446           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     447             :         int r;
     448             :         unsigned c;
     449             : 
     450           0 :         assert(bus);
     451           0 :         assert(unit);
     452             : 
     453           0 :         r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
     454           0 :         if (r < 0)
     455           0 :                 return r;
     456             : 
     457           0 :         if (isempty(cgroup))
     458           0 :                 return 0;
     459             : 
     460           0 :         c = columns();
     461           0 :         if (c > 18)
     462           0 :                 c -= 18;
     463             :         else
     464           0 :                 c = 0;
     465             : 
     466           0 :         r = unit_show_processes(bus, unit, cgroup, "\t\t  ", c, get_output_flags(), &error);
     467           0 :         if (r == -EBADR) {
     468             : 
     469           0 :                 if (arg_transport == BUS_TRANSPORT_REMOTE)
     470           0 :                         return 0;
     471             : 
     472             :                 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
     473             : 
     474           0 :                 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
     475           0 :                         return 0;
     476             : 
     477           0 :                 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, &leader, leader > 0, get_output_flags());
     478           0 :         } else if (r < 0)
     479           0 :                 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
     480             : 
     481           0 :         return 0;
     482             : }
     483             : 
     484           0 : static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
     485           0 :         _cleanup_free_ char *pretty = NULL;
     486             :         int r;
     487             : 
     488           0 :         assert(bus);
     489           0 :         assert(name);
     490           0 :         assert(prefix);
     491             : 
     492           0 :         r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
     493           0 :         if (r < 0)
     494           0 :                 return r;
     495             : 
     496           0 :         if (pretty)
     497           0 :                 printf("%s%s\n", prefix, pretty);
     498             : 
     499           0 :         return 0;
     500             : }
     501             : 
     502           0 : static int print_uid_shift(sd_bus *bus, const char *name) {
     503           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     504           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     505             :         uint32_t shift;
     506             :         int r;
     507             : 
     508           0 :         assert(bus);
     509           0 :         assert(name);
     510             : 
     511           0 :         r = sd_bus_call_method(bus,
     512             :                                "org.freedesktop.machine1",
     513             :                                "/org/freedesktop/machine1",
     514             :                                "org.freedesktop.machine1.Manager",
     515             :                                "GetMachineUIDShift",
     516             :                                &error,
     517             :                                &reply,
     518             :                                "s", name);
     519           0 :         if (r < 0)
     520           0 :                 return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
     521             : 
     522           0 :         r = sd_bus_message_read(reply, "u", &shift);
     523           0 :         if (r < 0)
     524           0 :                 return r;
     525             : 
     526           0 :         if (shift == 0) /* Don't show trivial mappings */
     527           0 :                 return 0;
     528             : 
     529           0 :         printf("       UID Shift: %" PRIu32 "\n", shift);
     530           0 :         return 0;
     531             : }
     532             : 
     533             : typedef struct MachineStatusInfo {
     534             :         const char *name;
     535             :         sd_id128_t id;
     536             :         const char *class;
     537             :         const char *service;
     538             :         const char *unit;
     539             :         const char *root_directory;
     540             :         pid_t leader;
     541             :         struct dual_timestamp timestamp;
     542             :         int *netif;
     543             :         size_t n_netif;
     544             : } MachineStatusInfo;
     545             : 
     546           0 : static void machine_status_info_clear(MachineStatusInfo *info) {
     547           0 :         if (info) {
     548           0 :                 free(info->netif);
     549           0 :                 zero(*info);
     550             :         }
     551           0 : }
     552             : 
     553           0 : static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
     554             :         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX];
     555             :         char since2[FORMAT_TIMESTAMP_MAX];
     556           0 :         _cleanup_free_ char *addresses = NULL;
     557             :         const char *s1, *s2;
     558           0 :         int ifi = -1;
     559             : 
     560           0 :         assert(bus);
     561           0 :         assert(i);
     562             : 
     563           0 :         fputs(strna(i->name), stdout);
     564             : 
     565           0 :         if (!sd_id128_is_null(i->id))
     566           0 :                 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
     567             :         else
     568           0 :                 putchar('\n');
     569             : 
     570           0 :         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
     571           0 :         s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
     572             : 
     573           0 :         if (s1)
     574           0 :                 printf("\t   Since: %s; %s\n", s2, s1);
     575           0 :         else if (s2)
     576           0 :                 printf("\t   Since: %s\n", s2);
     577             : 
     578           0 :         if (i->leader > 0) {
     579           0 :                 _cleanup_free_ char *t = NULL;
     580             : 
     581           0 :                 printf("\t  Leader: %u", (unsigned) i->leader);
     582             : 
     583           0 :                 get_process_comm(i->leader, &t);
     584           0 :                 if (t)
     585           0 :                         printf(" (%s)", t);
     586             : 
     587           0 :                 putchar('\n');
     588             :         }
     589             : 
     590           0 :         if (i->service) {
     591           0 :                 printf("\t Service: %s", i->service);
     592             : 
     593           0 :                 if (i->class)
     594           0 :                         printf("; class %s", i->class);
     595             : 
     596           0 :                 putchar('\n');
     597           0 :         } else if (i->class)
     598           0 :                 printf("\t   Class: %s\n", i->class);
     599             : 
     600           0 :         if (i->root_directory)
     601           0 :                 printf("\t    Root: %s\n", i->root_directory);
     602             : 
     603           0 :         if (i->n_netif > 0) {
     604             :                 size_t c;
     605             : 
     606           0 :                 fputs("\t   Iface:", stdout);
     607             : 
     608           0 :                 for (c = 0; c < i->n_netif; c++) {
     609             :                         char name[IF_NAMESIZE+1];
     610             : 
     611           0 :                         if (format_ifname(i->netif[c], name)) {
     612           0 :                                 fputc(' ', stdout);
     613           0 :                                 fputs(name, stdout);
     614             : 
     615           0 :                                 if (ifi < 0)
     616           0 :                                         ifi = i->netif[c];
     617             :                                 else
     618           0 :                                         ifi = 0;
     619             :                         } else
     620           0 :                                 printf(" %i", i->netif[c]);
     621             :                 }
     622             : 
     623           0 :                 fputc('\n', stdout);
     624             :         }
     625             : 
     626           0 :         if (call_get_addresses(bus, i->name, ifi,
     627             :                                "\t Address: ", "\n\t          ", ALL_IP_ADDRESSES,
     628             :                                &addresses) > 0) {
     629           0 :                 fputs(addresses, stdout);
     630           0 :                 fputc('\n', stdout);
     631             :         }
     632             : 
     633           0 :         print_os_release(bus, "GetMachineOSRelease", i->name, "\t      OS: ");
     634             : 
     635           0 :         print_uid_shift(bus, i->name);
     636             : 
     637           0 :         if (i->unit) {
     638           0 :                 printf("\t    Unit: %s\n", i->unit);
     639           0 :                 show_unit_cgroup(bus, i->unit, i->leader);
     640             : 
     641           0 :                 if (arg_transport == BUS_TRANSPORT_LOCAL)
     642             : 
     643           0 :                         show_journal_by_unit(
     644             :                                         stdout,
     645             :                                         i->unit,
     646             :                                         arg_output,
     647             :                                         0,
     648             :                                         i->timestamp.monotonic,
     649             :                                         arg_lines,
     650             :                                         0,
     651           0 :                                         get_output_flags() | OUTPUT_BEGIN_NEWLINE,
     652             :                                         SD_JOURNAL_LOCAL_ONLY,
     653             :                                         true,
     654             :                                         NULL);
     655             :         }
     656           0 : }
     657             : 
     658           0 : static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
     659           0 :         MachineStatusInfo *i = userdata;
     660             :         size_t l;
     661             :         const void *v;
     662             :         int r;
     663             : 
     664             :         assert_cc(sizeof(int32_t) == sizeof(int));
     665           0 :         r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
     666           0 :         if (r < 0)
     667           0 :                 return r;
     668           0 :         if (r == 0)
     669           0 :                 return -EBADMSG;
     670             : 
     671           0 :         i->n_netif = l / sizeof(int32_t);
     672           0 :         i->netif = memdup(v, l);
     673           0 :         if (!i->netif)
     674           0 :                 return -ENOMEM;
     675             : 
     676           0 :         return 0;
     677             : }
     678             : 
     679           0 : static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
     680             : 
     681             :         static const struct bus_properties_map map[]  = {
     682             :                 { "Name",               "s",  NULL,          offsetof(MachineStatusInfo, name)                },
     683             :                 { "Class",              "s",  NULL,          offsetof(MachineStatusInfo, class)               },
     684             :                 { "Service",            "s",  NULL,          offsetof(MachineStatusInfo, service)             },
     685             :                 { "Unit",               "s",  NULL,          offsetof(MachineStatusInfo, unit)                },
     686             :                 { "RootDirectory",      "s",  NULL,          offsetof(MachineStatusInfo, root_directory)      },
     687             :                 { "Leader",             "u",  NULL,          offsetof(MachineStatusInfo, leader)              },
     688             :                 { "Timestamp",          "t",  NULL,          offsetof(MachineStatusInfo, timestamp.realtime)  },
     689             :                 { "TimestampMonotonic", "t",  NULL,          offsetof(MachineStatusInfo, timestamp.monotonic) },
     690             :                 { "Id",                 "ay", bus_map_id128, offsetof(MachineStatusInfo, id)                  },
     691             :                 { "NetworkInterfaces",  "ai", map_netif,     0                                                },
     692             :                 {}
     693             :         };
     694             : 
     695           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     696           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
     697           0 :         _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
     698             :         int r;
     699             : 
     700           0 :         assert(verb);
     701           0 :         assert(bus);
     702           0 :         assert(path);
     703           0 :         assert(new_line);
     704             : 
     705           0 :         r = bus_map_all_properties(bus,
     706             :                                    "org.freedesktop.machine1",
     707             :                                    path,
     708             :                                    map,
     709             :                                    0,
     710             :                                    &error,
     711             :                                    &m,
     712             :                                    &info);
     713           0 :         if (r < 0)
     714           0 :                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
     715             : 
     716           0 :         if (*new_line)
     717           0 :                 printf("\n");
     718           0 :         *new_line = true;
     719             : 
     720           0 :         print_machine_status_info(bus, &info);
     721             : 
     722           0 :         return r;
     723             : }
     724             : 
     725           0 : static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
     726             :         int r;
     727             : 
     728           0 :         assert(bus);
     729           0 :         assert(path);
     730           0 :         assert(new_line);
     731             : 
     732           0 :         if (*new_line)
     733           0 :                 printf("\n");
     734             : 
     735           0 :         *new_line = true;
     736             : 
     737           0 :         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_value, arg_all, NULL);
     738           0 :         if (r < 0)
     739           0 :                 log_error_errno(r, "Could not get properties: %m");
     740             : 
     741           0 :         return r;
     742             : }
     743             : 
     744           0 : static int show_machine(int argc, char *argv[], void *userdata) {
     745             : 
     746           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     747           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     748           0 :         bool properties, new_line = false;
     749           0 :         sd_bus *bus = userdata;
     750           0 :         int r = 0, i;
     751             : 
     752           0 :         assert(bus);
     753             : 
     754           0 :         properties = !strstr(argv[0], "status");
     755             : 
     756           0 :         (void) pager_open(arg_pager_flags);
     757             : 
     758           0 :         if (properties && argc <= 1) {
     759             : 
     760             :                 /* If no argument is specified, inspect the manager
     761             :                  * itself */
     762           0 :                 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
     763           0 :                 if (r < 0)
     764           0 :                         return r;
     765             :         }
     766             : 
     767           0 :         for (i = 1; i < argc; i++) {
     768           0 :                 const char *path = NULL;
     769             : 
     770           0 :                 r = sd_bus_call_method(bus,
     771             :                                        "org.freedesktop.machine1",
     772             :                                        "/org/freedesktop/machine1",
     773             :                                        "org.freedesktop.machine1.Manager",
     774             :                                        "GetMachine",
     775             :                                        &error,
     776             :                                        &reply,
     777           0 :                                        "s", argv[i]);
     778           0 :                 if (r < 0)
     779           0 :                         return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, -r));
     780             : 
     781           0 :                 r = sd_bus_message_read(reply, "o", &path);
     782           0 :                 if (r < 0)
     783           0 :                         return bus_log_parse_error(r);
     784             : 
     785           0 :                 if (properties)
     786           0 :                         r = show_machine_properties(bus, path, &new_line);
     787             :                 else
     788           0 :                         r = show_machine_info(argv[0], bus, path, &new_line);
     789             :         }
     790             : 
     791           0 :         return r;
     792             : }
     793             : 
     794           0 : static int print_image_hostname(sd_bus *bus, const char *name) {
     795           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     796             :         const char *hn;
     797             :         int r;
     798             : 
     799           0 :         r = sd_bus_call_method(
     800             :                         bus,
     801             :                         "org.freedesktop.machine1",
     802             :                         "/org/freedesktop/machine1",
     803             :                         "org.freedesktop.machine1.Manager",
     804             :                         "GetImageHostname",
     805             :                         NULL, &reply, "s", name);
     806           0 :         if (r < 0)
     807           0 :                 return r;
     808             : 
     809           0 :         r = sd_bus_message_read(reply, "s", &hn);
     810           0 :         if (r < 0)
     811           0 :                 return r;
     812             : 
     813           0 :         if (!isempty(hn))
     814           0 :                 printf("\tHostname: %s\n", hn);
     815             : 
     816           0 :         return 0;
     817             : }
     818             : 
     819           0 : static int print_image_machine_id(sd_bus *bus, const char *name) {
     820           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     821           0 :         sd_id128_t id = SD_ID128_NULL;
     822             :         const void *p;
     823             :         size_t size;
     824             :         int r;
     825             : 
     826           0 :         r = sd_bus_call_method(
     827             :                         bus,
     828             :                         "org.freedesktop.machine1",
     829             :                         "/org/freedesktop/machine1",
     830             :                         "org.freedesktop.machine1.Manager",
     831             :                         "GetImageMachineID",
     832             :                         NULL, &reply, "s", name);
     833           0 :         if (r < 0)
     834           0 :                 return r;
     835             : 
     836           0 :         r = sd_bus_message_read_array(reply, 'y', &p, &size);
     837           0 :         if (r < 0)
     838           0 :                 return r;
     839             : 
     840           0 :         if (size == sizeof(sd_id128_t))
     841           0 :                 memcpy(&id, p, size);
     842             : 
     843           0 :         if (!sd_id128_is_null(id))
     844           0 :                 printf("      Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
     845             : 
     846           0 :         return 0;
     847             : }
     848             : 
     849           0 : static int print_image_machine_info(sd_bus *bus, const char *name) {
     850           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     851             :         int r;
     852             : 
     853           0 :         r = sd_bus_call_method(
     854             :                         bus,
     855             :                         "org.freedesktop.machine1",
     856             :                         "/org/freedesktop/machine1",
     857             :                         "org.freedesktop.machine1.Manager",
     858             :                         "GetImageMachineInfo",
     859             :                         NULL, &reply, "s", name);
     860           0 :         if (r < 0)
     861           0 :                 return r;
     862             : 
     863           0 :         r = sd_bus_message_enter_container(reply, 'a', "{ss}");
     864           0 :         if (r < 0)
     865           0 :                 return r;
     866             : 
     867           0 :         for (;;) {
     868             :                 const char *p, *q;
     869             : 
     870           0 :                 r = sd_bus_message_read(reply, "{ss}", &p, &q);
     871           0 :                 if (r < 0)
     872           0 :                         return r;
     873           0 :                 if (r == 0)
     874           0 :                         break;
     875             : 
     876           0 :                 if (streq(p, "DEPLOYMENT"))
     877           0 :                         printf("      Deployment: %s\n", q);
     878             :         }
     879             : 
     880           0 :         r = sd_bus_message_exit_container(reply);
     881           0 :         if (r < 0)
     882           0 :                 return r;
     883             : 
     884           0 :         return 0;
     885             : }
     886             : 
     887             : typedef struct ImageStatusInfo {
     888             :         const char *name;
     889             :         const char *path;
     890             :         const char *type;
     891             :         bool read_only;
     892             :         usec_t crtime;
     893             :         usec_t mtime;
     894             :         uint64_t usage;
     895             :         uint64_t limit;
     896             :         uint64_t usage_exclusive;
     897             :         uint64_t limit_exclusive;
     898             : } ImageStatusInfo;
     899             : 
     900           0 : static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
     901             :         char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX];
     902             :         char ts_absolute[FORMAT_TIMESTAMP_MAX];
     903             :         char bs[FORMAT_BYTES_MAX];
     904             :         char bs_exclusive[FORMAT_BYTES_MAX];
     905             :         const char *s1, *s2, *s3, *s4;
     906             : 
     907           0 :         assert(bus);
     908           0 :         assert(i);
     909             : 
     910           0 :         if (i->name) {
     911           0 :                 fputs(i->name, stdout);
     912           0 :                 putchar('\n');
     913             :         }
     914             : 
     915           0 :         if (i->type)
     916           0 :                 printf("\t    Type: %s\n", i->type);
     917             : 
     918           0 :         if (i->path)
     919           0 :                 printf("\t    Path: %s\n", i->path);
     920             : 
     921           0 :         (void) print_image_hostname(bus, i->name);
     922           0 :         (void) print_image_machine_id(bus, i->name);
     923           0 :         (void) print_image_machine_info(bus, i->name);
     924             : 
     925           0 :         print_os_release(bus, "GetImageOSRelease", i->name, "\t      OS: ");
     926             : 
     927           0 :         printf("\t      RO: %s%s%s\n",
     928           0 :                i->read_only ? ansi_highlight_red() : "",
     929           0 :                i->read_only ? "read-only" : "writable",
     930           0 :                i->read_only ? ansi_normal() : "");
     931             : 
     932           0 :         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
     933           0 :         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
     934           0 :         if (s1 && s2)
     935           0 :                 printf("\t Created: %s; %s\n", s2, s1);
     936           0 :         else if (s2)
     937           0 :                 printf("\t Created: %s\n", s2);
     938             : 
     939           0 :         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
     940           0 :         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
     941           0 :         if (s1 && s2)
     942           0 :                 printf("\tModified: %s; %s\n", s2, s1);
     943           0 :         else if (s2)
     944           0 :                 printf("\tModified: %s\n", s2);
     945             : 
     946           0 :         s3 = format_bytes(bs, sizeof(bs), i->usage);
     947           0 :         s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
     948           0 :         if (s3 && s4)
     949           0 :                 printf("\t   Usage: %s (exclusive: %s)\n", s3, s4);
     950           0 :         else if (s3)
     951           0 :                 printf("\t   Usage: %s\n", s3);
     952             : 
     953           0 :         s3 = format_bytes(bs, sizeof(bs), i->limit);
     954           0 :         s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
     955           0 :         if (s3 && s4)
     956           0 :                 printf("\t   Limit: %s (exclusive: %s)\n", s3, s4);
     957           0 :         else if (s3)
     958           0 :                 printf("\t   Limit: %s\n", s3);
     959           0 : }
     960             : 
     961           0 : static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
     962             : 
     963             :         static const struct bus_properties_map map[]  = {
     964             :                 { "Name",                  "s",  NULL, offsetof(ImageStatusInfo, name)            },
     965             :                 { "Path",                  "s",  NULL, offsetof(ImageStatusInfo, path)            },
     966             :                 { "Type",                  "s",  NULL, offsetof(ImageStatusInfo, type)            },
     967             :                 { "ReadOnly",              "b",  NULL, offsetof(ImageStatusInfo, read_only)       },
     968             :                 { "CreationTimestamp",     "t",  NULL, offsetof(ImageStatusInfo, crtime)          },
     969             :                 { "ModificationTimestamp", "t",  NULL, offsetof(ImageStatusInfo, mtime)           },
     970             :                 { "Usage",                 "t",  NULL, offsetof(ImageStatusInfo, usage)           },
     971             :                 { "Limit",                 "t",  NULL, offsetof(ImageStatusInfo, limit)           },
     972             :                 { "UsageExclusive",        "t",  NULL, offsetof(ImageStatusInfo, usage_exclusive) },
     973             :                 { "LimitExclusive",        "t",  NULL, offsetof(ImageStatusInfo, limit_exclusive) },
     974             :                 {}
     975             :         };
     976             : 
     977           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     978           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
     979           0 :         ImageStatusInfo info = {};
     980             :         int r;
     981             : 
     982           0 :         assert(bus);
     983           0 :         assert(path);
     984           0 :         assert(new_line);
     985             : 
     986           0 :         r = bus_map_all_properties(bus,
     987             :                                    "org.freedesktop.machine1",
     988             :                                    path,
     989             :                                    map,
     990             :                                    BUS_MAP_BOOLEAN_AS_BOOL,
     991             :                                    &error,
     992             :                                    &m,
     993             :                                    &info);
     994           0 :         if (r < 0)
     995           0 :                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
     996             : 
     997           0 :         if (*new_line)
     998           0 :                 printf("\n");
     999           0 :         *new_line = true;
    1000             : 
    1001           0 :         print_image_status_info(bus, &info);
    1002             : 
    1003           0 :         return r;
    1004             : }
    1005             : 
    1006             : typedef struct PoolStatusInfo {
    1007             :         const char *path;
    1008             :         uint64_t usage;
    1009             :         uint64_t limit;
    1010             : } PoolStatusInfo;
    1011             : 
    1012           0 : static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
    1013             :         char bs[FORMAT_BYTES_MAX], *s;
    1014             : 
    1015           0 :         if (i->path)
    1016           0 :                 printf("\t    Path: %s\n", i->path);
    1017             : 
    1018           0 :         s = format_bytes(bs, sizeof(bs), i->usage);
    1019           0 :         if (s)
    1020           0 :                 printf("\t   Usage: %s\n", s);
    1021             : 
    1022           0 :         s = format_bytes(bs, sizeof(bs), i->limit);
    1023           0 :         if (s)
    1024           0 :                 printf("\t   Limit: %s\n", s);
    1025           0 : }
    1026             : 
    1027           0 : static int show_pool_info(sd_bus *bus) {
    1028             : 
    1029             :         static const struct bus_properties_map map[]  = {
    1030             :                 { "PoolPath",  "s",  NULL, offsetof(PoolStatusInfo, path)  },
    1031             :                 { "PoolUsage", "t",  NULL, offsetof(PoolStatusInfo, usage) },
    1032             :                 { "PoolLimit", "t",  NULL, offsetof(PoolStatusInfo, limit) },
    1033             :                 {}
    1034             :         };
    1035             : 
    1036           0 :         PoolStatusInfo info = {
    1037             :                 .usage = (uint64_t) -1,
    1038             :                 .limit = (uint64_t) -1,
    1039             :         };
    1040             : 
    1041           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1042           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    1043             :         int r;
    1044             : 
    1045           0 :         assert(bus);
    1046             : 
    1047           0 :         r = bus_map_all_properties(bus,
    1048             :                                    "org.freedesktop.machine1",
    1049             :                                    "/org/freedesktop/machine1",
    1050             :                                    map,
    1051             :                                    0,
    1052             :                                    &error,
    1053             :                                    &m,
    1054             :                                    &info);
    1055           0 :         if (r < 0)
    1056           0 :                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
    1057             : 
    1058           0 :         print_pool_status_info(bus, &info);
    1059             : 
    1060           0 :         return 0;
    1061             : }
    1062             : 
    1063           0 : static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
    1064             :         int r;
    1065             : 
    1066           0 :         assert(bus);
    1067           0 :         assert(path);
    1068           0 :         assert(new_line);
    1069             : 
    1070           0 :         if (*new_line)
    1071           0 :                 printf("\n");
    1072             : 
    1073           0 :         *new_line = true;
    1074             : 
    1075           0 :         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_value, arg_all, NULL);
    1076           0 :         if (r < 0)
    1077           0 :                 log_error_errno(r, "Could not get properties: %m");
    1078             : 
    1079           0 :         return r;
    1080             : }
    1081             : 
    1082           0 : static int show_image(int argc, char *argv[], void *userdata) {
    1083             : 
    1084           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1085           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1086           0 :         bool properties, new_line = false;
    1087           0 :         sd_bus *bus = userdata;
    1088           0 :         int r = 0, i;
    1089             : 
    1090           0 :         assert(bus);
    1091             : 
    1092           0 :         properties = !strstr(argv[0], "status");
    1093             : 
    1094           0 :         (void) pager_open(arg_pager_flags);
    1095             : 
    1096           0 :         if (argc <= 1) {
    1097             : 
    1098             :                 /* If no argument is specified, inspect the manager
    1099             :                  * itself */
    1100             : 
    1101           0 :                 if (properties)
    1102           0 :                         r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
    1103             :                 else
    1104           0 :                         r = show_pool_info(bus);
    1105           0 :                 if (r < 0)
    1106           0 :                         return r;
    1107             :         }
    1108             : 
    1109           0 :         for (i = 1; i < argc; i++) {
    1110           0 :                 const char *path = NULL;
    1111             : 
    1112           0 :                 r = sd_bus_call_method(
    1113             :                                 bus,
    1114             :                                 "org.freedesktop.machine1",
    1115             :                                 "/org/freedesktop/machine1",
    1116             :                                 "org.freedesktop.machine1.Manager",
    1117             :                                 "GetImage",
    1118             :                                 &error,
    1119             :                                 &reply,
    1120           0 :                                 "s", argv[i]);
    1121           0 :                 if (r < 0)
    1122           0 :                         return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, -r));
    1123             : 
    1124           0 :                 r = sd_bus_message_read(reply, "o", &path);
    1125           0 :                 if (r < 0)
    1126           0 :                         return bus_log_parse_error(r);
    1127             : 
    1128           0 :                 if (properties)
    1129           0 :                         r = show_image_properties(bus, path, &new_line);
    1130             :                 else
    1131           0 :                         r = show_image_info(bus, path, &new_line);
    1132             :         }
    1133             : 
    1134           0 :         return r;
    1135             : }
    1136             : 
    1137           0 : static int kill_machine(int argc, char *argv[], void *userdata) {
    1138           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1139           0 :         sd_bus *bus = userdata;
    1140             :         int r, i;
    1141             : 
    1142           0 :         assert(bus);
    1143             : 
    1144           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1145             : 
    1146           0 :         if (!arg_kill_who)
    1147           0 :                 arg_kill_who = "all";
    1148             : 
    1149           0 :         for (i = 1; i < argc; i++) {
    1150           0 :                 r = sd_bus_call_method(
    1151             :                                 bus,
    1152             :                                 "org.freedesktop.machine1",
    1153             :                                 "/org/freedesktop/machine1",
    1154             :                                 "org.freedesktop.machine1.Manager",
    1155             :                                 "KillMachine",
    1156             :                                 &error,
    1157             :                                 NULL,
    1158           0 :                                 "ssi", argv[i], arg_kill_who, arg_signal);
    1159           0 :                 if (r < 0)
    1160           0 :                         return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, -r));
    1161             :         }
    1162             : 
    1163           0 :         return 0;
    1164             : }
    1165             : 
    1166           0 : static int reboot_machine(int argc, char *argv[], void *userdata) {
    1167           0 :         arg_kill_who = "leader";
    1168           0 :         arg_signal = SIGINT; /* sysvinit + systemd */
    1169             : 
    1170           0 :         return kill_machine(argc, argv, userdata);
    1171             : }
    1172             : 
    1173           0 : static int poweroff_machine(int argc, char *argv[], void *userdata) {
    1174           0 :         arg_kill_who = "leader";
    1175           0 :         arg_signal = SIGRTMIN+4; /* only systemd */
    1176             : 
    1177           0 :         return kill_machine(argc, argv, userdata);
    1178             : }
    1179             : 
    1180           0 : static int terminate_machine(int argc, char *argv[], void *userdata) {
    1181           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1182           0 :         sd_bus *bus = userdata;
    1183             :         int r, i;
    1184             : 
    1185           0 :         assert(bus);
    1186             : 
    1187           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1188             : 
    1189           0 :         for (i = 1; i < argc; i++) {
    1190           0 :                 r = sd_bus_call_method(
    1191             :                                 bus,
    1192             :                                 "org.freedesktop.machine1",
    1193             :                                 "/org/freedesktop/machine1",
    1194             :                                 "org.freedesktop.machine1.Manager",
    1195             :                                 "TerminateMachine",
    1196             :                                 &error,
    1197             :                                 NULL,
    1198           0 :                                 "s", argv[i]);
    1199           0 :                 if (r < 0)
    1200           0 :                         return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, -r));
    1201             :         }
    1202             : 
    1203           0 :         return 0;
    1204             : }
    1205             : 
    1206           0 : static int copy_files(int argc, char *argv[], void *userdata) {
    1207           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1208           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    1209           0 :         _cleanup_free_ char *abs_host_path = NULL;
    1210             :         char *dest, *host_path, *container_path;
    1211           0 :         sd_bus *bus = userdata;
    1212             :         bool copy_from;
    1213             :         int r;
    1214             : 
    1215           0 :         assert(bus);
    1216             : 
    1217           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1218             : 
    1219           0 :         copy_from = streq(argv[0], "copy-from");
    1220           0 :         dest = argv[3] ?: argv[2];
    1221           0 :         host_path = copy_from ? dest : argv[2];
    1222           0 :         container_path = copy_from ? argv[2] : dest;
    1223             : 
    1224           0 :         if (!path_is_absolute(host_path)) {
    1225           0 :                 r = path_make_absolute_cwd(host_path, &abs_host_path);
    1226           0 :                 if (r < 0)
    1227           0 :                         return log_error_errno(r, "Failed to make path absolute: %m");
    1228             : 
    1229           0 :                 host_path = abs_host_path;
    1230             :         }
    1231             : 
    1232           0 :         r = sd_bus_message_new_method_call(
    1233             :                         bus,
    1234             :                         &m,
    1235             :                         "org.freedesktop.machine1",
    1236             :                         "/org/freedesktop/machine1",
    1237             :                         "org.freedesktop.machine1.Manager",
    1238             :                         copy_from ? "CopyFromMachine" : "CopyToMachine");
    1239           0 :         if (r < 0)
    1240           0 :                 return bus_log_create_error(r);
    1241             : 
    1242           0 :         r = sd_bus_message_append(
    1243             :                         m,
    1244             :                         "sss",
    1245           0 :                         argv[1],
    1246             :                         copy_from ? container_path : host_path,
    1247             :                         copy_from ? host_path : container_path);
    1248           0 :         if (r < 0)
    1249           0 :                 return bus_log_create_error(r);
    1250             : 
    1251             :         /* This is a slow operation, hence turn off any method call timeouts */
    1252           0 :         r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
    1253           0 :         if (r < 0)
    1254           0 :                 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
    1255             : 
    1256           0 :         return 0;
    1257             : }
    1258             : 
    1259           0 : static int bind_mount(int argc, char *argv[], void *userdata) {
    1260           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1261           0 :         sd_bus *bus = userdata;
    1262             :         int r;
    1263             : 
    1264           0 :         assert(bus);
    1265             : 
    1266           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1267             : 
    1268           0 :         r = sd_bus_call_method(
    1269             :                         bus,
    1270             :                         "org.freedesktop.machine1",
    1271             :                         "/org/freedesktop/machine1",
    1272             :                         "org.freedesktop.machine1.Manager",
    1273             :                         "BindMountMachine",
    1274             :                         &error,
    1275             :                         NULL,
    1276             :                         "sssbb",
    1277           0 :                         argv[1],
    1278           0 :                         argv[2],
    1279           0 :                         argv[3],
    1280             :                         arg_read_only,
    1281             :                         arg_mkdir);
    1282           0 :         if (r < 0)
    1283           0 :                 return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, -r));
    1284             : 
    1285           0 :         return 0;
    1286             : }
    1287             : 
    1288           0 : static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
    1289           0 :         PTYForward ** forward = (PTYForward**) userdata;
    1290             :         int r;
    1291             : 
    1292           0 :         assert(m);
    1293           0 :         assert(forward);
    1294             : 
    1295           0 :         if (*forward) {
    1296             :                 /* If the forwarder is already initialized, tell it to
    1297             :                  * exit on the next vhangup(), so that we still flush
    1298             :                  * out what might be queued and exit then. */
    1299             : 
    1300           0 :                 r = pty_forward_set_ignore_vhangup(*forward, false);
    1301           0 :                 if (r >= 0)
    1302           0 :                         return 0;
    1303             : 
    1304           0 :                 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
    1305             :         }
    1306             : 
    1307             :         /* On error, or when the forwarder is not initialized yet, quit immediately */
    1308           0 :         sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
    1309           0 :         return 0;
    1310             : }
    1311             : 
    1312           0 : static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
    1313           0 :         char last_char = 0;
    1314             :         bool machine_died;
    1315             :         int r;
    1316             : 
    1317           0 :         assert(event);
    1318           0 :         assert(master >= 0);
    1319           0 :         assert(name);
    1320             : 
    1321           0 :         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
    1322             : 
    1323           0 :         if (!arg_quiet) {
    1324           0 :                 if (streq(name, ".host"))
    1325           0 :                         log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
    1326             :                 else
    1327           0 :                         log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
    1328             :         }
    1329             : 
    1330           0 :         (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
    1331           0 :         (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
    1332             : 
    1333           0 :         r = pty_forward_new(event, master, flags, forward);
    1334           0 :         if (r < 0)
    1335           0 :                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
    1336             : 
    1337           0 :         r = sd_event_loop(event);
    1338           0 :         if (r < 0)
    1339           0 :                 return log_error_errno(r, "Failed to run event loop: %m");
    1340             : 
    1341           0 :         pty_forward_get_last_char(*forward, &last_char);
    1342             : 
    1343           0 :         machine_died =
    1344           0 :                 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
    1345           0 :                 pty_forward_get_ignore_vhangup(*forward) == 0;
    1346             : 
    1347           0 :         *forward = pty_forward_free(*forward);
    1348             : 
    1349           0 :         if (last_char != '\n')
    1350           0 :                 fputc('\n', stdout);
    1351             : 
    1352           0 :         if (!arg_quiet) {
    1353           0 :                 if (machine_died)
    1354           0 :                         log_info("Machine %s terminated.", name);
    1355           0 :                 else if (streq(name, ".host"))
    1356           0 :                         log_info("Connection to the local host terminated.");
    1357             :                 else
    1358           0 :                         log_info("Connection to machine %s terminated.", name);
    1359             :         }
    1360             : 
    1361           0 :         return 0;
    1362             : }
    1363             : 
    1364           0 : static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
    1365             :         /*
    1366             :          * Whatever is specified in the spec takes priority over global arguments.
    1367             :          */
    1368           0 :         char *_uid = NULL;
    1369           0 :         const char *_machine = NULL;
    1370             : 
    1371           0 :         if (spec) {
    1372             :                 const char *at;
    1373             : 
    1374           0 :                 at = strchr(spec, '@');
    1375           0 :                 if (at) {
    1376           0 :                         if (at == spec)
    1377             :                                 /* Do the same as ssh and refuse "@host". */
    1378           0 :                                 return -EINVAL;
    1379             : 
    1380           0 :                         _machine = at + 1;
    1381           0 :                         _uid = strndup(spec, at - spec);
    1382           0 :                         if (!_uid)
    1383           0 :                                 return -ENOMEM;
    1384             :                 } else
    1385           0 :                         _machine = spec;
    1386             :         };
    1387             : 
    1388           0 :         if (arg_uid && !_uid) {
    1389           0 :                 _uid = strdup(arg_uid);
    1390           0 :                 if (!_uid)
    1391           0 :                         return -ENOMEM;
    1392             :         }
    1393             : 
    1394           0 :         *uid = _uid;
    1395           0 :         *machine = isempty(_machine) ? ".host" : _machine;
    1396           0 :         return 0;
    1397             : }
    1398             : 
    1399           0 : static int login_machine(int argc, char *argv[], void *userdata) {
    1400           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1401           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1402           0 :         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
    1403           0 :         _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
    1404           0 :         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
    1405           0 :         int master = -1, r;
    1406           0 :         sd_bus *bus = userdata;
    1407             :         const char *match, *machine;
    1408             : 
    1409           0 :         assert(bus);
    1410             : 
    1411           0 :         if (!strv_isempty(arg_setenv) || arg_uid) {
    1412           0 :                 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
    1413           0 :                 return -EINVAL;
    1414             :         }
    1415             : 
    1416           0 :         if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
    1417           0 :                 log_error("Login only supported on local machines.");
    1418           0 :                 return -EOPNOTSUPP;
    1419             :         }
    1420             : 
    1421           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1422             : 
    1423           0 :         r = sd_event_default(&event);
    1424           0 :         if (r < 0)
    1425           0 :                 return log_error_errno(r, "Failed to get event loop: %m");
    1426             : 
    1427           0 :         r = sd_bus_attach_event(bus, event, 0);
    1428           0 :         if (r < 0)
    1429           0 :                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
    1430             : 
    1431           0 :         machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
    1432             : 
    1433           0 :         match = strjoina("type='signal',"
    1434             :                          "sender='org.freedesktop.machine1',"
    1435             :                          "path='/org/freedesktop/machine1',",
    1436             :                          "interface='org.freedesktop.machine1.Manager',"
    1437             :                          "member='MachineRemoved',"
    1438             :                          "arg0='", machine, "'");
    1439             : 
    1440           0 :         r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
    1441           0 :         if (r < 0)
    1442           0 :                 return log_error_errno(r, "Failed to request machine removal match: %m");
    1443             : 
    1444           0 :         r = sd_bus_call_method(
    1445             :                         bus,
    1446             :                         "org.freedesktop.machine1",
    1447             :                         "/org/freedesktop/machine1",
    1448             :                         "org.freedesktop.machine1.Manager",
    1449             :                         "OpenMachineLogin",
    1450             :                         &error,
    1451             :                         &reply,
    1452             :                         "s", machine);
    1453           0 :         if (r < 0)
    1454           0 :                 return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, -r));
    1455             : 
    1456           0 :         r = sd_bus_message_read(reply, "hs", &master, NULL);
    1457           0 :         if (r < 0)
    1458           0 :                 return bus_log_parse_error(r);
    1459             : 
    1460           0 :         return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
    1461             : }
    1462             : 
    1463           0 : static int shell_machine(int argc, char *argv[], void *userdata) {
    1464           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
    1465           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1466           0 :         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
    1467           0 :         _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
    1468           0 :         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
    1469           0 :         int master = -1, r;
    1470           0 :         sd_bus *bus = userdata;
    1471             :         const char *match, *machine, *path;
    1472           0 :         _cleanup_free_ char *uid = NULL;
    1473             : 
    1474           0 :         assert(bus);
    1475             : 
    1476           0 :         if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
    1477           0 :                 log_error("Shell only supported on local machines.");
    1478           0 :                 return -EOPNOTSUPP;
    1479             :         }
    1480             : 
    1481             :         /* Pass $TERM to shell session, if not explicitly specified. */
    1482           0 :         if (!strv_find_prefix(arg_setenv, "TERM=")) {
    1483             :                 const char *t;
    1484             : 
    1485           0 :                 t = strv_find_prefix(environ, "TERM=");
    1486           0 :                 if (t) {
    1487           0 :                         if (strv_extend(&arg_setenv, t) < 0)
    1488           0 :                                 return log_oom();
    1489             :                 }
    1490             :         }
    1491             : 
    1492           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1493             : 
    1494           0 :         r = sd_event_default(&event);
    1495           0 :         if (r < 0)
    1496           0 :                 return log_error_errno(r, "Failed to get event loop: %m");
    1497             : 
    1498           0 :         r = sd_bus_attach_event(bus, event, 0);
    1499           0 :         if (r < 0)
    1500           0 :                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
    1501             : 
    1502           0 :         r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
    1503           0 :         if (r < 0)
    1504           0 :                 return log_error_errno(r, "Failed to parse machine specification: %m");
    1505             : 
    1506           0 :         match = strjoina("type='signal',"
    1507             :                          "sender='org.freedesktop.machine1',"
    1508             :                          "path='/org/freedesktop/machine1',",
    1509             :                          "interface='org.freedesktop.machine1.Manager',"
    1510             :                          "member='MachineRemoved',"
    1511             :                          "arg0='", machine, "'");
    1512             : 
    1513           0 :         r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
    1514           0 :         if (r < 0)
    1515           0 :                 return log_error_errno(r, "Failed to request machine removal match: %m");
    1516             : 
    1517           0 :         r = sd_bus_message_new_method_call(
    1518             :                         bus,
    1519             :                         &m,
    1520             :                         "org.freedesktop.machine1",
    1521             :                         "/org/freedesktop/machine1",
    1522             :                         "org.freedesktop.machine1.Manager",
    1523             :                         "OpenMachineShell");
    1524           0 :         if (r < 0)
    1525           0 :                 return bus_log_create_error(r);
    1526             : 
    1527           0 :         path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
    1528             : 
    1529           0 :         r = sd_bus_message_append(m, "sss", machine, uid, path);
    1530           0 :         if (r < 0)
    1531           0 :                 return bus_log_create_error(r);
    1532             : 
    1533           0 :         r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
    1534           0 :         if (r < 0)
    1535           0 :                 return bus_log_create_error(r);
    1536             : 
    1537           0 :         r = sd_bus_message_append_strv(m, arg_setenv);
    1538           0 :         if (r < 0)
    1539           0 :                 return bus_log_create_error(r);
    1540             : 
    1541           0 :         r = sd_bus_call(bus, m, 0, &error, &reply);
    1542           0 :         if (r < 0)
    1543           0 :                 return log_error_errno(r, "Failed to get shell PTY: %s", bus_error_message(&error, -r));
    1544             : 
    1545           0 :         r = sd_bus_message_read(reply, "hs", &master, NULL);
    1546           0 :         if (r < 0)
    1547           0 :                 return bus_log_parse_error(r);
    1548             : 
    1549           0 :         return process_forward(event, &forward, master, 0, machine);
    1550             : }
    1551             : 
    1552           0 : static int remove_image(int argc, char *argv[], void *userdata) {
    1553           0 :         sd_bus *bus = userdata;
    1554             :         int r, i;
    1555             : 
    1556           0 :         assert(bus);
    1557             : 
    1558           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1559             : 
    1560           0 :         for (i = 1; i < argc; i++) {
    1561           0 :                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1562           0 :                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    1563             : 
    1564           0 :                 r = sd_bus_message_new_method_call(
    1565             :                                 bus,
    1566             :                                 &m,
    1567             :                                 "org.freedesktop.machine1",
    1568             :                                 "/org/freedesktop/machine1",
    1569             :                                 "org.freedesktop.machine1.Manager",
    1570             :                                 "RemoveImage");
    1571           0 :                 if (r < 0)
    1572           0 :                         return bus_log_create_error(r);
    1573             : 
    1574           0 :                 r = sd_bus_message_append(m, "s", argv[i]);
    1575           0 :                 if (r < 0)
    1576           0 :                         return bus_log_create_error(r);
    1577             : 
    1578             :                 /* This is a slow operation, hence turn off any method call timeouts */
    1579           0 :                 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
    1580           0 :                 if (r < 0)
    1581           0 :                         return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
    1582             :         }
    1583             : 
    1584           0 :         return 0;
    1585             : }
    1586             : 
    1587           0 : static int rename_image(int argc, char *argv[], void *userdata) {
    1588           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1589           0 :         sd_bus *bus = userdata;
    1590             :         int r;
    1591             : 
    1592           0 :         assert(bus);
    1593             : 
    1594           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1595             : 
    1596           0 :         r = sd_bus_call_method(
    1597             :                         bus,
    1598             :                         "org.freedesktop.machine1",
    1599             :                         "/org/freedesktop/machine1",
    1600             :                         "org.freedesktop.machine1.Manager",
    1601             :                         "RenameImage",
    1602             :                         &error,
    1603             :                         NULL,
    1604           0 :                         "ss", argv[1], argv[2]);
    1605           0 :         if (r < 0)
    1606           0 :                 return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, -r));
    1607             : 
    1608           0 :         return 0;
    1609             : }
    1610             : 
    1611           0 : static int clone_image(int argc, char *argv[], void *userdata) {
    1612           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1613           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    1614           0 :         sd_bus *bus = userdata;
    1615             :         int r;
    1616             : 
    1617           0 :         assert(bus);
    1618             : 
    1619           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1620             : 
    1621           0 :         r = sd_bus_message_new_method_call(
    1622             :                         bus,
    1623             :                         &m,
    1624             :                         "org.freedesktop.machine1",
    1625             :                         "/org/freedesktop/machine1",
    1626             :                         "org.freedesktop.machine1.Manager",
    1627             :                         "CloneImage");
    1628           0 :         if (r < 0)
    1629           0 :                 return bus_log_create_error(r);
    1630             : 
    1631           0 :         r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
    1632           0 :         if (r < 0)
    1633           0 :                 return bus_log_create_error(r);
    1634             : 
    1635             :         /* This is a slow operation, hence turn off any method call timeouts */
    1636           0 :         r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
    1637           0 :         if (r < 0)
    1638           0 :                 return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
    1639             : 
    1640           0 :         return 0;
    1641             : }
    1642             : 
    1643           0 : static int read_only_image(int argc, char *argv[], void *userdata) {
    1644           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1645           0 :         sd_bus *bus = userdata;
    1646           0 :         int b = true, r;
    1647             : 
    1648           0 :         assert(bus);
    1649             : 
    1650           0 :         if (argc > 2) {
    1651           0 :                 b = parse_boolean(argv[2]);
    1652           0 :                 if (b < 0) {
    1653           0 :                         log_error("Failed to parse boolean argument: %s", argv[2]);
    1654           0 :                         return -EINVAL;
    1655             :                 }
    1656             :         }
    1657             : 
    1658           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1659             : 
    1660           0 :         r = sd_bus_call_method(
    1661             :                         bus,
    1662             :                         "org.freedesktop.machine1",
    1663             :                         "/org/freedesktop/machine1",
    1664             :                         "org.freedesktop.machine1.Manager",
    1665             :                         "MarkImageReadOnly",
    1666             :                         &error,
    1667             :                         NULL,
    1668           0 :                         "sb", argv[1], b);
    1669           0 :         if (r < 0)
    1670           0 :                 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, -r));
    1671             : 
    1672           0 :         return 0;
    1673             : }
    1674             : 
    1675           0 : static int image_exists(sd_bus *bus, const char *name) {
    1676           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1677             :         int r;
    1678             : 
    1679           0 :         assert(bus);
    1680           0 :         assert(name);
    1681             : 
    1682           0 :         r = sd_bus_call_method(
    1683             :                         bus,
    1684             :                         "org.freedesktop.machine1",
    1685             :                         "/org/freedesktop/machine1",
    1686             :                         "org.freedesktop.machine1.Manager",
    1687             :                         "GetImage",
    1688             :                         &error,
    1689             :                         NULL,
    1690             :                         "s", name);
    1691           0 :         if (r < 0) {
    1692           0 :                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
    1693           0 :                         return 0;
    1694             : 
    1695           0 :                 return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r));
    1696             :         }
    1697             : 
    1698           0 :         return 1;
    1699             : }
    1700             : 
    1701           0 : static int make_service_name(const char *name, char **ret) {
    1702             :         int r;
    1703             : 
    1704           0 :         assert(name);
    1705           0 :         assert(ret);
    1706             : 
    1707           0 :         if (!machine_name_is_valid(name))
    1708           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1709             :                                        "Invalid machine name %s.", name);
    1710             : 
    1711           0 :         r = unit_name_build("systemd-nspawn", name, ".service", ret);
    1712           0 :         if (r < 0)
    1713           0 :                 return log_error_errno(r, "Failed to build unit name: %m");
    1714             : 
    1715           0 :         return 0;
    1716             : }
    1717             : 
    1718           0 : static int start_machine(int argc, char *argv[], void *userdata) {
    1719           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1720           0 :         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
    1721           0 :         sd_bus *bus = userdata;
    1722             :         int r, i;
    1723             : 
    1724           0 :         assert(bus);
    1725             : 
    1726           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1727             : 
    1728           0 :         r = bus_wait_for_jobs_new(bus, &w);
    1729           0 :         if (r < 0)
    1730           0 :                 return log_oom();
    1731             : 
    1732           0 :         for (i = 1; i < argc; i++) {
    1733           0 :                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1734           0 :                 _cleanup_free_ char *unit = NULL;
    1735             :                 const char *object;
    1736             : 
    1737           0 :                 r = make_service_name(argv[i], &unit);
    1738           0 :                 if (r < 0)
    1739           0 :                         return r;
    1740             : 
    1741           0 :                 r = image_exists(bus, argv[i]);
    1742           0 :                 if (r < 0)
    1743           0 :                         return r;
    1744           0 :                 if (r == 0) {
    1745           0 :                         log_error("Machine image '%s' does not exist.", argv[i]);
    1746           0 :                         return -ENXIO;
    1747             :                 }
    1748             : 
    1749           0 :                 r = sd_bus_call_method(
    1750             :                                 bus,
    1751             :                                 "org.freedesktop.systemd1",
    1752             :                                 "/org/freedesktop/systemd1",
    1753             :                                 "org.freedesktop.systemd1.Manager",
    1754             :                                 "StartUnit",
    1755             :                                 &error,
    1756             :                                 &reply,
    1757             :                                 "ss", unit, "fail");
    1758           0 :                 if (r < 0)
    1759           0 :                         return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, -r));
    1760             : 
    1761           0 :                 r = sd_bus_message_read(reply, "o", &object);
    1762           0 :                 if (r < 0)
    1763           0 :                         return bus_log_parse_error(r);
    1764             : 
    1765           0 :                 r = bus_wait_for_jobs_add(w, object);
    1766           0 :                 if (r < 0)
    1767           0 :                         return log_oom();
    1768             :         }
    1769             : 
    1770           0 :         r = bus_wait_for_jobs(w, arg_quiet, NULL);
    1771           0 :         if (r < 0)
    1772           0 :                 return r;
    1773             : 
    1774           0 :         return 0;
    1775             : }
    1776             : 
    1777           0 : static int enable_machine(int argc, char *argv[], void *userdata) {
    1778           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
    1779           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1780           0 :         UnitFileChange *changes = NULL;
    1781           0 :         size_t n_changes = 0;
    1782           0 :         const char *method = NULL;
    1783           0 :         sd_bus *bus = userdata;
    1784             :         int r, i;
    1785             : 
    1786           0 :         assert(bus);
    1787             : 
    1788           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1789             : 
    1790           0 :         method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
    1791             : 
    1792           0 :         r = sd_bus_message_new_method_call(
    1793             :                         bus,
    1794             :                         &m,
    1795             :                         "org.freedesktop.systemd1",
    1796             :                         "/org/freedesktop/systemd1",
    1797             :                         "org.freedesktop.systemd1.Manager",
    1798             :                         method);
    1799           0 :         if (r < 0)
    1800           0 :                 return bus_log_create_error(r);
    1801             : 
    1802           0 :         r = sd_bus_message_open_container(m, 'a', "s");
    1803           0 :         if (r < 0)
    1804           0 :                 return bus_log_create_error(r);
    1805             : 
    1806           0 :         for (i = 1; i < argc; i++) {
    1807           0 :                 _cleanup_free_ char *unit = NULL;
    1808             : 
    1809           0 :                 r = make_service_name(argv[i], &unit);
    1810           0 :                 if (r < 0)
    1811           0 :                         return r;
    1812             : 
    1813           0 :                 r = image_exists(bus, argv[i]);
    1814           0 :                 if (r < 0)
    1815           0 :                         return r;
    1816           0 :                 if (r == 0) {
    1817           0 :                         log_error("Machine image '%s' does not exist.", argv[i]);
    1818           0 :                         return -ENXIO;
    1819             :                 }
    1820             : 
    1821           0 :                 r = sd_bus_message_append(m, "s", unit);
    1822           0 :                 if (r < 0)
    1823           0 :                         return bus_log_create_error(r);
    1824             :         }
    1825             : 
    1826           0 :         r = sd_bus_message_close_container(m);
    1827           0 :         if (r < 0)
    1828           0 :                 return bus_log_create_error(r);
    1829             : 
    1830           0 :         if (streq(argv[0], "enable"))
    1831           0 :                 r = sd_bus_message_append(m, "bb", false, false);
    1832             :         else
    1833           0 :                 r = sd_bus_message_append(m, "b", false);
    1834           0 :         if (r < 0)
    1835           0 :                 return bus_log_create_error(r);
    1836             : 
    1837           0 :         r = sd_bus_call(bus, m, 0, &error, &reply);
    1838           0 :         if (r < 0)
    1839           0 :                 return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, -r));
    1840             : 
    1841           0 :         if (streq(argv[0], "enable")) {
    1842           0 :                 r = sd_bus_message_read(reply, "b", NULL);
    1843           0 :                 if (r < 0)
    1844           0 :                         return bus_log_parse_error(r);
    1845             :         }
    1846             : 
    1847           0 :         r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
    1848           0 :         if (r < 0)
    1849           0 :                 goto finish;
    1850             : 
    1851           0 :         r = sd_bus_call_method(
    1852             :                         bus,
    1853             :                         "org.freedesktop.systemd1",
    1854             :                         "/org/freedesktop/systemd1",
    1855             :                         "org.freedesktop.systemd1.Manager",
    1856             :                         "Reload",
    1857             :                         &error,
    1858             :                         NULL,
    1859             :                         NULL);
    1860           0 :         if (r < 0) {
    1861           0 :                 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
    1862           0 :                 goto finish;
    1863             :         }
    1864             : 
    1865           0 :         r = 0;
    1866             : 
    1867           0 : finish:
    1868           0 :         unit_file_changes_free(changes, n_changes);
    1869             : 
    1870           0 :         return r;
    1871             : }
    1872             : 
    1873           0 : static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
    1874           0 :         const char **our_path = userdata, *line;
    1875             :         unsigned priority;
    1876             :         int r;
    1877             : 
    1878           0 :         assert(m);
    1879           0 :         assert(our_path);
    1880             : 
    1881           0 :         r = sd_bus_message_read(m, "us", &priority, &line);
    1882           0 :         if (r < 0) {
    1883           0 :                 bus_log_parse_error(r);
    1884           0 :                 return 0;
    1885             :         }
    1886             : 
    1887           0 :         if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
    1888           0 :                 return 0;
    1889             : 
    1890           0 :         if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
    1891           0 :                 return 0;
    1892             : 
    1893           0 :         log_full(priority, "%s", line);
    1894           0 :         return 0;
    1895             : }
    1896             : 
    1897           0 : static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
    1898           0 :         const char **our_path = userdata, *path, *result;
    1899             :         uint32_t id;
    1900             :         int r;
    1901             : 
    1902           0 :         assert(m);
    1903           0 :         assert(our_path);
    1904             : 
    1905           0 :         r = sd_bus_message_read(m, "uos", &id, &path, &result);
    1906           0 :         if (r < 0) {
    1907           0 :                 bus_log_parse_error(r);
    1908           0 :                 return 0;
    1909             :         }
    1910             : 
    1911           0 :         if (!streq_ptr(*our_path, path))
    1912           0 :                 return 0;
    1913             : 
    1914           0 :         sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
    1915           0 :         return 0;
    1916             : }
    1917             : 
    1918           0 : static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
    1919           0 :         assert(s);
    1920           0 :         assert(si);
    1921             : 
    1922           0 :         if (!arg_quiet)
    1923           0 :                 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
    1924             : 
    1925           0 :         sd_event_exit(sd_event_source_get_event(s), EINTR);
    1926           0 :         return 0;
    1927             : }
    1928             : 
    1929           0 : static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
    1930           0 :         _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
    1931           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1932           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1933           0 :         _cleanup_(sd_event_unrefp) sd_event* event = NULL;
    1934           0 :         const char *path = NULL;
    1935             :         uint32_t id;
    1936             :         int r;
    1937             : 
    1938           0 :         assert(bus);
    1939           0 :         assert(m);
    1940             : 
    1941           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    1942             : 
    1943           0 :         r = sd_event_default(&event);
    1944           0 :         if (r < 0)
    1945           0 :                 return log_error_errno(r, "Failed to get event loop: %m");
    1946             : 
    1947           0 :         r = sd_bus_attach_event(bus, event, 0);
    1948           0 :         if (r < 0)
    1949           0 :                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
    1950             : 
    1951           0 :         r = sd_bus_match_signal_async(
    1952             :                         bus,
    1953             :                         &slot_job_removed,
    1954             :                         "org.freedesktop.import1",
    1955             :                         "/org/freedesktop/import1",
    1956             :                         "org.freedesktop.import1.Manager",
    1957             :                         "TransferRemoved",
    1958             :                         match_transfer_removed, NULL, &path);
    1959           0 :         if (r < 0)
    1960           0 :                 return log_error_errno(r, "Failed to request match: %m");
    1961             : 
    1962           0 :         r = sd_bus_match_signal_async(
    1963             :                         bus,
    1964             :                         &slot_log_message,
    1965             :                         "org.freedesktop.import1",
    1966             :                         NULL,
    1967             :                         "org.freedesktop.import1.Transfer",
    1968             :                         "LogMessage",
    1969             :                         match_log_message, NULL, &path);
    1970           0 :         if (r < 0)
    1971           0 :                 return log_error_errno(r, "Failed to request match: %m");
    1972             : 
    1973           0 :         r = sd_bus_call(bus, m, 0, &error, &reply);
    1974           0 :         if (r < 0)
    1975           0 :                 return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, -r));
    1976             : 
    1977           0 :         r = sd_bus_message_read(reply, "uo", &id, &path);
    1978           0 :         if (r < 0)
    1979           0 :                 return bus_log_parse_error(r);
    1980             : 
    1981           0 :         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
    1982             : 
    1983           0 :         if (!arg_quiet)
    1984           0 :                 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
    1985             : 
    1986           0 :         (void) sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
    1987           0 :         (void) sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
    1988             : 
    1989           0 :         r = sd_event_loop(event);
    1990           0 :         if (r < 0)
    1991           0 :                 return log_error_errno(r, "Failed to run event loop: %m");
    1992             : 
    1993           0 :         return -r;
    1994             : }
    1995             : 
    1996           0 : static int import_tar(int argc, char *argv[], void *userdata) {
    1997           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    1998           0 :         _cleanup_free_ char *ll = NULL, *fn = NULL;
    1999           0 :         const char *local = NULL, *path = NULL;
    2000           0 :         _cleanup_close_ int fd = -1;
    2001           0 :         sd_bus *bus = userdata;
    2002             :         int r;
    2003             : 
    2004           0 :         assert(bus);
    2005             : 
    2006           0 :         if (argc >= 2)
    2007           0 :                 path = empty_or_dash_to_null(argv[1]);
    2008             : 
    2009           0 :         if (argc >= 3)
    2010           0 :                 local = empty_or_dash_to_null(argv[2]);
    2011           0 :         else if (path) {
    2012           0 :                 r = path_extract_filename(path, &fn);
    2013           0 :                 if (r < 0)
    2014           0 :                         return log_error_errno(r, "Cannot extract container name from filename: %m");
    2015             : 
    2016           0 :                 local = fn;
    2017             :         }
    2018           0 :         if (!local) {
    2019           0 :                 log_error("Need either path or local name.");
    2020           0 :                 return -EINVAL;
    2021             :         }
    2022             : 
    2023           0 :         r = tar_strip_suffixes(local, &ll);
    2024           0 :         if (r < 0)
    2025           0 :                 return log_oom();
    2026             : 
    2027           0 :         local = ll;
    2028             : 
    2029           0 :         if (!machine_name_is_valid(local)) {
    2030           0 :                 log_error("Local name %s is not a suitable machine name.", local);
    2031           0 :                 return -EINVAL;
    2032             :         }
    2033             : 
    2034           0 :         if (path) {
    2035           0 :                 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
    2036           0 :                 if (fd < 0)
    2037           0 :                         return log_error_errno(errno, "Failed to open %s: %m", path);
    2038             :         }
    2039             : 
    2040           0 :         r = sd_bus_message_new_method_call(
    2041             :                         bus,
    2042             :                         &m,
    2043             :                         "org.freedesktop.import1",
    2044             :                         "/org/freedesktop/import1",
    2045             :                         "org.freedesktop.import1.Manager",
    2046             :                         "ImportTar");
    2047           0 :         if (r < 0)
    2048           0 :                 return bus_log_create_error(r);
    2049             : 
    2050           0 :         r = sd_bus_message_append(
    2051             :                         m,
    2052             :                         "hsbb",
    2053             :                         fd >= 0 ? fd : STDIN_FILENO,
    2054             :                         local,
    2055             :                         arg_force,
    2056             :                         arg_read_only);
    2057           0 :         if (r < 0)
    2058           0 :                 return bus_log_create_error(r);
    2059             : 
    2060           0 :         return transfer_image_common(bus, m);
    2061             : }
    2062             : 
    2063           0 : static int import_raw(int argc, char *argv[], void *userdata) {
    2064           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2065           0 :         _cleanup_free_ char *ll = NULL, *fn = NULL;
    2066           0 :         const char *local = NULL, *path = NULL;
    2067           0 :         _cleanup_close_ int fd = -1;
    2068           0 :         sd_bus *bus = userdata;
    2069             :         int r;
    2070             : 
    2071           0 :         assert(bus);
    2072             : 
    2073           0 :         if (argc >= 2)
    2074           0 :                 path = empty_or_dash_to_null(argv[1]);
    2075             : 
    2076           0 :         if (argc >= 3)
    2077           0 :                 local = empty_or_dash_to_null(argv[2]);
    2078           0 :         else if (path) {
    2079           0 :                 r = path_extract_filename(path, &fn);
    2080           0 :                 if (r < 0)
    2081           0 :                         return log_error_errno(r, "Cannot extract container name from filename: %m");
    2082             : 
    2083           0 :                 local = fn;
    2084             :         }
    2085           0 :         if (!local) {
    2086           0 :                 log_error("Need either path or local name.");
    2087           0 :                 return -EINVAL;
    2088             :         }
    2089             : 
    2090           0 :         r = raw_strip_suffixes(local, &ll);
    2091           0 :         if (r < 0)
    2092           0 :                 return log_oom();
    2093             : 
    2094           0 :         local = ll;
    2095             : 
    2096           0 :         if (!machine_name_is_valid(local)) {
    2097           0 :                 log_error("Local name %s is not a suitable machine name.", local);
    2098           0 :                 return -EINVAL;
    2099             :         }
    2100             : 
    2101           0 :         if (path) {
    2102           0 :                 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
    2103           0 :                 if (fd < 0)
    2104           0 :                         return log_error_errno(errno, "Failed to open %s: %m", path);
    2105             :         }
    2106             : 
    2107           0 :         r = sd_bus_message_new_method_call(
    2108             :                         bus,
    2109             :                         &m,
    2110             :                         "org.freedesktop.import1",
    2111             :                         "/org/freedesktop/import1",
    2112             :                         "org.freedesktop.import1.Manager",
    2113             :                         "ImportRaw");
    2114           0 :         if (r < 0)
    2115           0 :                 return bus_log_create_error(r);
    2116             : 
    2117           0 :         r = sd_bus_message_append(
    2118             :                         m,
    2119             :                         "hsbb",
    2120             :                         fd >= 0 ? fd : STDIN_FILENO,
    2121             :                         local,
    2122             :                         arg_force,
    2123             :                         arg_read_only);
    2124           0 :         if (r < 0)
    2125           0 :                 return bus_log_create_error(r);
    2126             : 
    2127           0 :         return transfer_image_common(bus, m);
    2128             : }
    2129             : 
    2130           0 : static int import_fs(int argc, char *argv[], void *userdata) {
    2131           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2132           0 :         const char *local = NULL, *path = NULL;
    2133           0 :         _cleanup_free_ char *fn = NULL;
    2134           0 :         _cleanup_close_ int fd = -1;
    2135           0 :         sd_bus *bus = userdata;
    2136             :         int r;
    2137             : 
    2138           0 :         assert(bus);
    2139             : 
    2140           0 :         if (argc >= 2)
    2141           0 :                 path = empty_or_dash_to_null(argv[1]);
    2142             : 
    2143           0 :         if (argc >= 3)
    2144           0 :                 local = empty_or_dash_to_null(argv[2]);
    2145           0 :         else if (path) {
    2146           0 :                 r = path_extract_filename(path, &fn);
    2147           0 :                 if (r < 0)
    2148           0 :                         return log_error_errno(r, "Cannot extract container name from filename: %m");
    2149             : 
    2150           0 :                 local = fn;
    2151             :         }
    2152           0 :         if (!local) {
    2153           0 :                 log_error("Need either path or local name.");
    2154           0 :                 return -EINVAL;
    2155             :         }
    2156             : 
    2157           0 :         if (!machine_name_is_valid(local)) {
    2158           0 :                 log_error("Local name %s is not a suitable machine name.", local);
    2159           0 :                 return -EINVAL;
    2160             :         }
    2161             : 
    2162           0 :         if (path) {
    2163           0 :                 fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
    2164           0 :                 if (fd < 0)
    2165           0 :                         return log_error_errno(errno, "Failed to open directory '%s': %m", path);
    2166             :         }
    2167             : 
    2168           0 :         r = sd_bus_message_new_method_call(
    2169             :                         bus,
    2170             :                         &m,
    2171             :                         "org.freedesktop.import1",
    2172             :                         "/org/freedesktop/import1",
    2173             :                         "org.freedesktop.import1.Manager",
    2174             :                         "ImportFileSystem");
    2175           0 :         if (r < 0)
    2176           0 :                 return bus_log_create_error(r);
    2177             : 
    2178           0 :         r = sd_bus_message_append(
    2179             :                         m,
    2180             :                         "hsbb",
    2181             :                         fd >= 0 ? fd : STDIN_FILENO,
    2182             :                         local,
    2183             :                         arg_force,
    2184             :                         arg_read_only);
    2185           0 :         if (r < 0)
    2186           0 :                 return bus_log_create_error(r);
    2187             : 
    2188           0 :         return transfer_image_common(bus, m);
    2189             : }
    2190             : 
    2191           0 : static void determine_compression_from_filename(const char *p) {
    2192           0 :         if (arg_format)
    2193           0 :                 return;
    2194             : 
    2195           0 :         if (!p)
    2196           0 :                 return;
    2197             : 
    2198           0 :         if (endswith(p, ".xz"))
    2199           0 :                 arg_format = "xz";
    2200           0 :         else if (endswith(p, ".gz"))
    2201           0 :                 arg_format = "gzip";
    2202           0 :         else if (endswith(p, ".bz2"))
    2203           0 :                 arg_format = "bzip2";
    2204             : }
    2205             : 
    2206           0 : static int export_tar(int argc, char *argv[], void *userdata) {
    2207           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2208           0 :         _cleanup_close_ int fd = -1;
    2209           0 :         const char *local = NULL, *path = NULL;
    2210           0 :         sd_bus *bus = userdata;
    2211             :         int r;
    2212             : 
    2213           0 :         assert(bus);
    2214             : 
    2215           0 :         local = argv[1];
    2216           0 :         if (!machine_name_is_valid(local)) {
    2217           0 :                 log_error("Machine name %s is not valid.", local);
    2218           0 :                 return -EINVAL;
    2219             :         }
    2220             : 
    2221           0 :         if (argc >= 3)
    2222           0 :                 path = argv[2];
    2223           0 :         path = empty_or_dash_to_null(path);
    2224             : 
    2225           0 :         if (path) {
    2226           0 :                 determine_compression_from_filename(path);
    2227             : 
    2228           0 :                 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
    2229           0 :                 if (fd < 0)
    2230           0 :                         return log_error_errno(errno, "Failed to open %s: %m", path);
    2231             :         }
    2232             : 
    2233           0 :         r = sd_bus_message_new_method_call(
    2234             :                         bus,
    2235             :                         &m,
    2236             :                         "org.freedesktop.import1",
    2237             :                         "/org/freedesktop/import1",
    2238             :                         "org.freedesktop.import1.Manager",
    2239             :                         "ExportTar");
    2240           0 :         if (r < 0)
    2241           0 :                 return bus_log_create_error(r);
    2242             : 
    2243           0 :         r = sd_bus_message_append(
    2244             :                         m,
    2245             :                         "shs",
    2246             :                         local,
    2247           0 :                         fd >= 0 ? fd : STDOUT_FILENO,
    2248             :                         arg_format);
    2249           0 :         if (r < 0)
    2250           0 :                 return bus_log_create_error(r);
    2251             : 
    2252           0 :         return transfer_image_common(bus, m);
    2253             : }
    2254             : 
    2255           0 : static int export_raw(int argc, char *argv[], void *userdata) {
    2256           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2257           0 :         _cleanup_close_ int fd = -1;
    2258           0 :         const char *local = NULL, *path = NULL;
    2259           0 :         sd_bus *bus = userdata;
    2260             :         int r;
    2261             : 
    2262           0 :         assert(bus);
    2263             : 
    2264           0 :         local = argv[1];
    2265           0 :         if (!machine_name_is_valid(local)) {
    2266           0 :                 log_error("Machine name %s is not valid.", local);
    2267           0 :                 return -EINVAL;
    2268             :         }
    2269             : 
    2270           0 :         if (argc >= 3)
    2271           0 :                 path = argv[2];
    2272           0 :         path = empty_or_dash_to_null(path);
    2273             : 
    2274           0 :         if (path) {
    2275           0 :                 determine_compression_from_filename(path);
    2276             : 
    2277           0 :                 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
    2278           0 :                 if (fd < 0)
    2279           0 :                         return log_error_errno(errno, "Failed to open %s: %m", path);
    2280             :         }
    2281             : 
    2282           0 :         r = sd_bus_message_new_method_call(
    2283             :                         bus,
    2284             :                         &m,
    2285             :                         "org.freedesktop.import1",
    2286             :                         "/org/freedesktop/import1",
    2287             :                         "org.freedesktop.import1.Manager",
    2288             :                         "ExportRaw");
    2289           0 :         if (r < 0)
    2290           0 :                 return bus_log_create_error(r);
    2291             : 
    2292           0 :         r = sd_bus_message_append(
    2293             :                         m,
    2294             :                         "shs",
    2295             :                         local,
    2296           0 :                         fd >= 0 ? fd : STDOUT_FILENO,
    2297             :                         arg_format);
    2298           0 :         if (r < 0)
    2299           0 :                 return bus_log_create_error(r);
    2300             : 
    2301           0 :         return transfer_image_common(bus, m);
    2302             : }
    2303             : 
    2304           0 : static int pull_tar(int argc, char *argv[], void *userdata) {
    2305           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2306           0 :         _cleanup_free_ char *l = NULL, *ll = NULL;
    2307             :         const char *local, *remote;
    2308           0 :         sd_bus *bus = userdata;
    2309             :         int r;
    2310             : 
    2311           0 :         assert(bus);
    2312             : 
    2313           0 :         remote = argv[1];
    2314           0 :         if (!http_url_is_valid(remote)) {
    2315           0 :                 log_error("URL '%s' is not valid.", remote);
    2316           0 :                 return -EINVAL;
    2317             :         }
    2318             : 
    2319           0 :         if (argc >= 3)
    2320           0 :                 local = argv[2];
    2321             :         else {
    2322           0 :                 r = import_url_last_component(remote, &l);
    2323           0 :                 if (r < 0)
    2324           0 :                         return log_error_errno(r, "Failed to get final component of URL: %m");
    2325             : 
    2326           0 :                 local = l;
    2327             :         }
    2328             : 
    2329           0 :         local = empty_or_dash_to_null(local);
    2330             : 
    2331           0 :         if (local) {
    2332           0 :                 r = tar_strip_suffixes(local, &ll);
    2333           0 :                 if (r < 0)
    2334           0 :                         return log_oom();
    2335             : 
    2336           0 :                 local = ll;
    2337             : 
    2338           0 :                 if (!machine_name_is_valid(local)) {
    2339           0 :                         log_error("Local name %s is not a suitable machine name.", local);
    2340           0 :                         return -EINVAL;
    2341             :                 }
    2342             :         }
    2343             : 
    2344           0 :         r = sd_bus_message_new_method_call(
    2345             :                         bus,
    2346             :                         &m,
    2347             :                         "org.freedesktop.import1",
    2348             :                         "/org/freedesktop/import1",
    2349             :                         "org.freedesktop.import1.Manager",
    2350             :                         "PullTar");
    2351           0 :         if (r < 0)
    2352           0 :                 return bus_log_create_error(r);
    2353             : 
    2354           0 :         r = sd_bus_message_append(
    2355             :                         m,
    2356             :                         "sssb",
    2357             :                         remote,
    2358             :                         local,
    2359             :                         import_verify_to_string(arg_verify),
    2360             :                         arg_force);
    2361           0 :         if (r < 0)
    2362           0 :                 return bus_log_create_error(r);
    2363             : 
    2364           0 :         return transfer_image_common(bus, m);
    2365             : }
    2366             : 
    2367           0 : static int pull_raw(int argc, char *argv[], void *userdata) {
    2368           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
    2369           0 :         _cleanup_free_ char *l = NULL, *ll = NULL;
    2370             :         const char *local, *remote;
    2371           0 :         sd_bus *bus = userdata;
    2372             :         int r;
    2373             : 
    2374           0 :         assert(bus);
    2375             : 
    2376           0 :         remote = argv[1];
    2377           0 :         if (!http_url_is_valid(remote)) {
    2378           0 :                 log_error("URL '%s' is not valid.", remote);
    2379           0 :                 return -EINVAL;
    2380             :         }
    2381             : 
    2382           0 :         if (argc >= 3)
    2383           0 :                 local = argv[2];
    2384             :         else {
    2385           0 :                 r = import_url_last_component(remote, &l);
    2386           0 :                 if (r < 0)
    2387           0 :                         return log_error_errno(r, "Failed to get final component of URL: %m");
    2388             : 
    2389           0 :                 local = l;
    2390             :         }
    2391             : 
    2392           0 :         local = empty_or_dash_to_null(local);
    2393             : 
    2394           0 :         if (local) {
    2395           0 :                 r = raw_strip_suffixes(local, &ll);
    2396           0 :                 if (r < 0)
    2397           0 :                         return log_oom();
    2398             : 
    2399           0 :                 local = ll;
    2400             : 
    2401           0 :                 if (!machine_name_is_valid(local)) {
    2402           0 :                         log_error("Local name %s is not a suitable machine name.", local);
    2403           0 :                         return -EINVAL;
    2404             :                 }
    2405             :         }
    2406             : 
    2407           0 :         r = sd_bus_message_new_method_call(
    2408             :                         bus,
    2409             :                         &m,
    2410             :                         "org.freedesktop.import1",
    2411             :                         "/org/freedesktop/import1",
    2412             :                         "org.freedesktop.import1.Manager",
    2413             :                         "PullRaw");
    2414           0 :         if (r < 0)
    2415           0 :                 return bus_log_create_error(r);
    2416             : 
    2417           0 :         r = sd_bus_message_append(
    2418             :                         m,
    2419             :                         "sssb",
    2420             :                         remote,
    2421             :                         local,
    2422             :                         import_verify_to_string(arg_verify),
    2423             :                         arg_force);
    2424           0 :         if (r < 0)
    2425           0 :                 return bus_log_create_error(r);
    2426             : 
    2427           0 :         return transfer_image_common(bus, m);
    2428             : }
    2429             : 
    2430             : typedef struct TransferInfo {
    2431             :         uint32_t id;
    2432             :         const char *type;
    2433             :         const char *remote;
    2434             :         const char *local;
    2435             :         double progress;
    2436             : } TransferInfo;
    2437             : 
    2438           0 : static int compare_transfer_info(const TransferInfo *a, const TransferInfo *b) {
    2439           0 :         return strcmp(a->local, b->local);
    2440             : }
    2441             : 
    2442           0 : static int list_transfers(int argc, char *argv[], void *userdata) {
    2443           0 :         size_t max_type = STRLEN("TYPE"), max_local = STRLEN("LOCAL"), max_remote = STRLEN("REMOTE");
    2444           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    2445           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    2446           0 :         _cleanup_free_ TransferInfo *transfers = NULL;
    2447           0 :         size_t n_transfers = 0, n_allocated = 0, j;
    2448             :         const char *type, *remote, *local;
    2449           0 :         sd_bus *bus = userdata;
    2450           0 :         uint32_t id, max_id = 0;
    2451             :         double progress;
    2452             :         int r;
    2453             : 
    2454           0 :         (void) pager_open(arg_pager_flags);
    2455             : 
    2456           0 :         r = sd_bus_call_method(bus,
    2457             :                                "org.freedesktop.import1",
    2458             :                                "/org/freedesktop/import1",
    2459             :                                "org.freedesktop.import1.Manager",
    2460             :                                "ListTransfers",
    2461             :                                &error,
    2462             :                                &reply,
    2463             :                                NULL);
    2464           0 :         if (r < 0)
    2465           0 :                 return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, -r));
    2466             : 
    2467           0 :         r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
    2468           0 :         if (r < 0)
    2469           0 :                 return bus_log_parse_error(r);
    2470             : 
    2471           0 :         while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, NULL)) > 0) {
    2472             :                 size_t l;
    2473             : 
    2474           0 :                 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
    2475           0 :                         return log_oom();
    2476             : 
    2477           0 :                 transfers[n_transfers].id = id;
    2478           0 :                 transfers[n_transfers].type = type;
    2479           0 :                 transfers[n_transfers].remote = remote;
    2480           0 :                 transfers[n_transfers].local = local;
    2481           0 :                 transfers[n_transfers].progress = progress;
    2482             : 
    2483           0 :                 l = strlen(type);
    2484           0 :                 if (l > max_type)
    2485           0 :                         max_type = l;
    2486             : 
    2487           0 :                 l = strlen(remote);
    2488           0 :                 if (l > max_remote)
    2489           0 :                         max_remote = l;
    2490             : 
    2491           0 :                 l = strlen(local);
    2492           0 :                 if (l > max_local)
    2493           0 :                         max_local = l;
    2494             : 
    2495           0 :                 if (id > max_id)
    2496           0 :                         max_id = id;
    2497             : 
    2498           0 :                 n_transfers++;
    2499             :         }
    2500           0 :         if (r < 0)
    2501           0 :                 return bus_log_parse_error(r);
    2502             : 
    2503           0 :         r = sd_bus_message_exit_container(reply);
    2504           0 :         if (r < 0)
    2505           0 :                 return bus_log_parse_error(r);
    2506             : 
    2507           0 :         typesafe_qsort(transfers, n_transfers, compare_transfer_info);
    2508             : 
    2509           0 :         if (arg_legend && n_transfers > 0)
    2510           0 :                 printf("%-*s %-*s %-*s %-*s %-*s\n",
    2511           0 :                        (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
    2512             :                        (int) 7, "PERCENT",
    2513             :                        (int) max_type, "TYPE",
    2514             :                        (int) max_local, "LOCAL",
    2515             :                        (int) max_remote, "REMOTE");
    2516             : 
    2517           0 :         for (j = 0; j < n_transfers; j++)
    2518             : 
    2519           0 :                 if (transfers[j].progress < 0)
    2520           0 :                         printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
    2521           0 :                                (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
    2522             :                                (int) 7, "n/a",
    2523           0 :                                (int) max_type, transfers[j].type,
    2524           0 :                                (int) max_local, transfers[j].local,
    2525           0 :                                (int) max_remote, transfers[j].remote);
    2526             :                 else
    2527           0 :                         printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
    2528           0 :                                (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
    2529           0 :                                (int) 6, (unsigned) (transfers[j].progress * 100),
    2530           0 :                                (int) max_type, transfers[j].type,
    2531           0 :                                (int) max_local, transfers[j].local,
    2532           0 :                                (int) max_remote, transfers[j].remote);
    2533             : 
    2534           0 :         if (arg_legend) {
    2535           0 :                 if (n_transfers > 0)
    2536           0 :                         printf("\n%zu transfers listed.\n", n_transfers);
    2537             :                 else
    2538           0 :                         printf("No transfers.\n");
    2539             :         }
    2540             : 
    2541           0 :         return 0;
    2542             : }
    2543             : 
    2544           0 : static int cancel_transfer(int argc, char *argv[], void *userdata) {
    2545           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    2546           0 :         sd_bus *bus = userdata;
    2547             :         int r, i;
    2548             : 
    2549           0 :         assert(bus);
    2550             : 
    2551           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    2552             : 
    2553           0 :         for (i = 1; i < argc; i++) {
    2554             :                 uint32_t id;
    2555             : 
    2556           0 :                 r = safe_atou32(argv[i], &id);
    2557           0 :                 if (r < 0)
    2558           0 :                         return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
    2559             : 
    2560           0 :                 r = sd_bus_call_method(
    2561             :                                 bus,
    2562             :                                 "org.freedesktop.import1",
    2563             :                                 "/org/freedesktop/import1",
    2564             :                                 "org.freedesktop.import1.Manager",
    2565             :                                 "CancelTransfer",
    2566             :                                 &error,
    2567             :                                 NULL,
    2568             :                                 "u", id);
    2569           0 :                 if (r < 0)
    2570           0 :                         return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, -r));
    2571             :         }
    2572             : 
    2573           0 :         return 0;
    2574             : }
    2575             : 
    2576           0 : static int set_limit(int argc, char *argv[], void *userdata) {
    2577           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    2578           0 :         sd_bus *bus = userdata;
    2579             :         uint64_t limit;
    2580             :         int r;
    2581             : 
    2582           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    2583             : 
    2584           0 :         if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
    2585           0 :                 limit = (uint64_t) -1;
    2586             :         else {
    2587           0 :                 r = parse_size(argv[argc-1], 1024, &limit);
    2588           0 :                 if (r < 0)
    2589           0 :                         return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
    2590             :         }
    2591             : 
    2592           0 :         if (argc > 2)
    2593             :                 /* With two arguments changes the quota limit of the
    2594             :                  * specified image */
    2595           0 :                 r = sd_bus_call_method(
    2596             :                                 bus,
    2597             :                                 "org.freedesktop.machine1",
    2598             :                                 "/org/freedesktop/machine1",
    2599             :                                 "org.freedesktop.machine1.Manager",
    2600             :                                 "SetImageLimit",
    2601             :                                 &error,
    2602             :                                 NULL,
    2603           0 :                                 "st", argv[1], limit);
    2604             :         else
    2605             :                 /* With one argument changes the pool quota limit */
    2606           0 :                 r = sd_bus_call_method(
    2607             :                                 bus,
    2608             :                                 "org.freedesktop.machine1",
    2609             :                                 "/org/freedesktop/machine1",
    2610             :                                 "org.freedesktop.machine1.Manager",
    2611             :                                 "SetPoolLimit",
    2612             :                                 &error,
    2613             :                                 NULL,
    2614             :                                 "t", limit);
    2615             : 
    2616           0 :         if (r < 0)
    2617           0 :                 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
    2618             : 
    2619           0 :         return 0;
    2620             : }
    2621             : 
    2622           0 : static int clean_images(int argc, char *argv[], void *userdata) {
    2623           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
    2624           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    2625           0 :         uint64_t usage, total = 0;
    2626             :         char fb[FORMAT_BYTES_MAX];
    2627           0 :         sd_bus *bus = userdata;
    2628             :         const char *name;
    2629           0 :         unsigned c = 0;
    2630             :         int r;
    2631             : 
    2632           0 :         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
    2633             : 
    2634           0 :         r = sd_bus_message_new_method_call(
    2635             :                         bus,
    2636             :                         &m,
    2637             :                         "org.freedesktop.machine1",
    2638             :                         "/org/freedesktop/machine1",
    2639             :                         "org.freedesktop.machine1.Manager",
    2640             :                         "CleanPool");
    2641           0 :         if (r < 0)
    2642           0 :                 return bus_log_create_error(r);
    2643             : 
    2644           0 :         r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden");
    2645           0 :         if (r < 0)
    2646           0 :                 return bus_log_create_error(r);
    2647             : 
    2648             :         /* This is a slow operation, hence permit a longer time for completion. */
    2649           0 :         r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);
    2650           0 :         if (r < 0)
    2651           0 :                 return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
    2652             : 
    2653           0 :         r = sd_bus_message_enter_container(reply, 'a', "(st)");
    2654           0 :         if (r < 0)
    2655           0 :                 return bus_log_parse_error(r);
    2656             : 
    2657           0 :         while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
    2658           0 :                 if (usage == UINT64_MAX) {
    2659           0 :                         log_info("Removed image '%s'", name);
    2660           0 :                         total = UINT64_MAX;
    2661             :                 } else {
    2662           0 :                         log_info("Removed image '%s'. Freed exclusive disk space: %s",
    2663             :                                  name, format_bytes(fb, sizeof(fb), usage));
    2664           0 :                         if (total != UINT64_MAX)
    2665           0 :                                 total += usage;
    2666             :                 }
    2667           0 :                 c++;
    2668             :         }
    2669             : 
    2670           0 :         r = sd_bus_message_exit_container(reply);
    2671           0 :         if (r < 0)
    2672           0 :                 return bus_log_parse_error(r);
    2673             : 
    2674           0 :         if (total == UINT64_MAX)
    2675           0 :                 log_info("Removed %u images in total.", c);
    2676             :         else
    2677           0 :                 log_info("Removed %u images in total. Total freed exclusive disk space: %s.",
    2678             :                          c, format_bytes(fb, sizeof(fb), total));
    2679             : 
    2680           0 :         return 0;
    2681             : }
    2682             : 
    2683           3 : static int help(int argc, char *argv[], void *userdata) {
    2684           3 :         _cleanup_free_ char *link = NULL;
    2685             :         int r;
    2686             : 
    2687           3 :         (void) pager_open(arg_pager_flags);
    2688             : 
    2689           3 :         r = terminal_urlify_man("machinectl", "1", &link);
    2690           3 :         if (r < 0)
    2691           0 :                 return log_oom();
    2692             : 
    2693           3 :         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
    2694             :                "Send control commands to or query the virtual machine and container\n"
    2695             :                "registration manager.\n\n"
    2696             :                "  -h --help                   Show this help\n"
    2697             :                "     --version                Show package version\n"
    2698             :                "     --no-pager               Do not pipe output into a pager\n"
    2699             :                "     --no-legend              Do not show the headers and footers\n"
    2700             :                "     --no-ask-password        Do not ask for system passwords\n"
    2701             :                "  -H --host=[USER@]HOST       Operate on remote host\n"
    2702             :                "  -M --machine=CONTAINER      Operate on local container\n"
    2703             :                "  -p --property=NAME          Show only properties by this name\n"
    2704             :                "  -q --quiet                  Suppress output\n"
    2705             :                "  -a --all                    Show all properties, including empty ones\n"
    2706             :                "     --value                  When showing properties, only print the value\n"
    2707             :                "  -l --full                   Do not ellipsize output\n"
    2708             :                "     --kill-who=WHO           Who to send signal to\n"
    2709             :                "  -s --signal=SIGNAL          Which signal to send\n"
    2710             :                "     --uid=USER               Specify user ID to invoke shell as\n"
    2711             :                "  -E --setenv=VAR=VALUE       Add an environment variable for shell\n"
    2712             :                "     --read-only              Create read-only bind mount\n"
    2713             :                "     --mkdir                  Create directory before bind mounting, if missing\n"
    2714             :                "  -n --lines=INTEGER          Number of journal entries to show\n"
    2715             :                "     --max-addresses=INTEGER  Number of internet addresses to show at most\n"
    2716             :                "  -o --output=STRING          Change journal output mode (short, short-precise,\n"
    2717             :                "                               short-iso, short-iso-precise, short-full,\n"
    2718             :                "                               short-monotonic, short-unix, verbose, export,\n"
    2719             :                "                               json, json-pretty, json-sse, json-seq, cat,\n"
    2720             :                "                               with-unit)\n"
    2721             :                "     --verify=MODE            Verification mode for downloaded images (no,\n"
    2722             :                "                              checksum, signature)\n"
    2723             :                "     --force                  Download image even if already exists\n\n"
    2724             :                "Machine Commands:\n"
    2725             :                "  list                        List running VMs and containers\n"
    2726             :                "  status NAME...              Show VM/container details\n"
    2727             :                "  show [NAME...]              Show properties of one or more VMs/containers\n"
    2728             :                "  start NAME...               Start container as a service\n"
    2729             :                "  login [NAME]                Get a login prompt in a container or on the\n"
    2730             :                "                              local host\n"
    2731             :                "  shell [[USER@]NAME [COMMAND...]]\n"
    2732             :                "                              Invoke a shell (or other command) in a container\n"
    2733             :                "                              or on the local host\n"
    2734             :                "  enable NAME...              Enable automatic container start at boot\n"
    2735             :                "  disable NAME...             Disable automatic container start at boot\n"
    2736             :                "  poweroff NAME...            Power off one or more containers\n"
    2737             :                "  reboot NAME...              Reboot one or more containers\n"
    2738             :                "  terminate NAME...           Terminate one or more VMs/containers\n"
    2739             :                "  kill NAME...                Send signal to processes of a VM/container\n"
    2740             :                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
    2741             :                "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
    2742             :                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n\n"
    2743             :                "Image Commands:\n"
    2744             :                "  list-images                 Show available container and VM images\n"
    2745             :                "  image-status [NAME...]      Show image details\n"
    2746             :                "  show-image [NAME...]        Show properties of image\n"
    2747             :                "  clone NAME NAME             Clone an image\n"
    2748             :                "  rename NAME NAME            Rename an image\n"
    2749             :                "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
    2750             :                "  remove NAME...              Remove an image\n"
    2751             :                "  set-limit [NAME] BYTES      Set image or pool size limit (disk quota)\n"
    2752             :                "  clean                       Remove hidden (or all) images\n\n"
    2753             :                "Image Transfer Commands:\n"
    2754             :                "  pull-tar URL [NAME]         Download a TAR container image\n"
    2755             :                "  pull-raw URL [NAME]         Download a RAW container or VM image\n"
    2756             :                "  import-tar FILE [NAME]      Import a local TAR container image\n"
    2757             :                "  import-raw FILE [NAME]      Import a local RAW container or VM image\n"
    2758             :                "  import-fs DIRECTORY [NAME]  Import a local directory container image\n"
    2759             :                "  export-tar NAME [FILE]      Export a TAR container image locally\n"
    2760             :                "  export-raw NAME [FILE]      Export a RAW container or VM image locally\n"
    2761             :                "  list-transfers              Show list of downloads in progress\n"
    2762             :                "  cancel-transfer             Cancel a download\n"
    2763             :                "\nSee the %s for details.\n"
    2764             :                , program_invocation_short_name
    2765             :                , link
    2766             :         );
    2767             : 
    2768           3 :         return 0;
    2769             : }
    2770             : 
    2771           4 : static int parse_argv(int argc, char *argv[]) {
    2772             : 
    2773             :         enum {
    2774             :                 ARG_VERSION = 0x100,
    2775             :                 ARG_NO_PAGER,
    2776             :                 ARG_NO_LEGEND,
    2777             :                 ARG_VALUE,
    2778             :                 ARG_KILL_WHO,
    2779             :                 ARG_READ_ONLY,
    2780             :                 ARG_MKDIR,
    2781             :                 ARG_NO_ASK_PASSWORD,
    2782             :                 ARG_VERIFY,
    2783             :                 ARG_FORCE,
    2784             :                 ARG_FORMAT,
    2785             :                 ARG_UID,
    2786             :                 ARG_NUMBER_IPS,
    2787             :         };
    2788             : 
    2789             :         static const struct option options[] = {
    2790             :                 { "help",            no_argument,       NULL, 'h'                 },
    2791             :                 { "version",         no_argument,       NULL, ARG_VERSION         },
    2792             :                 { "property",        required_argument, NULL, 'p'                 },
    2793             :                 { "all",             no_argument,       NULL, 'a'                 },
    2794             :                 { "value",           no_argument,       NULL, ARG_VALUE           },
    2795             :                 { "full",            no_argument,       NULL, 'l'                 },
    2796             :                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
    2797             :                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
    2798             :                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
    2799             :                 { "signal",          required_argument, NULL, 's'                 },
    2800             :                 { "host",            required_argument, NULL, 'H'                 },
    2801             :                 { "machine",         required_argument, NULL, 'M'                 },
    2802             :                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
    2803             :                 { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
    2804             :                 { "quiet",           no_argument,       NULL, 'q'                 },
    2805             :                 { "lines",           required_argument, NULL, 'n'                 },
    2806             :                 { "output",          required_argument, NULL, 'o'                 },
    2807             :                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
    2808             :                 { "verify",          required_argument, NULL, ARG_VERIFY          },
    2809             :                 { "force",           no_argument,       NULL, ARG_FORCE           },
    2810             :                 { "format",          required_argument, NULL, ARG_FORMAT          },
    2811             :                 { "uid",             required_argument, NULL, ARG_UID             },
    2812             :                 { "setenv",          required_argument, NULL, 'E'                 },
    2813             :                 { "max-addresses",   required_argument, NULL, ARG_NUMBER_IPS      },
    2814             :                 {}
    2815             :         };
    2816             : 
    2817           4 :         bool reorder = false;
    2818           4 :         int c, r, shell = -1;
    2819             : 
    2820           4 :         assert(argc >= 0);
    2821           4 :         assert(argv);
    2822             : 
    2823           0 :         for (;;) {
    2824             :                 static const char option_string[] = "-hp:als:H:M:qn:o:E:";
    2825             : 
    2826           4 :                 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
    2827           4 :                 if (c < 0)
    2828           0 :                         break;
    2829             : 
    2830           4 :                 switch (c) {
    2831             : 
    2832           0 :                 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
    2833             :                          * non-option argument was discovered. */
    2834             : 
    2835           0 :                         assert(!reorder);
    2836             : 
    2837             :                         /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
    2838             :                          * for switches after the main verb. However, for "shell" we really don't want that, since we
    2839             :                          * want that switches specified after the machine name are passed to the program to execute,
    2840             :                          * and not processed by us. To make this possible, we'll first invoke getopt_long() with
    2841             :                          * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
    2842             :                          * non-option parameter. If it's the verb "shell" we remember its position and continue
    2843             :                          * processing options. In this case, as soon as we hit the next non-option argument we found
    2844             :                          * the machine name, and stop further processing. If the first non-option argument is any other
    2845             :                          * verb than "shell" we switch to normal reordering mode and continue processing arguments
    2846             :                          * normally. */
    2847             : 
    2848           0 :                         if (shell >= 0) {
    2849             :                                 /* If we already found the "shell" verb on the command line, and now found the next
    2850             :                                  * non-option argument, then this is the machine name and we should stop processing
    2851             :                                  * further arguments.  */
    2852           0 :                                 optind --; /* don't process this argument, go one step back */
    2853           0 :                                 goto done;
    2854             :                         }
    2855           0 :                         if (streq(optarg, "shell"))
    2856             :                                 /* Remember the position of the "shell" verb, and continue processing normally. */
    2857           0 :                                 shell = optind - 1;
    2858             :                         else {
    2859             :                                 int saved_optind;
    2860             : 
    2861             :                                 /* OK, this is some other verb. In this case, turn on reordering again, and continue
    2862             :                                  * processing normally. */
    2863           0 :                                 reorder = true;
    2864             : 
    2865             :                                 /* We changed the option string. getopt_long() only looks at it again if we invoke it
    2866             :                                  * at least once with a reset option index. Hence, let's reset the option index here,
    2867             :                                  * then invoke getopt_long() again (ignoring what it has to say, after all we most
    2868             :                                  * likely already processed it), and the bump the option index so that we read the
    2869             :                                  * intended argument again. */
    2870           0 :                                 saved_optind = optind;
    2871           0 :                                 optind = 0;
    2872           0 :                                 (void) getopt_long(argc, argv, option_string + reorder, options, NULL);
    2873           0 :                                 optind = saved_optind - 1; /* go one step back, process this argument again */
    2874             :                         }
    2875             : 
    2876           0 :                         break;
    2877             : 
    2878           3 :                 case 'h':
    2879           3 :                         return help(0, NULL, NULL);
    2880             : 
    2881           0 :                 case ARG_VERSION:
    2882           0 :                         return version();
    2883             : 
    2884           0 :                 case 'p':
    2885           0 :                         r = strv_extend(&arg_property, optarg);
    2886           0 :                         if (r < 0)
    2887           0 :                                 return log_oom();
    2888             : 
    2889             :                         /* If the user asked for a particular
    2890             :                          * property, show it to him, even if it is
    2891             :                          * empty. */
    2892           0 :                         arg_all = true;
    2893           0 :                         break;
    2894             : 
    2895           0 :                 case 'a':
    2896           0 :                         arg_all = true;
    2897           0 :                         break;
    2898             : 
    2899           0 :                 case ARG_VALUE:
    2900           0 :                         arg_value = true;
    2901           0 :                         break;
    2902             : 
    2903           0 :                 case 'l':
    2904           0 :                         arg_full = true;
    2905           0 :                         break;
    2906             : 
    2907           0 :                 case 'n':
    2908           0 :                         if (safe_atou(optarg, &arg_lines) < 0)
    2909           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2910             :                                                        "Failed to parse lines '%s'", optarg);
    2911           0 :                         break;
    2912             : 
    2913           0 :                 case 'o':
    2914           0 :                         if (streq(optarg, "help")) {
    2915           0 :                                 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
    2916           0 :                                 return 0;
    2917             :                         }
    2918             : 
    2919           0 :                         arg_output = output_mode_from_string(optarg);
    2920           0 :                         if (arg_output < 0)
    2921           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2922             :                                                        "Unknown output '%s'.", optarg);
    2923             : 
    2924           0 :                         if (OUTPUT_MODE_IS_JSON(arg_output))
    2925           0 :                                 arg_legend = false;
    2926           0 :                         break;
    2927             : 
    2928           0 :                 case ARG_NO_PAGER:
    2929           0 :                         arg_pager_flags |= PAGER_DISABLE;
    2930           0 :                         break;
    2931             : 
    2932           0 :                 case ARG_NO_LEGEND:
    2933           0 :                         arg_legend = false;
    2934           0 :                         break;
    2935             : 
    2936           0 :                 case ARG_KILL_WHO:
    2937           0 :                         arg_kill_who = optarg;
    2938           0 :                         break;
    2939             : 
    2940           0 :                 case 's':
    2941           0 :                         if (streq(optarg, "help")) {
    2942           0 :                                 DUMP_STRING_TABLE(signal, int, _NSIG);
    2943           0 :                                 return 0;
    2944             :                         }
    2945             : 
    2946           0 :                         arg_signal = signal_from_string(optarg);
    2947           0 :                         if (arg_signal < 0)
    2948           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2949             :                                                        "Failed to parse signal string %s.", optarg);
    2950           0 :                         break;
    2951             : 
    2952           0 :                 case ARG_NO_ASK_PASSWORD:
    2953           0 :                         arg_ask_password = false;
    2954           0 :                         break;
    2955             : 
    2956           0 :                 case 'H':
    2957           0 :                         arg_transport = BUS_TRANSPORT_REMOTE;
    2958           0 :                         arg_host = optarg;
    2959           0 :                         break;
    2960             : 
    2961           0 :                 case 'M':
    2962           0 :                         arg_transport = BUS_TRANSPORT_MACHINE;
    2963           0 :                         arg_host = optarg;
    2964           0 :                         break;
    2965             : 
    2966           0 :                 case ARG_READ_ONLY:
    2967           0 :                         arg_read_only = true;
    2968           0 :                         break;
    2969             : 
    2970           0 :                 case ARG_MKDIR:
    2971           0 :                         arg_mkdir = true;
    2972           0 :                         break;
    2973             : 
    2974           0 :                 case 'q':
    2975           0 :                         arg_quiet = true;
    2976           0 :                         break;
    2977             : 
    2978           0 :                 case ARG_VERIFY:
    2979           0 :                         if (streq(optarg, "help")) {
    2980           0 :                                 DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
    2981           0 :                                 return 0;
    2982             :                         }
    2983             : 
    2984           0 :                         arg_verify = import_verify_from_string(optarg);
    2985           0 :                         if (arg_verify < 0)
    2986           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2987             :                                                        "Failed to parse --verify= setting: %s", optarg);
    2988           0 :                         break;
    2989             : 
    2990           0 :                 case ARG_FORCE:
    2991           0 :                         arg_force = true;
    2992           0 :                         break;
    2993             : 
    2994           0 :                 case ARG_FORMAT:
    2995           0 :                         if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2"))
    2996           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2997             :                                                        "Unknown format: %s", optarg);
    2998             : 
    2999           0 :                         arg_format = optarg;
    3000           0 :                         break;
    3001             : 
    3002           0 :                 case ARG_UID:
    3003           0 :                         arg_uid = optarg;
    3004           0 :                         break;
    3005             : 
    3006           0 :                 case 'E':
    3007           0 :                         if (!env_assignment_is_valid(optarg))
    3008           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    3009             :                                                        "Environment assignment invalid: %s", optarg);
    3010             : 
    3011           0 :                         r = strv_extend(&arg_setenv, optarg);
    3012           0 :                         if (r < 0)
    3013           0 :                                 return log_oom();
    3014           0 :                         break;
    3015             : 
    3016           0 :                 case ARG_NUMBER_IPS:
    3017           0 :                         if (streq(optarg, "all"))
    3018           0 :                                 arg_addrs = ALL_IP_ADDRESSES;
    3019           0 :                         else if (safe_atoi(optarg, &arg_addrs) < 0)
    3020           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    3021             :                                                        "Invalid number of IPs");
    3022           0 :                         else if (arg_addrs < 0)
    3023           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    3024             :                                                        "Number of IPs cannot be negative");
    3025           0 :                         break;
    3026             : 
    3027           1 :                 case '?':
    3028           1 :                         return -EINVAL;
    3029             : 
    3030           0 :                 default:
    3031           0 :                         assert_not_reached("Unhandled option");
    3032             :                 }
    3033             :         }
    3034             : 
    3035           0 : done:
    3036           0 :         if (shell >= 0) {
    3037             :                 char *t;
    3038             :                 int i;
    3039             : 
    3040             :                 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
    3041             :                  * argument list initially let's readjust it now, and move the "shell" verb to the back. */
    3042             : 
    3043           0 :                 optind -= 1; /* place the option index where the "shell" verb will be placed */
    3044             : 
    3045           0 :                 t = argv[shell];
    3046           0 :                 for (i = shell; i < optind; i++)
    3047           0 :                         argv[i] = argv[i+1];
    3048           0 :                 argv[optind] = t;
    3049             :         }
    3050             : 
    3051           0 :         return 1;
    3052             : }
    3053             : 
    3054           0 : static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
    3055             : 
    3056             :         static const Verb verbs[] = {
    3057             :                 { "help",            VERB_ANY, VERB_ANY, 0,            help              },
    3058             :                 { "list",            VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
    3059             :                 { "list-images",     VERB_ANY, 1,        0,            list_images       },
    3060             :                 { "status",          2,        VERB_ANY, 0,            show_machine      },
    3061             :                 { "image-status",    VERB_ANY, VERB_ANY, 0,            show_image        },
    3062             :                 { "show",            VERB_ANY, VERB_ANY, 0,            show_machine      },
    3063             :                 { "show-image",      VERB_ANY, VERB_ANY, 0,            show_image        },
    3064             :                 { "terminate",       2,        VERB_ANY, 0,            terminate_machine },
    3065             :                 { "reboot",          2,        VERB_ANY, 0,            reboot_machine    },
    3066             :                 { "poweroff",        2,        VERB_ANY, 0,            poweroff_machine  },
    3067             :                 { "stop",            2,        VERB_ANY, 0,            poweroff_machine  }, /* Convenience alias */
    3068             :                 { "kill",            2,        VERB_ANY, 0,            kill_machine      },
    3069             :                 { "login",           VERB_ANY, 2,        0,            login_machine     },
    3070             :                 { "shell",           VERB_ANY, VERB_ANY, 0,            shell_machine     },
    3071             :                 { "bind",            3,        4,        0,            bind_mount        },
    3072             :                 { "copy-to",         3,        4,        0,            copy_files        },
    3073             :                 { "copy-from",       3,        4,        0,            copy_files        },
    3074             :                 { "remove",          2,        VERB_ANY, 0,            remove_image      },
    3075             :                 { "rename",          3,        3,        0,            rename_image      },
    3076             :                 { "clone",           3,        3,        0,            clone_image       },
    3077             :                 { "read-only",       2,        3,        0,            read_only_image   },
    3078             :                 { "start",           2,        VERB_ANY, 0,            start_machine     },
    3079             :                 { "enable",          2,        VERB_ANY, 0,            enable_machine    },
    3080             :                 { "disable",         2,        VERB_ANY, 0,            enable_machine    },
    3081             :                 { "import-tar",      2,        3,        0,            import_tar        },
    3082             :                 { "import-raw",      2,        3,        0,            import_raw        },
    3083             :                 { "import-fs",       2,        3,        0,            import_fs         },
    3084             :                 { "export-tar",      2,        3,        0,            export_tar        },
    3085             :                 { "export-raw",      2,        3,        0,            export_raw        },
    3086             :                 { "pull-tar",        2,        3,        0,            pull_tar          },
    3087             :                 { "pull-raw",        2,        3,        0,            pull_raw          },
    3088             :                 { "list-transfers",  VERB_ANY, 1,        0,            list_transfers    },
    3089             :                 { "cancel-transfer", 2,        VERB_ANY, 0,            cancel_transfer   },
    3090             :                 { "set-limit",       2,        3,        0,            set_limit         },
    3091             :                 { "clean",           VERB_ANY, 1,        0,            clean_images      },
    3092             :                 {}
    3093             :         };
    3094             : 
    3095           0 :         return dispatch_verb(argc, argv, verbs, bus);
    3096             : }
    3097             : 
    3098           4 : static int run(int argc, char *argv[]) {
    3099           4 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    3100             :         int r;
    3101             : 
    3102           4 :         setlocale(LC_ALL, "");
    3103           4 :         log_show_color(true);
    3104           4 :         log_parse_environment();
    3105           4 :         log_open();
    3106             : 
    3107             :         /* The journal merging logic potentially needs a lot of fds. */
    3108           4 :         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
    3109             : 
    3110           4 :         sigbus_install();
    3111             : 
    3112           4 :         r = parse_argv(argc, argv);
    3113           4 :         if (r <= 0)
    3114           4 :                 return r;
    3115             : 
    3116           0 :         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
    3117           0 :         if (r < 0)
    3118           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    3119             : 
    3120           0 :         (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
    3121             : 
    3122           0 :         return machinectl_main(argc, argv, bus);
    3123             : }
    3124             : 
    3125           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14