]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/loginctl.c
loginctl: use show_properties() to get login1 properties
[thirdparty/systemd.git] / src / login / loginctl.c
CommitLineData
abca4822
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
abca4822
LP
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
5430f7f2 16 Lesser General Public License for more details.
abca4822 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
abca4822
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
abca4822
LP
22#include <unistd.h>
23#include <errno.h>
24#include <string.h>
25#include <getopt.h>
a4c279f8 26#include <pwd.h>
a9cdc94f 27#include <locale.h>
abca4822 28
f8f14b36
SP
29#include "sd-bus.h"
30#include "bus-util.h"
31#include "bus-error.h"
abca4822
LP
32#include "log.h"
33#include "util.h"
34#include "macro.h"
35#include "pager.h"
abca4822 36#include "build.h"
a4c279f8 37#include "strv.h"
aa1936ea 38#include "unit-name.h"
a4c279f8 39#include "sysfs-show.h"
9d127096
LP
40#include "cgroup-show.h"
41#include "cgroup-util.h"
6bb92a16 42#include "spawn-polkit-agent.h"
abca4822 43
a4c279f8
LP
44static char **arg_property = NULL;
45static bool arg_all = false;
9bdbc2e2 46static bool arg_full = false;
abca4822 47static bool arg_no_pager = false;
a4c279f8
LP
48static const char *arg_kill_who = NULL;
49static int arg_signal = SIGTERM;
f8f14b36 50static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
6bb92a16 51static bool arg_ask_password = true;
7085053a 52static char *arg_host = NULL;
abca4822 53
abca4822 54static void pager_open_if_enabled(void) {
f8440af5 55
6bb92a16
LP
56 if (arg_no_pager)
57 return;
58
1b12a7b5 59 pager_open(false);
6bb92a16
LP
60}
61
62static void polkit_agent_open_if_enabled(void) {
63
64 /* Open the polkit agent as a child process if necessary */
65
66 if (!arg_ask_password)
67 return;
68
46e65dcc
LP
69 if (arg_transport != BUS_TRANSPORT_LOCAL)
70 return;
71
6bb92a16 72 polkit_agent_open();
abca4822
LP
73}
74
f8f14b36
SP
75static int list_sessions(sd_bus *bus, char **args, unsigned n) {
76 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
77 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
78 const char *id, *user, *seat, *object;
abca4822 79 unsigned k = 0;
f8f14b36
SP
80 uint32_t uid;
81 int r;
abca4822 82
abca4822
LP
83 pager_open_if_enabled();
84
f8f14b36 85 r = sd_bus_call_method(
2a3613b1 86 bus,
abca4822
LP
87 "org.freedesktop.login1",
88 "/org/freedesktop/login1",
89 "org.freedesktop.login1.Manager",
2a3613b1 90 "ListSessions",
f8f14b36
SP
91 &error, &reply,
92 "");
93 if (r < 0) {
94 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
4654e558 95 return r;
abca4822
LP
96 }
97
f8f14b36
SP
98 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
99 if (r < 0)
5b30bef8 100 return bus_log_parse_error(r);
abca4822 101
f8f14b36 102 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
abca4822 103
f8f14b36 104 while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
abca4822 105 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
abca4822 106 k++;
abca4822 107 }
f8f14b36 108 if (r < 0)
5b30bef8 109 return bus_log_parse_error(r);
abca4822 110
f8f14b36 111 printf("\n%u sessions listed.\n", k);
abca4822 112
4654e558 113 return 0;
abca4822
LP
114}
115
f8f14b36
SP
116static int list_users(sd_bus *bus, char **args, unsigned n) {
117 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
118 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
119 const char *user, *object;
abca4822 120 unsigned k = 0;
f8f14b36
SP
121 uint32_t uid;
122 int r;
abca4822 123
abca4822
LP
124 pager_open_if_enabled();
125
f8f14b36 126 r = sd_bus_call_method(
2a3613b1 127 bus,
abca4822
LP
128 "org.freedesktop.login1",
129 "/org/freedesktop/login1",
130 "org.freedesktop.login1.Manager",
2a3613b1 131 "ListUsers",
f8f14b36
SP
132 &error, &reply,
133 "");
134 if (r < 0) {
135 log_error("Failed to list users: %s", bus_error_message(&error, r));
4654e558 136 return r;
abca4822
LP
137 }
138
f8f14b36
SP
139 r = sd_bus_message_enter_container(reply, 'a', "(uso)");
140 if (r < 0)
5b30bef8 141 return bus_log_parse_error(r);
abca4822 142
f8f14b36 143 printf("%10s %-16s\n", "UID", "USER");
abca4822 144
f8f14b36 145 while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
abca4822 146 printf("%10u %-16s\n", (unsigned) uid, user);
abca4822 147 k++;
abca4822 148 }
f8f14b36 149 if (r < 0)
5b30bef8 150 return bus_log_parse_error(r);
abca4822 151
f8f14b36 152 printf("\n%u users listed.\n", k);
abca4822 153
4654e558 154 return 0;
abca4822
LP
155}
156
f8f14b36
SP
157static int list_seats(sd_bus *bus, char **args, unsigned n) {
158 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
159 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
160 const char *seat, *object;
abca4822 161 unsigned k = 0;
f8f14b36 162 int r;
abca4822 163
abca4822
LP
164 pager_open_if_enabled();
165
f8f14b36 166 r = sd_bus_call_method(
2a3613b1 167 bus,
abca4822
LP
168 "org.freedesktop.login1",
169 "/org/freedesktop/login1",
170 "org.freedesktop.login1.Manager",
2a3613b1 171 "ListSeats",
f8f14b36
SP
172 &error, &reply,
173 "");
174 if (r < 0) {
175 log_error("Failed to list seats: %s", bus_error_message(&error, r));
4654e558 176 return r;
abca4822
LP
177 }
178
f8f14b36
SP
179 r = sd_bus_message_enter_container(reply, 'a', "(so)");
180 if (r < 0)
5b30bef8 181 return bus_log_parse_error(r);
abca4822 182
f8f14b36 183 printf("%-16s\n", "SEAT");
abca4822 184
f8f14b36 185 while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
abca4822 186 printf("%-16s\n", seat);
abca4822 187 k++;
abca4822 188 }
f8f14b36 189 if (r < 0)
5b30bef8 190 return bus_log_parse_error(r);
abca4822 191
f8f14b36 192 printf("\n%u seats listed.\n", k);
abca4822 193
4654e558 194 return 0;
abca4822
LP
195}
196
f8f14b36
SP
197static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
198 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
199 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
aa1936ea 200 _cleanup_free_ char *path = NULL;
aa1936ea 201 const char *cgroup;
aa1936ea
LP
202 int r, output_flags;
203 unsigned c;
204
205 assert(bus);
206 assert(unit);
207
f8f14b36 208 if (arg_transport != BUS_TRANSPORT_LOCAL)
aa1936ea
LP
209 return 0;
210
211 path = unit_dbus_path_from_name(unit);
212 if (!path)
f8f14b36 213 return -ENOMEM;
aa1936ea 214
f8f14b36 215 r = sd_bus_get_property(
aa1936ea
LP
216 bus,
217 "org.freedesktop.systemd1",
218 path,
f8f14b36
SP
219 interface,
220 "ControlGroup",
221 &error, &reply, "s");
222 if (r < 0)
aa1936ea 223 return r;
aa1936ea 224
f8f14b36
SP
225 r = sd_bus_message_read(reply, "s", &cgroup);
226 if (r < 0)
227 return r;
aa1936ea 228
9d127096
LP
229 if (isempty(cgroup))
230 return 0;
231
232 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
233 return 0;
234
aa1936ea
LP
235 output_flags =
236 arg_all * OUTPUT_SHOW_ALL |
237 arg_full * OUTPUT_FULL_WIDTH;
238
239 c = columns();
240 if (c > 18)
241 c -= 18;
242 else
243 c = 0;
244
9d127096 245 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
aa1936ea
LP
246 return 0;
247}
248
a4c279f8
LP
249typedef struct SessionStatusInfo {
250 const char *id;
251 uid_t uid;
252 const char *name;
253 usec_t timestamp;
92bd5ff3 254 unsigned int vtnr;
a4c279f8
LP
255 const char *seat;
256 const char *tty;
257 const char *display;
258 bool remote;
259 const char *remote_host;
260 const char *remote_user;
261 const char *service;
262 pid_t leader;
263 const char *type;
55efac6c 264 const char *class;
0604381b 265 const char *state;
aa1936ea 266 const char *scope;
a4c279f8
LP
267} SessionStatusInfo;
268
269typedef struct UserStatusInfo {
270 uid_t uid;
271 const char *name;
272 usec_t timestamp;
a4c279f8
LP
273 const char *state;
274 char **sessions;
275 const char *display;
9444b1f2 276 const char *slice;
a4c279f8
LP
277} UserStatusInfo;
278
279typedef struct SeatStatusInfo {
280 const char *id;
281 const char *active_session;
282 char **sessions;
283} SeatStatusInfo;
284
f8f14b36
SP
285static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
286 const char *contents;
287 int r;
288
289 r = sd_bus_message_peek_type(m, NULL, &contents);
290 if (r < 0)
291 return r;
292
293 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
294 if (r < 0)
295 return r;
296
297 if (contents[0] == 's' || contents[0] == 'o') {
298 const char *s;
299 char **p = (char **) userdata;
300
301 r = sd_bus_message_read_basic(m, contents[0], &s);
302 if (r < 0)
303 return r;
304
305 free(*p);
306 *p = strdup(s);
307
308 if (!*p)
309 return -ENOMEM;
310 } else {
311 r = sd_bus_message_read_basic(m, contents[0], userdata);
312 if (r < 0)
313 return r;
314 }
315
316 r = sd_bus_message_skip(m, contents+1);
317 if (r < 0)
318 return r;
319
320 r = sd_bus_message_exit_container(m);
321 if (r < 0)
322 return r;
323
324 return 0;
325}
326
327static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
328 const char *name;
329 int r;
330
331 assert(bus);
332 assert(m);
333
334 r = sd_bus_message_enter_container(m, 'a', "(so)");
335 if (r < 0)
336 return r;
337
338 while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
339 r = strv_extend(userdata, name);
340 if (r < 0)
341 return r;
342 }
343 if (r < 0)
344 return r;
345
346 return sd_bus_message_exit_container(m);
347}
348
349static int print_session_status_info(sd_bus *bus, const char *path) {
350
351 static const struct bus_properties_map map[] = {
352 { "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
353 { "Name", "s", NULL, offsetof(SessionStatusInfo, name) },
354 { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
355 { "Display", "s", NULL, offsetof(SessionStatusInfo, display) },
356 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
357 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
358 { "Service", "s", NULL, offsetof(SessionStatusInfo, service) },
359 { "Type", "s", NULL, offsetof(SessionStatusInfo, type) },
360 { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
361 { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) },
362 { "State", "s", NULL, offsetof(SessionStatusInfo, state) },
363 { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) },
364 { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
365 { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) },
366 { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp) },
367 { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
368 { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, id) },
369 {}
370 };
371
9185c8e6 372 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
a4c279f8 373 char since2[FORMAT_TIMESTAMP_MAX], *s2;
46e65dcc 374 SessionStatusInfo i = {};
f8f14b36
SP
375 int r;
376
377 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
378 if (r < 0)
379 return r;
a4c279f8 380
f8f14b36 381 printf("%s - ", strna(i.id));
a4c279f8 382
f8f14b36
SP
383 if (i.name)
384 printf("%s (%u)\n", i.name, (unsigned) i.uid);
a4c279f8 385 else
f8f14b36 386 printf("%u\n", (unsigned) i.uid);
a4c279f8 387
f8f14b36
SP
388 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
389 s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
a4c279f8
LP
390
391 if (s1)
392 printf("\t Since: %s; %s\n", s2, s1);
393 else if (s2)
394 printf("\t Since: %s\n", s2);
395
f8f14b36 396 if (i.leader > 0) {
9444b1f2 397 _cleanup_free_ char *t = NULL;
a4c279f8 398
f8f14b36 399 printf("\t Leader: %u", (unsigned) i.leader);
a4c279f8 400
f8f14b36 401 get_process_comm(i.leader, &t);
9444b1f2 402 if (t)
a4c279f8 403 printf(" (%s)", t);
a4c279f8
LP
404
405 printf("\n");
406 }
407
f8f14b36
SP
408 if (i.seat) {
409 printf("\t Seat: %s", i.seat);
a4c279f8 410
f8f14b36
SP
411 if (i.vtnr > 0)
412 printf("; vc%i", i.vtnr);
a4c279f8
LP
413
414 printf("\n");
415 }
416
f8f14b36
SP
417 if (i.tty)
418 printf("\t TTY: %s\n", i.tty);
419 else if (i.display)
420 printf("\t Display: %s\n", i.display);
421
422 if (i.remote_host && i.remote_user)
423 printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host);
424 else if (i.remote_host)
425 printf("\t Remote: %s\n", i.remote_host);
426 else if (i.remote_user)
427 printf("\t Remote: user %s\n", i.remote_user);
428 else if (i.remote)
a4c279f8
LP
429 printf("\t Remote: Yes\n");
430
f8f14b36
SP
431 if (i.service) {
432 printf("\t Service: %s", i.service);
a4c279f8 433
f8f14b36
SP
434 if (i.type)
435 printf("; type %s", i.type);
a4c279f8 436
f8f14b36
SP
437 if (i.class)
438 printf("; class %s", i.class);
55efac6c 439
a4c279f8 440 printf("\n");
f8f14b36
SP
441 } else if (i.type) {
442 printf("\t Type: %s\n", i.type);
a4c279f8 443
f8f14b36
SP
444 if (i.class)
445 printf("; class %s", i.class);
446 } else if (i.class)
447 printf("\t Class: %s\n", i.class);
55efac6c 448
f8f14b36
SP
449 if (i.state)
450 printf("\t State: %s\n", i.state);
a4c279f8 451
f8f14b36
SP
452 if (i.scope) {
453 printf("\t Unit: %s\n", i.scope);
454 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
a4c279f8 455 }
f8f14b36
SP
456
457 return 0;
a4c279f8
LP
458}
459
f8f14b36
SP
460static int print_user_status_info(sd_bus *bus, const char *path) {
461
462 static const struct bus_properties_map map[] = {
463 { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
464 { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
465 { "State", "s", NULL, offsetof(UserStatusInfo, state) },
466 { "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
467 { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp) },
468 { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
469 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
470 {}
471 };
472
9185c8e6 473 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
a4c279f8 474 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f8f14b36
SP
475 UserStatusInfo i = {};
476 int r;
a4c279f8 477
f8f14b36
SP
478 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
479 if (r < 0)
480 goto finish;
481
482 if (i.name)
483 printf("%s (%u)\n", i.name, (unsigned) i.uid);
a4c279f8 484 else
f8f14b36 485 printf("%u\n", (unsigned) i.uid);
a4c279f8 486
f8f14b36
SP
487 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
488 s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
a4c279f8
LP
489
490 if (s1)
491 printf("\t Since: %s; %s\n", s2, s1);
492 else if (s2)
493 printf("\t Since: %s\n", s2);
494
f8f14b36
SP
495 if (!isempty(i.state))
496 printf("\t State: %s\n", i.state);
a4c279f8 497
f8f14b36 498 if (!strv_isempty(i.sessions)) {
a4c279f8
LP
499 char **l;
500 printf("\tSessions:");
501
f8f14b36
SP
502 STRV_FOREACH(l, i.sessions) {
503 if (streq_ptr(*l, i.display))
a4c279f8
LP
504 printf(" *%s", *l);
505 else
506 printf(" %s", *l);
507 }
508
509 printf("\n");
510 }
511
f8f14b36
SP
512 if (i.slice) {
513 printf("\t Unit: %s\n", i.slice);
514 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
a4c279f8 515 }
f8f14b36
SP
516
517finish:
518 strv_free(i.sessions);
519
520 return 0;
a4c279f8
LP
521}
522
f8f14b36 523static int print_seat_status_info(sd_bus *bus, const char *path) {
a4c279f8 524
f8f14b36
SP
525 static const struct bus_properties_map map[] = {
526 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
46e65dcc
LP
527 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
528 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
f8f14b36
SP
529 {}
530 };
a4c279f8 531
f8f14b36
SP
532 SeatStatusInfo i = {};
533 int r;
534
535 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
536 if (r < 0)
537 goto finish;
538
539 printf("%s\n", strna(i.id));
540
541 if (!strv_isempty(i.sessions)) {
a4c279f8
LP
542 char **l;
543 printf("\tSessions:");
544
f8f14b36
SP
545 STRV_FOREACH(l, i.sessions) {
546 if (streq_ptr(*l, i.active_session))
a4c279f8
LP
547 printf(" *%s", *l);
548 else
549 printf(" %s", *l);
550 }
551
552 printf("\n");
553 }
554
f8f14b36 555 if (arg_transport == BUS_TRANSPORT_LOCAL) {
a4c279f8
LP
556 unsigned c;
557
558 c = columns();
88e3dc90
LP
559 if (c > 21)
560 c -= 21;
a4c279f8
LP
561 else
562 c = 0;
563
564 printf("\t Devices:\n");
565
f8f14b36 566 show_sysfs(i.id, "\t\t ", c);
a4c279f8 567 }
a4c279f8 568
f8f14b36
SP
569finish:
570 strv_free(i.sessions);
a4c279f8
LP
571
572 return 0;
573}
574
97aa7b47
DH
575static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
576 int r;
577
578 if (*new_line)
579 printf("\n");
580
581 *new_line = true;
582
583 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
584 if (r < 0)
585 log_error("Could not get properties: %s", strerror(-r));
586
587 return r;
588}
589
f8f14b36 590static int show_session(sd_bus *bus, char **args, unsigned n) {
97aa7b47 591 bool properties, new_line = false;
f8f14b36
SP
592 unsigned i;
593 int r;
a4c279f8 594
f8f14b36
SP
595 assert(bus);
596 assert(args);
a4c279f8 597
97aa7b47 598 properties = !strstr(args[0], "status");
a4c279f8 599
f8f14b36 600 pager_open_if_enabled();
a4c279f8 601
97aa7b47 602 if (properties && n <= 1) {
f8f14b36
SP
603 /* If not argument is specified inspect the manager
604 * itself */
97aa7b47 605 return show_properties(bus, "/org/freedesktop/login1", &new_line);
a4c279f8
LP
606 }
607
f8f14b36
SP
608 for (i = 1; i < n; i++) {
609 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
610 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
611 const char *path = NULL;
a4c279f8 612
f8f14b36
SP
613 if (i != 1)
614 printf("\n");
a4c279f8 615
f8f14b36
SP
616 r = sd_bus_call_method(
617 bus,
618 "org.freedesktop.login1",
619 "/org/freedesktop/login1",
620 "org.freedesktop.login1.Manager",
621 "GetSession",
622 &error, &reply,
623 "s", args[i]);
624 if (r < 0) {
625 log_error("Failed to get session: %s", bus_error_message(&error, r));
626 return r;
a4c279f8 627 }
a4c279f8 628
f8f14b36
SP
629 r = sd_bus_message_read(reply, "o", &path);
630 if (r < 0)
5b30bef8 631 return bus_log_parse_error(r);
a4c279f8 632
97aa7b47
DH
633 if (properties)
634 r = show_properties(bus, path, &new_line);
f8f14b36
SP
635 else
636 r = print_session_status_info(bus, path);
637 if (r < 0) {
638 log_error("Failed to query session: %s", strerror(-r));
639 return r;
a4c279f8 640 }
a4c279f8
LP
641 }
642
a4c279f8
LP
643 return 0;
644}
645
f8f14b36 646static int show_user(sd_bus *bus, char **args, unsigned n) {
97aa7b47 647 bool properties, new_line = false;
f8f14b36 648 unsigned i;
a4c279f8 649 int r;
a4c279f8 650
f8f14b36
SP
651 assert(bus);
652 assert(args);
a4c279f8 653
97aa7b47 654 properties = !strstr(args[0], "status");
a4c279f8 655
f8f14b36 656 pager_open_if_enabled();
a4c279f8 657
97aa7b47 658 if (properties && n <= 1) {
f8f14b36
SP
659 /* If not argument is specified inspect the manager
660 * itself */
97aa7b47 661 return show_properties(bus, "/org/freedesktop/login1", &new_line);
f8f14b36 662 }
a4c279f8 663
f8f14b36
SP
664 for (i = 1; i < n; i++) {
665 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
666 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
667 const char *path = NULL;
668 uid_t uid;
a4c279f8 669
f8f14b36
SP
670 if (i != 1)
671 printf("\n");
a4c279f8 672
f8f14b36
SP
673 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
674 if (r < 0) {
675 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
676 return r;
a4c279f8
LP
677 }
678
f8f14b36
SP
679 r = sd_bus_call_method(
680 bus,
681 "org.freedesktop.login1",
682 "/org/freedesktop/login1",
683 "org.freedesktop.login1.Manager",
684 "GetUser",
685 &error, &reply,
686 "u", (uint32_t) uid);
687 if (r < 0) {
688 log_error("Failed to get user: %s", bus_error_message(&error, r));
689 return r;
a4c279f8
LP
690 }
691
f8f14b36
SP
692 r = sd_bus_message_read(reply, "o", &path);
693 if (r < 0)
5b30bef8 694 return bus_log_parse_error(r);
a4c279f8 695
97aa7b47
DH
696 if (properties)
697 r = show_properties(bus, path, &new_line);
9444b1f2 698 else
f8f14b36 699 r = print_user_status_info(bus, path);
a4c279f8 700 if (r < 0) {
f8f14b36
SP
701 log_error("Failed to query user: %s", strerror(-r));
702 return r;
a4c279f8 703 }
a4c279f8
LP
704 }
705
f8f14b36 706 return 0;
a4c279f8
LP
707}
708
f8f14b36 709static int show_seat(sd_bus *bus, char **args, unsigned n) {
97aa7b47 710 bool properties, new_line = false;
a4c279f8 711 unsigned i;
f8f14b36 712 int r;
a4c279f8
LP
713
714 assert(bus);
715 assert(args);
716
97aa7b47 717 properties = !strstr(args[0], "status");
a4c279f8 718
c846716a 719 pager_open_if_enabled();
a4c279f8 720
97aa7b47 721 if (properties && n <= 1) {
a4c279f8
LP
722 /* If not argument is specified inspect the manager
723 * itself */
97aa7b47 724 return show_properties(bus, "/org/freedesktop/login1", &new_line);
a4c279f8
LP
725 }
726
727 for (i = 1; i < n; i++) {
f8f14b36
SP
728 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
729 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
a4c279f8
LP
730 const char *path = NULL;
731
f8f14b36
SP
732 if (i != 1)
733 printf("\n");
9444b1f2 734
f8f14b36
SP
735 r = sd_bus_call_method(
736 bus,
737 "org.freedesktop.login1",
738 "/org/freedesktop/login1",
739 "org.freedesktop.login1.Manager",
740 "GetSeat",
741 &error, &reply,
742 "s", args[i]);
743 if (r < 0) {
744 log_error("Failed to get seat: %s", bus_error_message(&error, r));
745 return r;
a4c279f8 746 }
9444b1f2 747
f8f14b36
SP
748 r = sd_bus_message_read(reply, "o", &path);
749 if (r < 0)
5b30bef8 750 return bus_log_parse_error(r);
a4c279f8 751
97aa7b47
DH
752 if (properties)
753 r = show_properties(bus, path, &new_line);
f8f14b36
SP
754 else
755 r = print_seat_status_info(bus, path);
756 if (r < 0) {
757 log_error("Failed to query seat: %s", strerror(-r));
758 return r;
a4c279f8 759 }
a4c279f8
LP
760 }
761
f8f14b36 762 return 0;
a4c279f8
LP
763}
764
f8f14b36
SP
765static int activate(sd_bus *bus, char **args, unsigned n) {
766 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
24310c11 767 unsigned i;
f8f14b36 768 int r;
24310c11 769
24310c11
LP
770 assert(args);
771
24310c11 772 for (i = 1; i < n; i++) {
f8440af5 773
f8f14b36 774 r = sd_bus_call_method (
2a3613b1 775 bus,
24310c11
LP
776 "org.freedesktop.login1",
777 "/org/freedesktop/login1",
778 "org.freedesktop.login1.Manager",
88e3dc90
LP
779 streq(args[0], "lock-session") ? "LockSession" :
780 streq(args[0], "unlock-session") ? "UnlockSession" :
781 streq(args[0], "terminate-session") ? "TerminateSession" :
2a3613b1 782 "ActivateSession",
f8f14b36
SP
783 &error, NULL,
784 "s", args[i]);
785 if (r < 0) {
786 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
787 return r;
788 }
24310c11
LP
789 }
790
f8f14b36 791 return 0;
a4c279f8
LP
792}
793
f8f14b36
SP
794static int kill_session(sd_bus *bus, char **args, unsigned n) {
795 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de07ab16 796 unsigned i;
f8f14b36 797 int r;
de07ab16 798
de07ab16
LP
799 assert(args);
800
de07ab16
LP
801 if (!arg_kill_who)
802 arg_kill_who = "all";
803
804 for (i = 1; i < n; i++) {
4654e558 805
f8f14b36 806 r = sd_bus_call_method (
4654e558
ZJS
807 bus,
808 "org.freedesktop.login1",
809 "/org/freedesktop/login1",
810 "org.freedesktop.login1.Manager",
811 "KillSession",
f8f14b36
SP
812 &error, NULL,
813 "ssi", args[i], arg_kill_who, arg_signal);
814 if (r < 0) {
815 log_error("Could not kill session: %s", bus_error_message(&error, -r));
4654e558 816 return r;
f8f14b36 817 }
de07ab16
LP
818 }
819
4654e558 820 return 0;
a4c279f8
LP
821}
822
f8f14b36
SP
823static int enable_linger(sd_bus *bus, char **args, unsigned n) {
824 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 825 unsigned i;
f8f14b36
SP
826 bool b;
827 int r;
88e3dc90 828
88e3dc90
LP
829 assert(args);
830
6bb92a16
LP
831 polkit_agent_open_if_enabled();
832
88e3dc90
LP
833 b = streq(args[0], "enable-linger");
834
835 for (i = 1; i < n; i++) {
ddd88763 836 uid_t uid;
88e3dc90 837
4654e558
ZJS
838 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
839 if (r < 0) {
f8f14b36 840 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
4654e558 841 return r;
88e3dc90
LP
842 }
843
f8f14b36 844 r = sd_bus_call_method (
4654e558
ZJS
845 bus,
846 "org.freedesktop.login1",
847 "/org/freedesktop/login1",
848 "org.freedesktop.login1.Manager",
849 "SetUserLinger",
f8f14b36
SP
850 &error, NULL,
851 "ubb", (uint32_t) uid, b, true);
852 if (r < 0) {
853 log_error("Could not enable linger: %s", bus_error_message(&error, -r));
4654e558 854 return r;
f8f14b36 855 }
88e3dc90
LP
856 }
857
4654e558 858 return 0;
88e3dc90
LP
859}
860
f8f14b36
SP
861static int terminate_user(sd_bus *bus, char **args, unsigned n) {
862 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 863 unsigned i;
f8f14b36 864 int r;
88e3dc90 865
88e3dc90
LP
866 assert(args);
867
88e3dc90 868 for (i = 1; i < n; i++) {
ddd88763 869 uid_t uid;
88e3dc90 870
4654e558
ZJS
871 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
872 if (r < 0) {
873 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
874 return r;
88e3dc90
LP
875 }
876
f8f14b36 877 r = sd_bus_call_method (
4654e558
ZJS
878 bus,
879 "org.freedesktop.login1",
880 "/org/freedesktop/login1",
881 "org.freedesktop.login1.Manager",
882 "TerminateUser",
f8f14b36
SP
883 &error, NULL,
884 "u", (uint32_t) uid);
885 if (r < 0) {
886 log_error("Could not terminate user: %s", bus_error_message(&error, -r));
4654e558 887 return r;
f8f14b36 888 }
88e3dc90
LP
889 }
890
4654e558 891 return 0;
a4c279f8
LP
892}
893
f8f14b36
SP
894static int kill_user(sd_bus *bus, char **args, unsigned n) {
895 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de07ab16 896 unsigned i;
f8f14b36 897 int r;
de07ab16 898
de07ab16
LP
899 assert(args);
900
de07ab16
LP
901 if (!arg_kill_who)
902 arg_kill_who = "all";
903
904 for (i = 1; i < n; i++) {
ddd88763 905 uid_t uid;
de07ab16 906
4654e558
ZJS
907 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
908 if (r < 0) {
909 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
910 return r;
de07ab16
LP
911 }
912
f8f14b36 913 r = sd_bus_call_method (
4654e558
ZJS
914 bus,
915 "org.freedesktop.login1",
916 "/org/freedesktop/login1",
917 "org.freedesktop.login1.Manager",
918 "KillUser",
f8f14b36
SP
919 &error, NULL,
920 "ui", (uint32_t) uid, arg_signal);
921 if (r < 0) {
922 log_error("Could not kill user: %s", bus_error_message(&error, -r));
4654e558 923 return r;
f8f14b36 924 }
de07ab16
LP
925 }
926
4654e558 927 return 0;
de07ab16
LP
928}
929
f8f14b36
SP
930static int attach(sd_bus *bus, char **args, unsigned n) {
931 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 932 unsigned i;
f8f14b36 933 int r;
88e3dc90 934
88e3dc90
LP
935 assert(args);
936
6bb92a16
LP
937 polkit_agent_open_if_enabled();
938
88e3dc90 939 for (i = 2; i < n; i++) {
4654e558 940
f8f14b36 941 r = sd_bus_call_method (
4654e558
ZJS
942 bus,
943 "org.freedesktop.login1",
944 "/org/freedesktop/login1",
945 "org.freedesktop.login1.Manager",
946 "AttachDevice",
f8f14b36
SP
947 &error, NULL,
948 "ssb", args[1], args[i], true);
949
950 if (r < 0) {
951 log_error("Could not attach device: %s", bus_error_message(&error, -r));
4654e558 952 return r;
f8f14b36 953 }
88e3dc90
LP
954 }
955
4654e558 956 return 0;
a4c279f8
LP
957}
958
f8f14b36
SP
959static int flush_devices(sd_bus *bus, char **args, unsigned n) {
960 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
961 int r;
88e3dc90 962
88e3dc90
LP
963 assert(args);
964
6bb92a16
LP
965 polkit_agent_open_if_enabled();
966
f8f14b36 967 r = sd_bus_call_method (
2a3613b1 968 bus,
88e3dc90
LP
969 "org.freedesktop.login1",
970 "/org/freedesktop/login1",
971 "org.freedesktop.login1.Manager",
2a3613b1 972 "FlushDevices",
f8f14b36
SP
973 &error, NULL,
974 "b", true);
975 if (r < 0)
976 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
977
978 return r;
a4c279f8
LP
979}
980
f8f14b36
SP
981static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
982 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
983 int r;
b6160029 984
f8f14b36 985 assert(args);
7212a8a9 986
f8f14b36 987 r = sd_bus_call_method (
2a3613b1 988 bus,
7212a8a9
LP
989 "org.freedesktop.login1",
990 "/org/freedesktop/login1",
991 "org.freedesktop.login1.Manager",
b6160029 992 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
f8f14b36
SP
993 &error, NULL,
994 NULL);
995 if (r < 0)
996 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
997
998 return r;
7212a8a9
LP
999}
1000
f8f14b36
SP
1001static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
1002 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 1003 unsigned i;
f8f14b36 1004 int r;
88e3dc90 1005
88e3dc90
LP
1006 assert(args);
1007
88e3dc90 1008 for (i = 1; i < n; i++) {
4654e558 1009
f8f14b36 1010 r = sd_bus_call_method (
4654e558
ZJS
1011 bus,
1012 "org.freedesktop.login1",
1013 "/org/freedesktop/login1",
1014 "org.freedesktop.login1.Manager",
1015 "TerminateSeat",
f8f14b36
SP
1016 &error, NULL,
1017 "s", args[i]);
1018 if (r < 0) {
1019 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
4654e558 1020 return r;
f8f14b36 1021 }
88e3dc90
LP
1022 }
1023
4654e558 1024 return 0;
a4c279f8
LP
1025}
1026
abca4822
LP
1027static int help(void) {
1028
1029 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1030 "Send control commands to or query the login manager.\n\n"
6d0274f1
LP
1031 " -h --help Show this help\n"
1032 " --version Show package version\n"
f8f14b36
SP
1033 " --no-pager Do not pipe output into a pager\n"
1034 " --no-ask-password Don't prompt for password\n"
1035 " -H --host=[USER@]HOST Operate on remote host\n"
1036 " -M --machine=CONTAINER Operate on local container\n"
6d0274f1
LP
1037 " -p --property=NAME Show only properties by this name\n"
1038 " -a --all Show all properties, including empty ones\n"
98a6e132 1039 " -l --full Do not ellipsize output\n"
f8f14b36
SP
1040 " --kill-who=WHO Who to send signal to\n"
1041 " -s --signal=SIGNAL Which signal to send\n\n"
abca4822 1042 "Commands:\n"
4f8f66cb
ZJS
1043 " list-sessions List sessions\n"
1044 " session-status ID... Show session status\n"
1045 " show-session [ID...] Show properties of sessions or the manager\n"
1046 " activate ID Activate a session\n"
1047 " lock-session ID... Screen lock one or more sessions\n"
1048 " unlock-session ID... Screen unlock one or more sessions\n"
1049 " lock-sessions Screen lock all current sessions\n"
1050 " unlock-sessions Screen unlock all current sessions\n"
1051 " terminate-session ID... Terminate one or more sessions\n"
1052 " kill-session ID... Send signal to processes of a session\n"
1053 " list-users List users\n"
1054 " user-status USER... Show user status\n"
1055 " show-user [USER...] Show properties of users or the manager\n"
1056 " enable-linger USER... Enable linger state of one or more users\n"
1057 " disable-linger USER... Disable linger state of one or more users\n"
1058 " terminate-user USER... Terminate all sessions of one or more users\n"
1059 " kill-user USER... Send signal to processes of a user\n"
1060 " list-seats List seats\n"
1061 " seat-status NAME... Show seat status\n"
1062 " show-seat NAME... Show properties of one or more seats\n"
1063 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1064 " flush-devices Flush all device associations\n"
1065 " terminate-seat NAME... Terminate all sessions on one or more seats\n",
abca4822
LP
1066 program_invocation_short_name);
1067
1068 return 0;
1069}
1070
1071static int parse_argv(int argc, char *argv[]) {
1072
1073 enum {
1074 ARG_VERSION = 0x100,
a4c279f8 1075 ARG_NO_PAGER,
6bb92a16 1076 ARG_KILL_WHO,
9bdbc2e2 1077 ARG_NO_ASK_PASSWORD,
abca4822
LP
1078 };
1079
1080 static const struct option options[] = {
6d0274f1
LP
1081 { "help", no_argument, NULL, 'h' },
1082 { "version", no_argument, NULL, ARG_VERSION },
1083 { "property", required_argument, NULL, 'p' },
1084 { "all", no_argument, NULL, 'a' },
422fa650 1085 { "full", no_argument, NULL, 'l' },
6d0274f1
LP
1086 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1087 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1088 { "signal", required_argument, NULL, 's' },
1089 { "host", required_argument, NULL, 'H' },
f8f14b36 1090 { "machine", required_argument, NULL, 'M' },
6d0274f1 1091 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 1092 {}
abca4822
LP
1093 };
1094
1c3051eb 1095 int c, r;
abca4822
LP
1096
1097 assert(argc >= 0);
1098 assert(argv);
1099
f8f14b36 1100 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
abca4822
LP
1101
1102 switch (c) {
1103
1104 case 'h':
eb9da376 1105 return help();
abca4822
LP
1106
1107 case ARG_VERSION:
1108 puts(PACKAGE_STRING);
abca4822
LP
1109 puts(SYSTEMD_FEATURES);
1110 return 0;
1111
a4c279f8 1112 case 'p': {
1c3051eb
DH
1113 r = strv_extend(&arg_property, optarg);
1114 if (r < 0)
1115 return log_oom();
a4c279f8
LP
1116
1117 /* If the user asked for a particular
1118 * property, show it to him, even if it is
1119 * empty. */
1120 arg_all = true;
1121 break;
1122 }
1123
1124 case 'a':
1125 arg_all = true;
1126 break;
1127
422fa650
ZJS
1128 case 'l':
1129 arg_full = true;
1130 break;
1131
abca4822
LP
1132 case ARG_NO_PAGER:
1133 arg_no_pager = true;
1134 break;
1135
6bb92a16
LP
1136 case ARG_NO_ASK_PASSWORD:
1137 arg_ask_password = false;
5d5e98eb 1138 break;
6bb92a16 1139
a4c279f8
LP
1140 case ARG_KILL_WHO:
1141 arg_kill_who = optarg;
1142 break;
1143
1144 case 's':
1145 arg_signal = signal_from_string_try_harder(optarg);
1146 if (arg_signal < 0) {
1147 log_error("Failed to parse signal string %s.", optarg);
1148 return -EINVAL;
1149 }
1150 break;
1151
f8f14b36
SP
1152 case 'H':
1153 arg_transport = BUS_TRANSPORT_REMOTE;
1154 arg_host = optarg;
abca4822
LP
1155 break;
1156
f8f14b36
SP
1157 case 'M':
1158 arg_transport = BUS_TRANSPORT_CONTAINER;
1159 arg_host = optarg;
abca4822
LP
1160 break;
1161
1162 case '?':
1163 return -EINVAL;
1164
1165 default:
eb9da376 1166 assert_not_reached("Unhandled option");
abca4822
LP
1167 }
1168 }
1169
1170 return 1;
1171}
1172
f8f14b36 1173static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
abca4822
LP
1174
1175 static const struct {
1176 const char* verb;
1177 const enum {
1178 MORE,
1179 LESS,
1180 EQUAL
1181 } argc_cmp;
1182 const int argc;
f8f14b36 1183 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
abca4822 1184 } verbs[] = {
9444b1f2 1185 { "list-sessions", LESS, 1, list_sessions },
f8f14b36
SP
1186 { "session-status", MORE, 2, show_session },
1187 { "show-session", MORE, 1, show_session },
9444b1f2
LP
1188 { "activate", EQUAL, 2, activate },
1189 { "lock-session", MORE, 2, activate },
1190 { "unlock-session", MORE, 2, activate },
1191 { "lock-sessions", EQUAL, 1, lock_sessions },
1192 { "unlock-sessions", EQUAL, 1, lock_sessions },
1193 { "terminate-session", MORE, 2, activate },
1194 { "kill-session", MORE, 2, kill_session },
1195 { "list-users", EQUAL, 1, list_users },
f8f14b36
SP
1196 { "user-status", MORE, 2, show_user },
1197 { "show-user", MORE, 1, show_user },
9444b1f2
LP
1198 { "enable-linger", MORE, 2, enable_linger },
1199 { "disable-linger", MORE, 2, enable_linger },
1200 { "terminate-user", MORE, 2, terminate_user },
1201 { "kill-user", MORE, 2, kill_user },
1202 { "list-seats", EQUAL, 1, list_seats },
f8f14b36
SP
1203 { "seat-status", MORE, 2, show_seat },
1204 { "show-seat", MORE, 1, show_seat },
9444b1f2
LP
1205 { "attach", MORE, 3, attach },
1206 { "flush-devices", EQUAL, 1, flush_devices },
1207 { "terminate-seat", MORE, 2, terminate_seat },
abca4822
LP
1208 };
1209
1210 int left;
1211 unsigned i;
1212
1213 assert(argc >= 0);
1214 assert(argv);
abca4822
LP
1215
1216 left = argc - optind;
1217
1218 if (left <= 0)
1219 /* Special rule: no arguments means "list-sessions" */
1220 i = 0;
1221 else {
1222 if (streq(argv[optind], "help")) {
1223 help();
1224 return 0;
1225 }
1226
1227 for (i = 0; i < ELEMENTSOF(verbs); i++)
1228 if (streq(argv[optind], verbs[i].verb))
1229 break;
1230
1231 if (i >= ELEMENTSOF(verbs)) {
1232 log_error("Unknown operation %s", argv[optind]);
1233 return -EINVAL;
1234 }
1235 }
1236
1237 switch (verbs[i].argc_cmp) {
1238
1239 case EQUAL:
1240 if (left != verbs[i].argc) {
1241 log_error("Invalid number of arguments.");
1242 return -EINVAL;
1243 }
1244
1245 break;
1246
1247 case MORE:
1248 if (left < verbs[i].argc) {
1249 log_error("Too few arguments.");
1250 return -EINVAL;
1251 }
1252
1253 break;
1254
1255 case LESS:
1256 if (left > verbs[i].argc) {
1257 log_error("Too many arguments.");
1258 return -EINVAL;
1259 }
1260
1261 break;
1262
1263 default:
1264 assert_not_reached("Unknown comparison operator.");
1265 }
1266
abca4822
LP
1267 return verbs[i].dispatch(bus, argv + optind, left);
1268}
1269
f8f14b36
SP
1270int main(int argc, char *argv[]) {
1271 _cleanup_bus_unref_ sd_bus *bus = NULL;
1272 int r;
abca4822 1273
a9cdc94f 1274 setlocale(LC_ALL, "");
abca4822
LP
1275 log_parse_environment();
1276 log_open();
1277
1278 r = parse_argv(argc, argv);
f8f14b36 1279 if (r <= 0)
abca4822 1280 goto finish;
f8f14b36
SP
1281
1282 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1283 if (r < 0) {
1284 log_error("Failed to create bus connection: %s", strerror(-r));
abca4822
LP
1285 goto finish;
1286 }
1287
f8f14b36 1288 r = loginctl_main(bus, argc, argv);
abca4822
LP
1289
1290finish:
f8f14b36 1291 pager_close();
abca4822 1292
a4c279f8
LP
1293 strv_free(arg_property);
1294
f8f14b36 1295 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
abca4822 1296}