chiark / gitweb /
login/eloginctl.c: Sync the following methods from systemctl.c:
[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         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         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         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         char *id;
329         uid_t uid;
330         char *name;
331         struct dual_timestamp timestamp;
332         unsigned int vtnr;
333         char *seat;
334         char *tty;
335         char *display;
336         int remote;
337         char *remote_host;
338         char *remote_user;
339         char *service;
340         pid_t leader;
341         char *type;
342         char *class;
343         char *state;
344         char *scope;
345         char *desktop;
346 } SessionStatusInfo;
347
348 typedef struct UserStatusInfo {
349         uid_t uid;
350         int linger;
351         char *name;
352         struct dual_timestamp timestamp;
353         char *state;
354         char **sessions;
355         char *display;
356         char *slice;
357 } UserStatusInfo;
358
359 typedef struct SeatStatusInfo {
360         char *id;
361         char *active_session;
362         char **sessions;
363 } SeatStatusInfo;
364
365 static void session_status_info_clear(SessionStatusInfo *info) {
366         if (info) {
367                 free(info->id);
368                 free(info->name);
369                 free(info->seat);
370                 free(info->tty);
371                 free(info->display);
372                 free(info->remote_host);
373                 free(info->remote_user);
374                 free(info->service);
375                 free(info->type);
376                 free(info->class);
377                 free(info->state);
378                 free(info->scope);
379                 free(info->desktop);
380                 zero(*info);
381         }
382 }
383
384 static void user_status_info_clear(UserStatusInfo *info) {
385         if (info) {
386                 free(info->name);
387                 free(info->state);
388                 strv_free(info->sessions);
389                 free(info->display);
390                 free(info->slice);
391                 zero(*info);
392         }
393 }
394
395 static void seat_status_info_clear(SeatStatusInfo *info) {
396         if (info) {
397                 free(info->id);
398                 free(info->active_session);
399                 strv_free(info->sessions);
400                 zero(*info);
401         }
402 }
403
404 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
405         const char *contents;
406         int r;
407
408         r = sd_bus_message_peek_type(m, NULL, &contents);
409         if (r < 0)
410                 return r;
411
412         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
413         if (r < 0)
414                 return r;
415
416         if (IN_SET(contents[0], 's', 'o')) {
417                 const char *s;
418                 char **p = (char **) userdata;
419
420                 r = sd_bus_message_read_basic(m, contents[0], &s);
421                 if (r < 0)
422                         return r;
423
424                 r = free_and_strdup(p, s);
425                 if (r < 0)
426                         return r;
427         } else {
428                 r = sd_bus_message_read_basic(m, contents[0], userdata);
429                 if (r < 0)
430                         return r;
431         }
432
433         r = sd_bus_message_skip(m, contents+1);
434         if (r < 0)
435                 return r;
436
437         r = sd_bus_message_exit_container(m);
438         if (r < 0)
439                 return r;
440
441         return 0;
442 }
443
444 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
445         const char *name;
446         int r;
447
448         assert(bus);
449         assert(m);
450
451         r = sd_bus_message_enter_container(m, 'a', "(so)");
452         if (r < 0)
453                 return r;
454
455         while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
456                 r = strv_extend(userdata, name);
457                 if (r < 0)
458                         return r;
459         }
460         if (r < 0)
461                 return r;
462
463         return sd_bus_message_exit_container(m);
464 }
465
466 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
467
468         static const struct bus_properties_map map[]  = {
469                 { "Id",                  "s",    NULL,                     offsetof(SessionStatusInfo, id)                  },
470                 { "Name",                "s",    NULL,                     offsetof(SessionStatusInfo, name)                },
471                 { "TTY",                 "s",    NULL,                     offsetof(SessionStatusInfo, tty)                 },
472                 { "Display",             "s",    NULL,                     offsetof(SessionStatusInfo, display)             },
473                 { "RemoteHost",          "s",    NULL,                     offsetof(SessionStatusInfo, remote_host)         },
474                 { "RemoteUser",          "s",    NULL,                     offsetof(SessionStatusInfo, remote_user)         },
475                 { "Service",             "s",    NULL,                     offsetof(SessionStatusInfo, service)             },
476                 { "Desktop",             "s",    NULL,                     offsetof(SessionStatusInfo, desktop)             },
477                 { "Type",                "s",    NULL,                     offsetof(SessionStatusInfo, type)                },
478                 { "Class",               "s",    NULL,                     offsetof(SessionStatusInfo, class)               },
479                 { "Scope",               "s",    NULL,                     offsetof(SessionStatusInfo, scope)               },
480                 { "State",               "s",    NULL,                     offsetof(SessionStatusInfo, state)               },
481                 { "VTNr",                "u",    NULL,                     offsetof(SessionStatusInfo, vtnr)                },
482                 { "Leader",              "u",    NULL,                     offsetof(SessionStatusInfo, leader)              },
483                 { "Remote",              "b",    NULL,                     offsetof(SessionStatusInfo, remote)              },
484                 { "Timestamp",           "t",    NULL,                     offsetof(SessionStatusInfo, timestamp.realtime)  },
485                 { "TimestampMonotonic",  "t",    NULL,                     offsetof(SessionStatusInfo, timestamp.monotonic) },
486                 { "User",                "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid)                 },
487                 { "Seat",                "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat)                },
488                 {}
489         };
490
491         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
492         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
493         char since2[FORMAT_TIMESTAMP_MAX], *s2;
494         _cleanup_(session_status_info_clear) SessionStatusInfo i = {};
495         int r;
496
497         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
498         if (r < 0)
499                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
500
501         if (*new_line)
502                 printf("\n");
503
504         *new_line = true;
505
506         printf("%s - ", strna(i.id));
507
508         if (i.name)
509                 printf("%s (%"PRIu32")\n", i.name, i.uid);
510         else
511                 printf("%"PRIu32"\n", i.uid);
512
513         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
514         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
515
516         if (s1)
517                 printf("\t   Since: %s; %s\n", s2, s1);
518         else if (s2)
519                 printf("\t   Since: %s\n", s2);
520
521         if (i.leader > 0) {
522                 _cleanup_free_ char *t = NULL;
523
524                 printf("\t  Leader: %"PRIu32, i.leader);
525
526                 get_process_comm(i.leader, &t);
527                 if (t)
528                         printf(" (%s)", t);
529
530                 printf("\n");
531         }
532
533         if (!isempty(i.seat)) {
534                 printf("\t    Seat: %s", i.seat);
535
536                 if (i.vtnr > 0)
537                         printf("; vc%u", i.vtnr);
538
539                 printf("\n");
540         }
541
542         if (i.tty)
543                 printf("\t     TTY: %s\n", i.tty);
544         else if (i.display)
545                 printf("\t Display: %s\n", i.display);
546
547         if (i.remote_host && i.remote_user)
548                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
549         else if (i.remote_host)
550                 printf("\t  Remote: %s\n", i.remote_host);
551         else if (i.remote_user)
552                 printf("\t  Remote: user %s\n", i.remote_user);
553         else if (i.remote)
554                 printf("\t  Remote: Yes\n");
555
556         if (i.service) {
557                 printf("\t Service: %s", i.service);
558
559                 if (i.type)
560                         printf("; type %s", i.type);
561
562                 if (i.class)
563                         printf("; class %s", i.class);
564
565                 printf("\n");
566         } else if (i.type) {
567                 printf("\t    Type: %s", i.type);
568
569                 if (i.class)
570                         printf("; class %s", i.class);
571
572                 printf("\n");
573         } else if (i.class)
574                 printf("\t   Class: %s\n", i.class);
575
576         if (!isempty(i.desktop))
577                 printf("\t Desktop: %s\n", i.desktop);
578
579         if (i.state)
580                 printf("\t   State: %s\n", i.state);
581
582         if (i.scope) {
583                 printf("\t    Unit: %s\n", i.scope);
584 #if 0 /// UNNEEDED by elogind
585                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
586
587                 if (arg_transport == BUS_TRANSPORT_LOCAL) {
588
589                         show_journal_by_unit(
590                                         stdout,
591                                         i.scope,
592                                         arg_output,
593                                         0,
594                                         i.timestamp.monotonic,
595                                         arg_lines,
596                                         0,
597                                         get_output_flags() | OUTPUT_BEGIN_NEWLINE,
598                                         SD_JOURNAL_LOCAL_ONLY,
599                                         true,
600                                         NULL);
601                 }
602 #endif // 0
603         }
604
605         return 0;
606 }
607
608 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
609
610         static const struct bus_properties_map map[]  = {
611                 { "Name",               "s",     NULL,                     offsetof(UserStatusInfo, name)                },
612                 { "Linger",             "b",     NULL,                     offsetof(UserStatusInfo, linger)              },
613                 { "Slice",              "s",     NULL,                     offsetof(UserStatusInfo, slice)               },
614                 { "State",              "s",     NULL,                     offsetof(UserStatusInfo, state)               },
615                 { "UID",                "u",     NULL,                     offsetof(UserStatusInfo, uid)                 },
616                 { "Timestamp",          "t",     NULL,                     offsetof(UserStatusInfo, timestamp.realtime)  },
617                 { "TimestampMonotonic", "t",     NULL,                     offsetof(UserStatusInfo, timestamp.monotonic) },
618                 { "Display",            "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display)             },
619                 { "Sessions",           "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions)            },
620                 {}
621         };
622
623         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
624         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
625         char since2[FORMAT_TIMESTAMP_MAX], *s2;
626         _cleanup_(user_status_info_clear) UserStatusInfo i = {};
627         int r;
628
629         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
630         if (r < 0)
631                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
632
633         if (*new_line)
634                 printf("\n");
635
636         *new_line = true;
637
638         if (i.name)
639                 printf("%s (%"PRIu32")\n", i.name, i.uid);
640         else
641                 printf("%"PRIu32"\n", i.uid);
642
643         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
644         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
645
646         if (s1)
647                 printf("\t   Since: %s; %s\n", s2, s1);
648         else if (s2)
649                 printf("\t   Since: %s\n", s2);
650
651         if (!isempty(i.state))
652                 printf("\t   State: %s\n", i.state);
653
654         if (!strv_isempty(i.sessions)) {
655                 char **l;
656                 printf("\tSessions:");
657
658                 STRV_FOREACH(l, i.sessions)
659                         printf(" %s%s",
660                                streq_ptr(*l, i.display) ? "*" : "",
661                                *l);
662
663                 printf("\n");
664         }
665
666         printf("\t  Linger: %s\n", yes_no(i.linger));
667
668         if (i.slice) {
669                 printf("\t    Unit: %s\n", i.slice);
670 #if 0 /// UNNEEDED by elogind
671                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
672
673                 show_journal_by_unit(
674                                 stdout,
675                                 i.slice,
676                                 arg_output,
677                                 0,
678                                 i.timestamp.monotonic,
679                                 arg_lines,
680                                 0,
681                                 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
682                                 SD_JOURNAL_LOCAL_ONLY,
683                                 true,
684                                 NULL);
685 #endif // 0
686         }
687
688         return 0;
689 }
690
691 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
692
693         static const struct bus_properties_map map[]  = {
694                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
695                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
696                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
697                 {}
698         };
699
700         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
701         _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
702         int r;
703
704         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
705         if (r < 0)
706                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
707
708         if (*new_line)
709                 printf("\n");
710
711         *new_line = true;
712
713         printf("%s\n", strna(i.id));
714
715         if (!strv_isempty(i.sessions)) {
716                 char **l;
717                 printf("\tSessions:");
718
719                 STRV_FOREACH(l, i.sessions) {
720                         if (streq_ptr(*l, i.active_session))
721                                 printf(" *%s", *l);
722                         else
723                                 printf(" %s", *l);
724                 }
725
726                 printf("\n");
727         }
728
729         if (arg_transport == BUS_TRANSPORT_LOCAL) {
730                 unsigned c;
731
732                 c = columns();
733                 if (c > 21)
734                         c -= 21;
735                 else
736                         c = 0;
737
738                 printf("\t Devices:\n");
739
740                 show_sysfs(i.id, "\t\t  ", c, get_output_flags());
741         }
742
743         return 0;
744 }
745
746 #define property(name, fmt, ...)                                        \
747         do {                                                            \
748                 if (arg_value)                                          \
749                         printf(fmt "\n", __VA_ARGS__);                  \
750                 else                                                    \
751                         printf("%s=" fmt "\n", name, __VA_ARGS__);      \
752         } while (0)
753
754 static int print_property(const char *name, sd_bus_message *m, const char *contents) {
755         int r;
756
757         assert(name);
758         assert(m);
759         assert(contents);
760
761         if (arg_property && !strv_find(arg_property, name))
762                 /* skip what we didn't read */
763                 return sd_bus_message_skip(m, contents);
764
765         switch (contents[0]) {
766
767         case SD_BUS_TYPE_STRUCT_BEGIN:
768
769                 if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
770                         const char *s;
771
772                         r = sd_bus_message_read(m, "(so)", &s, NULL);
773                         if (r < 0)
774                                 return bus_log_parse_error(r);
775
776                         if (arg_all || !isempty(s))
777                                 property(name, "%s", s);
778
779                         return 0;
780
781                 } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
782                         uint32_t uid;
783
784                         r = sd_bus_message_read(m, "(uo)", &uid, NULL);
785                         if (r < 0)
786                                 return bus_log_parse_error(r);
787
788                         if (!uid_is_valid(uid)) {
789                                 log_error("Invalid user ID: " UID_FMT, uid);
790                                 return -EINVAL;
791                         }
792
793                         property(name, UID_FMT, uid);
794                         return 0;
795                 }
796
797                 break;
798
799         case SD_BUS_TYPE_ARRAY:
800
801                 if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
802                         const char *s;
803                         bool space = false;
804
805                         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
806                         if (r < 0)
807                                 return bus_log_parse_error(r);
808
809                         if (!arg_value)
810                                 printf("%s=", name);
811
812                         while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
813                                 printf("%s%s", space ? " " : "", s);
814                                 space = true;
815                         }
816
817                         if (space || !arg_value)
818                                 printf("\n");
819
820                         if (r < 0)
821                                 return bus_log_parse_error(r);
822
823                         r = sd_bus_message_exit_container(m);
824                         if (r < 0)
825                                 return bus_log_parse_error(r);
826
827                         return 0;
828                 }
829
830                 break;
831         }
832
833         r = bus_print_property(name, m, arg_value, arg_all);
834         if (r < 0)
835                 return bus_log_parse_error(r);
836
837         if (r == 0) {
838                 r = sd_bus_message_skip(m, contents);
839                 if (r < 0)
840                         return bus_log_parse_error(r);
841
842                 if (arg_all)
843                         printf("%s=[unprintable]\n", name);
844         }
845
846         return 0;
847 }
848
849 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
850         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
851         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
852         int r;
853
854         assert(bus);
855         assert(path);
856         assert(new_line);
857
858         r = sd_bus_call_method(
859                         bus,
860                         "org.freedesktop.login1",
861                         path,
862                         "org.freedesktop.DBus.Properties",
863                         "GetAll",
864                         &error,
865                         &reply,
866                         "s", "");
867         if (r < 0)
868                 return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
869
870         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
871         if (r < 0)
872                 return bus_log_parse_error(r);
873
874         if (*new_line)
875                 printf("\n");
876
877         *new_line = true;
878
879         while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
880                 const char *name, *contents;
881
882                 r = sd_bus_message_read(reply, "s", &name);
883                 if (r < 0)
884                         return bus_log_parse_error(r);
885
886                 r = sd_bus_message_peek_type(reply, NULL, &contents);
887                 if (r < 0)
888                         return bus_log_parse_error(r);
889
890                 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
891                 if (r < 0)
892                         return bus_log_parse_error(r);
893
894                 r = print_property(name, reply, contents);
895                 if (r < 0)
896                         return r;
897
898                 r = sd_bus_message_exit_container(reply);
899                 if (r < 0)
900                         return bus_log_parse_error(r);
901
902                 r = sd_bus_message_exit_container(reply);
903                 if (r < 0)
904                         return bus_log_parse_error(r);
905         }
906         if (r < 0)
907                 return bus_log_parse_error(r);
908
909         r = sd_bus_message_exit_container(reply);
910         if (r < 0)
911                 return bus_log_parse_error(r);
912
913         return 0;
914 }
915
916 static int show_session(int argc, char *argv[], void *userdata) {
917         bool properties, new_line = false;
918         sd_bus *bus = userdata;
919         int r, i;
920         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
921         _cleanup_free_ char *path = NULL;
922
923         assert(bus);
924         assert(argv);
925
926         properties = !strstr(argv[0], "status");
927
928         pager_open(arg_no_pager, false);
929
930         if (argc <= 1) {
931                 const char *session, *p = "/org/freedesktop/login1/session/self";
932
933                 if (properties)
934                         /* If no argument is specified inspect the manager itself */
935                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
936
937                 /* And in the pretty case, show data of the calling session */
938                 session = getenv("XDG_SESSION_ID");
939                 if (session) {
940                         r = get_session_path(bus, session, &error, &path);
941                         if (r < 0) {
942                                 log_error("Failed to get session path: %s", bus_error_message(&error, r));
943                                 return r;
944                         }
945                         p = path;
946                 }
947
948                 return print_session_status_info(bus, p, &new_line);
949         }
950
951         for (i = 1; i < argc; i++) {
952                 r = get_session_path(bus, argv[i], &error, &path);
953                 if (r < 0) {
954                         log_error("Failed to get session path: %s", bus_error_message(&error, r));
955                         return r;
956                 }
957
958                 if (properties)
959                         r = show_properties(bus, path, &new_line);
960                 else
961                         r = print_session_status_info(bus, path, &new_line);
962
963                 if (r < 0)
964                         return r;
965         }
966
967         return 0;
968 }
969
970 static int show_user(int argc, char *argv[], void *userdata) {
971         bool properties, new_line = false;
972         sd_bus *bus = userdata;
973         int r, i;
974
975         assert(bus);
976         assert(argv);
977
978         properties = !strstr(argv[0], "status");
979
980         pager_open(arg_no_pager, false);
981
982         if (argc <= 1) {
983                 /* If not argument is specified inspect the manager
984                  * itself */
985                 if (properties)
986                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
987
988                 return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
989         }
990
991         for (i = 1; i < argc; i++) {
992                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
993                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
994                 const char *path = NULL;
995                 uid_t uid;
996
997                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
998                 if (r < 0)
999                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1000
1001                 r = sd_bus_call_method(
1002                                 bus,
1003                                 "org.freedesktop.login1",
1004                                 "/org/freedesktop/login1",
1005                                 "org.freedesktop.login1.Manager",
1006                                 "GetUser",
1007                                 &error, &reply,
1008                                 "u", (uint32_t) uid);
1009                 if (r < 0) {
1010                         log_error("Failed to get user: %s", bus_error_message(&error, r));
1011                         return r;
1012                 }
1013
1014                 r = sd_bus_message_read(reply, "o", &path);
1015                 if (r < 0)
1016                         return bus_log_parse_error(r);
1017
1018                 if (properties)
1019                         r = show_properties(bus, path, &new_line);
1020                 else
1021                         r = print_user_status_info(bus, path, &new_line);
1022
1023                 if (r < 0)
1024                         return r;
1025         }
1026
1027         return 0;
1028 }
1029
1030 static int show_seat(int argc, char *argv[], void *userdata) {
1031         bool properties, new_line = false;
1032         sd_bus *bus = userdata;
1033         int r, i;
1034
1035         assert(bus);
1036         assert(argv);
1037
1038         properties = !strstr(argv[0], "status");
1039
1040         pager_open(arg_no_pager, false);
1041
1042         if (argc <= 1) {
1043                 /* If not argument is specified inspect the manager
1044                  * itself */
1045                 if (properties)
1046                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
1047
1048                 return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line);
1049         }
1050
1051         for (i = 1; i < argc; i++) {
1052                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1053                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
1054                 const char *path = NULL;
1055
1056                 r = sd_bus_call_method(
1057                                 bus,
1058                                 "org.freedesktop.login1",
1059                                 "/org/freedesktop/login1",
1060                                 "org.freedesktop.login1.Manager",
1061                                 "GetSeat",
1062                                 &error, &reply,
1063                                 "s", argv[i]);
1064                 if (r < 0) {
1065                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
1066                         return r;
1067                 }
1068
1069                 r = sd_bus_message_read(reply, "o", &path);
1070                 if (r < 0)
1071                         return bus_log_parse_error(r);
1072
1073                 if (properties)
1074                         r = show_properties(bus, path, &new_line);
1075                 else
1076                         r = print_seat_status_info(bus, path, &new_line);
1077
1078                 if (r < 0)
1079                         return r;
1080         }
1081
1082         return 0;
1083 }
1084
1085 static int activate(int argc, char *argv[], void *userdata) {
1086         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1087         sd_bus *bus = userdata;
1088         char *short_argv[3];
1089         int r, i;
1090
1091         assert(bus);
1092         assert(argv);
1093
1094         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1095
1096         if (argc < 2) {
1097                 /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty
1098                  * session name, in which case logind will try to guess our session. */
1099
1100                 short_argv[0] = argv[0];
1101                 short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) "";
1102                 short_argv[2] = NULL;
1103
1104                 argv = short_argv;
1105                 argc = 2;
1106         }
1107
1108         for (i = 1; i < argc; i++) {
1109
1110                 r = sd_bus_call_method(
1111                                 bus,
1112                                 "org.freedesktop.login1",
1113                                 "/org/freedesktop/login1",
1114                                 "org.freedesktop.login1.Manager",
1115                                 streq(argv[0], "lock-session")      ? "LockSession" :
1116                                 streq(argv[0], "unlock-session")    ? "UnlockSession" :
1117                                 streq(argv[0], "terminate-session") ? "TerminateSession" :
1118                                                                       "ActivateSession",
1119                                 &error, NULL,
1120                                 "s", argv[i]);
1121                 if (r < 0) {
1122                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1123                         return r;
1124                 }
1125         }
1126
1127         return 0;
1128 }
1129
1130 static int kill_session(int argc, char *argv[], void *userdata) {
1131         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1132         sd_bus *bus = userdata;
1133         int r, i;
1134
1135         assert(bus);
1136         assert(argv);
1137
1138         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1139
1140         if (!arg_kill_who)
1141                 arg_kill_who = "all";
1142
1143         for (i = 1; i < argc; i++) {
1144
1145                 r = sd_bus_call_method(
1146                         bus,
1147                         "org.freedesktop.login1",
1148                         "/org/freedesktop/login1",
1149                         "org.freedesktop.login1.Manager",
1150                         "KillSession",
1151                         &error, NULL,
1152                         "ssi", argv[i], arg_kill_who, arg_signal);
1153                 if (r < 0) {
1154                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
1155                         return r;
1156                 }
1157         }
1158
1159         return 0;
1160 }
1161
1162 static int enable_linger(int argc, char *argv[], void *userdata) {
1163         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1164         sd_bus *bus = userdata;
1165         char* short_argv[3];
1166         bool b;
1167         int r, i;
1168
1169         assert(bus);
1170         assert(argv);
1171
1172         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1173
1174         b = streq(argv[0], "enable-linger");
1175
1176         if (argc < 2) {
1177                 /* No argument? Let's use an empty user name,
1178                  * then logind will use our user. */
1179
1180                 short_argv[0] = argv[0];
1181                 short_argv[1] = (char*) "";
1182                 short_argv[2] = NULL;
1183                 argv = short_argv;
1184                 argc = 2;
1185         }
1186
1187         for (i = 1; i < argc; i++) {
1188                 uid_t uid;
1189
1190                 if (isempty(argv[i]))
1191                         uid = UID_INVALID;
1192                 else {
1193                         r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1194                         if (r < 0)
1195                                 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1196                 }
1197
1198                 r = sd_bus_call_method(
1199                         bus,
1200                         "org.freedesktop.login1",
1201                         "/org/freedesktop/login1",
1202                         "org.freedesktop.login1.Manager",
1203                         "SetUserLinger",
1204                         &error, NULL,
1205                         "ubb", (uint32_t) uid, b, true);
1206                 if (r < 0) {
1207                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
1208                         return r;
1209                 }
1210         }
1211
1212         return 0;
1213 }
1214
1215 static int terminate_user(int argc, char *argv[], void *userdata) {
1216         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1217         sd_bus *bus = userdata;
1218         int r, i;
1219
1220         assert(bus);
1221         assert(argv);
1222
1223         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1224
1225         for (i = 1; i < argc; i++) {
1226                 uid_t uid;
1227
1228                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1229                 if (r < 0)
1230                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1231
1232                 r = sd_bus_call_method(
1233                         bus,
1234                         "org.freedesktop.login1",
1235                         "/org/freedesktop/login1",
1236                         "org.freedesktop.login1.Manager",
1237                         "TerminateUser",
1238                         &error, NULL,
1239                         "u", (uint32_t) uid);
1240                 if (r < 0) {
1241                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
1242                         return r;
1243                 }
1244         }
1245
1246         return 0;
1247 }
1248
1249 static int kill_user(int argc, char *argv[], void *userdata) {
1250         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1251         sd_bus *bus = userdata;
1252         int r, i;
1253
1254         assert(bus);
1255         assert(argv);
1256
1257         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1258
1259         if (!arg_kill_who)
1260                 arg_kill_who = "all";
1261
1262         for (i = 1; i < argc; i++) {
1263                 uid_t uid;
1264
1265                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1266                 if (r < 0)
1267                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1268
1269                 r = sd_bus_call_method(
1270                         bus,
1271                         "org.freedesktop.login1",
1272                         "/org/freedesktop/login1",
1273                         "org.freedesktop.login1.Manager",
1274                         "KillUser",
1275                         &error, NULL,
1276                         "ui", (uint32_t) uid, arg_signal);
1277                 if (r < 0) {
1278                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
1279                         return r;
1280                 }
1281         }
1282
1283         return 0;
1284 }
1285
1286 static int attach(int argc, char *argv[], void *userdata) {
1287         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1288         sd_bus *bus = userdata;
1289         int r, i;
1290
1291         assert(bus);
1292         assert(argv);
1293
1294         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1295
1296         for (i = 2; i < argc; i++) {
1297
1298                 r = sd_bus_call_method(
1299                         bus,
1300                         "org.freedesktop.login1",
1301                         "/org/freedesktop/login1",
1302                         "org.freedesktop.login1.Manager",
1303                         "AttachDevice",
1304                         &error, NULL,
1305                         "ssb", argv[1], argv[i], true);
1306
1307                 if (r < 0) {
1308                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
1309                         return r;
1310                 }
1311         }
1312
1313         return 0;
1314 }
1315
1316 static int flush_devices(int argc, char *argv[], void *userdata) {
1317         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1318         sd_bus *bus = userdata;
1319         int r;
1320
1321         assert(bus);
1322         assert(argv);
1323
1324         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1325
1326         r = sd_bus_call_method(
1327                         bus,
1328                         "org.freedesktop.login1",
1329                         "/org/freedesktop/login1",
1330                         "org.freedesktop.login1.Manager",
1331                         "FlushDevices",
1332                         &error, NULL,
1333                         "b", true);
1334         if (r < 0)
1335                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1336
1337         return r;
1338 }
1339
1340 static int lock_sessions(int argc, char *argv[], void *userdata) {
1341         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1342         sd_bus *bus = userdata;
1343         int r;
1344
1345         assert(bus);
1346         assert(argv);
1347
1348         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1349
1350         r = sd_bus_call_method(
1351                         bus,
1352                         "org.freedesktop.login1",
1353                         "/org/freedesktop/login1",
1354                         "org.freedesktop.login1.Manager",
1355                         streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1356                         &error, NULL,
1357                         NULL);
1358         if (r < 0)
1359                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1360
1361         return r;
1362 }
1363
1364 static int terminate_seat(int argc, char *argv[], void *userdata) {
1365         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1366         sd_bus *bus = userdata;
1367         int r, i;
1368
1369         assert(bus);
1370         assert(argv);
1371
1372         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1373
1374         for (i = 1; i < argc; i++) {
1375
1376                 r = sd_bus_call_method(
1377                         bus,
1378                         "org.freedesktop.login1",
1379                         "/org/freedesktop/login1",
1380                         "org.freedesktop.login1.Manager",
1381                         "TerminateSeat",
1382                         &error, NULL,
1383                         "s", argv[i]);
1384                 if (r < 0) {
1385                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1386                         return r;
1387                 }
1388         }
1389
1390         return 0;
1391 }
1392
1393 static int help(int argc, char *argv[], void *userdata) {
1394
1395         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1396                "Send control commands to or query the login manager.\n\n"
1397                "  -h --help                Show this help\n"
1398                "     --version             Show package version\n"
1399                "     --no-pager            Do not pipe output into a pager\n"
1400 #if 1 /// elogind supports --no-wall and --dry-run
1401                "     --no-wall             Do not print any wall message\n"
1402                "     --dry-run             Only print what would be done\n"
1403                "  -q --quiet               Suppress output\n"
1404 #endif // 1
1405                "     --no-legend           Do not show the headers and footers\n"
1406                "     --no-ask-password     Don't prompt for password\n"
1407                "  -H --host=[USER@]HOST    Operate on remote host\n"
1408                "  -M --machine=CONTAINER   Operate on local container\n"
1409                "  -p --property=NAME       Show only properties by this name\n"
1410                "  -a --all                 Show all properties, including empty ones\n"
1411                "     --value               When showing properties, only print the value\n"
1412                "  -l --full                Do not ellipsize output\n"
1413                "     --kill-who=WHO        Who to send signal to\n"
1414                "  -s --signal=SIGNAL       Which signal to send\n"
1415 #if 0 /// UNNEEDED by elogind
1416                "  -n --lines=INTEGER       Number of journal entries to show\n"
1417                "  -o --output=STRING       Change journal output mode (short, short-precise,\n"
1418                "                             short-iso, short-iso-precise, short-full,\n"
1419                "                             short-monotonic, short-unix, verbose, export,\n"
1420                "                             json, json-pretty, json-sse, cat)\n"
1421 #else
1422                 /// elogind can cancel shutdowns and allows to ignore inhibitors
1423                "  -c                       Cancel a pending shutdown or reboot\n"
1424                "  -i --ignore-inhibitors   When shutting down or sleeping, ignore inhibitors\n\n"
1425 #endif // 0
1426                "Session Commands:\n"
1427 #if 0 /// elogind has "list" as a shorthand for "list-sessions"
1428                "  list-sessions            List sessions\n"
1429 #else
1430                "  list[-sessions]          List sessions (default command)\n"
1431 #endif // 0
1432                "  session-status [ID...]   Show session status\n"
1433                "  show-session [ID...]     Show properties of sessions or the manager\n"
1434                "  activate [ID]            Activate a session\n"
1435                "  lock-session [ID...]     Screen lock one or more sessions\n"
1436                "  unlock-session [ID...]   Screen unlock one or more sessions\n"
1437                "  lock-sessions            Screen lock all current sessions\n"
1438                "  unlock-sessions          Screen unlock all current sessions\n"
1439                "  terminate-session ID...  Terminate one or more sessions\n"
1440                "  kill-session ID...       Send signal to processes of a session\n\n"
1441                "User Commands:\n"
1442                "  list-users               List users\n"
1443                "  user-status [USER...]    Show user status\n"
1444                "  show-user [USER...]      Show properties of users or the manager\n"
1445                "  enable-linger [USER...]  Enable linger state of one or more users\n"
1446                "  disable-linger [USER...] Disable linger state of one or more users\n"
1447                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1448                "  kill-user USER...        Send signal to processes of a user\n\n"
1449                "Seat Commands:\n"
1450                "  list-seats               List seats\n"
1451                "  seat-status [NAME...]    Show seat status\n"
1452                "  show-seat [NAME...]      Show properties of seats or the manager\n"
1453                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1454                "  flush-devices            Flush all device associations\n"
1455 #if 0 /// elogind adds some system commands to loginctl
1456                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
1457 #else
1458                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n\n"
1459                "System Commands:\n"
1460                "  poweroff [TIME] [WALL...] Turn off the machine\n"
1461                "  reboot   [TIME] [WALL...] Reboot the machine\n"
1462                "  suspend                   Suspend the machine to memory\n"
1463                "  hibernate                 Suspend the machine to disk\n"
1464                "  hybrid-sleep              Suspend the machine to memory and disk\n"
1465 #endif // 0
1466                , program_invocation_short_name);
1467
1468         return 0;
1469 }
1470
1471 static int parse_argv(int argc, char *argv[]) {
1472
1473         enum {
1474                 ARG_VERSION = 0x100,
1475                 ARG_VALUE,
1476                 ARG_NO_PAGER,
1477 #if 1 /// elogind supports --no-wall and --dry-run
1478                 ARG_NO_WALL,
1479                 ARG_DRY_RUN,
1480 #endif // 1
1481                 ARG_NO_LEGEND,
1482                 ARG_KILL_WHO,
1483                 ARG_NO_ASK_PASSWORD,
1484         };
1485
1486         static const struct option options[] = {
1487                 { "help",            no_argument,       NULL, 'h'                 },
1488                 { "version",         no_argument,       NULL, ARG_VERSION         },
1489                 { "property",        required_argument, NULL, 'p'                 },
1490                 { "all",             no_argument,       NULL, 'a'                 },
1491                 { "value",           no_argument,       NULL, ARG_VALUE           },
1492                 { "full",            no_argument,       NULL, 'l'                 },
1493                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1494 #if 1 /// elogind supports --no-wall, --dry-run and --quiet
1495                 { "no-wall",         no_argument,       NULL, ARG_NO_WALL         },
1496                 { "dry-run",         no_argument,       NULL, ARG_DRY_RUN         },
1497                 { "quiet",           no_argument,       NULL, 'q'                 },
1498 #endif // 1
1499                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1500                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1501                 { "signal",          required_argument, NULL, 's'                 },
1502                 { "host",            required_argument, NULL, 'H'                 },
1503                 { "machine",         required_argument, NULL, 'M'                 },
1504                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1505 #if 0 /// UNNEEDED by elogind
1506                 { "lines",           required_argument, NULL, 'n'                 },
1507                 { "output",          required_argument, NULL, 'o'                 },
1508 #else
1509                 /// elogind allows to ignore inhibitors for system commands.
1510                 { "ignore-inhibitors", no_argument,     NULL, 'i'                 },
1511 #endif // 0
1512                 {}
1513         };
1514
1515         int c, r;
1516
1517         assert(argc >= 0);
1518         assert(argv);
1519
1520 #if 0 /// elogind adds some system commands to loginctl
1521         while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
1522 #else
1523         while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:ci", options, NULL)) >= 0)
1524 #endif // 0
1525
1526                 switch (c) {
1527
1528                 case 'h':
1529                         help(0, NULL, NULL);
1530                         return 0;
1531
1532                 case ARG_VERSION:
1533                         return version();
1534
1535                 case 'p': {
1536                         r = strv_extend(&arg_property, optarg);
1537                         if (r < 0)
1538                                 return log_oom();
1539
1540                         /* If the user asked for a particular
1541                          * property, show it to him, even if it is
1542                          * empty. */
1543                         arg_all = true;
1544                         break;
1545                 }
1546
1547                 case 'a':
1548                         arg_all = true;
1549                         break;
1550
1551                 case ARG_VALUE:
1552                         arg_value = true;
1553                         break;
1554
1555                 case 'l':
1556                         arg_full = true;
1557                         break;
1558
1559 #if 0 /// UNNEEDED by elogind
1560                 case 'n':
1561                         if (safe_atou(optarg, &arg_lines) < 0) {
1562                                 log_error("Failed to parse lines '%s'", optarg);
1563                                 return -EINVAL;
1564                         }
1565                         break;
1566
1567                 case 'o':
1568                         arg_output = output_mode_from_string(optarg);
1569                         if (arg_output < 0) {
1570                                 log_error("Unknown output '%s'.", optarg);
1571                                 return -EINVAL;
1572                         }
1573                         break;
1574 #endif // 0
1575
1576                 case ARG_NO_PAGER:
1577                         arg_no_pager = true;
1578                         break;
1579 #if 1 /// elogind supports --no-wall, -dry-run and --quiet
1580                 case ARG_NO_WALL:
1581                         arg_no_wall = true;
1582                         break;
1583
1584                 case ARG_DRY_RUN:
1585                         arg_dry_run = true;
1586                         break;
1587
1588                 case 'q':
1589                         arg_quiet = true;
1590                         break;
1591
1592 #endif // 1
1593
1594                 case ARG_NO_LEGEND:
1595                         arg_legend = false;
1596                         break;
1597
1598                 case ARG_NO_ASK_PASSWORD:
1599                         arg_ask_password = false;
1600                         break;
1601
1602                 case ARG_KILL_WHO:
1603                         arg_kill_who = optarg;
1604                         break;
1605
1606                 case 's':
1607                         arg_signal = signal_from_string_try_harder(optarg);
1608                         if (arg_signal < 0) {
1609                                 log_error("Failed to parse signal string %s.", optarg);
1610                                 return -EINVAL;
1611                         }
1612                         break;
1613
1614                 case 'H':
1615                         arg_transport = BUS_TRANSPORT_REMOTE;
1616                         arg_host = optarg;
1617                         break;
1618
1619                 case 'M':
1620                         arg_transport = BUS_TRANSPORT_MACHINE;
1621                         arg_host = optarg;
1622                         break;
1623 #if 1 /// elogind can cancel shutdowns and allows to ignore inhibitors
1624                 case 'c':
1625                         arg_action = ACTION_CANCEL_SHUTDOWN;
1626                         break;
1627
1628                 case 'i':
1629                         arg_ignore_inhibitors = true;
1630                         break;
1631 #endif // 1
1632                 case '?':
1633                         return -EINVAL;
1634
1635                 default:
1636                         assert_not_reached("Unhandled option");
1637                 }
1638
1639         return 1;
1640 }
1641
1642 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1643
1644         static const Verb verbs[] = {
1645                 { "help",              VERB_ANY, VERB_ANY, 0,            help              },
1646 #if 0 /// elogind has "list" as a shorthand for "list-sessions"
1647                 { "list-sessions",     VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
1648 #else
1649                 { "list",              VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
1650                 { "list-sessions",     VERB_ANY, 1,        0,            list_sessions     },
1651 #endif // 0
1652                 { "session-status",    VERB_ANY, VERB_ANY, 0,            show_session      },
1653                 { "show-session",      VERB_ANY, VERB_ANY, 0,            show_session      },
1654                 { "activate",          VERB_ANY, 2,        0,            activate          },
1655                 { "lock-session",      VERB_ANY, VERB_ANY, 0,            activate          },
1656                 { "unlock-session",    VERB_ANY, VERB_ANY, 0,            activate          },
1657                 { "lock-sessions",     VERB_ANY, 1,        0,            lock_sessions     },
1658                 { "unlock-sessions",   VERB_ANY, 1,        0,            lock_sessions     },
1659                 { "terminate-session", 2,        VERB_ANY, 0,            activate          },
1660                 { "kill-session",      2,        VERB_ANY, 0,            kill_session      },
1661                 { "list-users",        VERB_ANY, 1,        0,            list_users        },
1662                 { "user-status",       VERB_ANY, VERB_ANY, 0,            show_user         },
1663                 { "show-user",         VERB_ANY, VERB_ANY, 0,            show_user         },
1664                 { "enable-linger",     VERB_ANY, VERB_ANY, 0,            enable_linger     },
1665                 { "disable-linger",    VERB_ANY, VERB_ANY, 0,            enable_linger     },
1666                 { "terminate-user",    2,        VERB_ANY, 0,            terminate_user    },
1667                 { "kill-user",         2,        VERB_ANY, 0,            kill_user         },
1668                 { "list-seats",        VERB_ANY, 1,        0,            list_seats        },
1669                 { "seat-status",       VERB_ANY, VERB_ANY, 0,            show_seat         },
1670                 { "show-seat",         VERB_ANY, VERB_ANY, 0,            show_seat         },
1671                 { "attach",            3,        VERB_ANY, 0,            attach            },
1672                 { "flush-devices",     VERB_ANY, 1,        0,            flush_devices     },
1673                 { "terminate-seat",    2,        VERB_ANY, 0,            terminate_seat    },
1674 #if 1 /// elogind adds some system commands to loginctl
1675                 { "poweroff",          VERB_ANY, VERB_ANY, 0,            start_special     },
1676                 { "reboot",            VERB_ANY, VERB_ANY, 0,            start_special     },
1677                 { "suspend",           VERB_ANY, 1,        0,            start_special     },
1678                 { "hibernate",         VERB_ANY, 1,        0,            start_special     },
1679                 { "hybrid-sleep",      VERB_ANY, 1,        0,            start_special     },
1680                 { "cancel-shutdown",   VERB_ANY, 1,        0,            start_special     },
1681 #endif // 1
1682                 {}
1683         };
1684
1685 #if 1 /// elogind can do shutdown and allows its cancellation
1686         if ((argc == optind) && (ACTION_CANCEL_SHUTDOWN == arg_action))
1687                 return elogind_cancel_shutdown(bus);
1688 #endif // 1
1689         return dispatch_verb(argc, argv, verbs, bus);
1690 }
1691
1692 int main(int argc, char *argv[]) {
1693         sd_bus *bus = NULL;
1694         int r;
1695
1696         setlocale(LC_ALL, "");
1697         elogind_set_program_name(argv[0]);
1698         log_parse_environment();
1699         log_open();
1700         sigbus_install();
1701
1702         r = parse_argv(argc, argv);
1703         if (r <= 0)
1704                 goto finish;
1705
1706         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
1707         if (r < 0) {
1708                 log_error_errno(r, "Failed to create bus connection: %m");
1709                 goto finish;
1710         }
1711
1712         sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1713
1714         r = loginctl_main(argc, argv, bus);
1715
1716 finish:
1717         /* make sure we terminate the bus connection first, and then close the
1718          * pager, see issue #3543 for the details. */
1719         sd_bus_flush_close_unref(bus);
1720         pager_close();
1721 #if 0 /// elogind does that in elogind_cleanup()
1722         polkit_agent_close();
1723 #endif // 0
1724
1725         strv_free(arg_property);
1726
1727 #if 1 /// elogind has some own cleanups to do
1728         elogind_cleanup();
1729 #endif // 1
1730         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1731 }