chiark / gitweb /
machinectl: issue all bus commands while allowing interactive auth
[elogind.git] / src / machine / machinectl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/socket.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <sys/mount.h>
34
35 #include "sd-bus.h"
36 #include "log.h"
37 #include "util.h"
38 #include "macro.h"
39 #include "pager.h"
40 #include "spawn-polkit-agent.h"
41 #include "bus-util.h"
42 #include "bus-error.h"
43 #include "build.h"
44 #include "strv.h"
45 #include "unit-name.h"
46 #include "cgroup-show.h"
47 #include "logs-show.h"
48 #include "cgroup-util.h"
49 #include "ptyfwd.h"
50 #include "event-util.h"
51 #include "path-util.h"
52 #include "mkdir.h"
53 #include "copy.h"
54 #include "verbs.h"
55 #include "import-util.h"
56
57 static char **arg_property = NULL;
58 static bool arg_all = false;
59 static bool arg_full = false;
60 static bool arg_no_pager = false;
61 static bool arg_legend = true;
62 static const char *arg_kill_who = NULL;
63 static int arg_signal = SIGTERM;
64 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
65 static char *arg_host = NULL;
66 static bool arg_read_only = false;
67 static bool arg_mkdir = false;
68 static bool arg_quiet = false;
69 static bool arg_ask_password = true;
70 static unsigned arg_lines = 10;
71 static OutputMode arg_output = OUTPUT_SHORT;
72 static bool arg_force = false;
73 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
74 static const char* arg_dkr_index_url = NULL;
75
76 static void pager_open_if_enabled(void) {
77
78         if (arg_no_pager)
79                 return;
80
81         pager_open(false);
82 }
83
84 static void polkit_agent_open_if_enabled(void) {
85
86         /* Open the polkit agent as a child process if necessary */
87
88         if (!arg_ask_password)
89                 return;
90
91         if (arg_transport != BUS_TRANSPORT_LOCAL)
92                 return;
93
94         polkit_agent_open();
95 }
96
97 static OutputFlags get_output_flags(void) {
98         return
99                 arg_all * OUTPUT_SHOW_ALL |
100                 arg_full * OUTPUT_FULL_WIDTH |
101                 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
102                 on_tty() * OUTPUT_COLOR |
103                 !arg_quiet * OUTPUT_WARN_CUTOFF;
104 }
105
106 typedef struct MachineInfo {
107         const char *name;
108         const char *class;
109         const char *service;
110 } MachineInfo;
111
112 static int compare_machine_info(const void *a, const void *b) {
113         const MachineInfo *x = a, *y = b;
114
115         return strcmp(x->name, y->name);
116 }
117
118 static int list_machines(int argc, char *argv[], void *userdata) {
119
120         size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
121         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
122         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
123         _cleanup_free_ MachineInfo *machines = NULL;
124         const char *name, *class, *service, *object;
125         size_t n_machines = 0, n_allocated = 0, j;
126         sd_bus *bus = userdata;
127         int r;
128
129         assert(bus);
130
131         pager_open_if_enabled();
132
133         r = sd_bus_call_method(
134                                 bus,
135                                 "org.freedesktop.machine1",
136                                 "/org/freedesktop/machine1",
137                                 "org.freedesktop.machine1.Manager",
138                                 "ListMachines",
139                                 &error,
140                                 &reply,
141                                 NULL);
142         if (r < 0) {
143                 log_error("Could not get machines: %s", bus_error_message(&error, -r));
144                 return r;
145         }
146
147         r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
148         if (r < 0)
149                 return bus_log_parse_error(r);
150
151         while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
152                 size_t l;
153
154                 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
155                         return log_oom();
156
157                 machines[n_machines].name = name;
158                 machines[n_machines].class = class;
159                 machines[n_machines].service = service;
160
161                 l = strlen(name);
162                 if (l > max_name)
163                         max_name = l;
164
165                 l = strlen(class);
166                 if (l > max_class)
167                         max_class = l;
168
169                 l = strlen(service);
170                 if (l > max_service)
171                         max_service = l;
172
173                 n_machines ++;
174         }
175         if (r < 0)
176                 return bus_log_parse_error(r);
177
178         r = sd_bus_message_exit_container(reply);
179         if (r < 0)
180                 return bus_log_parse_error(r);
181
182         qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
183
184         if (arg_legend)
185                 printf("%-*s %-*s %-*s\n",
186                        (int) max_name, "MACHINE",
187                        (int) max_class, "CLASS",
188                        (int) max_service, "SERVICE");
189
190         for (j = 0; j < n_machines; j++)
191                 printf("%-*s %-*s %-*s\n",
192                        (int) max_name, machines[j].name,
193                        (int) max_class, machines[j].class,
194                        (int) max_service, machines[j].service);
195
196         if (arg_legend)
197                 printf("\n%zu machines listed.\n", n_machines);
198
199         return 0;
200 }
201
202 typedef struct ImageInfo {
203         const char *name;
204         const char *type;
205         bool read_only;
206         usec_t crtime;
207         usec_t mtime;
208         uint64_t size;
209 } ImageInfo;
210
211 static int compare_image_info(const void *a, const void *b) {
212         const ImageInfo *x = a, *y = b;
213
214         return strcmp(x->name, y->name);
215 }
216
217 static int list_images(int argc, char *argv[], void *userdata) {
218
219         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
220         size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
221         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
222         _cleanup_free_ ImageInfo *images = NULL;
223         size_t n_images = 0, n_allocated = 0, j;
224         const char *name, *type, *object;
225         sd_bus *bus = userdata;
226         uint64_t crtime, mtime, size;
227         int read_only, r;
228
229         assert(bus);
230
231         pager_open_if_enabled();
232
233         r = sd_bus_call_method(
234                                 bus,
235                                 "org.freedesktop.machine1",
236                                 "/org/freedesktop/machine1",
237                                 "org.freedesktop.machine1.Manager",
238                                 "ListImages",
239                                 &error,
240                                 &reply,
241                                 "");
242         if (r < 0) {
243                 log_error("Could not get images: %s", bus_error_message(&error, -r));
244                 return r;
245         }
246
247         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
248         if (r < 0)
249                 return bus_log_parse_error(r);
250
251         while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
252                 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
253                 size_t l;
254
255                 if (name[0] == '.' && !arg_all)
256                         continue;
257
258                 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
259                         return log_oom();
260
261                 images[n_images].name = name;
262                 images[n_images].type = type;
263                 images[n_images].read_only = read_only;
264                 images[n_images].crtime = crtime;
265                 images[n_images].mtime = mtime;
266                 images[n_images].size = size;
267
268                 l = strlen(name);
269                 if (l > max_name)
270                         max_name = l;
271
272                 l = strlen(type);
273                 if (l > max_type)
274                         max_type = l;
275
276                 if (crtime != 0) {
277                         l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
278                         if (l > max_crtime)
279                                 max_crtime = l;
280                 }
281
282                 if (mtime != 0) {
283                         l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
284                         if (l > max_mtime)
285                                 max_mtime = l;
286                 }
287
288                 if (size != (uint64_t) -1) {
289                         l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
290                         if (l > max_size)
291                                 max_size = l;
292                 }
293
294                 n_images++;
295         }
296         if (r < 0)
297                 return bus_log_parse_error(r);
298
299         r = sd_bus_message_exit_container(reply);
300         if (r < 0)
301                 return bus_log_parse_error(r);
302
303         qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
304
305         if (arg_legend)
306                 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
307                        (int) max_name, "NAME",
308                        (int) max_type, "TYPE",
309                        "RO",
310                        (int) max_size, "USAGE",
311                        (int) max_crtime, "CREATED",
312                        (int) max_mtime, "MODIFIED");
313
314         for (j = 0; j < n_images; j++) {
315                 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
316
317                 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
318                        (int) max_name, images[j].name,
319                        (int) max_type, images[j].type,
320                        images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
321                        (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
322                        (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
323                        (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
324         }
325
326         if (arg_legend)
327                 printf("\n%zu images listed.\n", n_images);
328
329         return 0;
330 }
331
332 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
333         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335         _cleanup_free_ char *path = NULL;
336         const char *cgroup;
337         int r;
338         unsigned c;
339
340         assert(bus);
341         assert(unit);
342
343         if (arg_transport == BUS_TRANSPORT_REMOTE)
344                 return 0;
345
346         path = unit_dbus_path_from_name(unit);
347         if (!path)
348                 return log_oom();
349
350         r = sd_bus_get_property(
351                         bus,
352                         "org.freedesktop.systemd1",
353                         path,
354                         endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
355                         "ControlGroup",
356                         &error,
357                         &reply,
358                         "s");
359         if (r < 0) {
360                 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
361                 return r;
362         }
363
364         r = sd_bus_message_read(reply, "s", &cgroup);
365         if (r < 0)
366                 return bus_log_parse_error(r);
367
368         if (isempty(cgroup))
369                 return 0;
370
371         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
372                 return 0;
373
374         c = columns();
375         if (c > 18)
376                 c -= 18;
377         else
378                 c = 0;
379
380         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, get_output_flags());
381         return 0;
382 }
383
384 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
385         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
386         int r;
387
388         assert(bus);
389         assert(name);
390         assert(prefix);
391         assert(prefix2);
392
393         r = sd_bus_call_method(bus,
394                                "org.freedesktop.machine1",
395                                "/org/freedesktop/machine1",
396                                "org.freedesktop.machine1.Manager",
397                                "GetMachineAddresses",
398                                NULL,
399                                &reply,
400                                "s", name);
401         if (r < 0)
402                 return r;
403
404         r = sd_bus_message_enter_container(reply, 'a', "(iay)");
405         if (r < 0)
406                 return bus_log_parse_error(r);
407
408         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
409                 int family;
410                 const void *a;
411                 size_t sz;
412                 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
413
414                 r = sd_bus_message_read(reply, "i", &family);
415                 if (r < 0)
416                         return bus_log_parse_error(r);
417
418                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
419                 if (r < 0)
420                         return bus_log_parse_error(r);
421
422                 fputs(prefix, stdout);
423                 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
424                 if (family == AF_INET6 && ifi > 0)
425                         printf("%%%i", ifi);
426                 fputc('\n', stdout);
427
428                 r = sd_bus_message_exit_container(reply);
429                 if (r < 0)
430                         return bus_log_parse_error(r);
431
432                 if (prefix != prefix2)
433                         prefix = prefix2;
434         }
435         if (r < 0)
436                 return bus_log_parse_error(r);
437
438         r = sd_bus_message_exit_container(reply);
439         if (r < 0)
440                 return bus_log_parse_error(r);
441
442         return 0;
443 }
444
445 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
446         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
447         const char *k, *v, *pretty = NULL;
448         int r;
449
450         assert(bus);
451         assert(name);
452         assert(prefix);
453
454         r = sd_bus_call_method(bus,
455                                "org.freedesktop.machine1",
456                                "/org/freedesktop/machine1",
457                                "org.freedesktop.machine1.Manager",
458                                "GetMachineOSRelease",
459                                NULL,
460                                &reply,
461                                "s", name);
462         if (r < 0)
463                 return r;
464
465         r = sd_bus_message_enter_container(reply, 'a', "{ss}");
466         if (r < 0)
467                 return bus_log_parse_error(r);
468
469         while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
470                 if (streq(k, "PRETTY_NAME"))
471                         pretty = v;
472
473         }
474         if (r < 0)
475                 return bus_log_parse_error(r);
476
477         r = sd_bus_message_exit_container(reply);
478         if (r < 0)
479                 return bus_log_parse_error(r);
480
481         if (pretty)
482                 printf("%s%s\n", prefix, pretty);
483
484         return 0;
485 }
486
487 typedef struct MachineStatusInfo {
488         char *name;
489         sd_id128_t id;
490         char *class;
491         char *service;
492         char *unit;
493         char *root_directory;
494         pid_t leader;
495         struct dual_timestamp timestamp;
496         int *netif;
497         unsigned n_netif;
498 } MachineStatusInfo;
499
500 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
501         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
502         char since2[FORMAT_TIMESTAMP_MAX], *s2;
503         int ifi = -1;
504
505         assert(bus);
506         assert(i);
507
508         fputs(strna(i->name), stdout);
509
510         if (!sd_id128_equal(i->id, SD_ID128_NULL))
511                 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
512         else
513                 putchar('\n');
514
515         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
516         s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
517
518         if (s1)
519                 printf("\t   Since: %s; %s\n", s2, s1);
520         else if (s2)
521                 printf("\t   Since: %s\n", s2);
522
523         if (i->leader > 0) {
524                 _cleanup_free_ char *t = NULL;
525
526                 printf("\t  Leader: %u", (unsigned) i->leader);
527
528                 get_process_comm(i->leader, &t);
529                 if (t)
530                         printf(" (%s)", t);
531
532                 putchar('\n');
533         }
534
535         if (i->service) {
536                 printf("\t Service: %s", i->service);
537
538                 if (i->class)
539                         printf("; class %s", i->class);
540
541                 putchar('\n');
542         } else if (i->class)
543                 printf("\t   Class: %s\n", i->class);
544
545         if (i->root_directory)
546                 printf("\t    Root: %s\n", i->root_directory);
547
548         if (i->n_netif > 0) {
549                 unsigned c;
550
551                 fputs("\t   Iface:", stdout);
552
553                 for (c = 0; c < i->n_netif; c++) {
554                         char name[IF_NAMESIZE+1] = "";
555
556                         if (if_indextoname(i->netif[c], name)) {
557                                 fputc(' ', stdout);
558                                 fputs(name, stdout);
559
560                                 if (ifi < 0)
561                                         ifi = i->netif[c];
562                                 else
563                                         ifi = 0;
564                         } else
565                                 printf(" %i", i->netif[c]);
566                 }
567
568                 fputc('\n', stdout);
569         }
570
571         print_addresses(bus, i->name, ifi,
572                        "\t Address: ",
573                        "\t          ");
574
575         print_os_release(bus, i->name, "\t      OS: ");
576
577         if (i->unit) {
578                 printf("\t    Unit: %s\n", i->unit);
579                 show_unit_cgroup(bus, i->unit, i->leader);
580
581                 if (arg_transport == BUS_TRANSPORT_LOCAL) {
582
583                         show_journal_by_unit(
584                                         stdout,
585                                         i->unit,
586                                         arg_output,
587                                         0,
588                                         i->timestamp.monotonic,
589                                         arg_lines,
590                                         0,
591                                         get_output_flags() | OUTPUT_BEGIN_NEWLINE,
592                                         SD_JOURNAL_LOCAL_ONLY,
593                                         true,
594                                         NULL);
595                 }
596         }
597 }
598
599 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
600         MachineStatusInfo *i = userdata;
601         size_t l;
602         const void *v;
603         int r;
604
605         assert_cc(sizeof(int32_t) == sizeof(int));
606         r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
607         if (r < 0)
608                 return r;
609         if (r == 0)
610                 return -EBADMSG;
611
612         i->n_netif = l / sizeof(int32_t);
613         i->netif = memdup(v, l);
614         if (!i->netif)
615                 return -ENOMEM;
616
617         return 0;
618 }
619
620 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
621
622         static const struct bus_properties_map map[]  = {
623                 { "Name",               "s",  NULL,          offsetof(MachineStatusInfo, name)                },
624                 { "Class",              "s",  NULL,          offsetof(MachineStatusInfo, class)               },
625                 { "Service",            "s",  NULL,          offsetof(MachineStatusInfo, service)             },
626                 { "Unit",               "s",  NULL,          offsetof(MachineStatusInfo, unit)                },
627                 { "RootDirectory",      "s",  NULL,          offsetof(MachineStatusInfo, root_directory)      },
628                 { "Leader",             "u",  NULL,          offsetof(MachineStatusInfo, leader)              },
629                 { "Timestamp",          "t",  NULL,          offsetof(MachineStatusInfo, timestamp.realtime)  },
630                 { "TimestampMonotonic", "t",  NULL,          offsetof(MachineStatusInfo, timestamp.monotonic) },
631                 { "Id",                 "ay", bus_map_id128, offsetof(MachineStatusInfo, id)                  },
632                 { "NetworkInterfaces",  "ai", map_netif,     0                                                },
633                 {}
634         };
635
636         MachineStatusInfo info = {};
637         int r;
638
639         assert(verb);
640         assert(bus);
641         assert(path);
642         assert(new_line);
643
644         r = bus_map_all_properties(bus,
645                                    "org.freedesktop.machine1",
646                                    path,
647                                    map,
648                                    &info);
649         if (r < 0)
650                 return log_error_errno(r, "Could not get properties: %m");
651
652         if (*new_line)
653                 printf("\n");
654         *new_line = true;
655
656         print_machine_status_info(bus, &info);
657
658         free(info.name);
659         free(info.class);
660         free(info.service);
661         free(info.unit);
662         free(info.root_directory);
663         free(info.netif);
664
665         return r;
666 }
667
668 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
669         int r;
670
671         assert(bus);
672         assert(path);
673         assert(new_line);
674
675         if (*new_line)
676                 printf("\n");
677
678         *new_line = true;
679
680         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
681         if (r < 0)
682                 log_error_errno(r, "Could not get properties: %m");
683
684         return r;
685 }
686
687 static int show_machine(int argc, char *argv[], void *userdata) {
688
689         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
690         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
691         bool properties, new_line = false;
692         sd_bus *bus = userdata;
693         int r = 0, i;
694
695         assert(bus);
696
697         properties = !strstr(argv[0], "status");
698
699         pager_open_if_enabled();
700
701         if (properties && argc <= 1) {
702
703                 /* If no argument is specified, inspect the manager
704                  * itself */
705                 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
706                 if (r < 0)
707                         return r;
708         }
709
710         for (i = 1; i < argc; i++) {
711                 const char *path = NULL;
712
713                 r = sd_bus_call_method(
714                                         bus,
715                                         "org.freedesktop.machine1",
716                                         "/org/freedesktop/machine1",
717                                         "org.freedesktop.machine1.Manager",
718                                         "GetMachine",
719                                         &error,
720                                         &reply,
721                                         "s", argv[i]);
722                 if (r < 0) {
723                         log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
724                         return r;
725                 }
726
727                 r = sd_bus_message_read(reply, "o", &path);
728                 if (r < 0)
729                         return bus_log_parse_error(r);
730
731                 if (properties)
732                         r = show_machine_properties(bus, path, &new_line);
733                 else
734                         r = show_machine_info(argv[0], bus, path, &new_line);
735         }
736
737         return r;
738 }
739
740 typedef struct ImageStatusInfo {
741         char *name;
742         char *path;
743         char *type;
744         int read_only;
745         usec_t crtime;
746         usec_t mtime;
747         uint64_t usage;
748         uint64_t limit;
749         uint64_t usage_exclusive;
750         uint64_t limit_exclusive;
751 } ImageStatusInfo;
752
753 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
754         char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
755         char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
756         char bs[FORMAT_BYTES_MAX], *s3;
757         char bs_exclusive[FORMAT_BYTES_MAX], *s4;
758
759         assert(bus);
760         assert(i);
761
762         if (i->name) {
763                 fputs(i->name, stdout);
764                 putchar('\n');
765         }
766
767         if (i->type)
768                 printf("\t    Type: %s\n", i->type);
769
770         if (i->path)
771                 printf("\t    Path: %s\n", i->path);
772
773         printf("\t      RO: %s%s%s\n",
774                i->read_only ? ansi_highlight_red() : "",
775                i->read_only ? "read-only" : "writable",
776                i->read_only ? ansi_highlight_off() : "");
777
778         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
779         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
780         if (s1 && s2)
781                 printf("\t Created: %s; %s\n", s2, s1);
782         else if (s2)
783                 printf("\t Created: %s\n", s2);
784
785         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
786         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
787         if (s1 && s2)
788                 printf("\tModified: %s; %s\n", s2, s1);
789         else if (s2)
790                 printf("\tModified: %s\n", s2);
791
792         s3 = format_bytes(bs, sizeof(bs), i->usage);
793         s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
794         if (s3 && s4)
795                 printf("\t   Usage: %s (exclusive: %s)\n", s3, s4);
796         else if (s3)
797                 printf("\t   Usage: %s\n", s3);
798
799         s3 = format_bytes(bs, sizeof(bs), i->limit);
800         s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
801         if (s3 && s4)
802                 printf("\t   Limit: %s (exclusive: %s)\n", s3, s4);
803         else if (s3)
804                 printf("\t   Limit: %s\n", s3);
805 }
806
807 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
808
809         static const struct bus_properties_map map[]  = {
810                 { "Name",                  "s",  NULL, offsetof(ImageStatusInfo, name)            },
811                 { "Path",                  "s",  NULL, offsetof(ImageStatusInfo, path)            },
812                 { "Type",                  "s",  NULL, offsetof(ImageStatusInfo, type)            },
813                 { "ReadOnly",              "b",  NULL, offsetof(ImageStatusInfo, read_only)       },
814                 { "CreationTimestamp",     "t",  NULL, offsetof(ImageStatusInfo, crtime)          },
815                 { "ModificationTimestamp", "t",  NULL, offsetof(ImageStatusInfo, mtime)           },
816                 { "Usage",                 "t",  NULL, offsetof(ImageStatusInfo, usage)           },
817                 { "Limit",                 "t",  NULL, offsetof(ImageStatusInfo, limit)           },
818                 { "UsageExclusive",        "t",  NULL, offsetof(ImageStatusInfo, usage_exclusive) },
819                 { "LimitExclusive",        "t",  NULL, offsetof(ImageStatusInfo, limit_exclusive) },
820                 {}
821         };
822
823         ImageStatusInfo info = {};
824         int r;
825
826         assert(verb);
827         assert(bus);
828         assert(path);
829         assert(new_line);
830
831         r = bus_map_all_properties(bus,
832                                    "org.freedesktop.machine1",
833                                    path,
834                                    map,
835                                    &info);
836         if (r < 0)
837                 return log_error_errno(r, "Could not get properties: %m");
838
839         if (*new_line)
840                 printf("\n");
841         *new_line = true;
842
843         print_image_status_info(bus, &info);
844
845         free(info.name);
846         free(info.path);
847         free(info.type);
848
849         return r;
850 }
851
852 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
853         int r;
854
855         assert(bus);
856         assert(path);
857         assert(new_line);
858
859         if (*new_line)
860                 printf("\n");
861
862         *new_line = true;
863
864         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
865         if (r < 0)
866                 log_error_errno(r, "Could not get properties: %m");
867
868         return r;
869 }
870
871 static int show_image(int argc, char *argv[], void *userdata) {
872
873         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
874         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
875         bool properties, new_line = false;
876         sd_bus *bus = userdata;
877         int r = 0, i;
878
879         assert(bus);
880
881         properties = !strstr(argv[0], "status");
882
883         pager_open_if_enabled();
884
885         if (properties && argc <= 1) {
886
887                 /* If no argument is specified, inspect the manager
888                  * itself */
889                 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
890                 if (r < 0)
891                         return r;
892         }
893
894         for (i = 1; i < argc; i++) {
895                 const char *path = NULL;
896
897                 r = sd_bus_call_method(
898                                 bus,
899                                 "org.freedesktop.machine1",
900                                 "/org/freedesktop/machine1",
901                                 "org.freedesktop.machine1.Manager",
902                                 "GetImage",
903                                 &error,
904                                 &reply,
905                                 "s", argv[i]);
906                 if (r < 0) {
907                         log_error("Could not get path to image: %s", bus_error_message(&error, -r));
908                         return r;
909                 }
910
911                 r = sd_bus_message_read(reply, "o", &path);
912                 if (r < 0)
913                         return bus_log_parse_error(r);
914
915                 if (properties)
916                         r = show_image_properties(bus, path, &new_line);
917                 else
918                         r = show_image_info(argv[0], bus, path, &new_line);
919         }
920
921         return r;
922 }
923
924 static int kill_machine(int argc, char *argv[], void *userdata) {
925         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
926         sd_bus *bus = userdata;
927         int r, i;
928
929         assert(bus);
930
931         polkit_agent_open_if_enabled();
932
933         if (!arg_kill_who)
934                 arg_kill_who = "all";
935
936         for (i = 1; i < argc; i++) {
937                 r = sd_bus_call_method(
938                                 bus,
939                                 "org.freedesktop.machine1",
940                                 "/org/freedesktop/machine1",
941                                 "org.freedesktop.machine1.Manager",
942                                 "KillMachine",
943                                 &error,
944                                 NULL,
945                                 "ssi", argv[i], arg_kill_who, arg_signal);
946                 if (r < 0) {
947                         log_error("Could not kill machine: %s", bus_error_message(&error, -r));
948                         return r;
949                 }
950         }
951
952         return 0;
953 }
954
955 static int reboot_machine(int argc, char *argv[], void *userdata) {
956         arg_kill_who = "leader";
957         arg_signal = SIGINT; /* sysvinit + systemd */
958
959         return kill_machine(argc, argv, userdata);
960 }
961
962 static int poweroff_machine(int argc, char *argv[], void *userdata) {
963         arg_kill_who = "leader";
964         arg_signal = SIGRTMIN+4; /* only systemd */
965
966         return kill_machine(argc, argv, userdata);
967 }
968
969 static int terminate_machine(int argc, char *argv[], void *userdata) {
970         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
971         sd_bus *bus = userdata;
972         int r, i;
973
974         assert(bus);
975
976         polkit_agent_open_if_enabled();
977
978         for (i = 1; i < argc; i++) {
979                 r = sd_bus_call_method(
980                                 bus,
981                                 "org.freedesktop.machine1",
982                                 "/org/freedesktop/machine1",
983                                 "org.freedesktop.machine1.Manager",
984                                 "TerminateMachine",
985                                 &error,
986                                 NULL,
987                                 "s", argv[i]);
988                 if (r < 0) {
989                         log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
990                         return r;
991                 }
992         }
993
994         return 0;
995 }
996
997 static int copy_files(int argc, char *argv[], void *userdata) {
998         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
999         sd_bus *bus = userdata;
1000         bool copy_from;
1001         int r;
1002
1003         assert(bus);
1004
1005         polkit_agent_open_if_enabled();
1006
1007         copy_from = streq(argv[0], "copy-from");
1008
1009         r = sd_bus_call_method(
1010                         bus,
1011                         "org.freedesktop.machine1",
1012                         "/org/freedesktop/machine1",
1013                         "org.freedesktop.machine1.Manager",
1014                         copy_from ? "CopyFromMachine" : "CopyToMachine",
1015                         &error,
1016                         NULL,
1017                         "sss",
1018                         argv[1],
1019                         argv[2],
1020                         argv[3]);
1021         if (r < 0) {
1022                 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1023                 return r;
1024         }
1025
1026         return 0;
1027 }
1028
1029 static int bind_mount(int argc, char *argv[], void *userdata) {
1030         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1031         sd_bus *bus = userdata;
1032         int r;
1033
1034         assert(bus);
1035
1036         polkit_agent_open_if_enabled();
1037
1038         r = sd_bus_call_method(
1039                         bus,
1040                         "org.freedesktop.machine1",
1041                         "/org/freedesktop/machine1",
1042                         "org.freedesktop.machine1.Manager",
1043                         "BindMountMachine",
1044                         &error,
1045                         NULL,
1046                         "sssbb",
1047                         argv[1],
1048                         argv[2],
1049                         argv[3],
1050                         arg_read_only,
1051                         arg_mkdir);
1052         if (r < 0) {
1053                 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1054                 return r;
1055         }
1056
1057         return 0;
1058 }
1059
1060 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1061         PTYForward ** forward = (PTYForward**) userdata;
1062         int r;
1063
1064         assert(bus);
1065         assert(m);
1066         assert(forward);
1067
1068         if (*forward) {
1069                 /* If the forwarder is already initialized, tell it to
1070                  * exit on the next vhangup(), so that we still flush
1071                  * out what might be queued and exit then. */
1072
1073                 r = pty_forward_set_ignore_vhangup(*forward, false);
1074                 if (r >= 0)
1075                         return 0;
1076
1077                 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1078         }
1079
1080         /* On error, or when the forwarder is not initialized yet, quit immediately */
1081         sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1082         return 0;
1083 }
1084
1085 static int login_machine(int argc, char *argv[], void *userdata) {
1086         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1087         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1088         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1089         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1090         _cleanup_event_unref_ sd_event *event = NULL;
1091         int master = -1, r, ret = 0;
1092         sd_bus *bus = userdata;
1093         const char *pty, *match;
1094         char last_char = 0;
1095         bool machine_died;
1096
1097         assert(bus);
1098
1099         if (arg_transport != BUS_TRANSPORT_LOCAL &&
1100             arg_transport != BUS_TRANSPORT_MACHINE) {
1101                 log_error("Login only supported on local machines.");
1102                 return -ENOTSUP;
1103         }
1104
1105         polkit_agent_open_if_enabled();
1106
1107         r = sd_event_default(&event);
1108         if (r < 0)
1109                 return log_error_errno(r, "Failed to get event loop: %m");
1110
1111         r = sd_bus_attach_event(bus, event, 0);
1112         if (r < 0)
1113                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1114
1115         match = strjoina("type='signal',"
1116                            "sender='org.freedesktop.machine1',"
1117                            "path='/org/freedesktop/machine1',",
1118                            "interface='org.freedesktop.machine1.Manager',"
1119                            "member='MachineRemoved',"
1120                            "arg0='",
1121                            argv[1],
1122                            "'");
1123
1124         r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1125         if (r < 0)
1126                 return log_error_errno(r, "Failed to add machine removal match: %m");
1127
1128         r = sd_bus_call_method(
1129                         bus,
1130                         "org.freedesktop.machine1",
1131                         "/org/freedesktop/machine1",
1132                         "org.freedesktop.machine1.Manager",
1133                         "OpenMachineLogin",
1134                         &error,
1135                         &reply,
1136                         "s", argv[1]);
1137         if (r < 0) {
1138                 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1139                 return r;
1140         }
1141
1142         r = sd_bus_message_read(reply, "hs", &master, &pty);
1143         if (r < 0)
1144                 return bus_log_parse_error(r);
1145
1146         sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1147
1148         log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1149
1150         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1151         sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1152
1153         r = pty_forward_new(event, master, true, &forward);
1154         if (r < 0)
1155                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1156
1157         r = sd_event_loop(event);
1158         if (r < 0)
1159                 return log_error_errno(r, "Failed to run event loop: %m");
1160
1161         pty_forward_get_last_char(forward, &last_char);
1162         machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1163
1164         forward = pty_forward_free(forward);
1165
1166         if (last_char != '\n')
1167                 fputc('\n', stdout);
1168
1169         if (machine_died)
1170                 log_info("Machine %s terminated.", argv[1]);
1171         else
1172                 log_info("Connection to machine %s terminated.", argv[1]);
1173
1174         sd_event_get_exit_code(event, &ret);
1175         return ret;
1176 }
1177
1178 static int remove_image(int argc, char *argv[], void *userdata) {
1179         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1180         sd_bus *bus = userdata;
1181         int r, i;
1182
1183         assert(bus);
1184
1185         polkit_agent_open_if_enabled();
1186
1187         for (i = 1; i < argc; i++) {
1188                 r = sd_bus_call_method(
1189                                 bus,
1190                                 "org.freedesktop.machine1",
1191                                 "/org/freedesktop/machine1",
1192                                 "org.freedesktop.machine1.Manager",
1193                                 "RemoveImage",
1194                                 &error,
1195                                 NULL,
1196                                 "s", argv[i]);
1197                 if (r < 0) {
1198                         log_error("Could not remove image: %s", bus_error_message(&error, -r));
1199                         return r;
1200                 }
1201         }
1202
1203         return 0;
1204 }
1205
1206 static int rename_image(int argc, char *argv[], void *userdata) {
1207         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1208         sd_bus *bus = userdata;
1209         int r;
1210
1211         polkit_agent_open_if_enabled();
1212
1213         r = sd_bus_call_method(
1214                         bus,
1215                         "org.freedesktop.machine1",
1216                         "/org/freedesktop/machine1",
1217                         "org.freedesktop.machine1.Manager",
1218                         "RenameImage",
1219                         &error,
1220                         NULL,
1221                         "ss", argv[1], argv[2]);
1222         if (r < 0) {
1223                 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1224                 return r;
1225         }
1226
1227         return 0;
1228 }
1229
1230 static int clone_image(int argc, char *argv[], void *userdata) {
1231         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1232         sd_bus *bus = userdata;
1233         int r;
1234
1235         polkit_agent_open_if_enabled();
1236
1237         r = sd_bus_call_method(
1238                         bus,
1239                         "org.freedesktop.machine1",
1240                         "/org/freedesktop/machine1",
1241                         "org.freedesktop.machine1.Manager",
1242                         "CloneImage",
1243                         &error,
1244                         NULL,
1245                         "ssb", argv[1], argv[2], arg_read_only);
1246         if (r < 0) {
1247                 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1248                 return r;
1249         }
1250
1251         return 0;
1252 }
1253
1254 static int read_only_image(int argc, char *argv[], void *userdata) {
1255         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1256         sd_bus *bus = userdata;
1257         int b = true, r;
1258
1259         if (argc > 2) {
1260                 b = parse_boolean(argv[2]);
1261                 if (b < 0) {
1262                         log_error("Failed to parse boolean argument: %s", argv[2]);
1263                         return -EINVAL;
1264                 }
1265         }
1266
1267         polkit_agent_open_if_enabled();
1268
1269         r = sd_bus_call_method(
1270                         bus,
1271                         "org.freedesktop.machine1",
1272                         "/org/freedesktop/machine1",
1273                         "org.freedesktop.machine1.Manager",
1274                         "MarkImageReadOnly",
1275                         &error,
1276                         NULL,
1277                         "sb", argv[1], b);
1278         if (r < 0) {
1279                 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1280                 return r;
1281         }
1282
1283         return 0;
1284 }
1285
1286 static int start_machine(int argc, char *argv[], void *userdata) {
1287         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1288         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1289         sd_bus *bus = userdata;
1290         int r, i;
1291
1292         assert(bus);
1293
1294         polkit_agent_open_if_enabled();
1295
1296         r = bus_wait_for_jobs_new(bus, &w);
1297         if (r < 0)
1298                 return log_oom();
1299
1300         for (i = 1; i < argc; i++) {
1301                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1302                 _cleanup_free_ char *e = NULL, *unit = NULL;
1303                 const char *object;
1304
1305                 if (!machine_name_is_valid(argv[i])) {
1306                         log_error("Invalid machine name %s.", argv[i]);
1307                         return -EINVAL;
1308                 }
1309
1310                 e = unit_name_escape(argv[i]);
1311                 if (!e)
1312                         return log_oom();
1313
1314                 unit = unit_name_build("systemd-nspawn", e, ".service");
1315                 if (!unit)
1316                         return log_oom();
1317
1318                 r = sd_bus_call_method(
1319                                 bus,
1320                                 "org.freedesktop.systemd1",
1321                                 "/org/freedesktop/systemd1",
1322                                 "org.freedesktop.systemd1.Manager",
1323                                 "StartUnit",
1324                                 &error,
1325                                 &reply,
1326                                 "ss", unit, "fail");
1327                 if (r < 0) {
1328                         log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1329                         return r;
1330                 }
1331
1332                 r = sd_bus_message_read(reply, "o", &object);
1333                 if (r < 0)
1334                         return bus_log_parse_error(r);
1335
1336                 r = bus_wait_for_jobs_add(w, object);
1337                 if (r < 0)
1338                         return log_oom();
1339         }
1340
1341         r = bus_wait_for_jobs(w, arg_quiet);
1342         if (r < 0)
1343                 return r;
1344
1345         return 0;
1346 }
1347
1348 static int enable_machine(int argc, char *argv[], void *userdata) {
1349         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1350         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1351         int carries_install_info = 0;
1352         const char *method = NULL;
1353         sd_bus *bus = userdata;
1354         int r, i;
1355
1356         assert(bus);
1357
1358         polkit_agent_open_if_enabled();
1359
1360         method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1361
1362         r = sd_bus_message_new_method_call(
1363                         bus,
1364                         &m,
1365                         "org.freedesktop.systemd1",
1366                         "/org/freedesktop/systemd1",
1367                         "org.freedesktop.systemd1.Manager",
1368                         method);
1369         if (r < 0)
1370                 return bus_log_create_error(r);
1371
1372         r = sd_bus_message_open_container(m, 'a', "s");
1373         if (r < 0)
1374                 return bus_log_create_error(r);
1375
1376         for (i = 1; i < argc; i++) {
1377                 _cleanup_free_ char *e = NULL, *unit = NULL;
1378
1379                 if (!machine_name_is_valid(argv[i])) {
1380                         log_error("Invalid machine name %s.", argv[i]);
1381                         return -EINVAL;
1382                 }
1383
1384                 e = unit_name_escape(argv[i]);
1385                 if (!e)
1386                         return log_oom();
1387
1388                 unit = unit_name_build("systemd-nspawn", e, ".service");
1389                 if (!unit)
1390                         return log_oom();
1391
1392                 r = sd_bus_message_append(m, "s", unit);
1393                 if (r < 0)
1394                         return bus_log_create_error(r);
1395         }
1396
1397         r = sd_bus_message_close_container(m);
1398         if (r < 0)
1399                 return bus_log_create_error(r);
1400
1401         if (streq(argv[0], "enable"))
1402                 r = sd_bus_message_append(m, "bb", false, false);
1403         else
1404                 r = sd_bus_message_append(m, "b", false);
1405         if (r < 0)
1406                 return bus_log_create_error(r);
1407
1408         r = sd_bus_call(bus, m, 0, &error, &reply);
1409         if (r < 0) {
1410                 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1411                 return r;
1412         }
1413
1414         if (streq(argv[0], "enable")) {
1415                 r = sd_bus_message_read(reply, "b", carries_install_info);
1416                 if (r < 0)
1417                         return bus_log_parse_error(r);
1418         }
1419
1420         r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1421         if (r < 0)
1422                 return r;
1423
1424         r = sd_bus_call_method(
1425                         bus,
1426                         "org.freedesktop.systemd1",
1427                         "/org/freedesktop/systemd1",
1428                         "org.freedesktop.systemd1.Manager",
1429                         "Reload",
1430                         &error,
1431                         NULL,
1432                         NULL);
1433         if (r < 0) {
1434                 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1435                 return r;
1436         }
1437
1438         return 0;
1439 }
1440
1441 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1442         const char **our_path = userdata, *line;
1443         unsigned priority;
1444         int r;
1445
1446         assert(bus);
1447         assert(m);
1448         assert(our_path);
1449
1450         r = sd_bus_message_read(m, "us", &priority, &line);
1451         if (r < 0) {
1452                 bus_log_parse_error(r);
1453                 return 0;
1454         }
1455
1456         if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1457                 return 0;
1458
1459         if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1460                 return 0;
1461
1462         log_full(priority, "%s", line);
1463         return 0;
1464 }
1465
1466 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1467         const char **our_path = userdata, *path, *result;
1468         uint32_t id;
1469         int r;
1470
1471         assert(bus);
1472         assert(m);
1473         assert(our_path);
1474
1475         r = sd_bus_message_read(m, "uos", &id, &path, &result);
1476         if (r < 0) {
1477                 bus_log_parse_error(r);
1478                 return 0;
1479         }
1480
1481         if (!streq_ptr(*our_path, path))
1482                 return 0;
1483
1484         sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1485         return 0;
1486 }
1487
1488 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1489         assert(s);
1490         assert(si);
1491
1492         if (!arg_quiet)
1493                 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1494
1495         sd_event_exit(sd_event_source_get_event(s), EINTR);
1496         return 0;
1497 }
1498
1499 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1500         _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1501         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1502         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1503         _cleanup_event_unref_ sd_event* event = NULL;
1504         const char *path = NULL;
1505         uint32_t id;
1506         int r;
1507
1508         assert(bus);
1509         assert(m);
1510
1511         polkit_agent_open_if_enabled();
1512
1513         r = sd_event_default(&event);
1514         if (r < 0)
1515                 return log_error_errno(r, "Failed to get event loop: %m");
1516
1517         r = sd_bus_attach_event(bus, event, 0);
1518         if (r < 0)
1519                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1520
1521         r = sd_bus_add_match(
1522                         bus,
1523                         &slot_job_removed,
1524                         "type='signal',"
1525                         "sender='org.freedesktop.import1',"
1526                         "interface='org.freedesktop.import1.Manager',"
1527                         "member='TransferRemoved',"
1528                         "path='/org/freedesktop/import1'",
1529                         match_transfer_removed, &path);
1530         if (r < 0)
1531                 return log_error_errno(r, "Failed to install match: %m");
1532
1533         r = sd_bus_add_match(
1534                         bus,
1535                         &slot_log_message,
1536                         "type='signal',"
1537                         "sender='org.freedesktop.import1',"
1538                         "interface='org.freedesktop.import1.Transfer',"
1539                         "member='LogMessage'",
1540                         match_log_message, &path);
1541         if (r < 0)
1542                 return log_error_errno(r, "Failed to install match: %m");
1543
1544         r = sd_bus_call(bus, m, 0, &error, &reply);
1545         if (r < 0) {
1546                 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1547                 return r;
1548         }
1549
1550         r = sd_bus_message_read(reply, "uo", &id, &path);
1551         if (r < 0)
1552                 return bus_log_parse_error(r);
1553
1554         sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1555
1556         if (!arg_quiet)
1557                 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1558
1559         sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1560         sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1561
1562         r = sd_event_loop(event);
1563         if (r < 0)
1564                 return log_error_errno(r, "Failed to run event loop: %m");
1565
1566         return -r;
1567 }
1568
1569 static int pull_tar(int argc, char *argv[], void *userdata) {
1570         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1571         _cleanup_free_ char *l = NULL, *ll = NULL;
1572         const char *local, *remote;
1573         sd_bus *bus = userdata;
1574         int r;
1575
1576         assert(bus);
1577
1578         remote = argv[1];
1579         if (!http_url_is_valid(remote)) {
1580                 log_error("URL '%s' is not valid.", remote);
1581                 return -EINVAL;
1582         }
1583
1584         if (argc >= 3)
1585                 local = argv[2];
1586         else {
1587                 r = import_url_last_component(remote, &l);
1588                 if (r < 0)
1589                         return log_error_errno(r, "Failed to get final component of URL: %m");
1590
1591                 local = l;
1592         }
1593
1594         if (isempty(local) || streq(local, "-"))
1595                 local = NULL;
1596
1597         if (local) {
1598                 r = tar_strip_suffixes(local, &ll);
1599                 if (r < 0)
1600                         return log_error_errno(r, "Failed to strip tar suffixes: %m");
1601
1602                 local = ll;
1603
1604                 if (!machine_name_is_valid(local)) {
1605                         log_error("Local name %s is not a suitable machine name.", local);
1606                         return -EINVAL;
1607                 }
1608         }
1609
1610         r = sd_bus_message_new_method_call(
1611                         bus,
1612                         &m,
1613                         "org.freedesktop.import1",
1614                         "/org/freedesktop/import1",
1615                         "org.freedesktop.import1.Manager",
1616                         "PullTar");
1617         if (r < 0)
1618                 return bus_log_create_error(r);
1619
1620         r = sd_bus_message_append(
1621                         m,
1622                         "sssb",
1623                         remote,
1624                         local,
1625                         import_verify_to_string(arg_verify),
1626                         arg_force);
1627         if (r < 0)
1628                 return bus_log_create_error(r);
1629
1630         return pull_image_common(bus, m);
1631 }
1632
1633 static int pull_raw(int argc, char *argv[], void *userdata) {
1634         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1635         _cleanup_free_ char *l = NULL, *ll = NULL;
1636         const char *local, *remote;
1637         sd_bus *bus = userdata;
1638         int r;
1639
1640         assert(bus);
1641
1642         remote = argv[1];
1643         if (!http_url_is_valid(remote)) {
1644                 log_error("URL '%s' is not valid.", remote);
1645                 return -EINVAL;
1646         }
1647
1648         if (argc >= 3)
1649                 local = argv[2];
1650         else {
1651                 r = import_url_last_component(remote, &l);
1652                 if (r < 0)
1653                         return log_error_errno(r, "Failed to get final component of URL: %m");
1654
1655                 local = l;
1656         }
1657
1658         if (isempty(local) || streq(local, "-"))
1659                 local = NULL;
1660
1661         if (local) {
1662                 r = raw_strip_suffixes(local, &ll);
1663                 if (r < 0)
1664                         return log_error_errno(r, "Failed to strip tar suffixes: %m");
1665
1666                 local = ll;
1667
1668                 if (!machine_name_is_valid(local)) {
1669                         log_error("Local name %s is not a suitable machine name.", local);
1670                         return -EINVAL;
1671                 }
1672         }
1673
1674         r = sd_bus_message_new_method_call(
1675                         bus,
1676                         &m,
1677                         "org.freedesktop.import1",
1678                         "/org/freedesktop/import1",
1679                         "org.freedesktop.import1.Manager",
1680                         "PullRaw");
1681         if (r < 0)
1682                 return bus_log_create_error(r);
1683
1684         r = sd_bus_message_append(
1685                         m,
1686                         "sssb",
1687                         remote,
1688                         local,
1689                         import_verify_to_string(arg_verify),
1690                         arg_force);
1691         if (r < 0)
1692                 return bus_log_create_error(r);
1693
1694         return pull_image_common(bus, m);
1695 }
1696
1697 static int pull_dkr(int argc, char *argv[], void *userdata) {
1698         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1699         const char *local, *remote, *tag;
1700         sd_bus *bus = userdata;
1701         int r;
1702
1703         if (arg_verify != IMPORT_VERIFY_NO) {
1704                 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1705                 return -EINVAL;
1706         }
1707
1708         remote = argv[1];
1709         tag = strchr(remote, ':');
1710         if (tag) {
1711                 remote = strndupa(remote, tag - remote);
1712                 tag++;
1713         }
1714
1715         if (!dkr_name_is_valid(remote)) {
1716                 log_error("DKR name '%s' is invalid.", remote);
1717                 return -EINVAL;
1718         }
1719         if (tag && !dkr_tag_is_valid(tag)) {
1720                 log_error("DKR tag '%s' is invalid.", remote);
1721                 return -EINVAL;
1722         }
1723
1724         if (argc >= 3)
1725                 local = argv[2];
1726         else {
1727                 local = strchr(remote, '/');
1728                 if (local)
1729                         local++;
1730                 else
1731                         local = remote;
1732         }
1733
1734         if (isempty(local) || streq(local, "-"))
1735                 local = NULL;
1736
1737         if (local) {
1738                 if (!machine_name_is_valid(local)) {
1739                         log_error("Local name %s is not a suitable machine name.", local);
1740                         return -EINVAL;
1741                 }
1742         }
1743
1744         r = sd_bus_message_new_method_call(
1745                         bus,
1746                         &m,
1747                         "org.freedesktop.import1",
1748                         "/org/freedesktop/import1",
1749                         "org.freedesktop.import1.Manager",
1750                         "PullDkr");
1751         if (r < 0)
1752                 return bus_log_create_error(r);
1753
1754         r = sd_bus_message_append(
1755                         m,
1756                         "sssssb",
1757                         arg_dkr_index_url,
1758                         remote,
1759                         tag,
1760                         local,
1761                         import_verify_to_string(arg_verify),
1762                         arg_force);
1763         if (r < 0)
1764                 return bus_log_create_error(r);
1765
1766         return pull_image_common(bus, m);
1767 }
1768
1769 typedef struct TransferInfo {
1770         uint32_t id;
1771         const char *type;
1772         const char *remote;
1773         const char *local;
1774         double progress;
1775 } TransferInfo;
1776
1777 static int compare_transfer_info(const void *a, const void *b) {
1778         const TransferInfo *x = a, *y = b;
1779
1780         return strcmp(x->local, y->local);
1781 }
1782
1783 static int list_transfers(int argc, char *argv[], void *userdata) {
1784         size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
1785         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1786         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1787         _cleanup_free_ TransferInfo *transfers = NULL;
1788         size_t n_transfers = 0, n_allocated = 0, j;
1789         const char *type, *remote, *local, *object;
1790         sd_bus *bus = userdata;
1791         uint32_t id, max_id = 0;
1792         double progress;
1793         int r;
1794
1795         pager_open_if_enabled();
1796
1797         r = sd_bus_call_method(
1798                                 bus,
1799                                 "org.freedesktop.import1",
1800                                 "/org/freedesktop/import1",
1801                                 "org.freedesktop.import1.Manager",
1802                                 "ListTransfers",
1803                                 &error,
1804                                 &reply,
1805                                 NULL);
1806         if (r < 0) {
1807                 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
1808                 return r;
1809         }
1810
1811         r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
1812         if (r < 0)
1813                 return bus_log_parse_error(r);
1814
1815         while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
1816                 size_t l;
1817
1818                 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
1819                         return log_oom();
1820
1821                 transfers[n_transfers].id = id;
1822                 transfers[n_transfers].type = type;
1823                 transfers[n_transfers].remote = remote;
1824                 transfers[n_transfers].local = local;
1825                 transfers[n_transfers].progress = progress;
1826
1827                 l = strlen(type);
1828                 if (l > max_type)
1829                         max_type = l;
1830
1831                 l = strlen(remote);
1832                 if (l > max_remote)
1833                         max_remote = l;
1834
1835                 l = strlen(local);
1836                 if (l > max_local)
1837                         max_local = l;
1838
1839                 if (id > max_id)
1840                         max_id = id;
1841
1842                 n_transfers ++;
1843         }
1844         if (r < 0)
1845                 return bus_log_parse_error(r);
1846
1847         r = sd_bus_message_exit_container(reply);
1848         if (r < 0)
1849                 return bus_log_parse_error(r);
1850
1851         qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
1852
1853         if (arg_legend)
1854                 printf("%-*s %-*s %-*s %-*s %-*s\n",
1855                        (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
1856                        (int) 7, "PERCENT",
1857                        (int) max_type, "TYPE",
1858                        (int) max_local, "LOCAL",
1859                        (int) max_remote, "REMOTE");
1860
1861         for (j = 0; j < n_transfers; j++)
1862                 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
1863                        (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
1864                        (int) 6, (unsigned) (transfers[j].progress * 100),
1865                        (int) max_type, transfers[j].type,
1866                        (int) max_local, transfers[j].local,
1867                        (int) max_remote, transfers[j].remote);
1868
1869         if (arg_legend)
1870                 printf("\n%zu transfers listed.\n", n_transfers);
1871
1872         return 0;
1873 }
1874
1875 static int cancel_transfer(int argc, char *argv[], void *userdata) {
1876         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1877         sd_bus *bus = userdata;
1878         int r, i;
1879
1880         assert(bus);
1881
1882         polkit_agent_open_if_enabled();
1883
1884         for (i = 1; i < argc; i++) {
1885                 uint32_t id;
1886
1887                 r = safe_atou32(argv[i], &id);
1888                 if (r < 0)
1889                         return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
1890
1891                 r = sd_bus_call_method(
1892                                 bus,
1893                                 "org.freedesktop.import1",
1894                                 "/org/freedesktop/import1",
1895                                 "org.freedesktop.import1.Manager",
1896                                 "CancelTransfer",
1897                                 &error,
1898                                 NULL,
1899                                 "u", id);
1900                 if (r < 0) {
1901                         log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
1902                         return r;
1903                 }
1904         }
1905
1906         return 0;
1907 }
1908
1909 static int help(int argc, char *argv[], void *userdata) {
1910
1911         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1912                "Send control commands to or query the virtual machine and container\n"
1913                "registration manager.\n\n"
1914                "  -h --help                   Show this help\n"
1915                "     --version                Show package version\n"
1916                "     --no-pager               Do not pipe output into a pager\n"
1917                "     --no-legend              Do not show the headers and footers\n"
1918                "     --no-ask-password        Do not ask for system passwords\n"
1919                "  -H --host=[USER@]HOST       Operate on remote host\n"
1920                "  -M --machine=CONTAINER      Operate on local container\n"
1921                "  -p --property=NAME          Show only properties by this name\n"
1922                "  -q --quiet                  Suppress output\n"
1923                "  -a --all                    Show all properties, including empty ones\n"
1924                "  -l --full                   Do not ellipsize output\n"
1925                "     --kill-who=WHO           Who to send signal to\n"
1926                "  -s --signal=SIGNAL          Which signal to send\n"
1927                "     --read-only              Create read-only bind mount\n"
1928                "     --mkdir                  Create directory before bind mounting, if missing\n"
1929                "  -n --lines=INTEGER          Number of journal entries to show\n"
1930                "  -o --output=STRING          Change journal output mode (short,\n"
1931                "                              short-monotonic, verbose, export, json,\n"
1932                "                              json-pretty, json-sse, cat)\n"
1933                "      --verify=MODE           Verification mode for downloaded images (no,\n"
1934                "                              checksum, signature)\n"
1935                "      --force                 Download image even if already exists\n"
1936                "      --dkr-index-url=URL     Specify the index URL to use for DKR image\n"
1937                "                              downloads\n\n"
1938                "Machine Commands:\n"
1939                "  list                        List running VMs and containers\n"
1940                "  status NAME...              Show VM/container details\n"
1941                "  show NAME...                Show properties of one or more VMs/containers\n"
1942                "  start NAME...               Start container as a service\n"
1943                "  login NAME                  Get a login prompt on a container\n"
1944                "  enable NAME...              Enable automatic container start at boot\n"
1945                "  disable NAME...             Disable automatic container start at boot\n"
1946                "  poweroff NAME...            Power off one or more containers\n"
1947                "  reboot NAME...              Reboot one or more containers\n"
1948                "  terminate NAME...           Terminate one or more VMs/containers\n"
1949                "  kill NAME...                Send signal to processes of a VM/container\n"
1950                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
1951                "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
1952                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n\n"
1953                "Image Commands:\n"
1954                "  list-images                 Show available container and VM images\n"
1955                "  image-status NAME...        Show image details\n"
1956                "  show-image NAME...          Show properties of image\n"
1957                "  clone NAME NAME             Clone an image\n"
1958                "  rename NAME NAME            Rename an image\n"
1959                "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
1960                "  remove NAME...              Remove an image\n\n"
1961                "Image Transfer Commands:\n"
1962                "  pull-tar URL [NAME]         Download a TAR container image\n"
1963                "  pull-raw URL [NAME]         Download a RAW container or VM image\n"
1964                "  pull-dkr REMOTE [NAME]      Download a DKR container image\n"
1965                "  list-transfers              Show list of downloads in progress\n"
1966                "  cancel-transfer             Cancel a download\n"
1967                , program_invocation_short_name);
1968
1969         return 0;
1970 }
1971
1972 static int parse_argv(int argc, char *argv[]) {
1973
1974         enum {
1975                 ARG_VERSION = 0x100,
1976                 ARG_NO_PAGER,
1977                 ARG_NO_LEGEND,
1978                 ARG_KILL_WHO,
1979                 ARG_READ_ONLY,
1980                 ARG_MKDIR,
1981                 ARG_NO_ASK_PASSWORD,
1982                 ARG_VERIFY,
1983                 ARG_FORCE,
1984                 ARG_DKR_INDEX_URL,
1985         };
1986
1987         static const struct option options[] = {
1988                 { "help",            no_argument,       NULL, 'h'                 },
1989                 { "version",         no_argument,       NULL, ARG_VERSION         },
1990                 { "property",        required_argument, NULL, 'p'                 },
1991                 { "all",             no_argument,       NULL, 'a'                 },
1992                 { "full",            no_argument,       NULL, 'l'                 },
1993                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1994                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1995                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1996                 { "signal",          required_argument, NULL, 's'                 },
1997                 { "host",            required_argument, NULL, 'H'                 },
1998                 { "machine",         required_argument, NULL, 'M'                 },
1999                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
2000                 { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
2001                 { "quiet",           no_argument,       NULL, 'q'                 },
2002                 { "lines",           required_argument, NULL, 'n'                 },
2003                 { "output",          required_argument, NULL, 'o'                 },
2004                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
2005                 { "verify",          required_argument, NULL, ARG_VERIFY          },
2006                 { "force",           no_argument,       NULL, ARG_FORCE           },
2007                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
2008                 {}
2009         };
2010
2011         int c, r;
2012
2013         assert(argc >= 0);
2014         assert(argv);
2015
2016         while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2017
2018                 switch (c) {
2019
2020                 case 'h':
2021                         return help(0, NULL, NULL);
2022
2023                 case ARG_VERSION:
2024                         puts(PACKAGE_STRING);
2025                         puts(SYSTEMD_FEATURES);
2026                         return 0;
2027
2028                 case 'p':
2029                         r = strv_extend(&arg_property, optarg);
2030                         if (r < 0)
2031                                 return log_oom();
2032
2033                         /* If the user asked for a particular
2034                          * property, show it to him, even if it is
2035                          * empty. */
2036                         arg_all = true;
2037                         break;
2038
2039                 case 'a':
2040                         arg_all = true;
2041                         break;
2042
2043                 case 'l':
2044                         arg_full = true;
2045                         break;
2046
2047                 case 'n':
2048                         if (safe_atou(optarg, &arg_lines) < 0) {
2049                                 log_error("Failed to parse lines '%s'", optarg);
2050                                 return -EINVAL;
2051                         }
2052                         break;
2053
2054                 case 'o':
2055                         arg_output = output_mode_from_string(optarg);
2056                         if (arg_output < 0) {
2057                                 log_error("Unknown output '%s'.", optarg);
2058                                 return -EINVAL;
2059                         }
2060                         break;
2061
2062                 case ARG_NO_PAGER:
2063                         arg_no_pager = true;
2064                         break;
2065
2066                 case ARG_NO_LEGEND:
2067                         arg_legend = false;
2068                         break;
2069
2070                 case ARG_KILL_WHO:
2071                         arg_kill_who = optarg;
2072                         break;
2073
2074                 case 's':
2075                         arg_signal = signal_from_string_try_harder(optarg);
2076                         if (arg_signal < 0) {
2077                                 log_error("Failed to parse signal string %s.", optarg);
2078                                 return -EINVAL;
2079                         }
2080                         break;
2081
2082                 case ARG_NO_ASK_PASSWORD:
2083                         arg_ask_password = false;
2084                         break;
2085
2086                 case 'H':
2087                         arg_transport = BUS_TRANSPORT_REMOTE;
2088                         arg_host = optarg;
2089                         break;
2090
2091                 case 'M':
2092                         arg_transport = BUS_TRANSPORT_MACHINE;
2093                         arg_host = optarg;
2094                         break;
2095
2096                 case ARG_READ_ONLY:
2097                         arg_read_only = true;
2098                         break;
2099
2100                 case ARG_MKDIR:
2101                         arg_mkdir = true;
2102                         break;
2103
2104                 case 'q':
2105                         arg_quiet = true;
2106                         break;
2107
2108                 case ARG_VERIFY:
2109                         arg_verify = import_verify_from_string(optarg);
2110                         if (arg_verify < 0) {
2111                                 log_error("Failed to parse --verify= setting: %s", optarg);
2112                                 return -EINVAL;
2113                         }
2114                         break;
2115
2116                 case ARG_FORCE:
2117                         arg_force = true;
2118                         break;
2119
2120                 case ARG_DKR_INDEX_URL:
2121                         if (!http_url_is_valid(optarg)) {
2122                                 log_error("Index URL is invalid: %s", optarg);
2123                                 return -EINVAL;
2124                         }
2125
2126                         arg_dkr_index_url = optarg;
2127                         break;
2128
2129                 case '?':
2130                         return -EINVAL;
2131
2132                 default:
2133                         assert_not_reached("Unhandled option");
2134                 }
2135
2136         return 1;
2137 }
2138
2139 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2140
2141         static const Verb verbs[] = {
2142                 { "help",            VERB_ANY, VERB_ANY, 0,            help              },
2143                 { "list",            VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
2144                 { "list-images",     VERB_ANY, 1,        0,            list_images       },
2145                 { "status",          2,        VERB_ANY, 0,            show_machine      },
2146                 { "image-status",    2,        VERB_ANY, 0,            show_image        },
2147                 { "show",            VERB_ANY, VERB_ANY, 0,            show_machine      },
2148                 { "show-image",      VERB_ANY, VERB_ANY, 0,            show_image        },
2149                 { "terminate",       2,        VERB_ANY, 0,            terminate_machine },
2150                 { "reboot",          2,        VERB_ANY, 0,            reboot_machine    },
2151                 { "poweroff",        2,        VERB_ANY, 0,            poweroff_machine  },
2152                 { "kill",            2,        VERB_ANY, 0,            kill_machine      },
2153                 { "login",           2,        2,        0,            login_machine     },
2154                 { "bind",            3,        4,        0,            bind_mount        },
2155                 { "copy-to",         3,        4,        0,            copy_files        },
2156                 { "copy-from",       3,        4,        0,            copy_files        },
2157                 { "remove",          2,        VERB_ANY, 0,            remove_image      },
2158                 { "rename",          3,        3,        0,            rename_image      },
2159                 { "clone",           3,        3,        0,            clone_image       },
2160                 { "read-only",       2,        3,        0,            read_only_image   },
2161                 { "start",           2,        VERB_ANY, 0,            start_machine     },
2162                 { "enable",          2,        VERB_ANY, 0,            enable_machine    },
2163                 { "disable",         2,        VERB_ANY, 0,            enable_machine    },
2164                 { "pull-tar",        2,        3,        0,            pull_tar          },
2165                 { "pull-raw",        2,        3,        0,            pull_raw          },
2166                 { "pull-dkr",        2,        3,        0,            pull_dkr          },
2167                 { "list-transfers",  VERB_ANY, 1,        0,            list_transfers    },
2168                 { "cancel-transfer", 2,        VERB_ANY, 0,            cancel_transfer   },
2169                 {}
2170         };
2171
2172         return dispatch_verb(argc, argv, verbs, bus);
2173 }
2174
2175 int main(int argc, char*argv[]) {
2176         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2177         int r;
2178
2179         setlocale(LC_ALL, "");
2180         log_parse_environment();
2181         log_open();
2182
2183         r = parse_argv(argc, argv);
2184         if (r <= 0)
2185                 goto finish;
2186
2187         r = bus_open_transport(arg_transport, arg_host, false, &bus);
2188         if (r < 0) {
2189                 log_error_errno(r, "Failed to create bus connection: %m");
2190                 goto finish;
2191         }
2192
2193         sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2194
2195         r = machinectl_main(argc, argv, bus);
2196
2197 finish:
2198         pager_close();
2199         polkit_agent_close();
2200
2201         strv_free(arg_property);
2202
2203         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2204 }