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