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