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