chiark / gitweb /
loginctl: port to generic verbs.h API
[elogind.git] / src / login / loginctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <locale.h>
28
29 #include "sd-bus.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "log.h"
33 #include "util.h"
34 #include "macro.h"
35 #include "pager.h"
36 #include "build.h"
37 #include "strv.h"
38 #include "unit-name.h"
39 #include "sysfs-show.h"
40 #include "logs-show.h"
41 #include "cgroup-show.h"
42 #include "cgroup-util.h"
43 #include "spawn-polkit-agent.h"
44 #include "verbs.h"
45
46 static char **arg_property = NULL;
47 static bool arg_all = false;
48 static bool arg_full = false;
49 static bool arg_no_pager = false;
50 static bool arg_legend = true;
51 static const char *arg_kill_who = NULL;
52 static int arg_signal = SIGTERM;
53 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
54 static char *arg_host = NULL;
55 static bool arg_ask_password = true;
56 static unsigned arg_lines = 10;
57 static OutputMode arg_output = OUTPUT_SHORT;
58
59 static void pager_open_if_enabled(void) {
60
61         if (arg_no_pager)
62                 return;
63
64         pager_open(false);
65 }
66
67 static void polkit_agent_open_if_enabled(void) {
68
69         /* Open the polkit agent as a child process if necessary */
70
71         if (!arg_ask_password)
72                 return;
73
74         if (arg_transport != BUS_TRANSPORT_LOCAL)
75                 return;
76
77         polkit_agent_open();
78 }
79
80 static OutputFlags get_output_flags(void) {
81
82         return
83                 arg_all * OUTPUT_SHOW_ALL |
84                 arg_full * OUTPUT_FULL_WIDTH |
85                 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
86                 on_tty() * OUTPUT_COLOR;
87 }
88
89 static int list_sessions(int argc, char *argv[], void *userdata) {
90         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
91         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
92         const char *id, *user, *seat, *object;
93         sd_bus *bus = userdata;
94         unsigned k = 0;
95         uint32_t uid;
96         int r;
97
98         assert(bus);
99         assert(argv);
100
101         pager_open_if_enabled();
102
103         r = sd_bus_call_method(
104                         bus,
105                         "org.freedesktop.login1",
106                         "/org/freedesktop/login1",
107                         "org.freedesktop.login1.Manager",
108                         "ListSessions",
109                         &error, &reply,
110                         "");
111         if (r < 0) {
112                 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
113                 return r;
114         }
115
116         r = sd_bus_message_enter_container(reply, 'a', "(susso)");
117         if (r < 0)
118                 return bus_log_parse_error(r);
119
120         if (arg_legend)
121                 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
122
123         while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
124                 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
125                 k++;
126         }
127         if (r < 0)
128                 return bus_log_parse_error(r);
129
130         if (arg_legend)
131                 printf("\n%u sessions listed.\n", k);
132
133         return 0;
134 }
135
136 static int list_users(int argc, char *argv[], void *userdata) {
137         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
138         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
139         const char *user, *object;
140         sd_bus *bus = userdata;
141         unsigned k = 0;
142         uint32_t uid;
143         int r;
144
145         assert(bus);
146         assert(argv);
147
148         pager_open_if_enabled();
149
150         r = sd_bus_call_method(
151                         bus,
152                         "org.freedesktop.login1",
153                         "/org/freedesktop/login1",
154                         "org.freedesktop.login1.Manager",
155                         "ListUsers",
156                         &error, &reply,
157                         "");
158         if (r < 0) {
159                 log_error("Failed to list users: %s", bus_error_message(&error, r));
160                 return r;
161         }
162
163         r = sd_bus_message_enter_container(reply, 'a', "(uso)");
164         if (r < 0)
165                 return bus_log_parse_error(r);
166
167         if (arg_legend)
168                 printf("%10s %-16s\n", "UID", "USER");
169
170         while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
171                 printf("%10u %-16s\n", (unsigned) uid, user);
172                 k++;
173         }
174         if (r < 0)
175                 return bus_log_parse_error(r);
176
177         if (arg_legend)
178                 printf("\n%u users listed.\n", k);
179
180         return 0;
181 }
182
183 static int list_seats(int argc, char *argv[], void *userdata) {
184         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
185         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
186         const char *seat, *object;
187         sd_bus *bus = userdata;
188         unsigned k = 0;
189         int r;
190
191         assert(bus);
192         assert(argv);
193
194         pager_open_if_enabled();
195
196         r = sd_bus_call_method(
197                         bus,
198                         "org.freedesktop.login1",
199                         "/org/freedesktop/login1",
200                         "org.freedesktop.login1.Manager",
201                         "ListSeats",
202                         &error, &reply,
203                         "");
204         if (r < 0) {
205                 log_error("Failed to list seats: %s", bus_error_message(&error, r));
206                 return r;
207         }
208
209         r = sd_bus_message_enter_container(reply, 'a', "(so)");
210         if (r < 0)
211                 return bus_log_parse_error(r);
212
213         if (arg_legend)
214                 printf("%-16s\n", "SEAT");
215
216         while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
217                 printf("%-16s\n", seat);
218                 k++;
219         }
220         if (r < 0)
221                 return bus_log_parse_error(r);
222
223         if (arg_legend)
224                 printf("\n%u seats listed.\n", k);
225
226         return 0;
227 }
228
229 static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
230         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
231         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
232         _cleanup_free_ char *path = NULL;
233         const char *cgroup;
234         int r;
235         unsigned c;
236
237         assert(bus);
238         assert(unit);
239
240         if (arg_transport != BUS_TRANSPORT_LOCAL)
241                 return 0;
242
243         path = unit_dbus_path_from_name(unit);
244         if (!path)
245                 return -ENOMEM;
246
247         r = sd_bus_get_property(
248                         bus,
249                         "org.freedesktop.systemd1",
250                         path,
251                         interface,
252                         "ControlGroup",
253                         &error, &reply, "s");
254         if (r < 0)
255                 return r;
256
257         r = sd_bus_message_read(reply, "s", &cgroup);
258         if (r < 0)
259                 return r;
260
261         if (isempty(cgroup))
262                 return 0;
263
264         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
265                 return 0;
266
267         c = columns();
268         if (c > 18)
269                 c -= 18;
270         else
271                 c = 0;
272
273         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, get_output_flags());
274         return 0;
275 }
276
277 typedef struct SessionStatusInfo {
278         const char *id;
279         uid_t uid;
280         const char *name;
281         struct dual_timestamp timestamp;
282         unsigned int vtnr;
283         const char *seat;
284         const char *tty;
285         const char *display;
286         bool remote;
287         const char *remote_host;
288         const char *remote_user;
289         const char *service;
290         pid_t leader;
291         const char *type;
292         const char *class;
293         const char *state;
294         const char *scope;
295         const char *desktop;
296 } SessionStatusInfo;
297
298 typedef struct UserStatusInfo {
299         uid_t uid;
300         const char *name;
301         struct dual_timestamp timestamp;
302         const char *state;
303         char **sessions;
304         const char *display;
305         const char *slice;
306 } UserStatusInfo;
307
308 typedef struct SeatStatusInfo {
309         const char *id;
310         const char *active_session;
311         char **sessions;
312 } SeatStatusInfo;
313
314 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
315         const char *contents;
316         int r;
317
318         r = sd_bus_message_peek_type(m, NULL, &contents);
319         if (r < 0)
320                 return r;
321
322         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
323         if (r < 0)
324                 return r;
325
326         if (contents[0] == 's' || contents[0] == 'o') {
327                 const char *s;
328                 char **p = (char **) userdata;
329
330                 r = sd_bus_message_read_basic(m, contents[0], &s);
331                 if (r < 0)
332                         return r;
333
334                 free(*p);
335                 *p = strdup(s);
336
337                 if (!*p)
338                         return -ENOMEM;
339         } else {
340                 r = sd_bus_message_read_basic(m, contents[0], userdata);
341                 if (r < 0)
342                         return r;
343         }
344
345         r = sd_bus_message_skip(m, contents+1);
346         if (r < 0)
347                 return r;
348
349         r = sd_bus_message_exit_container(m);
350         if (r < 0)
351                 return r;
352
353         return 0;
354 }
355
356 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
357         const char *name;
358         int r;
359
360         assert(bus);
361         assert(m);
362
363         r = sd_bus_message_enter_container(m, 'a', "(so)");
364         if (r < 0)
365                 return r;
366
367         while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
368                 r = strv_extend(userdata, name);
369                 if (r < 0)
370                         return r;
371         }
372         if (r < 0)
373                 return r;
374
375         return sd_bus_message_exit_container(m);
376 }
377
378 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
379
380         static const struct bus_properties_map map[]  = {
381                 { "Id",                  "s",    NULL,                     offsetof(SessionStatusInfo, id)                  },
382                 { "Name",                "s",    NULL,                     offsetof(SessionStatusInfo, name)                },
383                 { "TTY",                 "s",    NULL,                     offsetof(SessionStatusInfo, tty)                 },
384                 { "Display",             "s",    NULL,                     offsetof(SessionStatusInfo, display)             },
385                 { "RemoteHost",          "s",    NULL,                     offsetof(SessionStatusInfo, remote_host)         },
386                 { "RemoteUser",          "s",    NULL,                     offsetof(SessionStatusInfo, remote_user)         },
387                 { "Service",             "s",    NULL,                     offsetof(SessionStatusInfo, service)             },
388                 { "Desktop",             "s",    NULL,                     offsetof(SessionStatusInfo, desktop)             },
389                 { "Type",                "s",    NULL,                     offsetof(SessionStatusInfo, type)                },
390                 { "Class",               "s",    NULL,                     offsetof(SessionStatusInfo, class)               },
391                 { "Scope",               "s",    NULL,                     offsetof(SessionStatusInfo, scope)               },
392                 { "State",               "s",    NULL,                     offsetof(SessionStatusInfo, state)               },
393                 { "VTNr",                "u",    NULL,                     offsetof(SessionStatusInfo, vtnr)                },
394                 { "Leader",              "u",    NULL,                     offsetof(SessionStatusInfo, leader)              },
395                 { "Remote",              "b",    NULL,                     offsetof(SessionStatusInfo, remote)              },
396                 { "Timestamp",           "t",    NULL,                     offsetof(SessionStatusInfo, timestamp.realtime)  },
397                 { "TimestampMonotonic",  "t",    NULL,                     offsetof(SessionStatusInfo, timestamp.monotonic) },
398                 { "User",                "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid)                 },
399                 { "Seat",                "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat)                },
400                 {}
401         };
402
403         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
404         char since2[FORMAT_TIMESTAMP_MAX], *s2;
405         SessionStatusInfo i = {};
406         int r;
407
408         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
409         if (r < 0)
410                 return log_error_errno(r, "Could not get properties: %m");
411
412         if (*new_line)
413                 printf("\n");
414
415         *new_line = true;
416
417         printf("%s - ", strna(i.id));
418
419         if (i.name)
420                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
421         else
422                 printf("%u\n", (unsigned) i.uid);
423
424         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
425         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
426
427         if (s1)
428                 printf("\t   Since: %s; %s\n", s2, s1);
429         else if (s2)
430                 printf("\t   Since: %s\n", s2);
431
432         if (i.leader > 0) {
433                 _cleanup_free_ char *t = NULL;
434
435                 printf("\t  Leader: %u", (unsigned) i.leader);
436
437                 get_process_comm(i.leader, &t);
438                 if (t)
439                         printf(" (%s)", t);
440
441                 printf("\n");
442         }
443
444         if (!isempty(i.seat)) {
445                 printf("\t    Seat: %s", i.seat);
446
447                 if (i.vtnr > 0)
448                         printf("; vc%u", i.vtnr);
449
450                 printf("\n");
451         }
452
453         if (i.tty)
454                 printf("\t     TTY: %s\n", i.tty);
455         else if (i.display)
456                 printf("\t Display: %s\n", i.display);
457
458         if (i.remote_host && i.remote_user)
459                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
460         else if (i.remote_host)
461                 printf("\t  Remote: %s\n", i.remote_host);
462         else if (i.remote_user)
463                 printf("\t  Remote: user %s\n", i.remote_user);
464         else if (i.remote)
465                 printf("\t  Remote: Yes\n");
466
467         if (i.service) {
468                 printf("\t Service: %s", i.service);
469
470                 if (i.type)
471                         printf("; type %s", i.type);
472
473                 if (i.class)
474                         printf("; class %s", i.class);
475
476                 printf("\n");
477         } else if (i.type) {
478                 printf("\t    Type: %s", i.type);
479
480                 if (i.class)
481                         printf("; class %s", i.class);
482
483                 printf("\n");
484         } else if (i.class)
485                 printf("\t   Class: %s\n", i.class);
486
487         if (!isempty(i.desktop))
488                 printf("\t Desktop: %s\n", i.desktop);
489
490         if (i.state)
491                 printf("\t   State: %s\n", i.state);
492
493         if (i.scope) {
494                 printf("\t    Unit: %s\n", i.scope);
495                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
496
497                 if (arg_transport == BUS_TRANSPORT_LOCAL) {
498
499                         show_journal_by_unit(
500                                         stdout,
501                                         i.scope,
502                                         arg_output,
503                                         0,
504                                         i.timestamp.monotonic,
505                                         arg_lines,
506                                         0,
507                                         get_output_flags() | OUTPUT_BEGIN_NEWLINE,
508                                         SD_JOURNAL_LOCAL_ONLY,
509                                         true,
510                                         NULL);
511                 }
512         }
513
514         return 0;
515 }
516
517 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
518
519         static const struct bus_properties_map map[]  = {
520                 { "Name",               "s",     NULL,                     offsetof(UserStatusInfo, name)                },
521                 { "Slice",              "s",     NULL,                     offsetof(UserStatusInfo, slice)               },
522                 { "State",              "s",     NULL,                     offsetof(UserStatusInfo, state)               },
523                 { "UID",                "u",     NULL,                     offsetof(UserStatusInfo, uid)                 },
524                 { "Timestamp",          "t",     NULL,                     offsetof(UserStatusInfo, timestamp.realtime)  },
525                 { "TimestampMonotonic", "t",     NULL,                     offsetof(UserStatusInfo, timestamp.monotonic) },
526                 { "Display",            "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display)             },
527                 { "Sessions",           "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions)            },
528                 {}
529         };
530
531         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
532         char since2[FORMAT_TIMESTAMP_MAX], *s2;
533         UserStatusInfo i = {};
534         int r;
535
536         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
537         if (r < 0) {
538                 log_error_errno(r, "Could not get properties: %m");
539                 goto finish;
540         }
541
542         if (*new_line)
543                 printf("\n");
544
545         *new_line = true;
546
547         if (i.name)
548                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
549         else
550                 printf("%u\n", (unsigned) i.uid);
551
552         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
553         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
554
555         if (s1)
556                 printf("\t   Since: %s; %s\n", s2, s1);
557         else if (s2)
558                 printf("\t   Since: %s\n", s2);
559
560         if (!isempty(i.state))
561                 printf("\t   State: %s\n", i.state);
562
563         if (!strv_isempty(i.sessions)) {
564                 char **l;
565                 printf("\tSessions:");
566
567                 STRV_FOREACH(l, i.sessions) {
568                         if (streq_ptr(*l, i.display))
569                                 printf(" *%s", *l);
570                         else
571                                 printf(" %s", *l);
572                 }
573
574                 printf("\n");
575         }
576
577         if (i.slice) {
578                 printf("\t    Unit: %s\n", i.slice);
579                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
580
581                 show_journal_by_unit(
582                                 stdout,
583                                 i.slice,
584                                 arg_output,
585                                 0,
586                                 i.timestamp.monotonic,
587                                 arg_lines,
588                                 0,
589                                 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
590                                 SD_JOURNAL_LOCAL_ONLY,
591                                 true,
592                                 NULL);
593         }
594
595 finish:
596         strv_free(i.sessions);
597
598         return r;
599 }
600
601 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
602
603         static const struct bus_properties_map map[]  = {
604                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
605                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
606                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
607                 {}
608         };
609
610         SeatStatusInfo i = {};
611         int r;
612
613         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
614         if (r < 0) {
615                 log_error_errno(r, "Could not get properties: %m");
616                 goto finish;
617         }
618
619         if (*new_line)
620                 printf("\n");
621
622         *new_line = true;
623
624         printf("%s\n", strna(i.id));
625
626         if (!strv_isempty(i.sessions)) {
627                 char **l;
628                 printf("\tSessions:");
629
630                 STRV_FOREACH(l, i.sessions) {
631                         if (streq_ptr(*l, i.active_session))
632                                 printf(" *%s", *l);
633                         else
634                                 printf(" %s", *l);
635                 }
636
637                 printf("\n");
638         }
639
640         if (arg_transport == BUS_TRANSPORT_LOCAL) {
641                 unsigned c;
642
643                 c = columns();
644                 if (c > 21)
645                         c -= 21;
646                 else
647                         c = 0;
648
649                 printf("\t Devices:\n");
650
651                 show_sysfs(i.id, "\t\t  ", c);
652         }
653
654 finish:
655         strv_free(i.sessions);
656
657         return r;
658 }
659
660 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
661         int r;
662
663         if (*new_line)
664                 printf("\n");
665
666         *new_line = true;
667
668         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
669         if (r < 0)
670                 log_error_errno(r, "Could not get properties: %m");
671
672         return r;
673 }
674
675 static int show_session(int argc, char *argv[], void *userdata) {
676         bool properties, new_line = false;
677         sd_bus *bus = userdata;
678         int r, i;
679
680         assert(bus);
681         assert(argv);
682
683         properties = !strstr(argv[0], "status");
684
685         pager_open_if_enabled();
686
687         if (properties && argc <= 1) {
688                 /* If not argument is specified inspect the manager
689                  * itself */
690                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
691         }
692
693         for (i = 1; i < argc; i++) {
694                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
695                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
696                 const char *path = NULL;
697
698                 r = sd_bus_call_method(
699                                 bus,
700                                 "org.freedesktop.login1",
701                                 "/org/freedesktop/login1",
702                                 "org.freedesktop.login1.Manager",
703                                 "GetSession",
704                                 &error, &reply,
705                                 "s", argv[i]);
706                 if (r < 0) {
707                         log_error("Failed to get session: %s", bus_error_message(&error, r));
708                         return r;
709                 }
710
711                 r = sd_bus_message_read(reply, "o", &path);
712                 if (r < 0)
713                         return bus_log_parse_error(r);
714
715                 if (properties)
716                         r = show_properties(bus, path, &new_line);
717                 else
718                         r = print_session_status_info(bus, path, &new_line);
719
720                 if (r < 0)
721                         return r;
722         }
723
724         return 0;
725 }
726
727 static int show_user(int argc, char *argv[], void *userdata) {
728         bool properties, new_line = false;
729         sd_bus *bus = userdata;
730         int r, i;
731
732         assert(bus);
733         assert(argv);
734
735         properties = !strstr(argv[0], "status");
736
737         pager_open_if_enabled();
738
739         if (properties && argc <= 1) {
740                 /* If not argument is specified inspect the manager
741                  * itself */
742                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
743         }
744
745         for (i = 1; i < argc; i++) {
746                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
747                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
748                 const char *path = NULL;
749                 uid_t uid;
750
751                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
752                 if (r < 0)
753                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
754
755                 r = sd_bus_call_method(
756                                 bus,
757                                 "org.freedesktop.login1",
758                                 "/org/freedesktop/login1",
759                                 "org.freedesktop.login1.Manager",
760                                 "GetUser",
761                                 &error, &reply,
762                                 "u", (uint32_t) uid);
763                 if (r < 0) {
764                         log_error("Failed to get user: %s", bus_error_message(&error, r));
765                         return r;
766                 }
767
768                 r = sd_bus_message_read(reply, "o", &path);
769                 if (r < 0)
770                         return bus_log_parse_error(r);
771
772                 if (properties)
773                         r = show_properties(bus, path, &new_line);
774                 else
775                         r = print_user_status_info(bus, path, &new_line);
776
777                 if (r < 0)
778                         return r;
779         }
780
781         return 0;
782 }
783
784 static int show_seat(int argc, char *argv[], void *userdata) {
785         bool properties, new_line = false;
786         sd_bus *bus = userdata;
787         int r, i;
788
789         assert(bus);
790         assert(argv);
791
792         properties = !strstr(argv[0], "status");
793
794         pager_open_if_enabled();
795
796         if (properties && argc <= 1) {
797                 /* If not argument is specified inspect the manager
798                  * itself */
799                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
800         }
801
802         for (i = 1; i < argc; i++) {
803                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
804                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
805                 const char *path = NULL;
806
807                 r = sd_bus_call_method(
808                                 bus,
809                                 "org.freedesktop.login1",
810                                 "/org/freedesktop/login1",
811                                 "org.freedesktop.login1.Manager",
812                                 "GetSeat",
813                                 &error, &reply,
814                                 "s", argv[i]);
815                 if (r < 0) {
816                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
817                         return r;
818                 }
819
820                 r = sd_bus_message_read(reply, "o", &path);
821                 if (r < 0)
822                         return bus_log_parse_error(r);
823
824                 if (properties)
825                         r = show_properties(bus, path, &new_line);
826                 else
827                         r = print_seat_status_info(bus, path, &new_line);
828
829                 if (r < 0)
830                         return r;
831         }
832
833         return 0;
834 }
835
836 static int activate(int argc, char *argv[], void *userdata) {
837         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
838         sd_bus *bus = userdata;
839         int r, i;
840
841         assert(bus);
842         assert(argv);
843
844         polkit_agent_open_if_enabled();
845
846         for (i = 1; i < argc; i++) {
847
848                 r = sd_bus_call_method (
849                                 bus,
850                                 "org.freedesktop.login1",
851                                 "/org/freedesktop/login1",
852                                 "org.freedesktop.login1.Manager",
853                                 streq(argv[0], "lock-session")      ? "LockSession" :
854                                 streq(argv[0], "unlock-session")    ? "UnlockSession" :
855                                 streq(argv[0], "terminate-session") ? "TerminateSession" :
856                                                                       "ActivateSession",
857                                 &error, NULL,
858                                 "s", argv[i]);
859                 if (r < 0) {
860                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
861                         return r;
862                 }
863         }
864
865         return 0;
866 }
867
868 static int kill_session(int argc, char *argv[], void *userdata) {
869         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
870         sd_bus *bus = userdata;
871         int r, i;
872
873         assert(bus);
874         assert(argv);
875
876         polkit_agent_open_if_enabled();
877
878         if (!arg_kill_who)
879                 arg_kill_who = "all";
880
881         for (i = 1; i < argc; i++) {
882
883                 r = sd_bus_call_method (
884                         bus,
885                         "org.freedesktop.login1",
886                         "/org/freedesktop/login1",
887                         "org.freedesktop.login1.Manager",
888                         "KillSession",
889                         &error, NULL,
890                         "ssi", argv[i], arg_kill_who, arg_signal);
891                 if (r < 0) {
892                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
893                         return r;
894                 }
895         }
896
897         return 0;
898 }
899
900 static int enable_linger(int argc, char *argv[], void *userdata) {
901         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
902         sd_bus *bus = userdata;
903         bool b;
904         int r, i;
905
906         assert(bus);
907         assert(argv);
908
909         polkit_agent_open_if_enabled();
910
911         b = streq(argv[0], "enable-linger");
912
913         for (i = 1; i < argc; i++) {
914                 uid_t uid;
915
916                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
917                 if (r < 0)
918                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
919
920                 r = sd_bus_call_method (
921                         bus,
922                         "org.freedesktop.login1",
923                         "/org/freedesktop/login1",
924                         "org.freedesktop.login1.Manager",
925                         "SetUserLinger",
926                         &error, NULL,
927                         "ubb", (uint32_t) uid, b, true);
928                 if (r < 0) {
929                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
930                         return r;
931                 }
932         }
933
934         return 0;
935 }
936
937 static int terminate_user(int argc, char *argv[], void *userdata) {
938         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
939         sd_bus *bus = userdata;
940         int r, i;
941
942         assert(bus);
943         assert(argv);
944
945         polkit_agent_open_if_enabled();
946
947         for (i = 1; i < argc; i++) {
948                 uid_t uid;
949
950                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
951                 if (r < 0)
952                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
953
954                 r = sd_bus_call_method (
955                         bus,
956                         "org.freedesktop.login1",
957                         "/org/freedesktop/login1",
958                         "org.freedesktop.login1.Manager",
959                         "TerminateUser",
960                         &error, NULL,
961                         "u", (uint32_t) uid);
962                 if (r < 0) {
963                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
964                         return r;
965                 }
966         }
967
968         return 0;
969 }
970
971 static int kill_user(int argc, char *argv[], void *userdata) {
972         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
973         sd_bus *bus = userdata;
974         int r, i;
975
976         assert(bus);
977         assert(argv);
978
979         polkit_agent_open_if_enabled();
980
981         if (!arg_kill_who)
982                 arg_kill_who = "all";
983
984         for (i = 1; i < argc; i++) {
985                 uid_t uid;
986
987                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
988                 if (r < 0)
989                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
990
991                 r = sd_bus_call_method (
992                         bus,
993                         "org.freedesktop.login1",
994                         "/org/freedesktop/login1",
995                         "org.freedesktop.login1.Manager",
996                         "KillUser",
997                         &error, NULL,
998                         "ui", (uint32_t) uid, arg_signal);
999                 if (r < 0) {
1000                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
1001                         return r;
1002                 }
1003         }
1004
1005         return 0;
1006 }
1007
1008 static int attach(int argc, char *argv[], void *userdata) {
1009         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1010         sd_bus *bus = userdata;
1011         int r, i;
1012
1013         assert(bus);
1014         assert(argv);
1015
1016         polkit_agent_open_if_enabled();
1017
1018         for (i = 2; i < argc; i++) {
1019
1020                 r = sd_bus_call_method (
1021                         bus,
1022                         "org.freedesktop.login1",
1023                         "/org/freedesktop/login1",
1024                         "org.freedesktop.login1.Manager",
1025                         "AttachDevice",
1026                         &error, NULL,
1027                         "ssb", argv[1], argv[i], true);
1028
1029                 if (r < 0) {
1030                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
1031                         return r;
1032                 }
1033         }
1034
1035         return 0;
1036 }
1037
1038 static int flush_devices(int argc, char *argv[], void *userdata) {
1039         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1040         sd_bus *bus = userdata;
1041         int r;
1042
1043         assert(bus);
1044         assert(argv);
1045
1046         polkit_agent_open_if_enabled();
1047
1048         r = sd_bus_call_method (
1049                         bus,
1050                         "org.freedesktop.login1",
1051                         "/org/freedesktop/login1",
1052                         "org.freedesktop.login1.Manager",
1053                         "FlushDevices",
1054                         &error, NULL,
1055                         "b", true);
1056         if (r < 0)
1057                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1058
1059         return r;
1060 }
1061
1062 static int lock_sessions(int argc, char *argv[], void *userdata) {
1063         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1064         sd_bus *bus = userdata;
1065         int r;
1066
1067         assert(bus);
1068         assert(argv);
1069
1070         polkit_agent_open_if_enabled();
1071
1072         r = sd_bus_call_method(
1073                         bus,
1074                         "org.freedesktop.login1",
1075                         "/org/freedesktop/login1",
1076                         "org.freedesktop.login1.Manager",
1077                         streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1078                         &error, NULL,
1079                         NULL);
1080         if (r < 0)
1081                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1082
1083         return r;
1084 }
1085
1086 static int terminate_seat(int argc, char *argv[], void *userdata) {
1087         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1088         sd_bus *bus = userdata;
1089         int r, i;
1090
1091         assert(bus);
1092         assert(argv);
1093
1094         polkit_agent_open_if_enabled();
1095
1096         for (i = 1; i < argc; i++) {
1097
1098                 r = sd_bus_call_method(
1099                         bus,
1100                         "org.freedesktop.login1",
1101                         "/org/freedesktop/login1",
1102                         "org.freedesktop.login1.Manager",
1103                         "TerminateSeat",
1104                         &error, NULL,
1105                         "s", argv[i]);
1106                 if (r < 0) {
1107                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1108                         return r;
1109                 }
1110         }
1111
1112         return 0;
1113 }
1114
1115 static int help(int argc, char *argv[], void *userdata) {
1116
1117         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1118                "Send control commands to or query the login manager.\n\n"
1119                "  -h --help                Show this help\n"
1120                "     --version             Show package version\n"
1121                "     --no-pager            Do not pipe output into a pager\n"
1122                "     --no-legend           Do not show the headers and footers\n"
1123                "     --no-ask-password     Don't prompt for password\n"
1124                "  -H --host=[USER@]HOST    Operate on remote host\n"
1125                "  -M --machine=CONTAINER   Operate on local container\n"
1126                "  -p --property=NAME       Show only properties by this name\n"
1127                "  -a --all                 Show all properties, including empty ones\n"
1128                "  -l --full                Do not ellipsize output\n"
1129                "     --kill-who=WHO        Who to send signal to\n"
1130                "  -s --signal=SIGNAL       Which signal to send\n"
1131                "  -n --lines=INTEGER       Number of journal entries to show\n"
1132                "  -o --output=STRING       Change journal output mode (short, short-monotonic,\n"
1133                "                           verbose, export, json, json-pretty, json-sse, cat)\n\n"
1134                "Session Commands:\n"
1135                "  list-sessions            List sessions\n"
1136                "  session-status ID...     Show session status\n"
1137                "  show-session [ID...]     Show properties of sessions or the manager\n"
1138                "  activate ID              Activate a session\n"
1139                "  lock-session ID...       Screen lock one or more sessions\n"
1140                "  unlock-session ID...     Screen unlock one or more sessions\n"
1141                "  lock-sessions            Screen lock all current sessions\n"
1142                "  unlock-sessions          Screen unlock all current sessions\n"
1143                "  terminate-session ID...  Terminate one or more sessions\n"
1144                "  kill-session ID...       Send signal to processes of a session\n\n"
1145                "User Commands:\n"
1146                "  list-users               List users\n"
1147                "  user-status USER...      Show user status\n"
1148                "  show-user [USER...]      Show properties of users or the manager\n"
1149                "  enable-linger USER...    Enable linger state of one or more users\n"
1150                "  disable-linger USER...   Disable linger state of one or more users\n"
1151                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1152                "  kill-user USER...        Send signal to processes of a user\n\n"
1153                "Seat Commands:\n"
1154                "  list-seats               List seats\n"
1155                "  seat-status NAME...      Show seat status\n"
1156                "  show-seat NAME...        Show properties of one or more seats\n"
1157                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1158                "  flush-devices            Flush all device associations\n"
1159                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
1160                , program_invocation_short_name);
1161
1162         return 0;
1163 }
1164
1165 static int parse_argv(int argc, char *argv[]) {
1166
1167         enum {
1168                 ARG_VERSION = 0x100,
1169                 ARG_NO_PAGER,
1170                 ARG_NO_LEGEND,
1171                 ARG_KILL_WHO,
1172                 ARG_NO_ASK_PASSWORD,
1173         };
1174
1175         static const struct option options[] = {
1176                 { "help",            no_argument,       NULL, 'h'                 },
1177                 { "version",         no_argument,       NULL, ARG_VERSION         },
1178                 { "property",        required_argument, NULL, 'p'                 },
1179                 { "all",             no_argument,       NULL, 'a'                 },
1180                 { "full",            no_argument,       NULL, 'l'                 },
1181                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1182                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1183                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1184                 { "signal",          required_argument, NULL, 's'                 },
1185                 { "host",            required_argument, NULL, 'H'                 },
1186                 { "machine",         required_argument, NULL, 'M'                 },
1187                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1188                 { "lines",           required_argument, NULL, 'n'                 },
1189                 { "output",          required_argument, NULL, 'o'                 },
1190                 {}
1191         };
1192
1193         int c, r;
1194
1195         assert(argc >= 0);
1196         assert(argv);
1197
1198         while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
1199
1200                 switch (c) {
1201
1202                 case 'h':
1203                         help(0, NULL, NULL);
1204                         return 0;
1205
1206                 case ARG_VERSION:
1207                         puts(PACKAGE_STRING);
1208                         puts(SYSTEMD_FEATURES);
1209                         return 0;
1210
1211                 case 'p': {
1212                         r = strv_extend(&arg_property, optarg);
1213                         if (r < 0)
1214                                 return log_oom();
1215
1216                         /* If the user asked for a particular
1217                          * property, show it to him, even if it is
1218                          * empty. */
1219                         arg_all = true;
1220                         break;
1221                 }
1222
1223                 case 'a':
1224                         arg_all = true;
1225                         break;
1226
1227                 case 'l':
1228                         arg_full = true;
1229                         break;
1230
1231                 case 'n':
1232                         if (safe_atou(optarg, &arg_lines) < 0) {
1233                                 log_error("Failed to parse lines '%s'", optarg);
1234                                 return -EINVAL;
1235                         }
1236                         break;
1237
1238                 case 'o':
1239                         arg_output = output_mode_from_string(optarg);
1240                         if (arg_output < 0) {
1241                                 log_error("Unknown output '%s'.", optarg);
1242                                 return -EINVAL;
1243                         }
1244                         break;
1245
1246                 case ARG_NO_PAGER:
1247                         arg_no_pager = true;
1248                         break;
1249
1250                 case ARG_NO_LEGEND:
1251                         arg_legend = false;
1252                         break;
1253
1254                 case ARG_NO_ASK_PASSWORD:
1255                         arg_ask_password = false;
1256                         break;
1257
1258                 case ARG_KILL_WHO:
1259                         arg_kill_who = optarg;
1260                         break;
1261
1262                 case 's':
1263                         arg_signal = signal_from_string_try_harder(optarg);
1264                         if (arg_signal < 0) {
1265                                 log_error("Failed to parse signal string %s.", optarg);
1266                                 return -EINVAL;
1267                         }
1268                         break;
1269
1270                 case 'H':
1271                         arg_transport = BUS_TRANSPORT_REMOTE;
1272                         arg_host = optarg;
1273                         break;
1274
1275                 case 'M':
1276                         arg_transport = BUS_TRANSPORT_MACHINE;
1277                         arg_host = optarg;
1278                         break;
1279
1280                 case '?':
1281                         return -EINVAL;
1282
1283                 default:
1284                         assert_not_reached("Unhandled option");
1285                 }
1286
1287         return 1;
1288 }
1289
1290 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1291
1292         static const Verb verbs[] = {
1293                 { "help",              VERB_ANY, VERB_ANY, 0,            help              },
1294                 { "list-sessions",     VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
1295                 { "session-status",    2,        VERB_ANY, 0,            show_session      },
1296                 { "show-session",      VERB_ANY, VERB_ANY, 0,            show_session      },
1297                 { "activate",          2,        2,        0,            activate          },
1298                 { "lock-session",      2,        VERB_ANY, 0,            activate          },
1299                 { "unlock-session",    2,        VERB_ANY, 0,            activate          },
1300                 { "lock-sessions",     VERB_ANY, 1,        0,            lock_sessions     },
1301                 { "unlock-sessions",   VERB_ANY, 1,        0,            lock_sessions     },
1302                 { "terminate-session", 2,        VERB_ANY, 0,            activate          },
1303                 { "kill-session",      2,        VERB_ANY, 0,            kill_session      },
1304                 { "list-users",        VERB_ANY, 1,        0,            list_users        },
1305                 { "user-status",       2,        VERB_ANY, 0,            show_user         },
1306                 { "show-user",         VERB_ANY, VERB_ANY, 0,            show_user         },
1307                 { "enable-linger",     2,        VERB_ANY, 0,            enable_linger     },
1308                 { "disable-linger",    2,        VERB_ANY, 0,            enable_linger     },
1309                 { "terminate-user",    2,        VERB_ANY, 0,            terminate_user    },
1310                 { "kill-user",         2,        VERB_ANY, 0,            kill_user         },
1311                 { "list-seats",        VERB_ANY, 1,        0,            list_seats        },
1312                 { "seat-status",       2,        VERB_ANY, 0,            show_seat         },
1313                 { "show-seat",         VERB_ANY, 1,        0,            show_seat         },
1314                 { "attach",            3,        VERB_ANY, 0,            attach            },
1315                 { "flush-devices",     VERB_ANY, 1,        0,            flush_devices     },
1316                 { "terminate-seat",    2,        VERB_ANY, 0,            terminate_seat    },
1317                 {}
1318         };
1319
1320         return dispatch_verb(argc, argv, verbs, bus);
1321 }
1322
1323 int main(int argc, char *argv[]) {
1324         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1325         int r;
1326
1327         setlocale(LC_ALL, "");
1328         log_parse_environment();
1329         log_open();
1330
1331         r = parse_argv(argc, argv);
1332         if (r <= 0)
1333                 goto finish;
1334
1335         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1336         if (r < 0) {
1337                 log_error_errno(r, "Failed to create bus connection: %m");
1338                 goto finish;
1339         }
1340
1341         r = loginctl_main(argc, argv, bus);
1342
1343 finish:
1344         pager_close();
1345         polkit_agent_close();
1346
1347         strv_free(arg_property);
1348
1349         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1350 }