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