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