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