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