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