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