]>
Commit | Line | Data |
---|---|---|
1ee306e1 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
1ee306e1 LP |
22 | #include <unistd.h> |
23 | #include <errno.h> | |
24 | #include <string.h> | |
25 | #include <getopt.h> | |
26 | #include <pwd.h> | |
27 | #include <locale.h> | |
28 | ||
a1da8583 | 29 | #include "sd-bus.h" |
1ee306e1 LP |
30 | #include "log.h" |
31 | #include "util.h" | |
32 | #include "macro.h" | |
33 | #include "pager.h" | |
a1da8583 TG |
34 | #include "bus-util.h" |
35 | #include "bus-error.h" | |
1ee306e1 LP |
36 | #include "build.h" |
37 | #include "strv.h" | |
aa1936ea | 38 | #include "unit-name.h" |
1ee306e1 | 39 | #include "cgroup-show.h" |
9d127096 | 40 | #include "cgroup-util.h" |
1ee306e1 LP |
41 | |
42 | static char **arg_property = NULL; | |
43 | static bool arg_all = false; | |
44 | static bool arg_full = false; | |
45 | static bool arg_no_pager = false; | |
46 | static const char *arg_kill_who = NULL; | |
47 | static int arg_signal = SIGTERM; | |
1ee306e1 | 48 | static bool arg_ask_password = true; |
d21ed1ea | 49 | static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; |
1ee306e1 | 50 | static char *arg_host = NULL; |
1ee306e1 LP |
51 | |
52 | static void pager_open_if_enabled(void) { | |
53 | ||
54 | /* Cache result before we open the pager */ | |
55 | if (arg_no_pager) | |
56 | return; | |
57 | ||
58 | pager_open(false); | |
59 | } | |
60 | ||
a1da8583 TG |
61 | static int list_machines(sd_bus *bus, char **args, unsigned n) { |
62 | _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; | |
63 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
64 | const char *name, *class, *service, *object; | |
1ee306e1 LP |
65 | unsigned k = 0; |
66 | int r; | |
67 | ||
68 | pager_open_if_enabled(); | |
69 | ||
a1da8583 TG |
70 | r = sd_bus_call_method( |
71 | bus, | |
72 | "org.freedesktop.machine1", | |
73 | "/org/freedesktop/machine1", | |
74 | "org.freedesktop.machine1.Manager", | |
75 | "ListMachines", | |
76 | &error, | |
77 | &reply, | |
78 | ""); | |
79 | if (r < 0) { | |
80 | log_error("Could not get machines: %s", bus_error_message(&error, -r)); | |
1ee306e1 | 81 | return r; |
1ee306e1 LP |
82 | } |
83 | ||
1ee306e1 LP |
84 | if (on_tty()) |
85 | printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE"); | |
86 | ||
a1da8583 TG |
87 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)"); |
88 | if (r < 0) | |
89 | goto fail; | |
1ee306e1 | 90 | |
a1da8583 | 91 | while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { |
1ee306e1 LP |
92 | printf("%-32s %-9s %-16s\n", name, class, service); |
93 | ||
94 | k++; | |
1ee306e1 | 95 | } |
a1da8583 TG |
96 | if (r < 0) |
97 | goto fail; | |
98 | ||
99 | r = sd_bus_message_exit_container(reply); | |
100 | if (r < 0) | |
101 | goto fail; | |
1ee306e1 LP |
102 | |
103 | if (on_tty()) | |
104 | printf("\n%u machines listed.\n", k); | |
105 | ||
106 | return 0; | |
a1da8583 TG |
107 | |
108 | fail: | |
109 | log_error("Failed to parse reply: %s", strerror(-r)); | |
a7893c6b | 110 | return r; |
1ee306e1 LP |
111 | } |
112 | ||
a1da8583 TG |
113 | static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) { |
114 | _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; | |
115 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
aa1936ea | 116 | _cleanup_free_ char *path = NULL; |
aa1936ea | 117 | const char *cgroup; |
aa1936ea LP |
118 | int r, output_flags; |
119 | unsigned c; | |
120 | ||
121 | assert(bus); | |
122 | assert(unit); | |
123 | ||
d21ed1ea | 124 | if (arg_transport == BUS_TRANSPORT_REMOTE) |
aa1936ea LP |
125 | return 0; |
126 | ||
127 | path = unit_dbus_path_from_name(unit); | |
128 | if (!path) | |
129 | return log_oom(); | |
130 | ||
a7893c6b | 131 | r = sd_bus_get_property( |
aa1936ea LP |
132 | bus, |
133 | "org.freedesktop.systemd1", | |
134 | path, | |
a7893c6b LP |
135 | "org.freedesktop.systemd1.Scope", |
136 | "ControlGroup", | |
aa1936ea | 137 | &error, |
a1da8583 | 138 | &reply, |
a7893c6b | 139 | "s"); |
aa1936ea | 140 | if (r < 0) { |
a1da8583 | 141 | log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); |
aa1936ea LP |
142 | return r; |
143 | } | |
144 | ||
a7893c6b | 145 | r = sd_bus_message_read(reply, "s", &cgroup); |
a1da8583 TG |
146 | if (r < 0) { |
147 | log_error("Failed to parse reply: %s", strerror(-r)); | |
148 | return r; | |
aa1936ea LP |
149 | } |
150 | ||
9d127096 LP |
151 | if (isempty(cgroup)) |
152 | return 0; | |
153 | ||
154 | if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) | |
155 | return 0; | |
156 | ||
aa1936ea LP |
157 | output_flags = |
158 | arg_all * OUTPUT_SHOW_ALL | | |
159 | arg_full * OUTPUT_FULL_WIDTH; | |
160 | ||
161 | c = columns(); | |
162 | if (c > 18) | |
163 | c -= 18; | |
164 | else | |
165 | c = 0; | |
166 | ||
9d127096 | 167 | show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags); |
aa1936ea LP |
168 | return 0; |
169 | } | |
170 | ||
1ee306e1 LP |
171 | typedef struct MachineStatusInfo { |
172 | const char *name; | |
173 | sd_id128_t id; | |
1ee306e1 LP |
174 | const char *class; |
175 | const char *service; | |
aa1936ea | 176 | const char *scope; |
1ee306e1 LP |
177 | const char *root_directory; |
178 | pid_t leader; | |
179 | usec_t timestamp; | |
180 | } MachineStatusInfo; | |
181 | ||
a1da8583 | 182 | static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { |
1ee306e1 LP |
183 | char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; |
184 | char since2[FORMAT_TIMESTAMP_MAX], *s2; | |
185 | assert(i); | |
186 | ||
187 | fputs(strna(i->name), stdout); | |
188 | ||
189 | if (!sd_id128_equal(i->id, SD_ID128_NULL)) | |
190 | printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id)); | |
191 | else | |
192 | putchar('\n'); | |
193 | ||
194 | s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp); | |
195 | s2 = format_timestamp(since2, sizeof(since2), i->timestamp); | |
196 | ||
197 | if (s1) | |
198 | printf("\t Since: %s; %s\n", s2, s1); | |
199 | else if (s2) | |
200 | printf("\t Since: %s\n", s2); | |
201 | ||
202 | if (i->leader > 0) { | |
203 | _cleanup_free_ char *t = NULL; | |
204 | ||
205 | printf("\t Leader: %u", (unsigned) i->leader); | |
206 | ||
207 | get_process_comm(i->leader, &t); | |
208 | if (t) | |
209 | printf(" (%s)", t); | |
210 | ||
211 | putchar('\n'); | |
212 | } | |
213 | ||
214 | if (i->service) { | |
215 | printf("\t Service: %s", i->service); | |
216 | ||
217 | if (i->class) | |
218 | printf("; class %s", i->class); | |
219 | ||
220 | putchar('\n'); | |
221 | } else if (i->class) | |
222 | printf("\t Class: %s\n", i->class); | |
223 | ||
1ee306e1 LP |
224 | if (i->root_directory) |
225 | printf("\t Root: %s\n", i->root_directory); | |
226 | ||
aa1936ea LP |
227 | if (i->scope) { |
228 | printf("\t Unit: %s\n", i->scope); | |
9d127096 | 229 | show_scope_cgroup(bus, i->scope, i->leader); |
1ee306e1 LP |
230 | } |
231 | } | |
232 | ||
a1da8583 TG |
233 | static int status_property_machine(const char *name, sd_bus_message *property, MachineStatusInfo *i) { |
234 | char type; | |
235 | const char *contents; | |
236 | int r; | |
237 | ||
1ee306e1 | 238 | assert(name); |
a1da8583 | 239 | assert(property); |
1ee306e1 LP |
240 | assert(i); |
241 | ||
a1da8583 TG |
242 | r = sd_bus_message_peek_type(property, &type, &contents); |
243 | if (r < 0) { | |
244 | log_error("Could not determine type of message: %s", strerror(-r)); | |
245 | return r; | |
246 | } | |
247 | ||
248 | switch (type) { | |
1ee306e1 | 249 | |
a1da8583 | 250 | case SD_BUS_TYPE_STRING: { |
1ee306e1 LP |
251 | const char *s; |
252 | ||
a1da8583 | 253 | sd_bus_message_read_basic(property, type, &s); |
1ee306e1 LP |
254 | |
255 | if (!isempty(s)) { | |
256 | if (streq(name, "Name")) | |
257 | i->name = s; | |
1ee306e1 LP |
258 | else if (streq(name, "Class")) |
259 | i->class = s; | |
260 | else if (streq(name, "Service")) | |
261 | i->service = s; | |
aa1936ea LP |
262 | else if (streq(name, "Scope")) |
263 | i->scope = s; | |
1ee306e1 LP |
264 | else if (streq(name, "RootDirectory")) |
265 | i->root_directory = s; | |
266 | } | |
267 | break; | |
268 | } | |
269 | ||
a1da8583 | 270 | case SD_BUS_TYPE_UINT32: { |
1ee306e1 LP |
271 | uint32_t u; |
272 | ||
a1da8583 | 273 | sd_bus_message_read_basic(property, type, &u); |
1ee306e1 LP |
274 | |
275 | if (streq(name, "Leader")) | |
276 | i->leader = (pid_t) u; | |
277 | ||
278 | break; | |
279 | } | |
280 | ||
a1da8583 | 281 | case SD_BUS_TYPE_UINT64: { |
1ee306e1 LP |
282 | uint64_t u; |
283 | ||
a1da8583 | 284 | sd_bus_message_read_basic(property, type, &u); |
1ee306e1 LP |
285 | |
286 | if (streq(name, "Timestamp")) | |
287 | i->timestamp = (usec_t) u; | |
288 | ||
289 | break; | |
290 | } | |
291 | ||
a1da8583 TG |
292 | case SD_BUS_TYPE_ARRAY: { |
293 | if (streq(contents, "y") && streq(name, "Id")) { | |
294 | const void *v; | |
295 | size_t n; | |
1ee306e1 | 296 | |
a1da8583 | 297 | sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, &v, &n); |
1ee306e1 LP |
298 | if (n == 0) |
299 | i->id = SD_ID128_NULL; | |
300 | else if (n == 16) | |
301 | memcpy(&i->id, v, n); | |
302 | } | |
303 | ||
304 | break; | |
305 | } | |
306 | } | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
a1da8583 | 311 | static int print_property(const char *name, sd_bus_message *reply) { |
1ee306e1 | 312 | assert(name); |
a1da8583 | 313 | assert(reply); |
1ee306e1 LP |
314 | |
315 | if (arg_property && !strv_find(arg_property, name)) | |
316 | return 0; | |
317 | ||
a1da8583 | 318 | if (bus_generic_print_property(name, reply, arg_all) > 0) |
1ee306e1 LP |
319 | return 0; |
320 | ||
321 | if (arg_all) | |
322 | printf("%s=[unprintable]\n", name); | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
a1da8583 TG |
327 | static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_properties, bool *new_line) { |
328 | _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; | |
329 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
1ee306e1 | 330 | int r; |
1ee306e1 LP |
331 | MachineStatusInfo machine_info = {}; |
332 | ||
333 | assert(path); | |
334 | assert(new_line); | |
335 | ||
a1da8583 | 336 | r = sd_bus_call_method( |
1ee306e1 LP |
337 | bus, |
338 | "org.freedesktop.machine1", | |
339 | path, | |
340 | "org.freedesktop.DBus.Properties", | |
341 | "GetAll", | |
a1da8583 | 342 | &error, |
1ee306e1 | 343 | &reply, |
a1da8583 TG |
344 | "s", ""); |
345 | if (r < 0) { | |
346 | log_error("Could not get properties: %s", bus_error_message(&error, -r)); | |
347 | return r; | |
1ee306e1 LP |
348 | } |
349 | ||
1ee306e1 LP |
350 | |
351 | if (*new_line) | |
352 | printf("\n"); | |
353 | ||
354 | *new_line = true; | |
355 | ||
a1da8583 TG |
356 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); |
357 | if (r < 0) | |
358 | goto fail; | |
359 | ||
360 | while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { | |
1ee306e1 | 361 | const char *name; |
a1da8583 | 362 | const char *contents; |
1ee306e1 | 363 | |
a1da8583 TG |
364 | r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name); |
365 | if (r < 0) | |
366 | goto fail; | |
1ee306e1 | 367 | |
a1da8583 TG |
368 | r = sd_bus_message_peek_type(reply, NULL, &contents); |
369 | if (r < 0) | |
370 | goto fail; | |
1ee306e1 | 371 | |
a1da8583 TG |
372 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); |
373 | if (r < 0) | |
374 | goto fail; | |
1ee306e1 LP |
375 | |
376 | if (show_properties) | |
a1da8583 | 377 | r = print_property(name, reply); |
1ee306e1 | 378 | else |
a1da8583 TG |
379 | r = status_property_machine(name, reply, &machine_info); |
380 | if (r < 0) | |
381 | goto fail; | |
1ee306e1 | 382 | |
a1da8583 TG |
383 | r = sd_bus_message_exit_container(reply); |
384 | if (r < 0) | |
385 | goto fail; | |
1ee306e1 | 386 | |
a1da8583 TG |
387 | r = sd_bus_message_exit_container(reply); |
388 | if (r < 0) | |
389 | goto fail; | |
1ee306e1 | 390 | } |
a1da8583 TG |
391 | if (r < 0) |
392 | goto fail; | |
393 | ||
394 | r = sd_bus_message_exit_container(reply); | |
395 | if (r < 0) | |
396 | goto fail; | |
1ee306e1 LP |
397 | |
398 | if (!show_properties) | |
aa1936ea | 399 | print_machine_status_info(bus, &machine_info); |
1ee306e1 | 400 | |
a1da8583 | 401 | return 0; |
1ee306e1 | 402 | |
a1da8583 TG |
403 | fail: |
404 | log_error("Failed to parse reply: %s", strerror(-r)); | |
a7893c6b | 405 | return r; |
1ee306e1 LP |
406 | } |
407 | ||
a1da8583 TG |
408 | static int show(sd_bus *bus, char **args, unsigned n) { |
409 | _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; | |
410 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
1ee306e1 | 411 | int r, ret = 0; |
1ee306e1 LP |
412 | unsigned i; |
413 | bool show_properties, new_line = false; | |
414 | ||
415 | assert(bus); | |
416 | assert(args); | |
417 | ||
1ee306e1 LP |
418 | show_properties = !strstr(args[0], "status"); |
419 | ||
420 | pager_open_if_enabled(); | |
421 | ||
422 | if (show_properties && n <= 1) { | |
a1da8583 TG |
423 | |
424 | /* If no argument is specified inspect the manager | |
1ee306e1 LP |
425 | * itself */ |
426 | ||
a1da8583 | 427 | return show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line); |
1ee306e1 LP |
428 | } |
429 | ||
430 | for (i = 1; i < n; i++) { | |
431 | const char *path = NULL; | |
432 | ||
a1da8583 TG |
433 | r = sd_bus_call_method( |
434 | bus, | |
435 | "org.freedesktop.machine1", | |
436 | "/org/freedesktop/machine1", | |
437 | "org.freedesktop.machine1.Manager", | |
438 | "GetMachine", | |
439 | &error, | |
440 | &reply, | |
441 | "s", args[i]); | |
442 | if (r < 0) { | |
443 | log_error("Could not get path to machine: %s", bus_error_message(&error, -r)); | |
444 | return r; | |
445 | } | |
446 | ||
447 | r = sd_bus_message_read(reply, "o", &path); | |
448 | if (r < 0) { | |
449 | log_error("Failed to parse reply: %s", strerror(-r)); | |
a7893c6b | 450 | return r; |
1ee306e1 LP |
451 | } |
452 | ||
453 | r = show_one(args[0], bus, path, show_properties, &new_line); | |
454 | if (r != 0) | |
455 | ret = r; | |
456 | } | |
457 | ||
1ee306e1 LP |
458 | return ret; |
459 | } | |
460 | ||
a1da8583 TG |
461 | static int kill_machine(sd_bus *bus, char **args, unsigned n) { |
462 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
1ee306e1 LP |
463 | unsigned i; |
464 | ||
465 | assert(args); | |
466 | ||
467 | if (!arg_kill_who) | |
468 | arg_kill_who = "all"; | |
469 | ||
470 | for (i = 1; i < n; i++) { | |
471 | int r; | |
472 | ||
a1da8583 TG |
473 | r = sd_bus_call_method( |
474 | bus, | |
475 | "org.freedesktop.machine1", | |
476 | "/org/freedesktop/machine1", | |
477 | "org.freedesktop.machine1.Manager", | |
478 | "KillMachine", | |
479 | &error, | |
480 | NULL, | |
481 | "ssi", args[i], arg_kill_who, arg_signal); | |
482 | if (r < 0) { | |
483 | log_error("Could not kill machine: %s", bus_error_message(&error, -r)); | |
1ee306e1 | 484 | return r; |
a1da8583 | 485 | } |
1ee306e1 LP |
486 | } |
487 | ||
488 | return 0; | |
489 | } | |
490 | ||
a1da8583 TG |
491 | static int terminate_machine(sd_bus *bus, char **args, unsigned n) { |
492 | _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; | |
1ee306e1 LP |
493 | unsigned i; |
494 | ||
495 | assert(args); | |
496 | ||
497 | for (i = 1; i < n; i++) { | |
498 | int r; | |
499 | ||
a1da8583 TG |
500 | r = sd_bus_call_method( |
501 | bus, | |
502 | "org.freedesktop.machine1", | |
503 | "/org/freedesktop/machine1", | |
504 | "org.freedesktop.machine1.Manager", | |
505 | "TerminateMachine", | |
506 | &error, | |
507 | NULL, | |
508 | "s", args[i]); | |
509 | if (r < 0) { | |
510 | log_error("Could not terminate machine: %s", bus_error_message(&error, -r)); | |
1ee306e1 | 511 | return r; |
a1da8583 | 512 | } |
1ee306e1 LP |
513 | } |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
518 | static int help(void) { | |
519 | ||
520 | printf("%s [OPTIONS...] {COMMAND} ...\n\n" | |
521 | "Send control commands to or query the virtual machine and container registration manager.\n\n" | |
522 | " -h --help Show this help\n" | |
523 | " --version Show package version\n" | |
a7893c6b LP |
524 | " --no-pager Do not pipe output into a pager\n" |
525 | " --no-ask-password Don't prompt for password\n" | |
53755121 LP |
526 | " -H --host=[USER@]HOST Operate on remote host\n" |
527 | " -M --machine=CONTAINER Operate on local container\n" | |
1ee306e1 LP |
528 | " -p --property=NAME Show only properties by this name\n" |
529 | " -a --all Show all properties, including empty ones\n" | |
1ee306e1 | 530 | " -l --full Do not ellipsize output\n" |
a7893c6b LP |
531 | " --kill-who=WHO Who to send signal to\n" |
532 | " -s --signal=SIGNAL Which signal to send\n\n" | |
1ee306e1 LP |
533 | "Commands:\n" |
534 | " list List running VMs and containers\n" | |
535 | " status [NAME...] Show VM/container status\n" | |
19887cd0 | 536 | " show [NAME...] Show properties of one or more VMs/containers\n" |
1ee306e1 LP |
537 | " terminate [NAME...] Terminate one or more VMs/containers\n" |
538 | " kill [NAME...] Send signal to processes of a VM/container\n", | |
539 | program_invocation_short_name); | |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
544 | static int parse_argv(int argc, char *argv[]) { | |
545 | ||
546 | enum { | |
547 | ARG_VERSION = 0x100, | |
548 | ARG_NO_PAGER, | |
549 | ARG_KILL_WHO, | |
550 | ARG_NO_ASK_PASSWORD, | |
551 | }; | |
552 | ||
553 | static const struct option options[] = { | |
554 | { "help", no_argument, NULL, 'h' }, | |
555 | { "version", no_argument, NULL, ARG_VERSION }, | |
556 | { "property", required_argument, NULL, 'p' }, | |
557 | { "all", no_argument, NULL, 'a' }, | |
558 | { "full", no_argument, NULL, 'l' }, | |
559 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, | |
560 | { "kill-who", required_argument, NULL, ARG_KILL_WHO }, | |
561 | { "signal", required_argument, NULL, 's' }, | |
562 | { "host", required_argument, NULL, 'H' }, | |
a7893c6b | 563 | { "machine", required_argument, NULL, 'M' }, |
1ee306e1 LP |
564 | { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, |
565 | { NULL, 0, NULL, 0 } | |
566 | }; | |
567 | ||
a7893c6b | 568 | int c, r; |
1ee306e1 LP |
569 | |
570 | assert(argc >= 0); | |
571 | assert(argv); | |
572 | ||
a7893c6b | 573 | while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) { |
1ee306e1 LP |
574 | |
575 | switch (c) { | |
576 | ||
577 | case 'h': | |
578 | help(); | |
579 | return 0; | |
580 | ||
581 | case ARG_VERSION: | |
582 | puts(PACKAGE_STRING); | |
583 | puts(SYSTEMD_FEATURES); | |
584 | return 0; | |
585 | ||
a7893c6b LP |
586 | case 'p': |
587 | r = strv_extend(&arg_property, optarg); | |
588 | if (r < 0) | |
589 | return log_oom(); | |
1ee306e1 LP |
590 | |
591 | /* If the user asked for a particular | |
592 | * property, show it to him, even if it is | |
593 | * empty. */ | |
594 | arg_all = true; | |
595 | break; | |
1ee306e1 LP |
596 | |
597 | case 'a': | |
598 | arg_all = true; | |
599 | break; | |
600 | ||
601 | case 'l': | |
602 | arg_full = true; | |
603 | break; | |
604 | ||
605 | case ARG_NO_PAGER: | |
606 | arg_no_pager = true; | |
607 | break; | |
608 | ||
609 | case ARG_NO_ASK_PASSWORD: | |
610 | arg_ask_password = false; | |
611 | break; | |
612 | ||
613 | case ARG_KILL_WHO: | |
614 | arg_kill_who = optarg; | |
615 | break; | |
616 | ||
617 | case 's': | |
618 | arg_signal = signal_from_string_try_harder(optarg); | |
619 | if (arg_signal < 0) { | |
620 | log_error("Failed to parse signal string %s.", optarg); | |
621 | return -EINVAL; | |
622 | } | |
623 | break; | |
624 | ||
1ee306e1 | 625 | case 'H': |
d21ed1ea | 626 | arg_transport = BUS_TRANSPORT_REMOTE; |
a7893c6b LP |
627 | arg_host = optarg; |
628 | break; | |
629 | ||
630 | case 'M': | |
d21ed1ea | 631 | arg_transport = BUS_TRANSPORT_CONTAINER; |
a7893c6b | 632 | arg_host = optarg; |
1ee306e1 LP |
633 | break; |
634 | ||
635 | case '?': | |
636 | return -EINVAL; | |
637 | ||
638 | default: | |
639 | log_error("Unknown option code %c", c); | |
640 | return -EINVAL; | |
641 | } | |
642 | } | |
643 | ||
644 | return 1; | |
645 | } | |
646 | ||
a1da8583 | 647 | static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) { |
1ee306e1 LP |
648 | |
649 | static const struct { | |
650 | const char* verb; | |
651 | const enum { | |
652 | MORE, | |
653 | LESS, | |
654 | EQUAL | |
655 | } argc_cmp; | |
656 | const int argc; | |
a1da8583 | 657 | int (* const dispatch)(sd_bus *bus, char **args, unsigned n); |
1ee306e1 LP |
658 | } verbs[] = { |
659 | { "list", LESS, 1, list_machines }, | |
660 | { "status", MORE, 2, show }, | |
661 | { "show", MORE, 1, show }, | |
662 | { "terminate", MORE, 2, terminate_machine }, | |
663 | { "kill", MORE, 2, kill_machine }, | |
664 | }; | |
665 | ||
666 | int left; | |
667 | unsigned i; | |
668 | ||
669 | assert(argc >= 0); | |
670 | assert(argv); | |
1ee306e1 LP |
671 | |
672 | left = argc - optind; | |
673 | ||
674 | if (left <= 0) | |
675 | /* Special rule: no arguments means "list-sessions" */ | |
676 | i = 0; | |
677 | else { | |
678 | if (streq(argv[optind], "help")) { | |
679 | help(); | |
680 | return 0; | |
681 | } | |
682 | ||
683 | for (i = 0; i < ELEMENTSOF(verbs); i++) | |
684 | if (streq(argv[optind], verbs[i].verb)) | |
685 | break; | |
686 | ||
687 | if (i >= ELEMENTSOF(verbs)) { | |
688 | log_error("Unknown operation %s", argv[optind]); | |
689 | return -EINVAL; | |
690 | } | |
691 | } | |
692 | ||
693 | switch (verbs[i].argc_cmp) { | |
694 | ||
695 | case EQUAL: | |
696 | if (left != verbs[i].argc) { | |
697 | log_error("Invalid number of arguments."); | |
698 | return -EINVAL; | |
699 | } | |
700 | ||
701 | break; | |
702 | ||
703 | case MORE: | |
704 | if (left < verbs[i].argc) { | |
705 | log_error("Too few arguments."); | |
706 | return -EINVAL; | |
707 | } | |
708 | ||
709 | break; | |
710 | ||
711 | case LESS: | |
712 | if (left > verbs[i].argc) { | |
713 | log_error("Too many arguments."); | |
714 | return -EINVAL; | |
715 | } | |
716 | ||
717 | break; | |
718 | ||
719 | default: | |
720 | assert_not_reached("Unknown comparison operator."); | |
721 | } | |
722 | ||
a1da8583 TG |
723 | if (r < 0) { |
724 | log_error("Failed to get D-Bus connection: %s", strerror(-r)); | |
1ee306e1 LP |
725 | return -EIO; |
726 | } | |
727 | ||
728 | return verbs[i].dispatch(bus, argv + optind, left); | |
729 | } | |
730 | ||
731 | int main(int argc, char*argv[]) { | |
a7893c6b | 732 | int r, ret = EXIT_FAILURE; |
a1da8583 | 733 | _cleanup_bus_unref_ sd_bus *bus = NULL; |
1ee306e1 LP |
734 | |
735 | setlocale(LC_ALL, ""); | |
736 | log_parse_environment(); | |
737 | log_open(); | |
738 | ||
739 | r = parse_argv(argc, argv); | |
740 | if (r < 0) | |
741 | goto finish; | |
742 | else if (r == 0) { | |
a7893c6b | 743 | ret = EXIT_SUCCESS; |
1ee306e1 LP |
744 | goto finish; |
745 | } | |
746 | ||
d21ed1ea | 747 | r = bus_open_transport(arg_transport, arg_host, false, &bus); |
a1da8583 | 748 | if (r < 0) { |
d21ed1ea | 749 | log_error("Failed to create bus connection: %s", strerror(-r)); |
a7893c6b | 750 | ret = EXIT_FAILURE; |
a1da8583 TG |
751 | goto finish; |
752 | } | |
1ee306e1 | 753 | |
a1da8583 | 754 | r = machinectl_main(bus, argc, argv, r); |
a7893c6b | 755 | ret = r < 0 ? EXIT_FAILURE : r; |
1ee306e1 LP |
756 | |
757 | finish: | |
1ee306e1 LP |
758 | strv_free(arg_property); |
759 | ||
760 | pager_close(); | |
761 | ||
a7893c6b | 762 | return ret; |
1ee306e1 | 763 | } |