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