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