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