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