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