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