]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[thirdparty/systemd.git] / src / login / loginctl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <getopt.h>
5 #include <locale.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #include "sd-bus.h"
10
11 #include "alloc-util.h"
12 #include "bus-error.h"
13 #include "bus-unit-util.h"
14 #include "bus-util.h"
15 #include "cgroup-show.h"
16 #include "cgroup-util.h"
17 #include "format-table.h"
18 #include "log.h"
19 #include "logs-show.h"
20 #include "macro.h"
21 #include "pager.h"
22 #include "parse-util.h"
23 #include "process-util.h"
24 #include "rlimit-util.h"
25 #include "sigbus.h"
26 #include "signal-util.h"
27 #include "spawn-polkit-agent.h"
28 #include "string-table.h"
29 #include "strv.h"
30 #include "sysfs-show.h"
31 #include "terminal-util.h"
32 #include "unit-name.h"
33 #include "user-util.h"
34 #include "util.h"
35 #include "verbs.h"
36
37 static char **arg_property = NULL;
38 static bool arg_all = false;
39 static bool arg_value = false;
40 static bool arg_full = false;
41 static PagerFlags arg_pager_flags = 0;
42 static bool arg_legend = true;
43 static const char *arg_kill_who = NULL;
44 static int arg_signal = SIGTERM;
45 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
46 static char *arg_host = NULL;
47 static bool arg_ask_password = true;
48 static unsigned arg_lines = 10;
49 static OutputMode arg_output = OUTPUT_SHORT;
50
51 static OutputFlags get_output_flags(void) {
52
53 return
54 arg_all * OUTPUT_SHOW_ALL |
55 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
56 colors_enabled() * OUTPUT_COLOR;
57 }
58
59 static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *error, char **path) {
60 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
61 int r;
62 char *ans;
63
64 r = sd_bus_call_method(
65 bus,
66 "org.freedesktop.login1",
67 "/org/freedesktop/login1",
68 "org.freedesktop.login1.Manager",
69 "GetSession",
70 error, &reply,
71 "s", session_id);
72 if (r < 0)
73 return r;
74
75 r = sd_bus_message_read(reply, "o", &ans);
76 if (r < 0)
77 return r;
78
79 ans = strdup(ans);
80 if (!ans)
81 return -ENOMEM;
82
83 *path = ans;
84 return 0;
85 }
86
87 static int show_table(Table *table, const char *word) {
88 int r;
89
90 assert(table);
91 assert(word);
92
93 if (table_get_rows(table) > 1) {
94 r = table_set_sort(table, (size_t) 0, (size_t) -1);
95 if (r < 0)
96 return log_error_errno(r, "Failed to sort table: %m");
97
98 table_set_header(table, arg_legend);
99
100 r = table_print(table, NULL);
101 if (r < 0)
102 return log_error_errno(r, "Failed to show table: %m");
103 }
104
105 if (arg_legend) {
106 if (table_get_rows(table) > 1)
107 printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
108 else
109 printf("No %s.\n", word);
110 }
111
112 return 0;
113 }
114
115 static int list_sessions(int argc, char *argv[], void *userdata) {
116 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
117 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
118 _cleanup_(table_unrefp) Table *table = NULL;
119 sd_bus *bus = userdata;
120 int r;
121
122 assert(bus);
123 assert(argv);
124
125 (void) pager_open(arg_pager_flags);
126
127 r = sd_bus_call_method(
128 bus,
129 "org.freedesktop.login1",
130 "/org/freedesktop/login1",
131 "org.freedesktop.login1.Manager",
132 "ListSessions",
133 &error, &reply,
134 NULL);
135 if (r < 0)
136 return log_error_errno(r, "Failed to list sessions: %s", bus_error_message(&error, r));
137
138 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
139 if (r < 0)
140 return bus_log_parse_error(r);
141
142 table = table_new("SESSION", "UID", "USER", "SEAT", "TTY");
143 if (!table)
144 return log_oom();
145
146 /* Right-align the first two fields (since they are numeric) */
147 (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
148 (void) table_set_align_percent(table, TABLE_HEADER_CELL(1), 100);
149
150 for (;;) {
151 _cleanup_(sd_bus_error_free) sd_bus_error error_tty = SD_BUS_ERROR_NULL;
152 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_tty = NULL;
153 const char *id, *user, *seat, *object, *tty = NULL;
154 uint32_t uid;
155
156 r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object);
157 if (r < 0)
158 return bus_log_parse_error(r);
159 if (r == 0)
160 break;
161
162 r = sd_bus_get_property(
163 bus,
164 "org.freedesktop.login1",
165 object,
166 "org.freedesktop.login1.Session",
167 "TTY",
168 &error_tty,
169 &reply_tty,
170 "s");
171 if (r < 0)
172 log_warning_errno(r, "Failed to get TTY for session %s: %s", id, bus_error_message(&error_tty, r));
173 else {
174 r = sd_bus_message_read(reply_tty, "s", &tty);
175 if (r < 0)
176 return bus_log_parse_error(r);
177 }
178
179 r = table_add_many(table,
180 TABLE_STRING, id,
181 TABLE_UINT32, uid,
182 TABLE_STRING, user,
183 TABLE_STRING, seat,
184 TABLE_STRING, strna(tty));
185 if (r < 0)
186 return log_error_errno(r, "Failed to add row to table: %m");
187 }
188
189 r = sd_bus_message_exit_container(reply);
190 if (r < 0)
191 return bus_log_parse_error(r);
192
193 return show_table(table, "sessions");
194 }
195
196 static int list_users(int argc, char *argv[], void *userdata) {
197 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
198 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
199 _cleanup_(table_unrefp) Table *table = NULL;
200 sd_bus *bus = userdata;
201 int r;
202
203 assert(bus);
204 assert(argv);
205
206 (void) pager_open(arg_pager_flags);
207
208 r = sd_bus_call_method(
209 bus,
210 "org.freedesktop.login1",
211 "/org/freedesktop/login1",
212 "org.freedesktop.login1.Manager",
213 "ListUsers",
214 &error, &reply,
215 NULL);
216 if (r < 0)
217 return log_error_errno(r, "Failed to list users: %s", bus_error_message(&error, r));
218
219 r = sd_bus_message_enter_container(reply, 'a', "(uso)");
220 if (r < 0)
221 return bus_log_parse_error(r);
222
223 table = table_new("UID", "USER");
224 if (!table)
225 return log_oom();
226
227 (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
228
229 for (;;) {
230 const char *user;
231 uint32_t uid;
232
233 r = sd_bus_message_read(reply, "(uso)", &uid, &user, NULL);
234 if (r < 0)
235 return bus_log_parse_error(r);
236 if (r == 0)
237 break;
238
239 r = table_add_many(table,
240 TABLE_UINT32, uid,
241 TABLE_STRING, user);
242 if (r < 0)
243 return log_error_errno(r, "Failed to add row to table: %m");
244 }
245
246 r = sd_bus_message_exit_container(reply);
247 if (r < 0)
248 return bus_log_parse_error(r);
249
250 return show_table(table, "users");
251 }
252
253 static int list_seats(int argc, char *argv[], void *userdata) {
254 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
255 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
256 _cleanup_(table_unrefp) Table *table = NULL;
257 sd_bus *bus = userdata;
258 int r;
259
260 assert(bus);
261 assert(argv);
262
263 (void) pager_open(arg_pager_flags);
264
265 r = sd_bus_call_method(
266 bus,
267 "org.freedesktop.login1",
268 "/org/freedesktop/login1",
269 "org.freedesktop.login1.Manager",
270 "ListSeats",
271 &error, &reply,
272 NULL);
273 if (r < 0)
274 return log_error_errno(r, "Failed to list seats: %s", bus_error_message(&error, r));
275
276 r = sd_bus_message_enter_container(reply, 'a', "(so)");
277 if (r < 0)
278 return bus_log_parse_error(r);
279
280 table = table_new("SEAT");
281 if (!table)
282 return log_oom();
283
284 for (;;) {
285 const char *seat;
286
287 r = sd_bus_message_read(reply, "(so)", &seat, NULL);
288 if (r < 0)
289 return bus_log_parse_error(r);
290 if (r == 0)
291 break;
292
293 r = table_add_cell(table, NULL, TABLE_STRING, seat);
294 if (r < 0)
295 return log_error_errno(r, "Failed to add row to table: %m");
296 }
297
298 r = sd_bus_message_exit_container(reply);
299 if (r < 0)
300 return bus_log_parse_error(r);
301
302 return show_table(table, "seats");
303 }
304
305 static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
306 _cleanup_free_ char *cgroup = NULL;
307 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
308 unsigned c;
309 int r;
310
311 assert(bus);
312 assert(unit);
313
314 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
315 if (r < 0)
316 return r;
317
318 if (isempty(cgroup))
319 return 0;
320
321 c = columns();
322 if (c > 18)
323 c -= 18;
324 else
325 c = 0;
326
327 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
328 if (r == -EBADR) {
329
330 if (arg_transport == BUS_TRANSPORT_REMOTE)
331 return 0;
332
333 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
334
335 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
336 return 0;
337
338 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
339 } else if (r < 0)
340 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
341
342 return 0;
343 }
344
345 typedef struct SessionStatusInfo {
346 const char *id;
347 uid_t uid;
348 const char *name;
349 struct dual_timestamp timestamp;
350 unsigned vtnr;
351 const char *seat;
352 const char *tty;
353 const char *display;
354 bool remote;
355 const char *remote_host;
356 const char *remote_user;
357 const char *service;
358 pid_t leader;
359 const char *type;
360 const char *class;
361 const char *state;
362 const char *scope;
363 const char *desktop;
364 } SessionStatusInfo;
365
366 typedef struct UserStatusInfo {
367 uid_t uid;
368 bool linger;
369 const char *name;
370 struct dual_timestamp timestamp;
371 const char *state;
372 char **sessions;
373 const char *display;
374 const char *slice;
375 } UserStatusInfo;
376
377 typedef struct SeatStatusInfo {
378 const char *id;
379 const char *active_session;
380 char **sessions;
381 } SeatStatusInfo;
382
383 static void user_status_info_clear(UserStatusInfo *info) {
384 if (info) {
385 strv_free(info->sessions);
386 zero(*info);
387 }
388 }
389
390 static void seat_status_info_clear(SeatStatusInfo *info) {
391 if (info) {
392 strv_free(info->sessions);
393 zero(*info);
394 }
395 }
396
397 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
398 const char *contents;
399 int r;
400
401 r = sd_bus_message_peek_type(m, NULL, &contents);
402 if (r < 0)
403 return r;
404
405 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
406 if (r < 0)
407 return r;
408
409 r = sd_bus_message_read_basic(m, contents[0], userdata);
410 if (r < 0)
411 return r;
412
413 r = sd_bus_message_skip(m, contents+1);
414 if (r < 0)
415 return r;
416
417 r = sd_bus_message_exit_container(m);
418 if (r < 0)
419 return r;
420
421 return 0;
422 }
423
424 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
425 const char *name;
426 int r;
427
428 assert(bus);
429 assert(m);
430
431 r = sd_bus_message_enter_container(m, 'a', "(so)");
432 if (r < 0)
433 return r;
434
435 while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
436 r = strv_extend(userdata, name);
437 if (r < 0)
438 return r;
439 }
440 if (r < 0)
441 return r;
442
443 return sd_bus_message_exit_container(m);
444 }
445
446 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
447
448 static const struct bus_properties_map map[] = {
449 { "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
450 { "Name", "s", NULL, offsetof(SessionStatusInfo, name) },
451 { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
452 { "Display", "s", NULL, offsetof(SessionStatusInfo, display) },
453 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
454 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
455 { "Service", "s", NULL, offsetof(SessionStatusInfo, service) },
456 { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) },
457 { "Type", "s", NULL, offsetof(SessionStatusInfo, type) },
458 { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
459 { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) },
460 { "State", "s", NULL, offsetof(SessionStatusInfo, state) },
461 { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) },
462 { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
463 { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) },
464 { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) },
465 { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) },
466 { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
467 { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
468 {}
469 };
470
471 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
472 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
473 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX];
474 char since2[FORMAT_TIMESTAMP_MAX];
475 const char *s1, *s2;
476 SessionStatusInfo i = {};
477 int r;
478
479 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
480 if (r < 0)
481 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
482
483 if (*new_line)
484 printf("\n");
485
486 *new_line = true;
487
488 printf("%s - ", strna(i.id));
489
490 if (i.name)
491 printf("%s (%"PRIu32")\n", i.name, i.uid);
492 else
493 printf("%"PRIu32"\n", i.uid);
494
495 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
496 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
497
498 if (s1)
499 printf("\t Since: %s; %s\n", s2, s1);
500 else if (s2)
501 printf("\t Since: %s\n", s2);
502
503 if (i.leader > 0) {
504 _cleanup_free_ char *t = NULL;
505
506 printf("\t Leader: %"PRIu32, i.leader);
507
508 get_process_comm(i.leader, &t);
509 if (t)
510 printf(" (%s)", t);
511
512 printf("\n");
513 }
514
515 if (!isempty(i.seat)) {
516 printf("\t Seat: %s", i.seat);
517
518 if (i.vtnr > 0)
519 printf("; vc%u", i.vtnr);
520
521 printf("\n");
522 }
523
524 if (i.tty)
525 printf("\t TTY: %s\n", i.tty);
526 else if (i.display)
527 printf("\t Display: %s\n", i.display);
528
529 if (i.remote_host && i.remote_user)
530 printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host);
531 else if (i.remote_host)
532 printf("\t Remote: %s\n", i.remote_host);
533 else if (i.remote_user)
534 printf("\t Remote: user %s\n", i.remote_user);
535 else if (i.remote)
536 printf("\t Remote: Yes\n");
537
538 if (i.service) {
539 printf("\t Service: %s", i.service);
540
541 if (i.type)
542 printf("; type %s", i.type);
543
544 if (i.class)
545 printf("; class %s", i.class);
546
547 printf("\n");
548 } else if (i.type) {
549 printf("\t Type: %s", i.type);
550
551 if (i.class)
552 printf("; class %s", i.class);
553
554 printf("\n");
555 } else if (i.class)
556 printf("\t Class: %s\n", i.class);
557
558 if (!isempty(i.desktop))
559 printf("\t Desktop: %s\n", i.desktop);
560
561 if (i.state)
562 printf("\t State: %s\n", i.state);
563
564 if (i.scope) {
565 printf("\t Unit: %s\n", i.scope);
566 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
567
568 if (arg_transport == BUS_TRANSPORT_LOCAL) {
569
570 show_journal_by_unit(
571 stdout,
572 i.scope,
573 arg_output,
574 0,
575 i.timestamp.monotonic,
576 arg_lines,
577 0,
578 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
579 SD_JOURNAL_LOCAL_ONLY,
580 true,
581 NULL);
582 }
583 }
584
585 return 0;
586 }
587
588 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
589
590 static const struct bus_properties_map map[] = {
591 { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
592 { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) },
593 { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
594 { "State", "s", NULL, offsetof(UserStatusInfo, state) },
595 { "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
596 { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) },
597 { "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) },
598 { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
599 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
600 {}
601 };
602
603 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
604 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
605 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX];
606 char since2[FORMAT_TIMESTAMP_MAX];
607 const char *s1, *s2;
608 _cleanup_(user_status_info_clear) UserStatusInfo i = {};
609 int r;
610
611 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
612 if (r < 0)
613 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
614
615 if (*new_line)
616 printf("\n");
617
618 *new_line = true;
619
620 if (i.name)
621 printf("%s (%"PRIu32")\n", i.name, i.uid);
622 else
623 printf("%"PRIu32"\n", i.uid);
624
625 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
626 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
627
628 if (s1)
629 printf("\t Since: %s; %s\n", s2, s1);
630 else if (s2)
631 printf("\t Since: %s\n", s2);
632
633 if (!isempty(i.state))
634 printf("\t State: %s\n", i.state);
635
636 if (!strv_isempty(i.sessions)) {
637 char **l;
638 printf("\tSessions:");
639
640 STRV_FOREACH(l, i.sessions)
641 printf(" %s%s",
642 streq_ptr(*l, i.display) ? "*" : "",
643 *l);
644
645 printf("\n");
646 }
647
648 printf("\t Linger: %s\n", yes_no(i.linger));
649
650 if (i.slice) {
651 printf("\t Unit: %s\n", i.slice);
652 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
653
654 show_journal_by_unit(
655 stdout,
656 i.slice,
657 arg_output,
658 0,
659 i.timestamp.monotonic,
660 arg_lines,
661 0,
662 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
663 SD_JOURNAL_LOCAL_ONLY,
664 true,
665 NULL);
666 }
667
668 return 0;
669 }
670
671 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
672
673 static const struct bus_properties_map map[] = {
674 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
675 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
676 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
677 {}
678 };
679
680 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
681 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
682 _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
683 int r;
684
685 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, 0, &error, &m, &i);
686 if (r < 0)
687 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
688
689 if (*new_line)
690 printf("\n");
691
692 *new_line = true;
693
694 printf("%s\n", strna(i.id));
695
696 if (!strv_isempty(i.sessions)) {
697 char **l;
698 printf("\tSessions:");
699
700 STRV_FOREACH(l, i.sessions) {
701 if (streq_ptr(*l, i.active_session))
702 printf(" *%s", *l);
703 else
704 printf(" %s", *l);
705 }
706
707 printf("\n");
708 }
709
710 if (arg_transport == BUS_TRANSPORT_LOCAL) {
711 unsigned c;
712
713 c = columns();
714 if (c > 21)
715 c -= 21;
716 else
717 c = 0;
718
719 printf("\t Devices:\n");
720
721 show_sysfs(i.id, "\t\t ", c, get_output_flags());
722 }
723
724 return 0;
725 }
726
727 static int print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
728 char type;
729 const char *contents;
730 int r;
731
732 assert(name);
733 assert(m);
734
735 r = sd_bus_message_peek_type(m, &type, &contents);
736 if (r < 0)
737 return r;
738
739 switch (type) {
740
741 case SD_BUS_TYPE_STRUCT:
742
743 if (contents[0] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
744 const char *s;
745
746 r = sd_bus_message_read(m, "(so)", &s, NULL);
747 if (r < 0)
748 return bus_log_parse_error(r);
749
750 if (all || !isempty(s))
751 bus_print_property_value(name, expected_value, value, "%s", s);
752
753 return 1;
754
755 } else if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
756 uint32_t uid;
757
758 r = sd_bus_message_read(m, "(uo)", &uid, NULL);
759 if (r < 0)
760 return bus_log_parse_error(r);
761
762 if (!uid_is_valid(uid)) {
763 log_error("Invalid user ID: " UID_FMT, uid);
764 return -EINVAL;
765 }
766
767 bus_print_property_value(name, expected_value, value, UID_FMT, uid);
768 return 1;
769 }
770 break;
771
772 case SD_BUS_TYPE_ARRAY:
773
774 if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
775 const char *s;
776 bool space = false;
777
778 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
779 if (r < 0)
780 return bus_log_parse_error(r);
781
782 if (!value)
783 printf("%s=", name);
784
785 while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
786 printf("%s%s", space ? " " : "", s);
787 space = true;
788 }
789
790 if (space || !value)
791 printf("\n");
792
793 if (r < 0)
794 return bus_log_parse_error(r);
795
796 r = sd_bus_message_exit_container(m);
797 if (r < 0)
798 return bus_log_parse_error(r);
799
800 return 1;
801 }
802 break;
803 }
804
805 return 0;
806 }
807
808 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
809 int r;
810
811 assert(bus);
812 assert(path);
813 assert(new_line);
814
815 if (*new_line)
816 printf("\n");
817
818 *new_line = true;
819
820 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, print_property, arg_property, arg_value, arg_all, NULL);
821 if (r < 0)
822 return bus_log_parse_error(r);
823
824 return 0;
825 }
826
827 static int show_session(int argc, char *argv[], void *userdata) {
828 bool properties, new_line = false;
829 sd_bus *bus = userdata;
830 int r, i;
831 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
832 _cleanup_free_ char *path = NULL;
833
834 assert(bus);
835 assert(argv);
836
837 properties = !strstr(argv[0], "status");
838
839 (void) pager_open(arg_pager_flags);
840
841 if (argc <= 1) {
842 const char *session, *p = "/org/freedesktop/login1/session/self";
843
844 if (properties)
845 /* If no argument is specified inspect the manager itself */
846 return show_properties(bus, "/org/freedesktop/login1", &new_line);
847
848 /* And in the pretty case, show data of the calling session */
849 session = getenv("XDG_SESSION_ID");
850 if (session) {
851 r = get_session_path(bus, session, &error, &path);
852 if (r < 0)
853 return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
854
855 p = path;
856 }
857
858 return print_session_status_info(bus, p, &new_line);
859 }
860
861 for (i = 1; i < argc; i++) {
862 r = get_session_path(bus, argv[i], &error, &path);
863 if (r < 0)
864 return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
865
866 if (properties)
867 r = show_properties(bus, path, &new_line);
868 else
869 r = print_session_status_info(bus, path, &new_line);
870
871 if (r < 0)
872 return r;
873 }
874
875 return 0;
876 }
877
878 static int show_user(int argc, char *argv[], void *userdata) {
879 bool properties, new_line = false;
880 sd_bus *bus = userdata;
881 int r, i;
882
883 assert(bus);
884 assert(argv);
885
886 properties = !strstr(argv[0], "status");
887
888 (void) pager_open(arg_pager_flags);
889
890 if (argc <= 1) {
891 /* If not argument is specified inspect the manager
892 * itself */
893 if (properties)
894 return show_properties(bus, "/org/freedesktop/login1", &new_line);
895
896 return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
897 }
898
899 for (i = 1; i < argc; i++) {
900 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
901 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
902 const char *path = NULL;
903 uid_t uid;
904
905 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
906 if (r < 0)
907 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
908
909 r = sd_bus_call_method(
910 bus,
911 "org.freedesktop.login1",
912 "/org/freedesktop/login1",
913 "org.freedesktop.login1.Manager",
914 "GetUser",
915 &error, &reply,
916 "u", (uint32_t) uid);
917 if (r < 0)
918 return log_error_errno(r, "Failed to get user: %s", bus_error_message(&error, r));
919
920 r = sd_bus_message_read(reply, "o", &path);
921 if (r < 0)
922 return bus_log_parse_error(r);
923
924 if (properties)
925 r = show_properties(bus, path, &new_line);
926 else
927 r = print_user_status_info(bus, path, &new_line);
928
929 if (r < 0)
930 return r;
931 }
932
933 return 0;
934 }
935
936 static int show_seat(int argc, char *argv[], void *userdata) {
937 bool properties, new_line = false;
938 sd_bus *bus = userdata;
939 int r, i;
940
941 assert(bus);
942 assert(argv);
943
944 properties = !strstr(argv[0], "status");
945
946 (void) pager_open(arg_pager_flags);
947
948 if (argc <= 1) {
949 /* If not argument is specified inspect the manager
950 * itself */
951 if (properties)
952 return show_properties(bus, "/org/freedesktop/login1", &new_line);
953
954 return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line);
955 }
956
957 for (i = 1; i < argc; i++) {
958 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
959 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
960 const char *path = NULL;
961
962 r = sd_bus_call_method(
963 bus,
964 "org.freedesktop.login1",
965 "/org/freedesktop/login1",
966 "org.freedesktop.login1.Manager",
967 "GetSeat",
968 &error, &reply,
969 "s", argv[i]);
970 if (r < 0)
971 return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r));
972
973 r = sd_bus_message_read(reply, "o", &path);
974 if (r < 0)
975 return bus_log_parse_error(r);
976
977 if (properties)
978 r = show_properties(bus, path, &new_line);
979 else
980 r = print_seat_status_info(bus, path, &new_line);
981
982 if (r < 0)
983 return r;
984 }
985
986 return 0;
987 }
988
989 static int activate(int argc, char *argv[], void *userdata) {
990 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
991 sd_bus *bus = userdata;
992 char *short_argv[3];
993 int r, i;
994
995 assert(bus);
996 assert(argv);
997
998 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
999
1000 if (argc < 2) {
1001 /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty
1002 * session name, in which case logind will try to guess our session. */
1003
1004 short_argv[0] = argv[0];
1005 short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) "";
1006 short_argv[2] = NULL;
1007
1008 argv = short_argv;
1009 argc = 2;
1010 }
1011
1012 for (i = 1; i < argc; i++) {
1013
1014 r = sd_bus_call_method(
1015 bus,
1016 "org.freedesktop.login1",
1017 "/org/freedesktop/login1",
1018 "org.freedesktop.login1.Manager",
1019 streq(argv[0], "lock-session") ? "LockSession" :
1020 streq(argv[0], "unlock-session") ? "UnlockSession" :
1021 streq(argv[0], "terminate-session") ? "TerminateSession" :
1022 "ActivateSession",
1023 &error, NULL,
1024 "s", argv[i]);
1025 if (r < 0)
1026 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r));
1027 }
1028
1029 return 0;
1030 }
1031
1032 static int kill_session(int argc, char *argv[], void *userdata) {
1033 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1034 sd_bus *bus = userdata;
1035 int r, i;
1036
1037 assert(bus);
1038 assert(argv);
1039
1040 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1041
1042 if (!arg_kill_who)
1043 arg_kill_who = "all";
1044
1045 for (i = 1; i < argc; i++) {
1046
1047 r = sd_bus_call_method(
1048 bus,
1049 "org.freedesktop.login1",
1050 "/org/freedesktop/login1",
1051 "org.freedesktop.login1.Manager",
1052 "KillSession",
1053 &error, NULL,
1054 "ssi", argv[i], arg_kill_who, arg_signal);
1055 if (r < 0)
1056 return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, -r));
1057 }
1058
1059 return 0;
1060 }
1061
1062 static int enable_linger(int argc, char *argv[], void *userdata) {
1063 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1064 sd_bus *bus = userdata;
1065 char* short_argv[3];
1066 bool b;
1067 int r, i;
1068
1069 assert(bus);
1070 assert(argv);
1071
1072 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1073
1074 b = streq(argv[0], "enable-linger");
1075
1076 if (argc < 2) {
1077 /* No argument? Let's use an empty user name,
1078 * then logind will use our user. */
1079
1080 short_argv[0] = argv[0];
1081 short_argv[1] = (char*) "";
1082 short_argv[2] = NULL;
1083 argv = short_argv;
1084 argc = 2;
1085 }
1086
1087 for (i = 1; i < argc; i++) {
1088 uid_t uid;
1089
1090 if (isempty(argv[i]))
1091 uid = UID_INVALID;
1092 else {
1093 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
1094 if (r < 0)
1095 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1096 }
1097
1098 r = sd_bus_call_method(
1099 bus,
1100 "org.freedesktop.login1",
1101 "/org/freedesktop/login1",
1102 "org.freedesktop.login1.Manager",
1103 "SetUserLinger",
1104 &error, NULL,
1105 "ubb", (uint32_t) uid, b, true);
1106 if (r < 0)
1107 return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, -r));
1108 }
1109
1110 return 0;
1111 }
1112
1113 static int terminate_user(int argc, char *argv[], void *userdata) {
1114 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1115 sd_bus *bus = userdata;
1116 int r, i;
1117
1118 assert(bus);
1119 assert(argv);
1120
1121 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1122
1123 for (i = 1; i < argc; i++) {
1124 uid_t uid;
1125
1126 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
1127 if (r < 0)
1128 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1129
1130 r = sd_bus_call_method(
1131 bus,
1132 "org.freedesktop.login1",
1133 "/org/freedesktop/login1",
1134 "org.freedesktop.login1.Manager",
1135 "TerminateUser",
1136 &error, NULL,
1137 "u", (uint32_t) uid);
1138 if (r < 0)
1139 return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, -r));
1140 }
1141
1142 return 0;
1143 }
1144
1145 static int kill_user(int argc, char *argv[], void *userdata) {
1146 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1147 sd_bus *bus = userdata;
1148 int r, i;
1149
1150 assert(bus);
1151 assert(argv);
1152
1153 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1154
1155 if (!arg_kill_who)
1156 arg_kill_who = "all";
1157
1158 for (i = 1; i < argc; i++) {
1159 uid_t uid;
1160
1161 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
1162 if (r < 0)
1163 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1164
1165 r = sd_bus_call_method(
1166 bus,
1167 "org.freedesktop.login1",
1168 "/org/freedesktop/login1",
1169 "org.freedesktop.login1.Manager",
1170 "KillUser",
1171 &error, NULL,
1172 "ui", (uint32_t) uid, arg_signal);
1173 if (r < 0)
1174 return log_error_errno(r, "Could not kill user: %s", bus_error_message(&error, -r));
1175 }
1176
1177 return 0;
1178 }
1179
1180 static int attach(int argc, char *argv[], void *userdata) {
1181 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1182 sd_bus *bus = userdata;
1183 int r, i;
1184
1185 assert(bus);
1186 assert(argv);
1187
1188 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1189
1190 for (i = 2; i < argc; i++) {
1191
1192 r = sd_bus_call_method(
1193 bus,
1194 "org.freedesktop.login1",
1195 "/org/freedesktop/login1",
1196 "org.freedesktop.login1.Manager",
1197 "AttachDevice",
1198 &error, NULL,
1199 "ssb", argv[1], argv[i], true);
1200
1201 if (r < 0)
1202 return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, -r));
1203 }
1204
1205 return 0;
1206 }
1207
1208 static int flush_devices(int argc, char *argv[], void *userdata) {
1209 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1210 sd_bus *bus = userdata;
1211 int r;
1212
1213 assert(bus);
1214 assert(argv);
1215
1216 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1217
1218 r = sd_bus_call_method(
1219 bus,
1220 "org.freedesktop.login1",
1221 "/org/freedesktop/login1",
1222 "org.freedesktop.login1.Manager",
1223 "FlushDevices",
1224 &error, NULL,
1225 "b", true);
1226 if (r < 0)
1227 return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, -r));
1228
1229 return 0;
1230 }
1231
1232 static int lock_sessions(int argc, char *argv[], void *userdata) {
1233 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1234 sd_bus *bus = userdata;
1235 int r;
1236
1237 assert(bus);
1238 assert(argv);
1239
1240 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1241
1242 r = sd_bus_call_method(
1243 bus,
1244 "org.freedesktop.login1",
1245 "/org/freedesktop/login1",
1246 "org.freedesktop.login1.Manager",
1247 streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1248 &error, NULL,
1249 NULL);
1250 if (r < 0)
1251 return log_error_errno(r, "Could not lock sessions: %s", bus_error_message(&error, -r));
1252
1253 return 0;
1254 }
1255
1256 static int terminate_seat(int argc, char *argv[], void *userdata) {
1257 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1258 sd_bus *bus = userdata;
1259 int r, i;
1260
1261 assert(bus);
1262 assert(argv);
1263
1264 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1265
1266 for (i = 1; i < argc; i++) {
1267
1268 r = sd_bus_call_method(
1269 bus,
1270 "org.freedesktop.login1",
1271 "/org/freedesktop/login1",
1272 "org.freedesktop.login1.Manager",
1273 "TerminateSeat",
1274 &error, NULL,
1275 "s", argv[i]);
1276 if (r < 0)
1277 return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, -r));
1278 }
1279
1280 return 0;
1281 }
1282
1283 static int help(int argc, char *argv[], void *userdata) {
1284 _cleanup_free_ char *link = NULL;
1285 int r;
1286
1287 (void) pager_open(arg_pager_flags);
1288
1289 r = terminal_urlify_man("loginctl", "1", &link);
1290 if (r < 0)
1291 return log_oom();
1292
1293 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1294 "Send control commands to or query the login manager.\n\n"
1295 " -h --help Show this help\n"
1296 " --version Show package version\n"
1297 " --no-pager Do not pipe output into a pager\n"
1298 " --no-legend Do not show the headers and footers\n"
1299 " --no-ask-password Don't prompt for password\n"
1300 " -H --host=[USER@]HOST Operate on remote host\n"
1301 " -M --machine=CONTAINER Operate on local container\n"
1302 " -p --property=NAME Show only properties by this name\n"
1303 " -a --all Show all properties, including empty ones\n"
1304 " --value When showing properties, only print the value\n"
1305 " -l --full Do not ellipsize output\n"
1306 " --kill-who=WHO Who to send signal to\n"
1307 " -s --signal=SIGNAL Which signal to send\n"
1308 " -n --lines=INTEGER Number of journal entries to show\n"
1309 " -o --output=STRING Change journal output mode (short, short-precise,\n"
1310 " short-iso, short-iso-precise, short-full,\n"
1311 " short-monotonic, short-unix, verbose, export,\n"
1312 " json, json-pretty, json-sse, json-seq, cat,\n"
1313 " with-unit)\n"
1314 "Session Commands:\n"
1315 " list-sessions List sessions\n"
1316 " session-status [ID...] Show session status\n"
1317 " show-session [ID...] Show properties of sessions or the manager\n"
1318 " activate [ID] Activate a session\n"
1319 " lock-session [ID...] Screen lock one or more sessions\n"
1320 " unlock-session [ID...] Screen unlock one or more sessions\n"
1321 " lock-sessions Screen lock all current sessions\n"
1322 " unlock-sessions Screen unlock all current sessions\n"
1323 " terminate-session ID... Terminate one or more sessions\n"
1324 " kill-session ID... Send signal to processes of a session\n\n"
1325 "User Commands:\n"
1326 " list-users List users\n"
1327 " user-status [USER...] Show user status\n"
1328 " show-user [USER...] Show properties of users or the manager\n"
1329 " enable-linger [USER...] Enable linger state of one or more users\n"
1330 " disable-linger [USER...] Disable linger state of one or more users\n"
1331 " terminate-user USER... Terminate all sessions of one or more users\n"
1332 " kill-user USER... Send signal to processes of a user\n\n"
1333 "Seat Commands:\n"
1334 " list-seats List seats\n"
1335 " seat-status [NAME...] Show seat status\n"
1336 " show-seat [NAME...] Show properties of seats or the manager\n"
1337 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1338 " flush-devices Flush all device associations\n"
1339 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
1340 "\nSee the %s for details.\n"
1341 , program_invocation_short_name
1342 , link
1343 );
1344
1345 return 0;
1346 }
1347
1348 static int parse_argv(int argc, char *argv[]) {
1349
1350 enum {
1351 ARG_VERSION = 0x100,
1352 ARG_VALUE,
1353 ARG_NO_PAGER,
1354 ARG_NO_LEGEND,
1355 ARG_KILL_WHO,
1356 ARG_NO_ASK_PASSWORD,
1357 };
1358
1359 static const struct option options[] = {
1360 { "help", no_argument, NULL, 'h' },
1361 { "version", no_argument, NULL, ARG_VERSION },
1362 { "property", required_argument, NULL, 'p' },
1363 { "all", no_argument, NULL, 'a' },
1364 { "value", no_argument, NULL, ARG_VALUE },
1365 { "full", no_argument, NULL, 'l' },
1366 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1367 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1368 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1369 { "signal", required_argument, NULL, 's' },
1370 { "host", required_argument, NULL, 'H' },
1371 { "machine", required_argument, NULL, 'M' },
1372 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1373 { "lines", required_argument, NULL, 'n' },
1374 { "output", required_argument, NULL, 'o' },
1375 {}
1376 };
1377
1378 int c, r;
1379
1380 assert(argc >= 0);
1381 assert(argv);
1382
1383 while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
1384
1385 switch (c) {
1386
1387 case 'h':
1388 return help(0, NULL, NULL);
1389
1390 case ARG_VERSION:
1391 return version();
1392
1393 case 'p': {
1394 r = strv_extend(&arg_property, optarg);
1395 if (r < 0)
1396 return log_oom();
1397
1398 /* If the user asked for a particular
1399 * property, show it to him, even if it is
1400 * empty. */
1401 arg_all = true;
1402 break;
1403 }
1404
1405 case 'a':
1406 arg_all = true;
1407 break;
1408
1409 case ARG_VALUE:
1410 arg_value = true;
1411 break;
1412
1413 case 'l':
1414 arg_full = true;
1415 break;
1416
1417 case 'n':
1418 if (safe_atou(optarg, &arg_lines) < 0) {
1419 log_error("Failed to parse lines '%s'", optarg);
1420 return -EINVAL;
1421 }
1422 break;
1423
1424 case 'o':
1425 if (streq(optarg, "help")) {
1426 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
1427 return 0;
1428 }
1429
1430 arg_output = output_mode_from_string(optarg);
1431 if (arg_output < 0) {
1432 log_error("Unknown output '%s'.", optarg);
1433 return -EINVAL;
1434 }
1435 break;
1436
1437 case ARG_NO_PAGER:
1438 arg_pager_flags |= PAGER_DISABLE;
1439 break;
1440
1441 case ARG_NO_LEGEND:
1442 arg_legend = false;
1443 break;
1444
1445 case ARG_NO_ASK_PASSWORD:
1446 arg_ask_password = false;
1447 break;
1448
1449 case ARG_KILL_WHO:
1450 arg_kill_who = optarg;
1451 break;
1452
1453 case 's':
1454 if (streq(optarg, "help")) {
1455 DUMP_STRING_TABLE(signal, int, _NSIG);
1456 return 0;
1457 }
1458
1459 arg_signal = signal_from_string(optarg);
1460 if (arg_signal < 0) {
1461 log_error("Failed to parse signal string %s.", optarg);
1462 return -EINVAL;
1463 }
1464 break;
1465
1466 case 'H':
1467 arg_transport = BUS_TRANSPORT_REMOTE;
1468 arg_host = optarg;
1469 break;
1470
1471 case 'M':
1472 arg_transport = BUS_TRANSPORT_MACHINE;
1473 arg_host = optarg;
1474 break;
1475
1476 case '?':
1477 return -EINVAL;
1478
1479 default:
1480 assert_not_reached("Unhandled option");
1481 }
1482
1483 return 1;
1484 }
1485
1486 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1487
1488 static const Verb verbs[] = {
1489 { "help", VERB_ANY, VERB_ANY, 0, help },
1490 { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions },
1491 { "session-status", VERB_ANY, VERB_ANY, 0, show_session },
1492 { "show-session", VERB_ANY, VERB_ANY, 0, show_session },
1493 { "activate", VERB_ANY, 2, 0, activate },
1494 { "lock-session", VERB_ANY, VERB_ANY, 0, activate },
1495 { "unlock-session", VERB_ANY, VERB_ANY, 0, activate },
1496 { "lock-sessions", VERB_ANY, 1, 0, lock_sessions },
1497 { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions },
1498 { "terminate-session", 2, VERB_ANY, 0, activate },
1499 { "kill-session", 2, VERB_ANY, 0, kill_session },
1500 { "list-users", VERB_ANY, 1, 0, list_users },
1501 { "user-status", VERB_ANY, VERB_ANY, 0, show_user },
1502 { "show-user", VERB_ANY, VERB_ANY, 0, show_user },
1503 { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
1504 { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
1505 { "terminate-user", 2, VERB_ANY, 0, terminate_user },
1506 { "kill-user", 2, VERB_ANY, 0, kill_user },
1507 { "list-seats", VERB_ANY, 1, 0, list_seats },
1508 { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat },
1509 { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat },
1510 { "attach", 3, VERB_ANY, 0, attach },
1511 { "flush-devices", VERB_ANY, 1, 0, flush_devices },
1512 { "terminate-seat", 2, VERB_ANY, 0, terminate_seat },
1513 {}
1514 };
1515
1516 return dispatch_verb(argc, argv, verbs, bus);
1517 }
1518
1519 int main(int argc, char *argv[]) {
1520 sd_bus *bus = NULL;
1521 int r;
1522
1523 setlocale(LC_ALL, "");
1524 log_parse_environment();
1525 log_open();
1526
1527 /* The journal merging logic potentially needs a lot of fds. */
1528 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
1529
1530 sigbus_install();
1531
1532 r = parse_argv(argc, argv);
1533 if (r <= 0)
1534 goto finish;
1535
1536 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
1537 if (r < 0) {
1538 log_error_errno(r, "Failed to create bus connection: %m");
1539 goto finish;
1540 }
1541
1542 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1543
1544 r = loginctl_main(argc, argv, bus);
1545
1546 finish:
1547 /* make sure we terminate the bus connection first, and then close the
1548 * pager, see issue #3543 for the details. */
1549 sd_bus_flush_close_unref(bus);
1550 pager_close();
1551 polkit_agent_close();
1552
1553 strv_free(arg_property);
1554
1555 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1556 }