]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/loginctl.c
journalctl,zsh-completion: fix several issues in --help message text
[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
f8f14b36
SP
575static int show_session(sd_bus *bus, char **args, unsigned n) {
576 bool show_properties;
577 unsigned i;
578 int r;
a4c279f8 579
f8f14b36
SP
580 assert(bus);
581 assert(args);
a4c279f8 582
f8f14b36 583 show_properties = !strstr(args[0], "status");
a4c279f8 584
f8f14b36 585 pager_open_if_enabled();
a4c279f8 586
f8f14b36
SP
587 if (show_properties && n <= 1) {
588 /* If not argument is specified inspect the manager
589 * itself */
590 r = bus_print_all_properties(bus, "org.freedesktop.login1", "/org/freedesktop/login1", NULL, arg_all);
591 if (r < 0)
592 log_error("Failed to query login manager.");
a4c279f8 593
f8f14b36 594 return r;
a4c279f8
LP
595 }
596
f8f14b36
SP
597 for (i = 1; i < n; i++) {
598 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
599 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
600 const char *path = NULL;
a4c279f8 601
f8f14b36
SP
602 if (i != 1)
603 printf("\n");
a4c279f8 604
f8f14b36
SP
605 r = sd_bus_call_method(
606 bus,
607 "org.freedesktop.login1",
608 "/org/freedesktop/login1",
609 "org.freedesktop.login1.Manager",
610 "GetSession",
611 &error, &reply,
612 "s", args[i]);
613 if (r < 0) {
614 log_error("Failed to get session: %s", bus_error_message(&error, r));
615 return r;
a4c279f8 616 }
a4c279f8 617
f8f14b36
SP
618 r = sd_bus_message_read(reply, "o", &path);
619 if (r < 0)
5b30bef8 620 return bus_log_parse_error(r);
a4c279f8 621
f8f14b36
SP
622 if (show_properties)
623 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, NULL, arg_all);
624 else
625 r = print_session_status_info(bus, path);
626 if (r < 0) {
627 log_error("Failed to query session: %s", strerror(-r));
628 return r;
a4c279f8 629 }
a4c279f8
LP
630 }
631
a4c279f8
LP
632 return 0;
633}
634
f8f14b36
SP
635static int show_user(sd_bus *bus, char **args, unsigned n) {
636 bool show_properties;
637 unsigned i;
a4c279f8 638 int r;
a4c279f8 639
f8f14b36
SP
640 assert(bus);
641 assert(args);
a4c279f8 642
f8f14b36 643 show_properties = !strstr(args[0], "status");
a4c279f8 644
f8f14b36 645 pager_open_if_enabled();
a4c279f8 646
f8f14b36
SP
647 if (show_properties && n <= 1) {
648 /* If not argument is specified inspect the manager
649 * itself */
650 r = bus_print_all_properties(bus, "org.freedesktop.login1", "/org/freedesktop/login1", NULL, arg_all);
651 if (r < 0)
652 log_error("Failed to query login manager.");
a4c279f8 653
f8f14b36
SP
654 return r;
655 }
a4c279f8 656
f8f14b36
SP
657 for (i = 1; i < n; i++) {
658 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
659 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
660 const char *path = NULL;
661 uid_t uid;
a4c279f8 662
f8f14b36
SP
663 if (i != 1)
664 printf("\n");
a4c279f8 665
f8f14b36
SP
666 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
667 if (r < 0) {
668 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
669 return r;
a4c279f8
LP
670 }
671
f8f14b36
SP
672 r = sd_bus_call_method(
673 bus,
674 "org.freedesktop.login1",
675 "/org/freedesktop/login1",
676 "org.freedesktop.login1.Manager",
677 "GetUser",
678 &error, &reply,
679 "u", (uint32_t) uid);
680 if (r < 0) {
681 log_error("Failed to get user: %s", bus_error_message(&error, r));
682 return r;
a4c279f8
LP
683 }
684
f8f14b36
SP
685 r = sd_bus_message_read(reply, "o", &path);
686 if (r < 0)
5b30bef8 687 return bus_log_parse_error(r);
a4c279f8
LP
688
689 if (show_properties)
f8f14b36 690 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, NULL, arg_all);
9444b1f2 691 else
f8f14b36 692 r = print_user_status_info(bus, path);
a4c279f8 693 if (r < 0) {
f8f14b36
SP
694 log_error("Failed to query user: %s", strerror(-r));
695 return r;
a4c279f8 696 }
a4c279f8
LP
697 }
698
f8f14b36 699 return 0;
a4c279f8
LP
700}
701
f8f14b36
SP
702static int show_seat(sd_bus *bus, char **args, unsigned n) {
703 bool show_properties;
a4c279f8 704 unsigned i;
f8f14b36 705 int r;
a4c279f8
LP
706
707 assert(bus);
708 assert(args);
709
a4c279f8
LP
710 show_properties = !strstr(args[0], "status");
711
c846716a 712 pager_open_if_enabled();
a4c279f8
LP
713
714 if (show_properties && n <= 1) {
715 /* If not argument is specified inspect the manager
716 * itself */
f8f14b36
SP
717 r = bus_print_all_properties(bus, "org.freedesktop.login1", "/org/freedesktop/login1", NULL, arg_all);
718 if (r < 0)
719 log_error("Failed to query login manager.");
a4c279f8 720
f8f14b36 721 return r;
a4c279f8
LP
722 }
723
724 for (i = 1; i < n; i++) {
f8f14b36
SP
725 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
726 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
a4c279f8
LP
727 const char *path = NULL;
728
f8f14b36
SP
729 if (i != 1)
730 printf("\n");
9444b1f2 731
f8f14b36
SP
732 r = sd_bus_call_method(
733 bus,
734 "org.freedesktop.login1",
735 "/org/freedesktop/login1",
736 "org.freedesktop.login1.Manager",
737 "GetSeat",
738 &error, &reply,
739 "s", args[i]);
740 if (r < 0) {
741 log_error("Failed to get seat: %s", bus_error_message(&error, r));
742 return r;
a4c279f8 743 }
9444b1f2 744
f8f14b36
SP
745 r = sd_bus_message_read(reply, "o", &path);
746 if (r < 0)
5b30bef8 747 return bus_log_parse_error(r);
a4c279f8 748
f8f14b36
SP
749 if (show_properties)
750 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, NULL, arg_all);
751 else
752 r = print_seat_status_info(bus, path);
753 if (r < 0) {
754 log_error("Failed to query seat: %s", strerror(-r));
755 return r;
a4c279f8 756 }
a4c279f8
LP
757 }
758
f8f14b36 759 return 0;
a4c279f8
LP
760}
761
f8f14b36
SP
762static int activate(sd_bus *bus, char **args, unsigned n) {
763 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
24310c11 764 unsigned i;
f8f14b36 765 int r;
24310c11 766
24310c11
LP
767 assert(args);
768
24310c11 769 for (i = 1; i < n; i++) {
f8440af5 770
f8f14b36 771 r = sd_bus_call_method (
2a3613b1 772 bus,
24310c11
LP
773 "org.freedesktop.login1",
774 "/org/freedesktop/login1",
775 "org.freedesktop.login1.Manager",
88e3dc90
LP
776 streq(args[0], "lock-session") ? "LockSession" :
777 streq(args[0], "unlock-session") ? "UnlockSession" :
778 streq(args[0], "terminate-session") ? "TerminateSession" :
2a3613b1 779 "ActivateSession",
f8f14b36
SP
780 &error, NULL,
781 "s", args[i]);
782 if (r < 0) {
783 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
784 return r;
785 }
24310c11
LP
786 }
787
f8f14b36 788 return 0;
a4c279f8
LP
789}
790
f8f14b36
SP
791static int kill_session(sd_bus *bus, char **args, unsigned n) {
792 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de07ab16 793 unsigned i;
f8f14b36 794 int r;
de07ab16 795
de07ab16
LP
796 assert(args);
797
de07ab16
LP
798 if (!arg_kill_who)
799 arg_kill_who = "all";
800
801 for (i = 1; i < n; i++) {
4654e558 802
f8f14b36 803 r = sd_bus_call_method (
4654e558
ZJS
804 bus,
805 "org.freedesktop.login1",
806 "/org/freedesktop/login1",
807 "org.freedesktop.login1.Manager",
808 "KillSession",
f8f14b36
SP
809 &error, NULL,
810 "ssi", args[i], arg_kill_who, arg_signal);
811 if (r < 0) {
812 log_error("Could not kill session: %s", bus_error_message(&error, -r));
4654e558 813 return r;
f8f14b36 814 }
de07ab16
LP
815 }
816
4654e558 817 return 0;
a4c279f8
LP
818}
819
f8f14b36
SP
820static int enable_linger(sd_bus *bus, char **args, unsigned n) {
821 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 822 unsigned i;
f8f14b36
SP
823 bool b;
824 int r;
88e3dc90 825
88e3dc90
LP
826 assert(args);
827
6bb92a16
LP
828 polkit_agent_open_if_enabled();
829
88e3dc90
LP
830 b = streq(args[0], "enable-linger");
831
832 for (i = 1; i < n; i++) {
ddd88763 833 uid_t uid;
88e3dc90 834
4654e558
ZJS
835 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
836 if (r < 0) {
f8f14b36 837 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
4654e558 838 return r;
88e3dc90
LP
839 }
840
f8f14b36 841 r = sd_bus_call_method (
4654e558
ZJS
842 bus,
843 "org.freedesktop.login1",
844 "/org/freedesktop/login1",
845 "org.freedesktop.login1.Manager",
846 "SetUserLinger",
f8f14b36
SP
847 &error, NULL,
848 "ubb", (uint32_t) uid, b, true);
849 if (r < 0) {
850 log_error("Could not enable linger: %s", bus_error_message(&error, -r));
4654e558 851 return r;
f8f14b36 852 }
88e3dc90
LP
853 }
854
4654e558 855 return 0;
88e3dc90
LP
856}
857
f8f14b36
SP
858static int terminate_user(sd_bus *bus, char **args, unsigned n) {
859 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 860 unsigned i;
f8f14b36 861 int r;
88e3dc90 862
88e3dc90
LP
863 assert(args);
864
88e3dc90 865 for (i = 1; i < n; i++) {
ddd88763 866 uid_t uid;
88e3dc90 867
4654e558
ZJS
868 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
869 if (r < 0) {
870 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
871 return r;
88e3dc90
LP
872 }
873
f8f14b36 874 r = sd_bus_call_method (
4654e558
ZJS
875 bus,
876 "org.freedesktop.login1",
877 "/org/freedesktop/login1",
878 "org.freedesktop.login1.Manager",
879 "TerminateUser",
f8f14b36
SP
880 &error, NULL,
881 "u", (uint32_t) uid);
882 if (r < 0) {
883 log_error("Could not terminate user: %s", bus_error_message(&error, -r));
4654e558 884 return r;
f8f14b36 885 }
88e3dc90
LP
886 }
887
4654e558 888 return 0;
a4c279f8
LP
889}
890
f8f14b36
SP
891static int kill_user(sd_bus *bus, char **args, unsigned n) {
892 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de07ab16 893 unsigned i;
f8f14b36 894 int r;
de07ab16 895
de07ab16
LP
896 assert(args);
897
de07ab16
LP
898 if (!arg_kill_who)
899 arg_kill_who = "all";
900
901 for (i = 1; i < n; i++) {
ddd88763 902 uid_t uid;
de07ab16 903
4654e558
ZJS
904 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
905 if (r < 0) {
906 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
907 return r;
de07ab16
LP
908 }
909
f8f14b36 910 r = sd_bus_call_method (
4654e558
ZJS
911 bus,
912 "org.freedesktop.login1",
913 "/org/freedesktop/login1",
914 "org.freedesktop.login1.Manager",
915 "KillUser",
f8f14b36
SP
916 &error, NULL,
917 "ui", (uint32_t) uid, arg_signal);
918 if (r < 0) {
919 log_error("Could not kill user: %s", bus_error_message(&error, -r));
4654e558 920 return r;
f8f14b36 921 }
de07ab16
LP
922 }
923
4654e558 924 return 0;
de07ab16
LP
925}
926
f8f14b36
SP
927static int attach(sd_bus *bus, char **args, unsigned n) {
928 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 929 unsigned i;
f8f14b36 930 int r;
88e3dc90 931
88e3dc90
LP
932 assert(args);
933
6bb92a16
LP
934 polkit_agent_open_if_enabled();
935
88e3dc90 936 for (i = 2; i < n; i++) {
4654e558 937
f8f14b36 938 r = sd_bus_call_method (
4654e558
ZJS
939 bus,
940 "org.freedesktop.login1",
941 "/org/freedesktop/login1",
942 "org.freedesktop.login1.Manager",
943 "AttachDevice",
f8f14b36
SP
944 &error, NULL,
945 "ssb", args[1], args[i], true);
946
947 if (r < 0) {
948 log_error("Could not attach device: %s", bus_error_message(&error, -r));
4654e558 949 return r;
f8f14b36 950 }
88e3dc90
LP
951 }
952
4654e558 953 return 0;
a4c279f8
LP
954}
955
f8f14b36
SP
956static int flush_devices(sd_bus *bus, char **args, unsigned n) {
957 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
958 int r;
88e3dc90 959
88e3dc90
LP
960 assert(args);
961
6bb92a16
LP
962 polkit_agent_open_if_enabled();
963
f8f14b36 964 r = sd_bus_call_method (
2a3613b1 965 bus,
88e3dc90
LP
966 "org.freedesktop.login1",
967 "/org/freedesktop/login1",
968 "org.freedesktop.login1.Manager",
2a3613b1 969 "FlushDevices",
f8f14b36
SP
970 &error, NULL,
971 "b", true);
972 if (r < 0)
973 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
974
975 return r;
a4c279f8
LP
976}
977
f8f14b36
SP
978static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
979 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
980 int r;
b6160029 981
f8f14b36 982 assert(args);
7212a8a9 983
f8f14b36 984 r = sd_bus_call_method (
2a3613b1 985 bus,
7212a8a9
LP
986 "org.freedesktop.login1",
987 "/org/freedesktop/login1",
988 "org.freedesktop.login1.Manager",
b6160029 989 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
f8f14b36
SP
990 &error, NULL,
991 NULL);
992 if (r < 0)
993 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
994
995 return r;
7212a8a9
LP
996}
997
f8f14b36
SP
998static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
999 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88e3dc90 1000 unsigned i;
f8f14b36 1001 int r;
88e3dc90 1002
88e3dc90
LP
1003 assert(args);
1004
88e3dc90 1005 for (i = 1; i < n; i++) {
4654e558 1006
f8f14b36 1007 r = sd_bus_call_method (
4654e558
ZJS
1008 bus,
1009 "org.freedesktop.login1",
1010 "/org/freedesktop/login1",
1011 "org.freedesktop.login1.Manager",
1012 "TerminateSeat",
f8f14b36
SP
1013 &error, NULL,
1014 "s", args[i]);
1015 if (r < 0) {
1016 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
4654e558 1017 return r;
f8f14b36 1018 }
88e3dc90
LP
1019 }
1020
4654e558 1021 return 0;
a4c279f8
LP
1022}
1023
abca4822
LP
1024static int help(void) {
1025
1026 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1027 "Send control commands to or query the login manager.\n\n"
6d0274f1
LP
1028 " -h --help Show this help\n"
1029 " --version Show package version\n"
f8f14b36
SP
1030 " --no-pager Do not pipe output into a pager\n"
1031 " --no-ask-password Don't prompt for password\n"
1032 " -H --host=[USER@]HOST Operate on remote host\n"
1033 " -M --machine=CONTAINER Operate on local container\n"
6d0274f1
LP
1034 " -p --property=NAME Show only properties by this name\n"
1035 " -a --all Show all properties, including empty ones\n"
98a6e132 1036 " -l --full Do not ellipsize output\n"
f8f14b36
SP
1037 " --kill-who=WHO Who to send signal to\n"
1038 " -s --signal=SIGNAL Which signal to send\n\n"
abca4822 1039 "Commands:\n"
4f8f66cb
ZJS
1040 " list-sessions List sessions\n"
1041 " session-status ID... Show session status\n"
1042 " show-session [ID...] Show properties of sessions or the manager\n"
1043 " activate ID Activate a session\n"
1044 " lock-session ID... Screen lock one or more sessions\n"
1045 " unlock-session ID... Screen unlock one or more sessions\n"
1046 " lock-sessions Screen lock all current sessions\n"
1047 " unlock-sessions Screen unlock all current sessions\n"
1048 " terminate-session ID... Terminate one or more sessions\n"
1049 " kill-session ID... Send signal to processes of a session\n"
1050 " list-users List users\n"
1051 " user-status USER... Show user status\n"
1052 " show-user [USER...] Show properties of users or the manager\n"
1053 " enable-linger USER... Enable linger state of one or more users\n"
1054 " disable-linger USER... Disable linger state of one or more users\n"
1055 " terminate-user USER... Terminate all sessions of one or more users\n"
1056 " kill-user USER... Send signal to processes of a user\n"
1057 " list-seats List seats\n"
1058 " seat-status NAME... Show seat status\n"
1059 " show-seat NAME... Show properties of one or more seats\n"
1060 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1061 " flush-devices Flush all device associations\n"
1062 " terminate-seat NAME... Terminate all sessions on one or more seats\n",
abca4822
LP
1063 program_invocation_short_name);
1064
1065 return 0;
1066}
1067
1068static int parse_argv(int argc, char *argv[]) {
1069
1070 enum {
1071 ARG_VERSION = 0x100,
a4c279f8 1072 ARG_NO_PAGER,
6bb92a16 1073 ARG_KILL_WHO,
9bdbc2e2 1074 ARG_NO_ASK_PASSWORD,
abca4822
LP
1075 };
1076
1077 static const struct option options[] = {
6d0274f1
LP
1078 { "help", no_argument, NULL, 'h' },
1079 { "version", no_argument, NULL, ARG_VERSION },
1080 { "property", required_argument, NULL, 'p' },
1081 { "all", no_argument, NULL, 'a' },
422fa650 1082 { "full", no_argument, NULL, 'l' },
6d0274f1
LP
1083 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1084 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1085 { "signal", required_argument, NULL, 's' },
1086 { "host", required_argument, NULL, 'H' },
f8f14b36 1087 { "machine", required_argument, NULL, 'M' },
6d0274f1 1088 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 1089 {}
abca4822
LP
1090 };
1091
1092 int c;
1093
1094 assert(argc >= 0);
1095 assert(argv);
1096
f8f14b36 1097 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
abca4822
LP
1098
1099 switch (c) {
1100
1101 case 'h':
eb9da376 1102 return help();
abca4822
LP
1103
1104 case ARG_VERSION:
1105 puts(PACKAGE_STRING);
abca4822
LP
1106 puts(SYSTEMD_FEATURES);
1107 return 0;
1108
a4c279f8
LP
1109 case 'p': {
1110 char **l;
1111
1112 l = strv_append(arg_property, optarg);
1113 if (!l)
1114 return -ENOMEM;
1115
1116 strv_free(arg_property);
1117 arg_property = l;
1118
1119 /* If the user asked for a particular
1120 * property, show it to him, even if it is
1121 * empty. */
1122 arg_all = true;
1123 break;
1124 }
1125
1126 case 'a':
1127 arg_all = true;
1128 break;
1129
422fa650
ZJS
1130 case 'l':
1131 arg_full = true;
1132 break;
1133
abca4822
LP
1134 case ARG_NO_PAGER:
1135 arg_no_pager = true;
1136 break;
1137
6bb92a16
LP
1138 case ARG_NO_ASK_PASSWORD:
1139 arg_ask_password = false;
5d5e98eb 1140 break;
6bb92a16 1141
a4c279f8
LP
1142 case ARG_KILL_WHO:
1143 arg_kill_who = optarg;
1144 break;
1145
1146 case 's':
1147 arg_signal = signal_from_string_try_harder(optarg);
1148 if (arg_signal < 0) {
1149 log_error("Failed to parse signal string %s.", optarg);
1150 return -EINVAL;
1151 }
1152 break;
1153
f8f14b36
SP
1154 case 'H':
1155 arg_transport = BUS_TRANSPORT_REMOTE;
1156 arg_host = optarg;
abca4822
LP
1157 break;
1158
f8f14b36
SP
1159 case 'M':
1160 arg_transport = BUS_TRANSPORT_CONTAINER;
1161 arg_host = optarg;
abca4822
LP
1162 break;
1163
1164 case '?':
1165 return -EINVAL;
1166
1167 default:
eb9da376 1168 assert_not_reached("Unhandled option");
abca4822
LP
1169 }
1170 }
1171
1172 return 1;
1173}
1174
f8f14b36 1175static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
abca4822
LP
1176
1177 static const struct {
1178 const char* verb;
1179 const enum {
1180 MORE,
1181 LESS,
1182 EQUAL
1183 } argc_cmp;
1184 const int argc;
f8f14b36 1185 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
abca4822 1186 } verbs[] = {
9444b1f2 1187 { "list-sessions", LESS, 1, list_sessions },
f8f14b36
SP
1188 { "session-status", MORE, 2, show_session },
1189 { "show-session", MORE, 1, show_session },
9444b1f2
LP
1190 { "activate", EQUAL, 2, activate },
1191 { "lock-session", MORE, 2, activate },
1192 { "unlock-session", MORE, 2, activate },
1193 { "lock-sessions", EQUAL, 1, lock_sessions },
1194 { "unlock-sessions", EQUAL, 1, lock_sessions },
1195 { "terminate-session", MORE, 2, activate },
1196 { "kill-session", MORE, 2, kill_session },
1197 { "list-users", EQUAL, 1, list_users },
f8f14b36
SP
1198 { "user-status", MORE, 2, show_user },
1199 { "show-user", MORE, 1, show_user },
9444b1f2
LP
1200 { "enable-linger", MORE, 2, enable_linger },
1201 { "disable-linger", MORE, 2, enable_linger },
1202 { "terminate-user", MORE, 2, terminate_user },
1203 { "kill-user", MORE, 2, kill_user },
1204 { "list-seats", EQUAL, 1, list_seats },
f8f14b36
SP
1205 { "seat-status", MORE, 2, show_seat },
1206 { "show-seat", MORE, 1, show_seat },
9444b1f2
LP
1207 { "attach", MORE, 3, attach },
1208 { "flush-devices", EQUAL, 1, flush_devices },
1209 { "terminate-seat", MORE, 2, terminate_seat },
abca4822
LP
1210 };
1211
1212 int left;
1213 unsigned i;
1214
1215 assert(argc >= 0);
1216 assert(argv);
abca4822
LP
1217
1218 left = argc - optind;
1219
1220 if (left <= 0)
1221 /* Special rule: no arguments means "list-sessions" */
1222 i = 0;
1223 else {
1224 if (streq(argv[optind], "help")) {
1225 help();
1226 return 0;
1227 }
1228
1229 for (i = 0; i < ELEMENTSOF(verbs); i++)
1230 if (streq(argv[optind], verbs[i].verb))
1231 break;
1232
1233 if (i >= ELEMENTSOF(verbs)) {
1234 log_error("Unknown operation %s", argv[optind]);
1235 return -EINVAL;
1236 }
1237 }
1238
1239 switch (verbs[i].argc_cmp) {
1240
1241 case EQUAL:
1242 if (left != verbs[i].argc) {
1243 log_error("Invalid number of arguments.");
1244 return -EINVAL;
1245 }
1246
1247 break;
1248
1249 case MORE:
1250 if (left < verbs[i].argc) {
1251 log_error("Too few arguments.");
1252 return -EINVAL;
1253 }
1254
1255 break;
1256
1257 case LESS:
1258 if (left > verbs[i].argc) {
1259 log_error("Too many arguments.");
1260 return -EINVAL;
1261 }
1262
1263 break;
1264
1265 default:
1266 assert_not_reached("Unknown comparison operator.");
1267 }
1268
abca4822
LP
1269 return verbs[i].dispatch(bus, argv + optind, left);
1270}
1271
f8f14b36
SP
1272int main(int argc, char *argv[]) {
1273 _cleanup_bus_unref_ sd_bus *bus = NULL;
1274 int r;
abca4822 1275
a9cdc94f 1276 setlocale(LC_ALL, "");
abca4822
LP
1277 log_parse_environment();
1278 log_open();
1279
1280 r = parse_argv(argc, argv);
f8f14b36 1281 if (r <= 0)
abca4822 1282 goto finish;
f8f14b36
SP
1283
1284 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1285 if (r < 0) {
1286 log_error("Failed to create bus connection: %s", strerror(-r));
abca4822
LP
1287 goto finish;
1288 }
1289
f8f14b36 1290 r = loginctl_main(bus, argc, argv);
abca4822
LP
1291
1292finish:
f8f14b36 1293 pager_close();
abca4822 1294
a4c279f8
LP
1295 strv_free(arg_property);
1296
f8f14b36 1297 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
abca4822 1298}