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