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