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