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