]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/loginctl.c
core/scope: drop effectively unused unit_watch_pidref() calls (#38186)
[thirdparty/systemd.git] / src / login / loginctl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
abca4822 2
abca4822 3#include <getopt.h>
a9cdc94f 4#include <locale.h>
3f6fd1ba 5#include <unistd.h>
abca4822 6
f8f14b36 7#include "sd-bus.h"
6ad1d1ed 8#include "sd-journal.h"
3f6fd1ba 9
b5efdb8a 10#include "alloc-util.h"
d6b4d1c7 11#include "build.h"
f8f14b36 12#include "bus-error.h"
9b71e4ab 13#include "bus-locator.h"
807542be 14#include "bus-map-properties.h"
9176326b 15#include "bus-print-properties.h"
25b1d72d 16#include "bus-unit-procs.h"
1e35e81b 17#include "bus-util.h"
3f6fd1ba
LP
18#include "cgroup-show.h"
19#include "cgroup-util.h"
99f1229d 20#include "format-table.h"
6ad1d1ed 21#include "format-util.h"
abca4822 22#include "log.h"
3f6fd1ba 23#include "logs-show.h"
eae5c847 24#include "main-func.h"
abca4822 25#include "pager.h"
86beb213 26#include "parse-argument.h"
6bedfcbb 27#include "parse-util.h"
1b919ca4 28#include "polkit-agent.h"
294bf0c3 29#include "pretty-print.h"
3f6fd1ba 30#include "process-util.h"
6ad1d1ed 31#include "runtime-scope.h"
5c828e66 32#include "string-table.h"
6ad1d1ed 33#include "string-util.h"
a4c279f8 34#include "strv.h"
a4c279f8 35#include "sysfs-show.h"
288a74cc 36#include "terminal-util.h"
6ad1d1ed 37#include "time-util.h"
b1d4f8e1 38#include "user-util.h"
3f6fd1ba 39#include "verbs.h"
abca4822 40
a4c279f8 41static char **arg_property = NULL;
255b1fc8 42static BusPrintPropertyFlags arg_print_flags = 0;
9bdbc2e2 43static bool arg_full = false;
0221d68a 44static PagerFlags arg_pager_flags = 0;
841aa8c0 45static bool arg_legend = true;
309a747f 46static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
4ccde410 47static const char *arg_kill_whom = NULL;
a4c279f8 48static int arg_signal = SIGTERM;
f8f14b36 49static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
a59cc386 50static const char *arg_host = NULL;
079dac08 51static bool arg_ask_password = true;
3c756001
LP
52static unsigned arg_lines = 10;
53static OutputMode arg_output = OUTPUT_SHORT;
abca4822 54
eae5c847
YW
55STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
56
5b7d1536
DT
57typedef struct SessionStatusInfo {
58 const char *id;
59 uid_t uid;
60 const char *name;
c9443393 61 dual_timestamp timestamp;
5b7d1536
DT
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;
556723e7 76 bool idle_hint;
e8d58f5c 77 dual_timestamp idle_hint_timestamp;
5b7d1536
DT
78} SessionStatusInfo;
79
c9443393
MY
80typedef 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;
3c756001 90
c9443393
MY
91typedef struct SeatStatusInfo {
92 const char *id;
93 const char *active_session;
94 char **sessions;
95} SeatStatusInfo;
96
97static void user_status_info_done(UserStatusInfo *info) {
98 assert(info);
99
100 strv_free(info->sessions);
101}
102
103static void seat_status_info_done(SeatStatusInfo *info) {
104 assert(info);
105
106 strv_free(info->sessions);
107}
108
109static OutputFlags get_output_flags(void) {
3c756001 110 return
255b1fc8 111 FLAGS_SET(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) * OUTPUT_SHOW_ALL |
459b9f9f 112 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
40c9fe4c 113 colors_enabled() * OUTPUT_COLOR;
3c756001
LP
114}
115
ea545174 116static int list_table_print(Table *table, const char *type) {
99f1229d
LP
117 int r;
118
119 assert(table);
ea545174 120 assert(type);
99f1229d 121
ea545174
MY
122 r = table_set_sort(table, (size_t) 0);
123 if (r < 0)
124 return table_log_sort_error(r);
99f1229d 125
ea545174
MY
126 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
127 if (r < 0)
128 return r;
99f1229d
LP
129
130 if (arg_legend) {
2413a0fa 131 if (table_isempty(table))
ea545174 132 printf("No %s.\n", type);
2413a0fa 133 else
ea545174 134 printf("\n%zu %s listed.\n", table_get_rows(table) - 1, type);
99f1229d
LP
135 }
136
137 return 0;
138}
139
4a3e43dd
MY
140static 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
199static int list_sessions_table_add_fallback(Table *table, sd_bus_message *reply, sd_bus *bus) {
5b7d1536 200
be88af3d 201 static const struct bus_properties_map map[] = {
4a3e43dd
MY
202 { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
203 { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
204 { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
e8d58f5c
MY
205 { "IdleHint", "b", NULL, offsetof(SessionStatusInfo, idle_hint) },
206 { "IdleSinceHintMonotonic", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.monotonic) },
5b7d1536
DT
207 {},
208 };
209
f8f14b36 210 int r;
abca4822 211
4a3e43dd
MY
212 assert(table);
213 assert(reply);
214 assert(bus);
abca4822 215
f8f14b36
SP
216 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
217 if (r < 0)
5b30bef8 218 return bus_log_parse_error(r);
abca4822 219
99f1229d 220 for (;;) {
86f12855 221 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
4a3e43dd 222 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
486f61a8 223 const char *id, *user, *seat, *object;
99f1229d 224 uint32_t uid;
5b7d1536 225 SessionStatusInfo i = {};
5611ddeb 226
99f1229d 227 r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object);
5611ddeb 228 if (r < 0)
99f1229d
LP
229 return bus_log_parse_error(r);
230 if (r == 0)
231 break;
232
5b7d1536 233 r = bus_map_all_properties(bus, "org.freedesktop.login1", object, map, BUS_MAP_BOOLEAN_AS_BOOL, &e, &m, &i);
8b6c039a 234 if (r < 0) {
556723e7
DT
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;
8b6c039a
MY
240 }
241
99f1229d
LP
242 r = table_add_many(table,
243 TABLE_STRING, id,
805f2df1 244 TABLE_UID, (uid_t) uid,
99f1229d 245 TABLE_STRING, user,
febfec08 246 TABLE_STRING, empty_to_null(seat),
4a3e43dd
MY
247 TABLE_PID, i.leader,
248 TABLE_STRING, i.class,
febfec08 249 TABLE_STRING, empty_to_null(i.tty),
556723e7
DT
250 TABLE_BOOLEAN, i.idle_hint);
251 if (r < 0)
252 return table_log_add_error(r);
253
254 if (i.idle_hint)
e8d58f5c 255 r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE_MONOTONIC, &i.idle_hint_timestamp.monotonic);
556723e7
DT
256 else
257 r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
99f1229d 258 if (r < 0)
bd17fa8c 259 return table_log_add_error(r);
abca4822 260 }
99f1229d
LP
261
262 r = sd_bus_message_exit_container(reply);
f8f14b36 263 if (r < 0)
5b30bef8 264 return bus_log_parse_error(r);
abca4822 265
4a3e43dd
MY
266 return 0;
267}
268
269static 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
4a3e43dd
MY
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
ea545174 308 return list_table_print(table, "sessions");
abca4822
LP
309}
310
f7621db0 311static int list_users(int argc, char *argv[], void *userdata) {
8b0da997
MY
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
4afd3348
LP
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;
99f1229d 321 _cleanup_(table_unrefp) Table *table = NULL;
99534007 322 sd_bus *bus = ASSERT_PTR(userdata);
f8f14b36 323 int r;
abca4822 324
f7621db0
LP
325 assert(argv);
326
5d990cc5 327 r = bus_call_method(bus, bus_login_mgr, "ListUsers", &error, &reply, NULL);
99f1229d
LP
328 if (r < 0)
329 return log_error_errno(r, "Failed to list users: %s", bus_error_message(&error, r));
abca4822 330
f8f14b36
SP
331 r = sd_bus_message_enter_container(reply, 'a', "(uso)");
332 if (r < 0)
5b30bef8 333 return bus_log_parse_error(r);
abca4822 334
bae05711 335 table = table_new("uid", "user", "linger", "state");
99f1229d
LP
336 if (!table)
337 return log_oom();
abca4822 338
99f1229d 339 (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
8bfa22f0 340 (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
99f1229d
LP
341
342 for (;;) {
bae05711 343 _cleanup_(sd_bus_error_free) sd_bus_error error_property = SD_BUS_ERROR_NULL;
8b0da997
MY
344 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_property = NULL;
345 _cleanup_(user_status_info_done) UserStatusInfo info = {};
c5912adc 346 const char *user, *object;
99f1229d
LP
347 uint32_t uid;
348
c5912adc 349 r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object);
99f1229d
LP
350 if (r < 0)
351 return bus_log_parse_error(r);
352 if (r == 0)
353 break;
354
8b0da997
MY
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);
bae05711 363 if (r < 0) {
8b0da997
MY
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;
bae05711 369 }
c5912adc 370
99f1229d 371 r = table_add_many(table,
805f2df1 372 TABLE_UID, (uid_t) uid,
c5912adc 373 TABLE_STRING, user,
8b0da997
MY
374 TABLE_BOOLEAN, info.linger,
375 TABLE_STRING, info.state);
99f1229d 376 if (r < 0)
bd17fa8c 377 return table_log_add_error(r);
abca4822 378 }
99f1229d
LP
379
380 r = sd_bus_message_exit_container(reply);
f8f14b36 381 if (r < 0)
5b30bef8 382 return bus_log_parse_error(r);
abca4822 383
ea545174 384 return list_table_print(table, "users");
abca4822
LP
385}
386
f7621db0 387static int list_seats(int argc, char *argv[], void *userdata) {
4afd3348
LP
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;
99f1229d 390 _cleanup_(table_unrefp) Table *table = NULL;
99534007 391 sd_bus *bus = ASSERT_PTR(userdata);
f8f14b36 392 int r;
99f1229d 393
f7621db0
LP
394 assert(argv);
395
5d990cc5 396 r = bus_call_method(bus, bus_login_mgr, "ListSeats", &error, &reply, NULL);
99f1229d
LP
397 if (r < 0)
398 return log_error_errno(r, "Failed to list seats: %s", bus_error_message(&error, r));
abca4822 399
f8f14b36
SP
400 r = sd_bus_message_enter_container(reply, 'a', "(so)");
401 if (r < 0)
5b30bef8 402 return bus_log_parse_error(r);
abca4822 403
9969b542 404 table = table_new("seat");
99f1229d
LP
405 if (!table)
406 return log_oom();
407
8bfa22f0
LP
408 (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
409
99f1229d 410 for (;;) {
31d99bd1 411 const char *seat;
abca4822 412
31d99bd1 413 r = sd_bus_message_read(reply, "(so)", &seat, NULL);
99f1229d
LP
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)
bd17fa8c 421 return table_log_add_error(r);
abca4822 422 }
99f1229d
LP
423
424 r = sd_bus_message_exit_container(reply);
f8f14b36 425 if (r < 0)
5b30bef8 426 return bus_log_parse_error(r);
abca4822 427
ea545174 428 return list_table_print(table, "seats");
abca4822
LP
429}
430
bf366954
MY
431static int show_unit_cgroup(
432 sd_bus *bus,
433 const char *unit,
434 pid_t leader,
435 const char *prefix) {
436
4afd3348 437 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bf366954 438 _cleanup_free_ char *cgroup = NULL;
a0e27019 439 int r;
aa1936ea
LP
440
441 assert(bus);
442 assert(unit);
bf366954 443 assert(prefix);
aa1936ea 444
bc06be75 445 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
f8f14b36 446 if (r < 0)
bc06be75 447 return r;
aa1936ea 448
9d127096
LP
449 if (isempty(cgroup))
450 return 0;
451
3ae6e646 452 unsigned c = MAX(LESS_BY(columns(), 18U), 10U);
bf366954 453 r = unit_show_processes(bus, unit, cgroup, prefix, c, get_output_flags(), &error);
a0e27019 454 if (r == -EBADR) {
a0e27019
LP
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
c3f90077 460 if (cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
a0e27019
LP
461 return 0;
462
bf366954 463 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, prefix, c, &leader, leader > 0, get_output_flags());
a0e27019
LP
464 } else if (r < 0)
465 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
466
aa1936ea
LP
467 return 0;
468}
469
f8f14b36
SP
470static 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
c9443393
MY
474 assert(bus);
475 assert(m);
476
f8f14b36
SP
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
f37f8a61
YW
485 r = sd_bus_message_read_basic(m, contents[0], userdata);
486 if (r < 0)
487 return r;
f8f14b36
SP
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
500static 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
bf366954
MY
522static 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
540static int print_session_status_info(sd_bus *bus, const char *path) {
f8f14b36 541
c9443393 542 static const struct bus_properties_map map[] = {
e8d58f5c
MY
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) },
f8f14b36
SP
565 {}
566 };
567
f9e0eefc 568 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 569 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
bf366954 570 _cleanup_(table_unrefp) Table *table = NULL;
f37f8a61 571 SessionStatusInfo i = {};
f8f14b36
SP
572 int r;
573
a7e4861c 574 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
f647962d 575 if (r < 0)
f9e0eefc 576 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb 577
bf366954
MY
578 table = table_new_vertical();
579 if (!table)
580 return log_oom();
a4c279f8 581
8bfa22f0
LP
582 (void) table_set_ersatz_string(table, TABLE_ERSATZ_NA);
583
bf366954
MY
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);
a4c279f8 588
bf366954
MY
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 }
a4c279f8 595
bf366954
MY
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);
a4c279f8 601
f8f14b36 602 if (i.leader > 0) {
bf366954 603 _cleanup_free_ char *name = NULL;
a4c279f8 604
d7d74854 605 (void) pid_get_comm(i.leader, &name);
a4c279f8 606
bf366954
MY
607 r = table_add_cell(table, NULL, TABLE_FIELD, "Leader");
608 if (r < 0)
609 return table_log_add_error(r);
a4c279f8 610
bf366954
MY
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);
a4c279f8
LP
618 }
619
febfec08
LP
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 }
a4c279f8 632
febfec08 633 if (!isempty(i.tty))
bf366954
MY
634 r = table_add_many(table,
635 TABLE_FIELD, "TTY",
636 TABLE_STRING, i.tty);
febfec08 637 else if (!isempty(i.display))
bf366954
MY
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);
f8f14b36
SP
649
650 if (i.remote_host && i.remote_user)
bf366954 651 r = table_add_cell_stringf(table, NULL, "%s@%s", i.remote_user, i.remote_host);
f8f14b36 652 else if (i.remote_host)
bf366954 653 r = table_add_cell(table, NULL, TABLE_STRING, i.remote_host);
f8f14b36 654 else if (i.remote_user)
bf366954
MY
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);
a4c279f8 660
f8f14b36 661 if (i.service) {
bf366954
MY
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 }
a4c279f8 668
bf366954
MY
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 }
a4c279f8 676
bf366954
MY
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 }
55efac6c 684
bf366954
MY
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 }
a4c279f8 692
bf366954
MY
693 r = table_add_cell(table, NULL, TABLE_FIELD, "Idle");
694 if (r < 0)
695 return table_log_add_error(r);
91d53e2b 696
bf366954
MY
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);
55efac6c 706
bf366954
MY
707 if (i.scope) {
708 r = table_add_many(table,
709 TABLE_FIELD, "Unit",
e533dad1 710 TABLE_SET_MINIMUM_WIDTH, STRLEN("Display"), /* For alignment with show_unit_cgroup */
bf366954
MY
711 TABLE_STRING, i.scope);
712 if (r < 0)
713 return table_log_add_error(r);
714 }
a4cd87e9 715
bf366954
MY
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());
a4c279f8 718
bf366954
MY
719 r = table_print(table, NULL);
720 if (r < 0)
721 return table_log_print_error(r);
82449055 722
f8f14b36 723 if (i.scope) {
bf366954 724 show_unit_cgroup(bus, i.scope, i.leader, /* prefix = */ strrepa(" ", STRLEN("Display: ")));
3c756001 725
d7a0f1f4 726 if (arg_transport == BUS_TRANSPORT_LOCAL)
3c756001
LP
727 show_journal_by_unit(
728 stdout,
729 i.scope,
8ac0810f 730 /* namespace = */ NULL,
3c756001 731 arg_output,
8ac0810f 732 /* n_columns = */ 0,
3c756001
LP
733 i.timestamp.monotonic,
734 arg_lines,
3c756001
LP
735 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
736 SD_JOURNAL_LOCAL_ONLY,
8ac0810f
YW
737 /* system_unit = */ true,
738 /* ellipsized = */ NULL);
a4c279f8 739 }
f8f14b36
SP
740
741 return 0;
a4c279f8
LP
742}
743
bf366954 744static int print_user_status_info(sd_bus *bus, const char *path) {
f8f14b36 745
c9443393 746 static const struct bus_properties_map map[] = {
3c756001 747 { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
26e00f0e 748 { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) },
3c756001
LP
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) },
f8f14b36
SP
756 {}
757 };
758
f9e0eefc 759 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 760 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
c9443393 761 _cleanup_(user_status_info_done) UserStatusInfo i = {};
bf366954 762 _cleanup_(table_unrefp) Table *table = NULL;
f8f14b36 763 int r;
a4c279f8 764
a7e4861c 765 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
e7e55dbd 766 if (r < 0)
f9e0eefc 767 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb 768
bf366954
MY
769 table = table_new_vertical();
770 if (!table)
771 return log_oom();
f8f14b36 772
8bfa22f0
LP
773 (void) table_set_ersatz_string(table, TABLE_ERSATZ_NA);
774
bf366954
MY
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);
a4c279f8 779
bf366954
MY
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 }
a4c279f8 786
bf366954
MY
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);
a4c279f8 792
f8f14b36 793 if (!strv_isempty(i.sessions)) {
bf366954 794 _cleanup_strv_free_ char **sessions = TAKE_PTR(i.sessions);
a4c279f8 795
bf366954
MY
796 r = mark_session(sessions, i.display);
797 if (r < 0)
798 return r;
a4c279f8 799
bf366954
MY
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);
a4c279f8
LP
805 }
806
bf366954
MY
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);
26e00f0e 812
f8f14b36 813 if (i.slice) {
bf366954
MY
814 r = table_add_many(table,
815 TABLE_FIELD, "Unit",
e533dad1 816 TABLE_SET_MINIMUM_WIDTH, STRLEN("Sessions"), /* For alignment with show_unit_cgroup */
bf366954
MY
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,
8ac0810f 835 /* namespace = */ NULL,
bf366954 836 arg_output,
8ac0810f 837 /* n_columns = */ 0,
bf366954
MY
838 i.timestamp.monotonic,
839 arg_lines,
bf366954
MY
840 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
841 SD_JOURNAL_LOCAL_ONLY,
8ac0810f
YW
842 /* system_unit = */ true,
843 /* ellipsized = */ NULL);
a4c279f8 844 }
f8f14b36 845
e7e55dbd 846 return 0;
a4c279f8
LP
847}
848
bf366954 849static int print_seat_status_info(sd_bus *bus, const char *path) {
a4c279f8 850
c9443393 851 static const struct bus_properties_map map[] = {
1d47a268 852 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
46e65dcc 853 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
1d47a268 854 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
f8f14b36
SP
855 {}
856 };
a4c279f8 857
f9e0eefc 858 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 859 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
c9443393 860 _cleanup_(seat_status_info_done) SeatStatusInfo i = {};
bf366954 861 _cleanup_(table_unrefp) Table *table = NULL;
f8f14b36
SP
862 int r;
863
a7e4861c 864 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, 0, &error, &m, &i);
e7e55dbd 865 if (r < 0)
f9e0eefc 866 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb 867
bf366954
MY
868 table = table_new_vertical();
869 if (!table)
870 return log_oom();
f8f14b36 871
8bfa22f0
LP
872 (void) table_set_ersatz_string(table, TABLE_ERSATZ_NA);
873
f8f14b36 874 if (!strv_isempty(i.sessions)) {
bf366954 875 _cleanup_strv_free_ char **sessions = TAKE_PTR(i.sessions);
a4c279f8 876
bf366954
MY
877 r = mark_session(sessions, i.active_session);
878 if (r < 0)
879 return r;
a4c279f8 880
bf366954
MY
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);
a4c279f8
LP
886 }
887
f8f14b36 888 if (arg_transport == BUS_TRANSPORT_LOCAL) {
bf366954
MY
889 r = table_add_many(table,
890 TABLE_FIELD, "Devices",
e533dad1 891 TABLE_SET_MINIMUM_WIDTH, STRLEN("Sessions"), /* For alignment with show_sysfs */
bf366954
MY
892 TABLE_EMPTY);
893 if (r < 0)
894 return table_log_add_error(r);
895 }
a4c279f8 896
bf366954
MY
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) {
3ae6e646 904 unsigned c = MAX(LESS_BY(columns(), 21U), 10U);
bf366954 905 show_sysfs(i.id, strrepa(" ", STRLEN("Sessions:")), c, get_output_flags());
a4c279f8 906 }
a4c279f8 907
e7e55dbd 908 return 0;
a4c279f8
LP
909}
910
255b1fc8 911static int print_property(const char *name, const char *expected_value, sd_bus_message *m, BusPrintPropertyFlags flags) {
07636114
YW
912 char type;
913 const char *contents;
2a998c74
LN
914 int r;
915
916 assert(name);
917 assert(m);
2a998c74 918
07636114
YW
919 r = sd_bus_message_peek_type(m, &type, &contents);
920 if (r < 0)
921 return r;
2a998c74 922
07636114 923 switch (type) {
2a998c74 924
07636114 925 case SD_BUS_TYPE_STRUCT:
2a998c74 926
07636114 927 if (contents[0] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
2a998c74
LN
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
255b1fc8 934 bus_print_property_value(name, expected_value, flags, s);
2a998c74 935
07636114 936 return 1;
2a998c74 937
07636114 938 } else if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
2a998c74
LN
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
baaa35ad
ZJS
945 if (!uid_is_valid(uid))
946 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
947 "Invalid user ID: " UID_FMT,
948 uid);
2a998c74 949
255b1fc8 950 bus_print_property_valuef(name, expected_value, flags, UID_FMT, uid);
07636114 951 return 1;
2a998c74 952 }
2a998c74
LN
953 break;
954
955 case SD_BUS_TYPE_ARRAY:
956
07636114 957 if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
2a998c74
LN
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
255b1fc8 965 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
f4046fe0 966 printf("%s=", name);
2a998c74
LN
967
968 while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
969 printf("%s%s", space ? " " : "", s);
970 space = true;
971 }
972
255b1fc8 973 if (space || !FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
f4046fe0 974 printf("\n");
2a998c74
LN
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
07636114 983 return 1;
2a998c74 984 }
2a998c74
LN
985 break;
986 }
987
2a998c74
LN
988 return 0;
989}
990
bf366954 991static int show_properties(sd_bus *bus, const char *path) {
97aa7b47
DH
992 int r;
993
2a998c74
LN
994 assert(bus);
995 assert(path);
97aa7b47 996
8183ebcd
ZJS
997 r = bus_print_all_properties(
998 bus,
999 "org.freedesktop.login1",
1000 path,
1001 print_property,
1002 arg_property,
255b1fc8 1003 arg_print_flags,
8183ebcd 1004 NULL);
2a998c74
LN
1005 if (r < 0)
1006 return bus_log_parse_error(r);
1007
1008 return 0;
97aa7b47
DH
1009}
1010
bf366954
MY
1011static 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;
bf366954
MY
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
454318d3 1038 return strdup_to(ret, path);
bf366954
MY
1039}
1040
f7621db0 1041static int show_session(int argc, char *argv[], void *userdata) {
99534007 1042 sd_bus *bus = ASSERT_PTR(userdata);
bf366954 1043 bool properties;
f7091f45 1044 int r;
a4c279f8 1045
f7621db0 1046 assert(argv);
a4c279f8 1047
f7621db0 1048 properties = !strstr(argv[0], "status");
a4c279f8 1049
384c2c32 1050 pager_open(arg_pager_flags);
a4c279f8 1051
86e1f46f 1052 if (argc <= 1) {
b28940ca
FS
1053 _cleanup_free_ char *path = NULL;
1054
544c4e1e 1055 /* If no argument is specified inspect the manager itself */
86e1f46f 1056 if (properties)
bf366954 1057 return show_properties(bus, "/org/freedesktop/login1");
86e1f46f 1058
b28940ca
FS
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);
a4c279f8
LP
1064 }
1065
bf366954
MY
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);
4ae25393 1070 if (r < 0)
bf366954
MY
1071 return r;
1072
1073 if (!first)
1074 putchar('\n');
a4c279f8 1075
97aa7b47 1076 if (properties)
bf366954 1077 r = show_properties(bus, path);
f8f14b36 1078 else
bf366954 1079 r = print_session_status_info(bus, path);
495cb9bb 1080 if (r < 0)
f8f14b36 1081 return r;
a4c279f8
LP
1082 }
1083
a4c279f8
LP
1084 return 0;
1085}
1086
f7621db0 1087static int show_user(int argc, char *argv[], void *userdata) {
99534007 1088 sd_bus *bus = ASSERT_PTR(userdata);
bf366954 1089 bool properties;
f7091f45 1090 int r;
a4c279f8 1091
f7621db0 1092 assert(argv);
a4c279f8 1093
f7621db0 1094 properties = !strstr(argv[0], "status");
a4c279f8 1095
384c2c32 1096 pager_open(arg_pager_flags);
a4c279f8 1097
86e1f46f 1098 if (argc <= 1) {
544c4e1e 1099 /* If no argument is specified inspect the manager itself */
86e1f46f 1100 if (properties)
bf366954 1101 return show_properties(bus, "/org/freedesktop/login1");
86e1f46f 1102
bf366954 1103 return print_user_status_info(bus, "/org/freedesktop/login1/user/self");
f8f14b36 1104 }
a4c279f8 1105
bf366954 1106 for (int i = 1, first = true; i < argc; i++, first = false) {
4afd3348 1107 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bf366954
MY
1108 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1109 const char *path;
f8f14b36 1110 uid_t uid;
a4c279f8 1111
fafff8f1 1112 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
f647962d 1113 if (r < 0)
f7621db0 1114 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
a4c279f8 1115
d962e737 1116 r = bus_call_method(bus, bus_login_mgr, "GetUser", &error, &reply, "u", (uint32_t) uid);
4ae25393
YW
1117 if (r < 0)
1118 return log_error_errno(r, "Failed to get user: %s", bus_error_message(&error, r));
a4c279f8 1119
f8f14b36
SP
1120 r = sd_bus_message_read(reply, "o", &path);
1121 if (r < 0)
5b30bef8 1122 return bus_log_parse_error(r);
a4c279f8 1123
bf366954
MY
1124 if (!first)
1125 putchar('\n');
1126
97aa7b47 1127 if (properties)
bf366954 1128 r = show_properties(bus, path);
9444b1f2 1129 else
bf366954 1130 r = print_user_status_info(bus, path);
495cb9bb 1131 if (r < 0)
f8f14b36 1132 return r;
a4c279f8
LP
1133 }
1134
f8f14b36 1135 return 0;
a4c279f8
LP
1136}
1137
f7621db0 1138static int show_seat(int argc, char *argv[], void *userdata) {
99534007 1139 sd_bus *bus = ASSERT_PTR(userdata);
bf366954 1140 bool properties;
f7091f45 1141 int r;
a4c279f8 1142
f7621db0 1143 assert(argv);
a4c279f8 1144
f7621db0 1145 properties = !strstr(argv[0], "status");
a4c279f8 1146
384c2c32 1147 pager_open(arg_pager_flags);
a4c279f8 1148
86e1f46f 1149 if (argc <= 1) {
b28940ca
FS
1150 _cleanup_free_ char *path = NULL;
1151
544c4e1e 1152 /* If no argument is specified inspect the manager itself */
86e1f46f 1153 if (properties)
bf366954 1154 return show_properties(bus, "/org/freedesktop/login1");
86e1f46f 1155
b28940ca
FS
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);
a4c279f8
LP
1161 }
1162
bf366954
MY
1163 for (int i = 1, first = true; i < argc; i++, first = false) {
1164 _cleanup_free_ char *path = NULL;
a4c279f8 1165
bf366954 1166 r = get_bus_path_by_id(bus, "seat", "GetSeat", argv[i], &path);
4ae25393 1167 if (r < 0)
bf366954 1168 return r;
9444b1f2 1169
bf366954
MY
1170 if (!first)
1171 putchar('\n');
a4c279f8 1172
97aa7b47 1173 if (properties)
bf366954 1174 r = show_properties(bus, path);
f8f14b36 1175 else
bf366954 1176 r = print_seat_status_info(bus, path);
495cb9bb 1177 if (r < 0)
f8f14b36 1178 return r;
a4c279f8
LP
1179 }
1180
f8f14b36 1181 return 0;
a4c279f8
LP
1182}
1183
f7621db0 1184static int activate(int argc, char *argv[], void *userdata) {
4afd3348 1185 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1186 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1187 int r;
24310c11 1188
f7621db0 1189 assert(argv);
24310c11 1190
f3cf6167 1191 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1192
906b76b2 1193 if (argc < 2) {
dc084399
LP
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));
2fbcde74 1206
dc084399 1207 return 0;
906b76b2
LP
1208 }
1209
f7091f45 1210 for (int i = 1; i < argc; i++) {
5d990cc5 1211 r = bus_call_method(
2a3613b1 1212 bus,
5d990cc5 1213 bus_login_mgr,
f7621db0
LP
1214 streq(argv[0], "lock-session") ? "LockSession" :
1215 streq(argv[0], "unlock-session") ? "UnlockSession" :
1216 streq(argv[0], "terminate-session") ? "TerminateSession" :
2a3613b1 1217 "ActivateSession",
f8f14b36 1218 &error, NULL,
f7621db0 1219 "s", argv[i]);
4ae25393 1220 if (r < 0)
544c4e1e 1221 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
24310c11
LP
1222 }
1223
f8f14b36 1224 return 0;
a4c279f8
LP
1225}
1226
f7621db0 1227static int kill_session(int argc, char *argv[], void *userdata) {
4afd3348 1228 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1229 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1230 int r;
de07ab16 1231
f7621db0 1232 assert(argv);
de07ab16 1233
f3cf6167 1234 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1235
4ccde410
ZJS
1236 if (!arg_kill_whom)
1237 arg_kill_whom = "all";
de07ab16 1238
f7091f45 1239 for (int i = 1; i < argc; i++) {
5d990cc5
VC
1240 r = bus_call_method(
1241 bus,
1242 bus_login_mgr,
1243 "KillSession",
1244 &error, NULL,
4ccde410 1245 "ssi", argv[i], arg_kill_whom, arg_signal);
4ae25393 1246 if (r < 0)
2a03b9ed 1247 return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, r));
de07ab16
LP
1248 }
1249
4654e558 1250 return 0;
a4c279f8
LP
1251}
1252
f7621db0 1253static int enable_linger(int argc, char *argv[], void *userdata) {
4afd3348 1254 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1255 sd_bus *bus = ASSERT_PTR(userdata);
2fbcde74 1256 char* short_argv[3];
f8f14b36 1257 bool b;
f7091f45 1258 int r;
88e3dc90 1259
f7621db0 1260 assert(argv);
88e3dc90 1261
f3cf6167 1262 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1263
f7621db0 1264 b = streq(argv[0], "enable-linger");
88e3dc90 1265
906b76b2 1266 if (argc < 2) {
545f779f
AJ
1267 /* No argument? Let's use an empty user name,
1268 * then logind will use our user. */
bdb07fa5 1269
2fbcde74 1270 short_argv[0] = argv[0];
545f779f 1271 short_argv[1] = (char*) "";
2fbcde74
LP
1272 short_argv[2] = NULL;
1273 argv = short_argv;
906b76b2
LP
1274 argc = 2;
1275 }
1276
f7091f45 1277 for (int i = 1; i < argc; i++) {
ddd88763 1278 uid_t uid;
88e3dc90 1279
906b76b2
LP
1280 if (isempty(argv[i]))
1281 uid = UID_INVALID;
1282 else {
fafff8f1 1283 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
906b76b2
LP
1284 if (r < 0)
1285 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1286 }
88e3dc90 1287
5d990cc5
VC
1288 r = bus_call_method(
1289 bus,
1290 bus_login_mgr,
1291 "SetUserLinger",
1292 &error, NULL,
1293 "ubb", (uint32_t) uid, b, true);
4ae25393 1294 if (r < 0)
2a03b9ed 1295 return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, r));
88e3dc90
LP
1296 }
1297
4654e558 1298 return 0;
88e3dc90
LP
1299}
1300
f7621db0 1301static int terminate_user(int argc, char *argv[], void *userdata) {
4afd3348 1302 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1303 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1304 int r;
88e3dc90 1305
f7621db0 1306 assert(argv);
88e3dc90 1307
f3cf6167 1308 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1309
f7091f45 1310 for (int i = 1; i < argc; i++) {
ddd88763 1311 uid_t uid;
88e3dc90 1312
68892f94
LP
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 }
88e3dc90 1322
5d990cc5 1323 r = bus_call_method(bus, bus_login_mgr, "TerminateUser", &error, NULL, "u", (uint32_t) uid);
4ae25393 1324 if (r < 0)
2a03b9ed 1325 return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, r));
88e3dc90
LP
1326 }
1327
4654e558 1328 return 0;
a4c279f8
LP
1329}
1330
f7621db0 1331static int kill_user(int argc, char *argv[], void *userdata) {
4afd3348 1332 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1333 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1334 int r;
de07ab16 1335
f7621db0 1336 assert(argv);
de07ab16 1337
f3cf6167 1338 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1339
4ccde410
ZJS
1340 if (!arg_kill_whom)
1341 arg_kill_whom = "all";
de07ab16 1342
f7091f45 1343 for (int i = 1; i < argc; i++) {
ddd88763 1344 uid_t uid;
de07ab16 1345
68892f94
LP
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 }
de07ab16 1355
5d990cc5 1356 r = bus_call_method(
4654e558 1357 bus,
5d990cc5 1358 bus_login_mgr,
4654e558 1359 "KillUser",
f8f14b36
SP
1360 &error, NULL,
1361 "ui", (uint32_t) uid, arg_signal);
4ae25393 1362 if (r < 0)
2a03b9ed 1363 return log_error_errno(r, "Could not kill user: %s", bus_error_message(&error, r));
de07ab16
LP
1364 }
1365
4654e558 1366 return 0;
de07ab16
LP
1367}
1368
f7621db0 1369static int attach(int argc, char *argv[], void *userdata) {
4afd3348 1370 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1371 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1372 int r;
88e3dc90 1373
f7621db0 1374 assert(argv);
88e3dc90 1375
f3cf6167 1376 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1377
f7091f45 1378 for (int i = 2; i < argc; i++) {
4654e558 1379
5d990cc5 1380 r = bus_call_method(
4654e558 1381 bus,
5d990cc5 1382 bus_login_mgr,
4654e558 1383 "AttachDevice",
f8f14b36 1384 &error, NULL,
f7621db0 1385 "ssb", argv[1], argv[i], true);
4ae25393 1386 if (r < 0)
2a03b9ed 1387 return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, r));
88e3dc90
LP
1388 }
1389
4654e558 1390 return 0;
a4c279f8
LP
1391}
1392
f7621db0 1393static int flush_devices(int argc, char *argv[], void *userdata) {
4afd3348 1394 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1395 sd_bus *bus = ASSERT_PTR(userdata);
f8f14b36 1396 int r;
88e3dc90 1397
f7621db0 1398 assert(argv);
88e3dc90 1399
f3cf6167 1400 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1401
5d990cc5 1402 r = bus_call_method(bus, bus_login_mgr, "FlushDevices", &error, NULL, "b", true);
f8f14b36 1403 if (r < 0)
2a03b9ed 1404 return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, r));
f8f14b36 1405
4ae25393 1406 return 0;
a4c279f8
LP
1407}
1408
f7621db0 1409static int lock_sessions(int argc, char *argv[], void *userdata) {
4afd3348 1410 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1411 sd_bus *bus = ASSERT_PTR(userdata);
f8f14b36 1412 int r;
b6160029 1413
f7621db0 1414 assert(argv);
7212a8a9 1415
f3cf6167 1416 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1417
5d990cc5 1418 r = bus_call_method(
2a3613b1 1419 bus,
5d990cc5 1420 bus_login_mgr,
f7621db0 1421 streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
f8f14b36
SP
1422 &error, NULL,
1423 NULL);
1424 if (r < 0)
2a03b9ed 1425 return log_error_errno(r, "Could not lock sessions: %s", bus_error_message(&error, r));
f8f14b36 1426
4ae25393 1427 return 0;
7212a8a9
LP
1428}
1429
f7621db0 1430static int terminate_seat(int argc, char *argv[], void *userdata) {
4afd3348 1431 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1432 sd_bus *bus = ASSERT_PTR(userdata);
f7091f45 1433 int r;
88e3dc90 1434
f7621db0 1435 assert(argv);
88e3dc90 1436
f3cf6167 1437 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1438
f7091f45 1439 for (int i = 1; i < argc; i++) {
4654e558 1440
5d990cc5 1441 r = bus_call_method(bus, bus_login_mgr, "TerminateSeat", &error, NULL, "s", argv[i]);
4ae25393 1442 if (r < 0)
2a03b9ed 1443 return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, r));
88e3dc90
LP
1444 }
1445
4654e558 1446 return 0;
a4c279f8
LP
1447}
1448
f7621db0 1449static int help(int argc, char *argv[], void *userdata) {
37ec0fdd
LP
1450 _cleanup_free_ char *link = NULL;
1451 int r;
1452
384c2c32 1453 pager_open(arg_pager_flags);
37ec0fdd
LP
1454
1455 r = terminal_urlify_man("loginctl", "1", &link);
1456 if (r < 0)
1457 return log_oom();
079dac08 1458
7c9437fd
LP
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"
4f8f66cb 1462 " list-sessions List sessions\n"
86e1f46f 1463 " session-status [ID...] Show session status\n"
4f8f66cb 1464 " show-session [ID...] Show properties of sessions or the manager\n"
906b76b2
LP
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"
4f8f66cb
ZJS
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"
353b2baa 1471 " kill-session ID... Send signal to processes of a session\n"
7c9437fd 1472 "\n%3$sUser Commands:%4$s\n"
4f8f66cb 1473 " list-users List users\n"
86e1f46f 1474 " user-status [USER...] Show user status\n"
4f8f66cb 1475 " show-user [USER...] Show properties of users or the manager\n"
906b76b2
LP
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"
4f8f66cb 1478 " terminate-user USER... Terminate all sessions of one or more users\n"
353b2baa 1479 " kill-user USER... Send signal to processes of a user\n"
7c9437fd 1480 "\n%3$sSeat Commands:%4$s\n"
4f8f66cb 1481 " list-seats List seats\n"
86e1f46f
LP
1482 " seat-status [NAME...] Show seat status\n"
1483 " show-seat [NAME...] Show properties of seats or the manager\n"
4f8f66cb
ZJS
1484 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1485 " flush-devices Flush all device associations\n"
601185b4 1486 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
7c9437fd 1487 "\n%3$sOptions:%4$s\n"
e1fac8a6
ZJS
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"
60b254ca 1496 " -P NAME Equivalent to --value --property=NAME\n"
e1fac8a6
ZJS
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"
4ccde410 1500 " --kill-whom=WHOM Whom to send signal to\n"
e1fac8a6
ZJS
1501 " -s --signal=SIGNAL Which signal to send\n"
1502 " -n --lines=INTEGER Number of journal entries to show\n"
017f53e1
MY
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"
e1fac8a6 1507 " short-iso, short-iso-precise, short-full,\n"
893bcd3d 1508 " short-monotonic, short-unix, short-delta,\n"
e1fac8a6 1509 " json, json-pretty, json-sse, json-seq, cat,\n"
893bcd3d 1510 " verbose, export, with-unit)\n"
7c9437fd 1511 "\nSee the %2$s for details.\n",
bc556335 1512 program_invocation_short_name,
7c9437fd
LP
1513 link,
1514 ansi_underline(),
bc556335 1515 ansi_normal(),
7c9437fd
LP
1516 ansi_highlight(),
1517 ansi_normal());
f7621db0
LP
1518
1519 return 0;
abca4822
LP
1520}
1521
1522static int parse_argv(int argc, char *argv[]) {
abca4822
LP
1523 enum {
1524 ARG_VERSION = 0x100,
f4046fe0 1525 ARG_VALUE,
a4c279f8 1526 ARG_NO_PAGER,
841aa8c0 1527 ARG_NO_LEGEND,
ea545174 1528 ARG_JSON,
4ccde410 1529 ARG_KILL_WHOM,
9bdbc2e2 1530 ARG_NO_ASK_PASSWORD,
abca4822
LP
1531 };
1532
1533 static const struct option options[] = {
6d0274f1
LP
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' },
f4046fe0 1538 { "value", no_argument, NULL, ARG_VALUE },
422fa650 1539 { "full", no_argument, NULL, 'l' },
6d0274f1 1540 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
841aa8c0 1541 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
ea545174 1542 { "json", required_argument, NULL, ARG_JSON },
4ccde410 1543 { "kill-whom", required_argument, NULL, ARG_KILL_WHOM },
6d0274f1
LP
1544 { "signal", required_argument, NULL, 's' },
1545 { "host", required_argument, NULL, 'H' },
f8f14b36 1546 { "machine", required_argument, NULL, 'M' },
6d0274f1 1547 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3c756001
LP
1548 { "lines", required_argument, NULL, 'n' },
1549 { "output", required_argument, NULL, 'o' },
eb9da376 1550 {}
abca4822
LP
1551 };
1552
1c3051eb 1553 int c, r;
abca4822
LP
1554
1555 assert(argc >= 0);
1556 assert(argv);
1557
ea545174 1558 while ((c = getopt_long(argc, argv, "hp:P:als:H:M:n:o:j", options, NULL)) >= 0)
abca4822
LP
1559
1560 switch (c) {
1561
1562 case 'h':
37ec0fdd 1563 return help(0, NULL, NULL);
abca4822
LP
1564
1565 case ARG_VERSION:
3f6fd1ba 1566 return version();
abca4822 1567
60b254ca 1568 case 'P':
255b1fc8 1569 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
60b254ca
RP
1570 _fallthrough_;
1571
a4c279f8 1572 case 'p': {
1c3051eb
DH
1573 r = strv_extend(&arg_property, optarg);
1574 if (r < 0)
1575 return log_oom();
a4c279f8
LP
1576
1577 /* If the user asked for a particular
e8607daf 1578 * property, show it to them, even if it is
a4c279f8 1579 * empty. */
255b1fc8 1580 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
a4c279f8
LP
1581 break;
1582 }
1583
1584 case 'a':
255b1fc8 1585 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
a4c279f8
LP
1586 break;
1587
f4046fe0 1588 case ARG_VALUE:
255b1fc8 1589 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
f4046fe0
ZJS
1590 break;
1591
422fa650
ZJS
1592 case 'l':
1593 arg_full = true;
1594 break;
1595
3c756001 1596 case 'n':
baaa35ad
ZJS
1597 if (safe_atou(optarg, &arg_lines) < 0)
1598 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1599 "Failed to parse lines '%s'", optarg);
3c756001
LP
1600 break;
1601
1602 case 'o':
5c828e66
LP
1603 if (streq(optarg, "help")) {
1604 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
1605 return 0;
1606 }
1607
3c756001 1608 arg_output = output_mode_from_string(optarg);
baaa35ad 1609 if (arg_output < 0)
7211c853 1610 return log_error_errno(arg_output, "Unknown output '%s'.", optarg);
e3483674 1611
ea545174
MY
1612 break;
1613
1614 case 'j':
309a747f 1615 arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
ea545174
MY
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
23441a3d 1624 if (sd_json_format_enabled(arg_json_format_flags))
e3483674
LP
1625 arg_legend = false;
1626
3c756001
LP
1627 break;
1628
abca4822 1629 case ARG_NO_PAGER:
0221d68a 1630 arg_pager_flags |= PAGER_DISABLE;
abca4822
LP
1631 break;
1632
841aa8c0
ZJS
1633 case ARG_NO_LEGEND:
1634 arg_legend = false;
1635 break;
1636
6bb92a16
LP
1637 case ARG_NO_ASK_PASSWORD:
1638 arg_ask_password = false;
5d5e98eb 1639 break;
6bb92a16 1640
4ccde410
ZJS
1641 case ARG_KILL_WHOM:
1642 arg_kill_whom = optarg;
a4c279f8
LP
1643 break;
1644
1645 case 's':
86beb213
ZJS
1646 r = parse_signal_argument(optarg, &arg_signal);
1647 if (r <= 0)
1648 return r;
a4c279f8
LP
1649 break;
1650
f8f14b36
SP
1651 case 'H':
1652 arg_transport = BUS_TRANSPORT_REMOTE;
1653 arg_host = optarg;
abca4822
LP
1654 break;
1655
f8f14b36 1656 case 'M':
a59cc386
MY
1657 r = parse_machine_argument(optarg, &arg_host, &arg_transport);
1658 if (r < 0)
1659 return r;
abca4822
LP
1660 break;
1661
1662 case '?':
1663 return -EINVAL;
1664
1665 default:
04499a70 1666 assert_not_reached();
abca4822 1667 }
abca4822
LP
1668
1669 return 1;
1670}
1671
f7621db0 1672static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
f7621db0
LP
1673 static const Verb verbs[] = {
1674 { "help", VERB_ANY, VERB_ANY, 0, help },
1675 { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions },
86e1f46f 1676 { "session-status", VERB_ANY, VERB_ANY, 0, show_session },
f7621db0 1677 { "show-session", VERB_ANY, VERB_ANY, 0, show_session },
906b76b2
LP
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 },
f7621db0
LP
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 },
86e1f46f 1686 { "user-status", VERB_ANY, VERB_ANY, 0, show_user },
f7621db0 1687 { "show-user", VERB_ANY, VERB_ANY, 0, show_user },
906b76b2
LP
1688 { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
1689 { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
f7621db0
LP
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 },
86e1f46f
LP
1693 { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat },
1694 { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat },
f7621db0
LP
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 {}
abca4822
LP
1699 };
1700
f7621db0 1701 return dispatch_verb(argc, argv, verbs, bus);
abca4822
LP
1702}
1703
eae5c847
YW
1704static int run(int argc, char *argv[]) {
1705 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f8f14b36 1706 int r;
abca4822 1707
a9cdc94f 1708 setlocale(LC_ALL, "");
d2acb93d 1709 log_setup();
1abaf488 1710
abca4822 1711 r = parse_argv(argc, argv);
f8f14b36 1712 if (r <= 0)
eae5c847 1713 return r;
f8f14b36 1714
fad3feec
MY
1715 journal_browse_prepare();
1716
4870133b 1717 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
eae5c847 1718 if (r < 0)
d8a77d55 1719 return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
abca4822 1720
eae5c847 1721 (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
a4c279f8 1722
eae5c847 1723 return loginctl_main(argc, argv, bus);
abca4822 1724}
eae5c847
YW
1725
1726DEFINE_MAIN_FUNCTION(run);