chiark / gitweb /
loginctl: use show_properties() to get login1 properties
[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_properties(sd_bus *bus, const char *path, bool *new_line) {
576         int r;
577
578         if (*new_line)
579                 printf("\n");
580
581         *new_line = true;
582
583         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
584         if (r < 0)
585                 log_error("Could not get properties: %s", strerror(-r));
586
587         return r;
588 }
589
590 static int show_session(sd_bus *bus, char **args, unsigned n) {
591         bool properties, new_line = false;
592         unsigned i;
593         int r;
594
595         assert(bus);
596         assert(args);
597
598         properties = !strstr(args[0], "status");
599
600         pager_open_if_enabled();
601
602         if (properties && n <= 1) {
603                 /* If not argument is specified inspect the manager
604                  * itself */
605                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
606         }
607
608         for (i = 1; i < n; i++) {
609                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
610                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
611                 const char *path = NULL;
612
613                 if (i != 1)
614                         printf("\n");
615
616                 r = sd_bus_call_method(
617                                 bus,
618                                 "org.freedesktop.login1",
619                                 "/org/freedesktop/login1",
620                                 "org.freedesktop.login1.Manager",
621                                 "GetSession",
622                                 &error, &reply,
623                                 "s", args[i]);
624                 if (r < 0) {
625                         log_error("Failed to get session: %s", bus_error_message(&error, r));
626                         return r;
627                 }
628
629                 r = sd_bus_message_read(reply, "o", &path);
630                 if (r < 0)
631                         return bus_log_parse_error(r);
632
633                 if (properties)
634                         r = show_properties(bus, path, &new_line);
635                 else
636                         r = print_session_status_info(bus, path);
637                 if (r < 0) {
638                         log_error("Failed to query session: %s", strerror(-r));
639                         return r;
640                 }
641         }
642
643         return 0;
644 }
645
646 static int show_user(sd_bus *bus, char **args, unsigned n) {
647         bool properties, new_line = false;
648         unsigned i;
649         int r;
650
651         assert(bus);
652         assert(args);
653
654         properties = !strstr(args[0], "status");
655
656         pager_open_if_enabled();
657
658         if (properties && n <= 1) {
659                 /* If not argument is specified inspect the manager
660                  * itself */
661                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
662         }
663
664         for (i = 1; i < n; i++) {
665                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
666                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
667                 const char *path = NULL;
668                 uid_t uid;
669
670                 if (i != 1)
671                         printf("\n");
672
673                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
674                 if (r < 0) {
675                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
676                         return r;
677                 }
678
679                 r = sd_bus_call_method(
680                                 bus,
681                                 "org.freedesktop.login1",
682                                 "/org/freedesktop/login1",
683                                 "org.freedesktop.login1.Manager",
684                                 "GetUser",
685                                 &error, &reply,
686                                 "u", (uint32_t) uid);
687                 if (r < 0) {
688                         log_error("Failed to get user: %s", bus_error_message(&error, r));
689                         return r;
690                 }
691
692                 r = sd_bus_message_read(reply, "o", &path);
693                 if (r < 0)
694                         return bus_log_parse_error(r);
695
696                 if (properties)
697                         r = show_properties(bus, path, &new_line);
698                 else
699                         r = print_user_status_info(bus, path);
700                 if (r < 0) {
701                         log_error("Failed to query user: %s", strerror(-r));
702                         return r;
703                 }
704         }
705
706         return 0;
707 }
708
709 static int show_seat(sd_bus *bus, char **args, unsigned n) {
710         bool properties, new_line = false;
711         unsigned i;
712         int r;
713
714         assert(bus);
715         assert(args);
716
717         properties = !strstr(args[0], "status");
718
719         pager_open_if_enabled();
720
721         if (properties && n <= 1) {
722                 /* If not argument is specified inspect the manager
723                  * itself */
724                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
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 bus_log_parse_error(r);
751
752                 if (properties)
753                         r = show_properties(bus, path, &new_line);
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 sessions or the manager\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 users or the manager\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, r;
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                         r = strv_extend(&arg_property, optarg);
1114                         if (r < 0)
1115                                 return log_oom();
1116
1117                         /* If the user asked for a particular
1118                          * property, show it to him, even if it is
1119                          * empty. */
1120                         arg_all = true;
1121                         break;
1122                 }
1123
1124                 case 'a':
1125                         arg_all = true;
1126                         break;
1127
1128                 case 'l':
1129                         arg_full = true;
1130                         break;
1131
1132                 case ARG_NO_PAGER:
1133                         arg_no_pager = true;
1134                         break;
1135
1136                 case ARG_NO_ASK_PASSWORD:
1137                         arg_ask_password = false;
1138                         break;
1139
1140                 case ARG_KILL_WHO:
1141                         arg_kill_who = optarg;
1142                         break;
1143
1144                 case 's':
1145                         arg_signal = signal_from_string_try_harder(optarg);
1146                         if (arg_signal < 0) {
1147                                 log_error("Failed to parse signal string %s.", optarg);
1148                                 return -EINVAL;
1149                         }
1150                         break;
1151
1152                 case 'H':
1153                         arg_transport = BUS_TRANSPORT_REMOTE;
1154                         arg_host = optarg;
1155                         break;
1156
1157                 case 'M':
1158                         arg_transport = BUS_TRANSPORT_CONTAINER;
1159                         arg_host = optarg;
1160                         break;
1161
1162                 case '?':
1163                         return -EINVAL;
1164
1165                 default:
1166                         assert_not_reached("Unhandled option");
1167                 }
1168         }
1169
1170         return 1;
1171 }
1172
1173 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1174
1175         static const struct {
1176                 const char* verb;
1177                 const enum {
1178                         MORE,
1179                         LESS,
1180                         EQUAL
1181                 } argc_cmp;
1182                 const int argc;
1183                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1184         } verbs[] = {
1185                 { "list-sessions",         LESS,   1, list_sessions     },
1186                 { "session-status",        MORE,   2, show_session      },
1187                 { "show-session",          MORE,   1, show_session      },
1188                 { "activate",              EQUAL,  2, activate          },
1189                 { "lock-session",          MORE,   2, activate          },
1190                 { "unlock-session",        MORE,   2, activate          },
1191                 { "lock-sessions",         EQUAL,  1, lock_sessions     },
1192                 { "unlock-sessions",       EQUAL,  1, lock_sessions     },
1193                 { "terminate-session",     MORE,   2, activate          },
1194                 { "kill-session",          MORE,   2, kill_session      },
1195                 { "list-users",            EQUAL,  1, list_users        },
1196                 { "user-status",           MORE,   2, show_user         },
1197                 { "show-user",             MORE,   1, show_user         },
1198                 { "enable-linger",         MORE,   2, enable_linger     },
1199                 { "disable-linger",        MORE,   2, enable_linger     },
1200                 { "terminate-user",        MORE,   2, terminate_user    },
1201                 { "kill-user",             MORE,   2, kill_user         },
1202                 { "list-seats",            EQUAL,  1, list_seats        },
1203                 { "seat-status",           MORE,   2, show_seat         },
1204                 { "show-seat",             MORE,   1, show_seat         },
1205                 { "attach",                MORE,   3, attach            },
1206                 { "flush-devices",         EQUAL,  1, flush_devices     },
1207                 { "terminate-seat",        MORE,   2, terminate_seat    },
1208         };
1209
1210         int left;
1211         unsigned i;
1212
1213         assert(argc >= 0);
1214         assert(argv);
1215
1216         left = argc - optind;
1217
1218         if (left <= 0)
1219                 /* Special rule: no arguments means "list-sessions" */
1220                 i = 0;
1221         else {
1222                 if (streq(argv[optind], "help")) {
1223                         help();
1224                         return 0;
1225                 }
1226
1227                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1228                         if (streq(argv[optind], verbs[i].verb))
1229                                 break;
1230
1231                 if (i >= ELEMENTSOF(verbs)) {
1232                         log_error("Unknown operation %s", argv[optind]);
1233                         return -EINVAL;
1234                 }
1235         }
1236
1237         switch (verbs[i].argc_cmp) {
1238
1239         case EQUAL:
1240                 if (left != verbs[i].argc) {
1241                         log_error("Invalid number of arguments.");
1242                         return -EINVAL;
1243                 }
1244
1245                 break;
1246
1247         case MORE:
1248                 if (left < verbs[i].argc) {
1249                         log_error("Too few arguments.");
1250                         return -EINVAL;
1251                 }
1252
1253                 break;
1254
1255         case LESS:
1256                 if (left > verbs[i].argc) {
1257                         log_error("Too many arguments.");
1258                         return -EINVAL;
1259                 }
1260
1261                 break;
1262
1263         default:
1264                 assert_not_reached("Unknown comparison operator.");
1265         }
1266
1267         return verbs[i].dispatch(bus, argv + optind, left);
1268 }
1269
1270 int main(int argc, char *argv[]) {
1271         _cleanup_bus_unref_ sd_bus *bus = NULL;
1272         int r;
1273
1274         setlocale(LC_ALL, "");
1275         log_parse_environment();
1276         log_open();
1277
1278         r = parse_argv(argc, argv);
1279         if (r <= 0)
1280                 goto finish;
1281
1282         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1283         if (r < 0) {
1284                 log_error("Failed to create bus connection: %s", strerror(-r));
1285                 goto finish;
1286         }
1287
1288         r = loginctl_main(bus, argc, argv);
1289
1290 finish:
1291         pager_close();
1292
1293         strv_free(arg_property);
1294
1295         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1296 }