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