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