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