chiark / gitweb /
ddd2a4aadb43bbf81cd0b506f49a5eb21110ad09
[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(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(bus);
826         assert(path);
827         assert(new_line);
828
829         r = bus_map_all_properties(bus,
830                                    "org.freedesktop.machine1",
831                                    path,
832                                    map,
833                                    &info);
834         if (r < 0)
835                 return log_error_errno(r, "Could not get properties: %m");
836
837         if (*new_line)
838                 printf("\n");
839         *new_line = true;
840
841         print_image_status_info(bus, &info);
842
843         free(info.name);
844         free(info.path);
845         free(info.type);
846
847         return r;
848 }
849
850 typedef struct PoolStatusInfo {
851         char *path;
852         uint64_t usage;
853         uint64_t limit;
854 } PoolStatusInfo;
855
856 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
857         char bs[FORMAT_BYTES_MAX], *s;
858
859         if (i->path)
860                 printf("\t    Path: %s\n", i->path);
861
862         s = format_bytes(bs, sizeof(bs), i->usage);
863         if (s)
864                 printf("\t   Usage: %s\n", s);
865
866         s = format_bytes(bs, sizeof(bs), i->limit);
867         if (s)
868                 printf("\t   Limit: %s\n", s);
869 }
870
871 static int show_pool_info(sd_bus *bus) {
872
873         static const struct bus_properties_map map[]  = {
874                 { "PoolPath",  "s",  NULL, offsetof(PoolStatusInfo, path)  },
875                 { "PoolUsage", "t",  NULL, offsetof(PoolStatusInfo, usage) },
876                 { "PoolLimit", "t",  NULL, offsetof(PoolStatusInfo, limit) },
877                 {}
878         };
879
880         PoolStatusInfo info = {
881                 .usage = (uint64_t) -1,
882                 .limit = (uint64_t) -1,
883         };
884         int r;
885
886         assert(bus);
887
888         r = bus_map_all_properties(bus,
889                                    "org.freedesktop.machine1",
890                                    "/org/freedesktop/machine1",
891                                    map,
892                                    &info);
893         if (r < 0)
894                 return log_error_errno(r, "Could not get properties: %m");
895
896         print_pool_status_info(bus, &info);
897
898         free(info.path);
899         return 0;
900 }
901
902
903 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
904         int r;
905
906         assert(bus);
907         assert(path);
908         assert(new_line);
909
910         if (*new_line)
911                 printf("\n");
912
913         *new_line = true;
914
915         r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
916         if (r < 0)
917                 log_error_errno(r, "Could not get properties: %m");
918
919         return r;
920 }
921
922 static int show_image(int argc, char *argv[], void *userdata) {
923
924         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
925         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
926         bool properties, new_line = false;
927         sd_bus *bus = userdata;
928         int r = 0, i;
929
930         assert(bus);
931
932         properties = !strstr(argv[0], "status");
933
934         pager_open_if_enabled();
935
936         if (argc <= 1) {
937
938                 /* If no argument is specified, inspect the manager
939                  * itself */
940
941                 if (properties)
942                         r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
943                 else
944                         r = show_pool_info(bus);
945                 if (r < 0)
946                         return r;
947         }
948
949         for (i = 1; i < argc; i++) {
950                 const char *path = NULL;
951
952                 r = sd_bus_call_method(
953                                 bus,
954                                 "org.freedesktop.machine1",
955                                 "/org/freedesktop/machine1",
956                                 "org.freedesktop.machine1.Manager",
957                                 "GetImage",
958                                 &error,
959                                 &reply,
960                                 "s", argv[i]);
961                 if (r < 0) {
962                         log_error("Could not get path to image: %s", bus_error_message(&error, -r));
963                         return r;
964                 }
965
966                 r = sd_bus_message_read(reply, "o", &path);
967                 if (r < 0)
968                         return bus_log_parse_error(r);
969
970                 if (properties)
971                         r = show_image_properties(bus, path, &new_line);
972                 else
973                         r = show_image_info(bus, path, &new_line);
974         }
975
976         return r;
977 }
978
979 static int kill_machine(int argc, char *argv[], void *userdata) {
980         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
981         sd_bus *bus = userdata;
982         int r, i;
983
984         assert(bus);
985
986         polkit_agent_open_if_enabled();
987
988         if (!arg_kill_who)
989                 arg_kill_who = "all";
990
991         for (i = 1; i < argc; i++) {
992                 r = sd_bus_call_method(
993                                 bus,
994                                 "org.freedesktop.machine1",
995                                 "/org/freedesktop/machine1",
996                                 "org.freedesktop.machine1.Manager",
997                                 "KillMachine",
998                                 &error,
999                                 NULL,
1000                                 "ssi", argv[i], arg_kill_who, arg_signal);
1001                 if (r < 0) {
1002                         log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1003                         return r;
1004                 }
1005         }
1006
1007         return 0;
1008 }
1009
1010 static int reboot_machine(int argc, char *argv[], void *userdata) {
1011         arg_kill_who = "leader";
1012         arg_signal = SIGINT; /* sysvinit + systemd */
1013
1014         return kill_machine(argc, argv, userdata);
1015 }
1016
1017 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1018         arg_kill_who = "leader";
1019         arg_signal = SIGRTMIN+4; /* only systemd */
1020
1021         return kill_machine(argc, argv, userdata);
1022 }
1023
1024 static int terminate_machine(int argc, char *argv[], void *userdata) {
1025         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1026         sd_bus *bus = userdata;
1027         int r, i;
1028
1029         assert(bus);
1030
1031         polkit_agent_open_if_enabled();
1032
1033         for (i = 1; i < argc; i++) {
1034                 r = sd_bus_call_method(
1035                                 bus,
1036                                 "org.freedesktop.machine1",
1037                                 "/org/freedesktop/machine1",
1038                                 "org.freedesktop.machine1.Manager",
1039                                 "TerminateMachine",
1040                                 &error,
1041                                 NULL,
1042                                 "s", argv[i]);
1043                 if (r < 0) {
1044                         log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1045                         return r;
1046                 }
1047         }
1048
1049         return 0;
1050 }
1051
1052 static int copy_files(int argc, char *argv[], void *userdata) {
1053         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1054         sd_bus *bus = userdata;
1055         bool copy_from;
1056         int r;
1057
1058         assert(bus);
1059
1060         polkit_agent_open_if_enabled();
1061
1062         copy_from = streq(argv[0], "copy-from");
1063
1064         r = sd_bus_call_method(
1065                         bus,
1066                         "org.freedesktop.machine1",
1067                         "/org/freedesktop/machine1",
1068                         "org.freedesktop.machine1.Manager",
1069                         copy_from ? "CopyFromMachine" : "CopyToMachine",
1070                         &error,
1071                         NULL,
1072                         "sss",
1073                         argv[1],
1074                         argv[2],
1075                         argv[3]);
1076         if (r < 0) {
1077                 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1078                 return r;
1079         }
1080
1081         return 0;
1082 }
1083
1084 static int bind_mount(int argc, char *argv[], void *userdata) {
1085         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1086         sd_bus *bus = userdata;
1087         int r;
1088
1089         assert(bus);
1090
1091         polkit_agent_open_if_enabled();
1092
1093         r = sd_bus_call_method(
1094                         bus,
1095                         "org.freedesktop.machine1",
1096                         "/org/freedesktop/machine1",
1097                         "org.freedesktop.machine1.Manager",
1098                         "BindMountMachine",
1099                         &error,
1100                         NULL,
1101                         "sssbb",
1102                         argv[1],
1103                         argv[2],
1104                         argv[3],
1105                         arg_read_only,
1106                         arg_mkdir);
1107         if (r < 0) {
1108                 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1109                 return r;
1110         }
1111
1112         return 0;
1113 }
1114
1115 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1116         PTYForward ** forward = (PTYForward**) userdata;
1117         int r;
1118
1119         assert(bus);
1120         assert(m);
1121         assert(forward);
1122
1123         if (*forward) {
1124                 /* If the forwarder is already initialized, tell it to
1125                  * exit on the next vhangup(), so that we still flush
1126                  * out what might be queued and exit then. */
1127
1128                 r = pty_forward_set_ignore_vhangup(*forward, false);
1129                 if (r >= 0)
1130                         return 0;
1131
1132                 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1133         }
1134
1135         /* On error, or when the forwarder is not initialized yet, quit immediately */
1136         sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1137         return 0;
1138 }
1139
1140 static int login_machine(int argc, char *argv[], void *userdata) {
1141         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1142         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1143         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1144         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1145         _cleanup_event_unref_ sd_event *event = NULL;
1146         int master = -1, r, ret = 0;
1147         sd_bus *bus = userdata;
1148         const char *pty, *match;
1149         char last_char = 0;
1150         bool machine_died;
1151
1152         assert(bus);
1153
1154         if (arg_transport != BUS_TRANSPORT_LOCAL &&
1155             arg_transport != BUS_TRANSPORT_MACHINE) {
1156                 log_error("Login only supported on local machines.");
1157                 return -ENOTSUP;
1158         }
1159
1160         polkit_agent_open_if_enabled();
1161
1162         r = sd_event_default(&event);
1163         if (r < 0)
1164                 return log_error_errno(r, "Failed to get event loop: %m");
1165
1166         r = sd_bus_attach_event(bus, event, 0);
1167         if (r < 0)
1168                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1169
1170         match = strjoina("type='signal',"
1171                            "sender='org.freedesktop.machine1',"
1172                            "path='/org/freedesktop/machine1',",
1173                            "interface='org.freedesktop.machine1.Manager',"
1174                            "member='MachineRemoved',"
1175                            "arg0='",
1176                            argv[1],
1177                            "'");
1178
1179         r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1180         if (r < 0)
1181                 return log_error_errno(r, "Failed to add machine removal match: %m");
1182
1183         r = sd_bus_call_method(
1184                         bus,
1185                         "org.freedesktop.machine1",
1186                         "/org/freedesktop/machine1",
1187                         "org.freedesktop.machine1.Manager",
1188                         "OpenMachineLogin",
1189                         &error,
1190                         &reply,
1191                         "s", argv[1]);
1192         if (r < 0) {
1193                 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1194                 return r;
1195         }
1196
1197         r = sd_bus_message_read(reply, "hs", &master, &pty);
1198         if (r < 0)
1199                 return bus_log_parse_error(r);
1200
1201         sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1202
1203         log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1204
1205         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1206         sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1207
1208         r = pty_forward_new(event, master, true, false, &forward);
1209         if (r < 0)
1210                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1211
1212         r = sd_event_loop(event);
1213         if (r < 0)
1214                 return log_error_errno(r, "Failed to run event loop: %m");
1215
1216         pty_forward_get_last_char(forward, &last_char);
1217         machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1218
1219         forward = pty_forward_free(forward);
1220
1221         if (last_char != '\n')
1222                 fputc('\n', stdout);
1223
1224         if (machine_died)
1225                 log_info("Machine %s terminated.", argv[1]);
1226         else
1227                 log_info("Connection to machine %s terminated.", argv[1]);
1228
1229         sd_event_get_exit_code(event, &ret);
1230         return ret;
1231 }
1232
1233 static int remove_image(int argc, char *argv[], void *userdata) {
1234         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1235         sd_bus *bus = userdata;
1236         int r, i;
1237
1238         assert(bus);
1239
1240         polkit_agent_open_if_enabled();
1241
1242         for (i = 1; i < argc; i++) {
1243                 r = sd_bus_call_method(
1244                                 bus,
1245                                 "org.freedesktop.machine1",
1246                                 "/org/freedesktop/machine1",
1247                                 "org.freedesktop.machine1.Manager",
1248                                 "RemoveImage",
1249                                 &error,
1250                                 NULL,
1251                                 "s", argv[i]);
1252                 if (r < 0) {
1253                         log_error("Could not remove image: %s", bus_error_message(&error, -r));
1254                         return r;
1255                 }
1256         }
1257
1258         return 0;
1259 }
1260
1261 static int rename_image(int argc, char *argv[], void *userdata) {
1262         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1263         sd_bus *bus = userdata;
1264         int r;
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                         "RenameImage",
1274                         &error,
1275                         NULL,
1276                         "ss", argv[1], argv[2]);
1277         if (r < 0) {
1278                 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1279                 return r;
1280         }
1281
1282         return 0;
1283 }
1284
1285 static int clone_image(int argc, char *argv[], void *userdata) {
1286         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1287         sd_bus *bus = userdata;
1288         int r;
1289
1290         polkit_agent_open_if_enabled();
1291
1292         r = sd_bus_call_method(
1293                         bus,
1294                         "org.freedesktop.machine1",
1295                         "/org/freedesktop/machine1",
1296                         "org.freedesktop.machine1.Manager",
1297                         "CloneImage",
1298                         &error,
1299                         NULL,
1300                         "ssb", argv[1], argv[2], arg_read_only);
1301         if (r < 0) {
1302                 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1303                 return r;
1304         }
1305
1306         return 0;
1307 }
1308
1309 static int read_only_image(int argc, char *argv[], void *userdata) {
1310         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1311         sd_bus *bus = userdata;
1312         int b = true, r;
1313
1314         if (argc > 2) {
1315                 b = parse_boolean(argv[2]);
1316                 if (b < 0) {
1317                         log_error("Failed to parse boolean argument: %s", argv[2]);
1318                         return -EINVAL;
1319                 }
1320         }
1321
1322         polkit_agent_open_if_enabled();
1323
1324         r = sd_bus_call_method(
1325                         bus,
1326                         "org.freedesktop.machine1",
1327                         "/org/freedesktop/machine1",
1328                         "org.freedesktop.machine1.Manager",
1329                         "MarkImageReadOnly",
1330                         &error,
1331                         NULL,
1332                         "sb", argv[1], b);
1333         if (r < 0) {
1334                 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1335                 return r;
1336         }
1337
1338         return 0;
1339 }
1340
1341 static int start_machine(int argc, char *argv[], void *userdata) {
1342         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1343         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1344         sd_bus *bus = userdata;
1345         int r, i;
1346
1347         assert(bus);
1348
1349         polkit_agent_open_if_enabled();
1350
1351         r = bus_wait_for_jobs_new(bus, &w);
1352         if (r < 0)
1353                 return log_oom();
1354
1355         for (i = 1; i < argc; i++) {
1356                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1357                 _cleanup_free_ char *e = NULL, *unit = NULL;
1358                 const char *object;
1359
1360                 if (!machine_name_is_valid(argv[i])) {
1361                         log_error("Invalid machine name %s.", argv[i]);
1362                         return -EINVAL;
1363                 }
1364
1365                 e = unit_name_escape(argv[i]);
1366                 if (!e)
1367                         return log_oom();
1368
1369                 unit = unit_name_build("systemd-nspawn", e, ".service");
1370                 if (!unit)
1371                         return log_oom();
1372
1373                 r = sd_bus_call_method(
1374                                 bus,
1375                                 "org.freedesktop.systemd1",
1376                                 "/org/freedesktop/systemd1",
1377                                 "org.freedesktop.systemd1.Manager",
1378                                 "StartUnit",
1379                                 &error,
1380                                 &reply,
1381                                 "ss", unit, "fail");
1382                 if (r < 0) {
1383                         log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1384                         return r;
1385                 }
1386
1387                 r = sd_bus_message_read(reply, "o", &object);
1388                 if (r < 0)
1389                         return bus_log_parse_error(r);
1390
1391                 r = bus_wait_for_jobs_add(w, object);
1392                 if (r < 0)
1393                         return log_oom();
1394         }
1395
1396         r = bus_wait_for_jobs(w, arg_quiet);
1397         if (r < 0)
1398                 return r;
1399
1400         return 0;
1401 }
1402
1403 static int enable_machine(int argc, char *argv[], void *userdata) {
1404         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1405         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1406         int carries_install_info = 0;
1407         const char *method = NULL;
1408         sd_bus *bus = userdata;
1409         int r, i;
1410
1411         assert(bus);
1412
1413         polkit_agent_open_if_enabled();
1414
1415         method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1416
1417         r = sd_bus_message_new_method_call(
1418                         bus,
1419                         &m,
1420                         "org.freedesktop.systemd1",
1421                         "/org/freedesktop/systemd1",
1422                         "org.freedesktop.systemd1.Manager",
1423                         method);
1424         if (r < 0)
1425                 return bus_log_create_error(r);
1426
1427         r = sd_bus_message_open_container(m, 'a', "s");
1428         if (r < 0)
1429                 return bus_log_create_error(r);
1430
1431         for (i = 1; i < argc; i++) {
1432                 _cleanup_free_ char *e = NULL, *unit = NULL;
1433
1434                 if (!machine_name_is_valid(argv[i])) {
1435                         log_error("Invalid machine name %s.", argv[i]);
1436                         return -EINVAL;
1437                 }
1438
1439                 e = unit_name_escape(argv[i]);
1440                 if (!e)
1441                         return log_oom();
1442
1443                 unit = unit_name_build("systemd-nspawn", e, ".service");
1444                 if (!unit)
1445                         return log_oom();
1446
1447                 r = sd_bus_message_append(m, "s", unit);
1448                 if (r < 0)
1449                         return bus_log_create_error(r);
1450         }
1451
1452         r = sd_bus_message_close_container(m);
1453         if (r < 0)
1454                 return bus_log_create_error(r);
1455
1456         if (streq(argv[0], "enable"))
1457                 r = sd_bus_message_append(m, "bb", false, false);
1458         else
1459                 r = sd_bus_message_append(m, "b", false);
1460         if (r < 0)
1461                 return bus_log_create_error(r);
1462
1463         r = sd_bus_call(bus, m, 0, &error, &reply);
1464         if (r < 0) {
1465                 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1466                 return r;
1467         }
1468
1469         if (streq(argv[0], "enable")) {
1470                 r = sd_bus_message_read(reply, "b", carries_install_info);
1471                 if (r < 0)
1472                         return bus_log_parse_error(r);
1473         }
1474
1475         r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1476         if (r < 0)
1477                 return r;
1478
1479         r = sd_bus_call_method(
1480                         bus,
1481                         "org.freedesktop.systemd1",
1482                         "/org/freedesktop/systemd1",
1483                         "org.freedesktop.systemd1.Manager",
1484                         "Reload",
1485                         &error,
1486                         NULL,
1487                         NULL);
1488         if (r < 0) {
1489                 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1490                 return r;
1491         }
1492
1493         return 0;
1494 }
1495
1496 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1497         const char **our_path = userdata, *line;
1498         unsigned priority;
1499         int r;
1500
1501         assert(bus);
1502         assert(m);
1503         assert(our_path);
1504
1505         r = sd_bus_message_read(m, "us", &priority, &line);
1506         if (r < 0) {
1507                 bus_log_parse_error(r);
1508                 return 0;
1509         }
1510
1511         if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1512                 return 0;
1513
1514         if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1515                 return 0;
1516
1517         log_full(priority, "%s", line);
1518         return 0;
1519 }
1520
1521 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1522         const char **our_path = userdata, *path, *result;
1523         uint32_t id;
1524         int r;
1525
1526         assert(bus);
1527         assert(m);
1528         assert(our_path);
1529
1530         r = sd_bus_message_read(m, "uos", &id, &path, &result);
1531         if (r < 0) {
1532                 bus_log_parse_error(r);
1533                 return 0;
1534         }
1535
1536         if (!streq_ptr(*our_path, path))
1537                 return 0;
1538
1539         sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1540         return 0;
1541 }
1542
1543 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1544         assert(s);
1545         assert(si);
1546
1547         if (!arg_quiet)
1548                 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1549
1550         sd_event_exit(sd_event_source_get_event(s), EINTR);
1551         return 0;
1552 }
1553
1554 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1555         _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1556         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1557         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1558         _cleanup_event_unref_ sd_event* event = NULL;
1559         const char *path = NULL;
1560         uint32_t id;
1561         int r;
1562
1563         assert(bus);
1564         assert(m);
1565
1566         polkit_agent_open_if_enabled();
1567
1568         r = sd_event_default(&event);
1569         if (r < 0)
1570                 return log_error_errno(r, "Failed to get event loop: %m");
1571
1572         r = sd_bus_attach_event(bus, event, 0);
1573         if (r < 0)
1574                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1575
1576         r = sd_bus_add_match(
1577                         bus,
1578                         &slot_job_removed,
1579                         "type='signal',"
1580                         "sender='org.freedesktop.import1',"
1581                         "interface='org.freedesktop.import1.Manager',"
1582                         "member='TransferRemoved',"
1583                         "path='/org/freedesktop/import1'",
1584                         match_transfer_removed, &path);
1585         if (r < 0)
1586                 return log_error_errno(r, "Failed to install match: %m");
1587
1588         r = sd_bus_add_match(
1589                         bus,
1590                         &slot_log_message,
1591                         "type='signal',"
1592                         "sender='org.freedesktop.import1',"
1593                         "interface='org.freedesktop.import1.Transfer',"
1594                         "member='LogMessage'",
1595                         match_log_message, &path);
1596         if (r < 0)
1597                 return log_error_errno(r, "Failed to install match: %m");
1598
1599         r = sd_bus_call(bus, m, 0, &error, &reply);
1600         if (r < 0) {
1601                 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1602                 return r;
1603         }
1604
1605         r = sd_bus_message_read(reply, "uo", &id, &path);
1606         if (r < 0)
1607                 return bus_log_parse_error(r);
1608
1609         sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1610
1611         if (!arg_quiet)
1612                 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1613
1614         sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1615         sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1616
1617         r = sd_event_loop(event);
1618         if (r < 0)
1619                 return log_error_errno(r, "Failed to run event loop: %m");
1620
1621         return -r;
1622 }
1623
1624 static int pull_tar(int argc, char *argv[], void *userdata) {
1625         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1626         _cleanup_free_ char *l = NULL, *ll = NULL;
1627         const char *local, *remote;
1628         sd_bus *bus = userdata;
1629         int r;
1630
1631         assert(bus);
1632
1633         remote = argv[1];
1634         if (!http_url_is_valid(remote)) {
1635                 log_error("URL '%s' is not valid.", remote);
1636                 return -EINVAL;
1637         }
1638
1639         if (argc >= 3)
1640                 local = argv[2];
1641         else {
1642                 r = import_url_last_component(remote, &l);
1643                 if (r < 0)
1644                         return log_error_errno(r, "Failed to get final component of URL: %m");
1645
1646                 local = l;
1647         }
1648
1649         if (isempty(local) || streq(local, "-"))
1650                 local = NULL;
1651
1652         if (local) {
1653                 r = tar_strip_suffixes(local, &ll);
1654                 if (r < 0)
1655                         return log_error_errno(r, "Failed to strip tar suffixes: %m");
1656
1657                 local = ll;
1658
1659                 if (!machine_name_is_valid(local)) {
1660                         log_error("Local name %s is not a suitable machine name.", local);
1661                         return -EINVAL;
1662                 }
1663         }
1664
1665         r = sd_bus_message_new_method_call(
1666                         bus,
1667                         &m,
1668                         "org.freedesktop.import1",
1669                         "/org/freedesktop/import1",
1670                         "org.freedesktop.import1.Manager",
1671                         "PullTar");
1672         if (r < 0)
1673                 return bus_log_create_error(r);
1674
1675         r = sd_bus_message_append(
1676                         m,
1677                         "sssb",
1678                         remote,
1679                         local,
1680                         import_verify_to_string(arg_verify),
1681                         arg_force);
1682         if (r < 0)
1683                 return bus_log_create_error(r);
1684
1685         return pull_image_common(bus, m);
1686 }
1687
1688 static int pull_raw(int argc, char *argv[], void *userdata) {
1689         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1690         _cleanup_free_ char *l = NULL, *ll = NULL;
1691         const char *local, *remote;
1692         sd_bus *bus = userdata;
1693         int r;
1694
1695         assert(bus);
1696
1697         remote = argv[1];
1698         if (!http_url_is_valid(remote)) {
1699                 log_error("URL '%s' is not valid.", remote);
1700                 return -EINVAL;
1701         }
1702
1703         if (argc >= 3)
1704                 local = argv[2];
1705         else {
1706                 r = import_url_last_component(remote, &l);
1707                 if (r < 0)
1708                         return log_error_errno(r, "Failed to get final component of URL: %m");
1709
1710                 local = l;
1711         }
1712
1713         if (isempty(local) || streq(local, "-"))
1714                 local = NULL;
1715
1716         if (local) {
1717                 r = raw_strip_suffixes(local, &ll);
1718                 if (r < 0)
1719                         return log_error_errno(r, "Failed to strip tar suffixes: %m");
1720
1721                 local = ll;
1722
1723                 if (!machine_name_is_valid(local)) {
1724                         log_error("Local name %s is not a suitable machine name.", local);
1725                         return -EINVAL;
1726                 }
1727         }
1728
1729         r = sd_bus_message_new_method_call(
1730                         bus,
1731                         &m,
1732                         "org.freedesktop.import1",
1733                         "/org/freedesktop/import1",
1734                         "org.freedesktop.import1.Manager",
1735                         "PullRaw");
1736         if (r < 0)
1737                 return bus_log_create_error(r);
1738
1739         r = sd_bus_message_append(
1740                         m,
1741                         "sssb",
1742                         remote,
1743                         local,
1744                         import_verify_to_string(arg_verify),
1745                         arg_force);
1746         if (r < 0)
1747                 return bus_log_create_error(r);
1748
1749         return pull_image_common(bus, m);
1750 }
1751
1752 static int pull_dkr(int argc, char *argv[], void *userdata) {
1753         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1754         const char *local, *remote, *tag;
1755         sd_bus *bus = userdata;
1756         int r;
1757
1758         if (arg_verify != IMPORT_VERIFY_NO) {
1759                 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1760                 return -EINVAL;
1761         }
1762
1763         remote = argv[1];
1764         tag = strchr(remote, ':');
1765         if (tag) {
1766                 remote = strndupa(remote, tag - remote);
1767                 tag++;
1768         }
1769
1770         if (!dkr_name_is_valid(remote)) {
1771                 log_error("DKR name '%s' is invalid.", remote);
1772                 return -EINVAL;
1773         }
1774         if (tag && !dkr_tag_is_valid(tag)) {
1775                 log_error("DKR tag '%s' is invalid.", remote);
1776                 return -EINVAL;
1777         }
1778
1779         if (argc >= 3)
1780                 local = argv[2];
1781         else {
1782                 local = strchr(remote, '/');
1783                 if (local)
1784                         local++;
1785                 else
1786                         local = remote;
1787         }
1788
1789         if (isempty(local) || streq(local, "-"))
1790                 local = NULL;
1791
1792         if (local) {
1793                 if (!machine_name_is_valid(local)) {
1794                         log_error("Local name %s is not a suitable machine name.", local);
1795                         return -EINVAL;
1796                 }
1797         }
1798
1799         r = sd_bus_message_new_method_call(
1800                         bus,
1801                         &m,
1802                         "org.freedesktop.import1",
1803                         "/org/freedesktop/import1",
1804                         "org.freedesktop.import1.Manager",
1805                         "PullDkr");
1806         if (r < 0)
1807                 return bus_log_create_error(r);
1808
1809         r = sd_bus_message_append(
1810                         m,
1811                         "sssssb",
1812                         arg_dkr_index_url,
1813                         remote,
1814                         tag,
1815                         local,
1816                         import_verify_to_string(arg_verify),
1817                         arg_force);
1818         if (r < 0)
1819                 return bus_log_create_error(r);
1820
1821         return pull_image_common(bus, m);
1822 }
1823
1824 typedef struct TransferInfo {
1825         uint32_t id;
1826         const char *type;
1827         const char *remote;
1828         const char *local;
1829         double progress;
1830 } TransferInfo;
1831
1832 static int compare_transfer_info(const void *a, const void *b) {
1833         const TransferInfo *x = a, *y = b;
1834
1835         return strcmp(x->local, y->local);
1836 }
1837
1838 static int list_transfers(int argc, char *argv[], void *userdata) {
1839         size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
1840         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1841         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1842         _cleanup_free_ TransferInfo *transfers = NULL;
1843         size_t n_transfers = 0, n_allocated = 0, j;
1844         const char *type, *remote, *local, *object;
1845         sd_bus *bus = userdata;
1846         uint32_t id, max_id = 0;
1847         double progress;
1848         int r;
1849
1850         pager_open_if_enabled();
1851
1852         r = sd_bus_call_method(
1853                                 bus,
1854                                 "org.freedesktop.import1",
1855                                 "/org/freedesktop/import1",
1856                                 "org.freedesktop.import1.Manager",
1857                                 "ListTransfers",
1858                                 &error,
1859                                 &reply,
1860                                 NULL);
1861         if (r < 0) {
1862                 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
1863                 return r;
1864         }
1865
1866         r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
1867         if (r < 0)
1868                 return bus_log_parse_error(r);
1869
1870         while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
1871                 size_t l;
1872
1873                 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
1874                         return log_oom();
1875
1876                 transfers[n_transfers].id = id;
1877                 transfers[n_transfers].type = type;
1878                 transfers[n_transfers].remote = remote;
1879                 transfers[n_transfers].local = local;
1880                 transfers[n_transfers].progress = progress;
1881
1882                 l = strlen(type);
1883                 if (l > max_type)
1884                         max_type = l;
1885
1886                 l = strlen(remote);
1887                 if (l > max_remote)
1888                         max_remote = l;
1889
1890                 l = strlen(local);
1891                 if (l > max_local)
1892                         max_local = l;
1893
1894                 if (id > max_id)
1895                         max_id = id;
1896
1897                 n_transfers ++;
1898         }
1899         if (r < 0)
1900                 return bus_log_parse_error(r);
1901
1902         r = sd_bus_message_exit_container(reply);
1903         if (r < 0)
1904                 return bus_log_parse_error(r);
1905
1906         qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
1907
1908         if (arg_legend)
1909                 printf("%-*s %-*s %-*s %-*s %-*s\n",
1910                        (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
1911                        (int) 7, "PERCENT",
1912                        (int) max_type, "TYPE",
1913                        (int) max_local, "LOCAL",
1914                        (int) max_remote, "REMOTE");
1915
1916         for (j = 0; j < n_transfers; j++)
1917                 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
1918                        (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
1919                        (int) 6, (unsigned) (transfers[j].progress * 100),
1920                        (int) max_type, transfers[j].type,
1921                        (int) max_local, transfers[j].local,
1922                        (int) max_remote, transfers[j].remote);
1923
1924         if (arg_legend)
1925                 printf("\n%zu transfers listed.\n", n_transfers);
1926
1927         return 0;
1928 }
1929
1930 static int cancel_transfer(int argc, char *argv[], void *userdata) {
1931         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1932         sd_bus *bus = userdata;
1933         int r, i;
1934
1935         assert(bus);
1936
1937         polkit_agent_open_if_enabled();
1938
1939         for (i = 1; i < argc; i++) {
1940                 uint32_t id;
1941
1942                 r = safe_atou32(argv[i], &id);
1943                 if (r < 0)
1944                         return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
1945
1946                 r = sd_bus_call_method(
1947                                 bus,
1948                                 "org.freedesktop.import1",
1949                                 "/org/freedesktop/import1",
1950                                 "org.freedesktop.import1.Manager",
1951                                 "CancelTransfer",
1952                                 &error,
1953                                 NULL,
1954                                 "u", id);
1955                 if (r < 0) {
1956                         log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
1957                         return r;
1958                 }
1959         }
1960
1961         return 0;
1962 }
1963
1964 static int help(int argc, char *argv[], void *userdata) {
1965
1966         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1967                "Send control commands to or query the virtual machine and container\n"
1968                "registration manager.\n\n"
1969                "  -h --help                   Show this help\n"
1970                "     --version                Show package version\n"
1971                "     --no-pager               Do not pipe output into a pager\n"
1972                "     --no-legend              Do not show the headers and footers\n"
1973                "     --no-ask-password        Do not ask for system passwords\n"
1974                "  -H --host=[USER@]HOST       Operate on remote host\n"
1975                "  -M --machine=CONTAINER      Operate on local container\n"
1976                "  -p --property=NAME          Show only properties by this name\n"
1977                "  -q --quiet                  Suppress output\n"
1978                "  -a --all                    Show all properties, including empty ones\n"
1979                "  -l --full                   Do not ellipsize output\n"
1980                "     --kill-who=WHO           Who to send signal to\n"
1981                "  -s --signal=SIGNAL          Which signal to send\n"
1982                "     --read-only              Create read-only bind mount\n"
1983                "     --mkdir                  Create directory before bind mounting, if missing\n"
1984                "  -n --lines=INTEGER          Number of journal entries to show\n"
1985                "  -o --output=STRING          Change journal output mode (short,\n"
1986                "                              short-monotonic, verbose, export, json,\n"
1987                "                              json-pretty, json-sse, cat)\n"
1988                "      --verify=MODE           Verification mode for downloaded images (no,\n"
1989                "                              checksum, signature)\n"
1990                "      --force                 Download image even if already exists\n"
1991                "      --dkr-index-url=URL     Specify the index URL to use for DKR image\n"
1992                "                              downloads\n\n"
1993                "Machine Commands:\n"
1994                "  list                        List running VMs and containers\n"
1995                "  status NAME...              Show VM/container details\n"
1996                "  show NAME...                Show properties of one or more VMs/containers\n"
1997                "  start NAME...               Start container as a service\n"
1998                "  login NAME                  Get a login prompt on a container\n"
1999                "  enable NAME...              Enable automatic container start at boot\n"
2000                "  disable NAME...             Disable automatic container start at boot\n"
2001                "  poweroff NAME...            Power off one or more containers\n"
2002                "  reboot NAME...              Reboot one or more containers\n"
2003                "  terminate NAME...           Terminate one or more VMs/containers\n"
2004                "  kill NAME...                Send signal to processes of a VM/container\n"
2005                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
2006                "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
2007                "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n\n"
2008                "Image Commands:\n"
2009                "  list-images                 Show available container and VM images\n"
2010                "  image-status NAME...        Show image details\n"
2011                "  show-image NAME...          Show properties of image\n"
2012                "  clone NAME NAME             Clone an image\n"
2013                "  rename NAME NAME            Rename an image\n"
2014                "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
2015                "  remove NAME...              Remove an image\n\n"
2016                "Image Transfer Commands:\n"
2017                "  pull-tar URL [NAME]         Download a TAR container image\n"
2018                "  pull-raw URL [NAME]         Download a RAW container or VM image\n"
2019                "  pull-dkr REMOTE [NAME]      Download a DKR container image\n"
2020                "  list-transfers              Show list of downloads in progress\n"
2021                "  cancel-transfer             Cancel a download\n"
2022                , program_invocation_short_name);
2023
2024         return 0;
2025 }
2026
2027 static int parse_argv(int argc, char *argv[]) {
2028
2029         enum {
2030                 ARG_VERSION = 0x100,
2031                 ARG_NO_PAGER,
2032                 ARG_NO_LEGEND,
2033                 ARG_KILL_WHO,
2034                 ARG_READ_ONLY,
2035                 ARG_MKDIR,
2036                 ARG_NO_ASK_PASSWORD,
2037                 ARG_VERIFY,
2038                 ARG_FORCE,
2039                 ARG_DKR_INDEX_URL,
2040         };
2041
2042         static const struct option options[] = {
2043                 { "help",            no_argument,       NULL, 'h'                 },
2044                 { "version",         no_argument,       NULL, ARG_VERSION         },
2045                 { "property",        required_argument, NULL, 'p'                 },
2046                 { "all",             no_argument,       NULL, 'a'                 },
2047                 { "full",            no_argument,       NULL, 'l'                 },
2048                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
2049                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
2050                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
2051                 { "signal",          required_argument, NULL, 's'                 },
2052                 { "host",            required_argument, NULL, 'H'                 },
2053                 { "machine",         required_argument, NULL, 'M'                 },
2054                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
2055                 { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
2056                 { "quiet",           no_argument,       NULL, 'q'                 },
2057                 { "lines",           required_argument, NULL, 'n'                 },
2058                 { "output",          required_argument, NULL, 'o'                 },
2059                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
2060                 { "verify",          required_argument, NULL, ARG_VERIFY          },
2061                 { "force",           no_argument,       NULL, ARG_FORCE           },
2062                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
2063                 {}
2064         };
2065
2066         int c, r;
2067
2068         assert(argc >= 0);
2069         assert(argv);
2070
2071         while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2072
2073                 switch (c) {
2074
2075                 case 'h':
2076                         return help(0, NULL, NULL);
2077
2078                 case ARG_VERSION:
2079                         puts(PACKAGE_STRING);
2080                         puts(SYSTEMD_FEATURES);
2081                         return 0;
2082
2083                 case 'p':
2084                         r = strv_extend(&arg_property, optarg);
2085                         if (r < 0)
2086                                 return log_oom();
2087
2088                         /* If the user asked for a particular
2089                          * property, show it to him, even if it is
2090                          * empty. */
2091                         arg_all = true;
2092                         break;
2093
2094                 case 'a':
2095                         arg_all = true;
2096                         break;
2097
2098                 case 'l':
2099                         arg_full = true;
2100                         break;
2101
2102                 case 'n':
2103                         if (safe_atou(optarg, &arg_lines) < 0) {
2104                                 log_error("Failed to parse lines '%s'", optarg);
2105                                 return -EINVAL;
2106                         }
2107                         break;
2108
2109                 case 'o':
2110                         arg_output = output_mode_from_string(optarg);
2111                         if (arg_output < 0) {
2112                                 log_error("Unknown output '%s'.", optarg);
2113                                 return -EINVAL;
2114                         }
2115                         break;
2116
2117                 case ARG_NO_PAGER:
2118                         arg_no_pager = true;
2119                         break;
2120
2121                 case ARG_NO_LEGEND:
2122                         arg_legend = false;
2123                         break;
2124
2125                 case ARG_KILL_WHO:
2126                         arg_kill_who = optarg;
2127                         break;
2128
2129                 case 's':
2130                         arg_signal = signal_from_string_try_harder(optarg);
2131                         if (arg_signal < 0) {
2132                                 log_error("Failed to parse signal string %s.", optarg);
2133                                 return -EINVAL;
2134                         }
2135                         break;
2136
2137                 case ARG_NO_ASK_PASSWORD:
2138                         arg_ask_password = false;
2139                         break;
2140
2141                 case 'H':
2142                         arg_transport = BUS_TRANSPORT_REMOTE;
2143                         arg_host = optarg;
2144                         break;
2145
2146                 case 'M':
2147                         arg_transport = BUS_TRANSPORT_MACHINE;
2148                         arg_host = optarg;
2149                         break;
2150
2151                 case ARG_READ_ONLY:
2152                         arg_read_only = true;
2153                         break;
2154
2155                 case ARG_MKDIR:
2156                         arg_mkdir = true;
2157                         break;
2158
2159                 case 'q':
2160                         arg_quiet = true;
2161                         break;
2162
2163                 case ARG_VERIFY:
2164                         arg_verify = import_verify_from_string(optarg);
2165                         if (arg_verify < 0) {
2166                                 log_error("Failed to parse --verify= setting: %s", optarg);
2167                                 return -EINVAL;
2168                         }
2169                         break;
2170
2171                 case ARG_FORCE:
2172                         arg_force = true;
2173                         break;
2174
2175                 case ARG_DKR_INDEX_URL:
2176                         if (!http_url_is_valid(optarg)) {
2177                                 log_error("Index URL is invalid: %s", optarg);
2178                                 return -EINVAL;
2179                         }
2180
2181                         arg_dkr_index_url = optarg;
2182                         break;
2183
2184                 case '?':
2185                         return -EINVAL;
2186
2187                 default:
2188                         assert_not_reached("Unhandled option");
2189                 }
2190
2191         return 1;
2192 }
2193
2194 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2195
2196         static const Verb verbs[] = {
2197                 { "help",            VERB_ANY, VERB_ANY, 0,            help              },
2198                 { "list",            VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
2199                 { "list-images",     VERB_ANY, 1,        0,            list_images       },
2200                 { "status",          2,        VERB_ANY, 0,            show_machine      },
2201                 { "image-status",    VERB_ANY, VERB_ANY, 0,            show_image        },
2202                 { "show",            VERB_ANY, VERB_ANY, 0,            show_machine      },
2203                 { "show-image",      VERB_ANY, VERB_ANY, 0,            show_image        },
2204                 { "terminate",       2,        VERB_ANY, 0,            terminate_machine },
2205                 { "reboot",          2,        VERB_ANY, 0,            reboot_machine    },
2206                 { "poweroff",        2,        VERB_ANY, 0,            poweroff_machine  },
2207                 { "kill",            2,        VERB_ANY, 0,            kill_machine      },
2208                 { "login",           2,        2,        0,            login_machine     },
2209                 { "bind",            3,        4,        0,            bind_mount        },
2210                 { "copy-to",         3,        4,        0,            copy_files        },
2211                 { "copy-from",       3,        4,        0,            copy_files        },
2212                 { "remove",          2,        VERB_ANY, 0,            remove_image      },
2213                 { "rename",          3,        3,        0,            rename_image      },
2214                 { "clone",           3,        3,        0,            clone_image       },
2215                 { "read-only",       2,        3,        0,            read_only_image   },
2216                 { "start",           2,        VERB_ANY, 0,            start_machine     },
2217                 { "enable",          2,        VERB_ANY, 0,            enable_machine    },
2218                 { "disable",         2,        VERB_ANY, 0,            enable_machine    },
2219                 { "pull-tar",        2,        3,        0,            pull_tar          },
2220                 { "pull-raw",        2,        3,        0,            pull_raw          },
2221                 { "pull-dkr",        2,        3,        0,            pull_dkr          },
2222                 { "list-transfers",  VERB_ANY, 1,        0,            list_transfers    },
2223                 { "cancel-transfer", 2,        VERB_ANY, 0,            cancel_transfer   },
2224                 {}
2225         };
2226
2227         return dispatch_verb(argc, argv, verbs, bus);
2228 }
2229
2230 int main(int argc, char*argv[]) {
2231         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2232         int r;
2233
2234         setlocale(LC_ALL, "");
2235         log_parse_environment();
2236         log_open();
2237
2238         r = parse_argv(argc, argv);
2239         if (r <= 0)
2240                 goto finish;
2241
2242         r = bus_open_transport(arg_transport, arg_host, false, &bus);
2243         if (r < 0) {
2244                 log_error_errno(r, "Failed to create bus connection: %m");
2245                 goto finish;
2246         }
2247
2248         sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2249
2250         r = machinectl_main(argc, argv, bus);
2251
2252 finish:
2253         pager_close();
2254         polkit_agent_close();
2255
2256         strv_free(arg_property);
2257
2258         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2259 }