]>
Commit | Line | Data |
---|---|---|
abca4822 LP |
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 | |
5430f7f2 LP |
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 | |
abca4822 LP |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
abca4822 | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
abca4822 LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <dbus/dbus.h> | |
23 | #include <unistd.h> | |
24 | #include <errno.h> | |
25 | #include <string.h> | |
26 | #include <getopt.h> | |
a4c279f8 | 27 | #include <pwd.h> |
abca4822 LP |
28 | |
29 | #include "log.h" | |
30 | #include "util.h" | |
31 | #include "macro.h" | |
32 | #include "pager.h" | |
33 | #include "dbus-common.h" | |
34 | #include "build.h" | |
a4c279f8 LP |
35 | #include "strv.h" |
36 | #include "cgroup-show.h" | |
37 | #include "sysfs-show.h" | |
6bb92a16 | 38 | #include "spawn-polkit-agent.h" |
abca4822 | 39 | |
a4c279f8 LP |
40 | static char **arg_property = NULL; |
41 | static bool arg_all = false; | |
abca4822 | 42 | static bool arg_no_pager = false; |
a4c279f8 LP |
43 | static const char *arg_kill_who = NULL; |
44 | static int arg_signal = SIGTERM; | |
abca4822 LP |
45 | static enum transport { |
46 | TRANSPORT_NORMAL, | |
47 | TRANSPORT_SSH, | |
48 | TRANSPORT_POLKIT | |
49 | } arg_transport = TRANSPORT_NORMAL; | |
6bb92a16 | 50 | static bool arg_ask_password = true; |
abca4822 LP |
51 | static const char *arg_host = NULL; |
52 | ||
53 | static bool on_tty(void) { | |
54 | static int t = -1; | |
55 | ||
56 | /* Note that this is invoked relatively early, before we start | |
57 | * the pager. That means the value we return reflects whether | |
58 | * we originally were started on a tty, not if we currently | |
59 | * are. But this is intended, since we want colour and so on | |
60 | * when run in our own pager. */ | |
61 | ||
62 | if (_unlikely_(t < 0)) | |
63 | t = isatty(STDOUT_FILENO) > 0; | |
64 | ||
65 | return t; | |
66 | } | |
67 | ||
68 | static void pager_open_if_enabled(void) { | |
f8440af5 LP |
69 | |
70 | /* Cache result before we open the pager */ | |
abca4822 LP |
71 | on_tty(); |
72 | ||
6bb92a16 LP |
73 | if (arg_no_pager) |
74 | return; | |
75 | ||
76 | pager_open(); | |
77 | } | |
78 | ||
79 | static void polkit_agent_open_if_enabled(void) { | |
80 | ||
81 | /* Open the polkit agent as a child process if necessary */ | |
82 | ||
83 | if (!arg_ask_password) | |
84 | return; | |
85 | ||
86 | polkit_agent_open(); | |
abca4822 LP |
87 | } |
88 | ||
89 | static int list_sessions(DBusConnection *bus, char **args, unsigned n) { | |
2a3613b1 | 90 | DBusMessage *reply = NULL; |
abca4822 LP |
91 | int r; |
92 | DBusMessageIter iter, sub, sub2; | |
93 | unsigned k = 0; | |
94 | ||
abca4822 LP |
95 | pager_open_if_enabled(); |
96 | ||
2a3613b1 SP |
97 | r = bus_method_call_with_reply ( |
98 | bus, | |
abca4822 LP |
99 | "org.freedesktop.login1", |
100 | "/org/freedesktop/login1", | |
101 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
102 | "ListSessions", |
103 | &reply, | |
104 | NULL, | |
105 | DBUS_TYPE_INVALID); | |
106 | if (r) | |
abca4822 | 107 | goto finish; |
abca4822 LP |
108 | |
109 | if (!dbus_message_iter_init(reply, &iter) || | |
110 | dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || | |
111 | dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { | |
112 | log_error("Failed to parse reply."); | |
113 | r = -EIO; | |
114 | goto finish; | |
115 | } | |
116 | ||
117 | dbus_message_iter_recurse(&iter, &sub); | |
118 | ||
119 | if (on_tty()) | |
120 | printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT"); | |
121 | ||
122 | while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { | |
123 | const char *id, *user, *seat, *object; | |
124 | uint32_t uid; | |
125 | ||
126 | if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { | |
127 | log_error("Failed to parse reply."); | |
128 | r = -EIO; | |
129 | goto finish; | |
130 | } | |
131 | ||
132 | dbus_message_iter_recurse(&sub, &sub2); | |
133 | ||
134 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 || | |
135 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || | |
136 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || | |
137 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 || | |
138 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { | |
139 | log_error("Failed to parse reply."); | |
140 | r = -EIO; | |
141 | goto finish; | |
142 | } | |
143 | ||
144 | printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat); | |
145 | ||
146 | k++; | |
147 | ||
148 | dbus_message_iter_next(&sub); | |
149 | } | |
150 | ||
151 | if (on_tty()) | |
152 | printf("\n%u sessions listed.\n", k); | |
153 | ||
154 | r = 0; | |
155 | ||
156 | finish: | |
abca4822 LP |
157 | if (reply) |
158 | dbus_message_unref(reply); | |
159 | ||
abca4822 LP |
160 | return r; |
161 | } | |
162 | ||
163 | static int list_users(DBusConnection *bus, char **args, unsigned n) { | |
2a3613b1 | 164 | DBusMessage *reply = NULL; |
abca4822 LP |
165 | int r; |
166 | DBusMessageIter iter, sub, sub2; | |
167 | unsigned k = 0; | |
168 | ||
abca4822 LP |
169 | pager_open_if_enabled(); |
170 | ||
2a3613b1 SP |
171 | r = bus_method_call_with_reply ( |
172 | bus, | |
abca4822 LP |
173 | "org.freedesktop.login1", |
174 | "/org/freedesktop/login1", | |
175 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
176 | "ListUsers", |
177 | &reply, | |
178 | NULL, | |
179 | DBUS_TYPE_INVALID); | |
180 | if (r) | |
181 | goto finish; | |
abca4822 LP |
182 | |
183 | if (!dbus_message_iter_init(reply, &iter) || | |
184 | dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || | |
185 | dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { | |
186 | log_error("Failed to parse reply."); | |
187 | r = -EIO; | |
188 | goto finish; | |
189 | } | |
190 | ||
191 | dbus_message_iter_recurse(&iter, &sub); | |
192 | ||
193 | if (on_tty()) | |
194 | printf("%10s %-16s\n", "UID", "USER"); | |
195 | ||
196 | while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { | |
197 | const char *user, *object; | |
198 | uint32_t uid; | |
199 | ||
200 | if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { | |
201 | log_error("Failed to parse reply."); | |
202 | r = -EIO; | |
203 | goto finish; | |
204 | } | |
205 | ||
206 | dbus_message_iter_recurse(&sub, &sub2); | |
207 | ||
208 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || | |
209 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || | |
210 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { | |
211 | log_error("Failed to parse reply."); | |
212 | r = -EIO; | |
213 | goto finish; | |
214 | } | |
215 | ||
216 | printf("%10u %-16s\n", (unsigned) uid, user); | |
217 | ||
218 | k++; | |
219 | ||
220 | dbus_message_iter_next(&sub); | |
221 | } | |
222 | ||
223 | if (on_tty()) | |
224 | printf("\n%u users listed.\n", k); | |
225 | ||
226 | r = 0; | |
227 | ||
228 | finish: | |
abca4822 LP |
229 | if (reply) |
230 | dbus_message_unref(reply); | |
231 | ||
abca4822 LP |
232 | return r; |
233 | } | |
234 | ||
235 | static int list_seats(DBusConnection *bus, char **args, unsigned n) { | |
2a3613b1 | 236 | DBusMessage *reply = NULL; |
abca4822 LP |
237 | int r; |
238 | DBusMessageIter iter, sub, sub2; | |
239 | unsigned k = 0; | |
240 | ||
abca4822 LP |
241 | pager_open_if_enabled(); |
242 | ||
2a3613b1 SP |
243 | r = bus_method_call_with_reply ( |
244 | bus, | |
abca4822 LP |
245 | "org.freedesktop.login1", |
246 | "/org/freedesktop/login1", | |
247 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
248 | "ListSeats", |
249 | &reply, | |
250 | NULL, | |
251 | DBUS_TYPE_INVALID); | |
252 | if (r) | |
abca4822 | 253 | goto finish; |
abca4822 LP |
254 | |
255 | if (!dbus_message_iter_init(reply, &iter) || | |
256 | dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || | |
257 | dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { | |
258 | log_error("Failed to parse reply."); | |
259 | r = -EIO; | |
260 | goto finish; | |
261 | } | |
262 | ||
263 | dbus_message_iter_recurse(&iter, &sub); | |
264 | ||
265 | if (on_tty()) | |
266 | printf("%-16s\n", "SEAT"); | |
267 | ||
268 | while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { | |
269 | const char *seat, *object; | |
270 | ||
271 | if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { | |
272 | log_error("Failed to parse reply."); | |
273 | r = -EIO; | |
274 | goto finish; | |
275 | } | |
276 | ||
277 | dbus_message_iter_recurse(&sub, &sub2); | |
278 | ||
279 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 || | |
280 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { | |
281 | log_error("Failed to parse reply."); | |
282 | r = -EIO; | |
283 | goto finish; | |
284 | } | |
285 | ||
286 | printf("%-16s\n", seat); | |
287 | ||
288 | k++; | |
289 | ||
290 | dbus_message_iter_next(&sub); | |
291 | } | |
292 | ||
293 | if (on_tty()) | |
294 | printf("\n%u seats listed.\n", k); | |
295 | ||
296 | r = 0; | |
297 | ||
298 | finish: | |
abca4822 LP |
299 | if (reply) |
300 | dbus_message_unref(reply); | |
301 | ||
abca4822 LP |
302 | return r; |
303 | } | |
304 | ||
a4c279f8 LP |
305 | typedef struct SessionStatusInfo { |
306 | const char *id; | |
307 | uid_t uid; | |
308 | const char *name; | |
309 | usec_t timestamp; | |
b69d29ce | 310 | const char *default_control_group; |
a4c279f8 LP |
311 | int vtnr; |
312 | const char *seat; | |
313 | const char *tty; | |
314 | const char *display; | |
315 | bool remote; | |
316 | const char *remote_host; | |
317 | const char *remote_user; | |
318 | const char *service; | |
319 | pid_t leader; | |
320 | const char *type; | |
55efac6c | 321 | const char *class; |
0604381b | 322 | const char *state; |
a4c279f8 LP |
323 | } SessionStatusInfo; |
324 | ||
325 | typedef struct UserStatusInfo { | |
326 | uid_t uid; | |
327 | const char *name; | |
328 | usec_t timestamp; | |
b69d29ce | 329 | const char *default_control_group; |
a4c279f8 LP |
330 | const char *state; |
331 | char **sessions; | |
332 | const char *display; | |
333 | } UserStatusInfo; | |
334 | ||
335 | typedef struct SeatStatusInfo { | |
336 | const char *id; | |
337 | const char *active_session; | |
338 | char **sessions; | |
339 | } SeatStatusInfo; | |
340 | ||
341 | static void print_session_status_info(SessionStatusInfo *i) { | |
342 | char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; | |
343 | char since2[FORMAT_TIMESTAMP_MAX], *s2; | |
344 | assert(i); | |
345 | ||
346 | printf("%s - ", strna(i->id)); | |
347 | ||
348 | if (i->name) | |
349 | printf("%s (%u)\n", i->name, (unsigned) i->uid); | |
350 | else | |
351 | printf("%u\n", (unsigned) i->uid); | |
352 | ||
353 | s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp); | |
354 | s2 = format_timestamp(since2, sizeof(since2), i->timestamp); | |
355 | ||
356 | if (s1) | |
357 | printf("\t Since: %s; %s\n", s2, s1); | |
358 | else if (s2) | |
359 | printf("\t Since: %s\n", s2); | |
360 | ||
361 | if (i->leader > 0) { | |
362 | char *t = NULL; | |
363 | ||
364 | printf("\t Leader: %u", (unsigned) i->leader); | |
365 | ||
87d2c1ff | 366 | get_process_comm(i->leader, &t); |
a4c279f8 LP |
367 | if (t) { |
368 | printf(" (%s)", t); | |
369 | free(t); | |
370 | } | |
371 | ||
372 | printf("\n"); | |
373 | } | |
374 | ||
375 | if (i->seat) { | |
376 | printf("\t Seat: %s", i->seat); | |
377 | ||
378 | if (i->vtnr > 0) | |
379 | printf("; vc%i", i->vtnr); | |
380 | ||
381 | printf("\n"); | |
382 | } | |
383 | ||
384 | if (i->tty) | |
385 | printf("\t TTY: %s\n", i->tty); | |
386 | else if (i->display) | |
387 | printf("\t Display: %s\n", i->display); | |
388 | ||
389 | if (i->remote_host && i->remote_user) | |
390 | printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host); | |
391 | else if (i->remote_host) | |
392 | printf("\t Remote: %s\n", i->remote_host); | |
393 | else if (i->remote_user) | |
394 | printf("\t Remote: user %s\n", i->remote_user); | |
395 | else if (i->remote) | |
396 | printf("\t Remote: Yes\n"); | |
397 | ||
398 | if (i->service) { | |
399 | printf("\t Service: %s", i->service); | |
400 | ||
401 | if (i->type) | |
402 | printf("; type %s", i->type); | |
403 | ||
55efac6c LP |
404 | if (i->class) |
405 | printf("; class %s", i->class); | |
406 | ||
a4c279f8 | 407 | printf("\n"); |
55efac6c | 408 | } else if (i->type) { |
a4c279f8 LP |
409 | printf("\t Type: %s\n", i->type); |
410 | ||
55efac6c LP |
411 | if (i->class) |
412 | printf("; class %s", i->class); | |
413 | } else if (i->class) | |
414 | printf("\t Class: %s\n", i->class); | |
415 | ||
0604381b LP |
416 | if (i->state) |
417 | printf("\t State: %s\n", i->state); | |
a4c279f8 | 418 | |
b69d29ce | 419 | if (i->default_control_group) { |
a4c279f8 LP |
420 | unsigned c; |
421 | ||
b69d29ce | 422 | printf("\t CGroup: %s\n", i->default_control_group); |
a4c279f8 LP |
423 | |
424 | if (arg_transport != TRANSPORT_SSH) { | |
425 | c = columns(); | |
426 | if (c > 18) | |
427 | c -= 18; | |
428 | else | |
429 | c = 0; | |
430 | ||
b69d29ce | 431 | show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0); |
a4c279f8 LP |
432 | } |
433 | } | |
434 | } | |
435 | ||
436 | static void print_user_status_info(UserStatusInfo *i) { | |
437 | char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; | |
438 | char since2[FORMAT_TIMESTAMP_MAX], *s2; | |
439 | assert(i); | |
440 | ||
441 | if (i->name) | |
442 | printf("%s (%u)\n", i->name, (unsigned) i->uid); | |
443 | else | |
444 | printf("%u\n", (unsigned) i->uid); | |
445 | ||
446 | s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp); | |
447 | s2 = format_timestamp(since2, sizeof(since2), i->timestamp); | |
448 | ||
449 | if (s1) | |
450 | printf("\t Since: %s; %s\n", s2, s1); | |
451 | else if (s2) | |
452 | printf("\t Since: %s\n", s2); | |
453 | ||
454 | if (!isempty(i->state)) | |
455 | printf("\t State: %s\n", i->state); | |
456 | ||
457 | if (!strv_isempty(i->sessions)) { | |
458 | char **l; | |
459 | printf("\tSessions:"); | |
460 | ||
461 | STRV_FOREACH(l, i->sessions) { | |
462 | if (streq_ptr(*l, i->display)) | |
463 | printf(" *%s", *l); | |
464 | else | |
465 | printf(" %s", *l); | |
466 | } | |
467 | ||
468 | printf("\n"); | |
469 | } | |
470 | ||
b69d29ce | 471 | if (i->default_control_group) { |
a4c279f8 LP |
472 | unsigned c; |
473 | ||
b69d29ce | 474 | printf("\t CGroup: %s\n", i->default_control_group); |
a4c279f8 LP |
475 | |
476 | if (arg_transport != TRANSPORT_SSH) { | |
477 | c = columns(); | |
478 | if (c > 18) | |
479 | c -= 18; | |
480 | else | |
481 | c = 0; | |
482 | ||
b69d29ce | 483 | show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all); |
a4c279f8 LP |
484 | } |
485 | } | |
486 | } | |
487 | ||
488 | static void print_seat_status_info(SeatStatusInfo *i) { | |
489 | assert(i); | |
490 | ||
491 | printf("%s\n", strna(i->id)); | |
492 | ||
493 | if (!strv_isempty(i->sessions)) { | |
494 | char **l; | |
495 | printf("\tSessions:"); | |
496 | ||
497 | STRV_FOREACH(l, i->sessions) { | |
498 | if (streq_ptr(*l, i->active_session)) | |
499 | printf(" *%s", *l); | |
500 | else | |
501 | printf(" %s", *l); | |
502 | } | |
503 | ||
504 | printf("\n"); | |
505 | } | |
506 | ||
507 | if (arg_transport != TRANSPORT_SSH) { | |
508 | unsigned c; | |
509 | ||
510 | c = columns(); | |
88e3dc90 LP |
511 | if (c > 21) |
512 | c -= 21; | |
a4c279f8 LP |
513 | else |
514 | c = 0; | |
515 | ||
516 | printf("\t Devices:\n"); | |
517 | ||
a4c279f8 LP |
518 | show_sysfs(i->id, "\t\t ", c); |
519 | } | |
520 | } | |
521 | ||
522 | static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) { | |
523 | assert(name); | |
524 | assert(iter); | |
525 | assert(i); | |
526 | ||
527 | switch (dbus_message_iter_get_arg_type(iter)) { | |
528 | ||
529 | case DBUS_TYPE_STRING: { | |
530 | const char *s; | |
531 | ||
532 | dbus_message_iter_get_basic(iter, &s); | |
533 | ||
534 | if (!isempty(s)) { | |
535 | if (streq(name, "Id")) | |
536 | i->id = s; | |
537 | else if (streq(name, "Name")) | |
538 | i->name = s; | |
b69d29ce LP |
539 | else if (streq(name, "DefaultControlGroup")) |
540 | i->default_control_group = s; | |
a4c279f8 LP |
541 | else if (streq(name, "TTY")) |
542 | i->tty = s; | |
543 | else if (streq(name, "Display")) | |
544 | i->display = s; | |
545 | else if (streq(name, "RemoteHost")) | |
546 | i->remote_host = s; | |
547 | else if (streq(name, "RemoteUser")) | |
548 | i->remote_user = s; | |
549 | else if (streq(name, "Service")) | |
550 | i->service = s; | |
551 | else if (streq(name, "Type")) | |
552 | i->type = s; | |
55efac6c LP |
553 | else if (streq(name, "Class")) |
554 | i->class = s; | |
0604381b LP |
555 | else if (streq(name, "State")) |
556 | i->state = s; | |
a4c279f8 LP |
557 | } |
558 | break; | |
559 | } | |
560 | ||
561 | case DBUS_TYPE_UINT32: { | |
562 | uint32_t u; | |
563 | ||
564 | dbus_message_iter_get_basic(iter, &u); | |
565 | ||
566 | if (streq(name, "VTNr")) | |
567 | i->vtnr = (int) u; | |
568 | else if (streq(name, "Leader")) | |
569 | i->leader = (pid_t) u; | |
570 | ||
571 | break; | |
572 | } | |
573 | ||
574 | case DBUS_TYPE_BOOLEAN: { | |
575 | dbus_bool_t b; | |
576 | ||
577 | dbus_message_iter_get_basic(iter, &b); | |
578 | ||
579 | if (streq(name, "Remote")) | |
580 | i->remote = b; | |
a4c279f8 LP |
581 | |
582 | break; | |
583 | } | |
584 | ||
585 | case DBUS_TYPE_UINT64: { | |
586 | uint64_t u; | |
587 | ||
588 | dbus_message_iter_get_basic(iter, &u); | |
589 | ||
590 | if (streq(name, "Timestamp")) | |
591 | i->timestamp = (usec_t) u; | |
592 | ||
593 | break; | |
594 | } | |
595 | ||
596 | case DBUS_TYPE_STRUCT: { | |
597 | DBusMessageIter sub; | |
598 | ||
599 | dbus_message_iter_recurse(iter, &sub); | |
600 | ||
601 | if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) { | |
602 | uint32_t u; | |
603 | ||
604 | dbus_message_iter_get_basic(&sub, &u); | |
605 | i->uid = (uid_t) u; | |
606 | ||
607 | } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) { | |
608 | const char *s; | |
609 | ||
610 | dbus_message_iter_get_basic(&sub, &s); | |
611 | ||
612 | if (!isempty(s)) | |
613 | i->seat = s; | |
614 | } | |
615 | ||
616 | break; | |
617 | } | |
618 | } | |
619 | ||
620 | return 0; | |
621 | } | |
622 | ||
623 | static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) { | |
624 | assert(name); | |
625 | assert(iter); | |
626 | assert(i); | |
627 | ||
628 | switch (dbus_message_iter_get_arg_type(iter)) { | |
629 | ||
630 | case DBUS_TYPE_STRING: { | |
631 | const char *s; | |
632 | ||
633 | dbus_message_iter_get_basic(iter, &s); | |
634 | ||
635 | if (!isempty(s)) { | |
636 | if (streq(name, "Name")) | |
637 | i->name = s; | |
b69d29ce LP |
638 | else if (streq(name, "DefaultControlGroup")) |
639 | i->default_control_group = s; | |
a4c279f8 LP |
640 | else if (streq(name, "State")) |
641 | i->state = s; | |
642 | } | |
643 | break; | |
644 | } | |
645 | ||
646 | case DBUS_TYPE_UINT32: { | |
647 | uint32_t u; | |
648 | ||
649 | dbus_message_iter_get_basic(iter, &u); | |
650 | ||
651 | if (streq(name, "UID")) | |
652 | i->uid = (uid_t) u; | |
653 | ||
654 | break; | |
655 | } | |
656 | ||
657 | case DBUS_TYPE_UINT64: { | |
658 | uint64_t u; | |
659 | ||
660 | dbus_message_iter_get_basic(iter, &u); | |
661 | ||
662 | if (streq(name, "Timestamp")) | |
663 | i->timestamp = (usec_t) u; | |
664 | ||
665 | break; | |
666 | } | |
667 | ||
668 | case DBUS_TYPE_STRUCT: { | |
669 | DBusMessageIter sub; | |
670 | ||
671 | dbus_message_iter_recurse(iter, &sub); | |
672 | ||
673 | if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) { | |
674 | const char *s; | |
675 | ||
676 | dbus_message_iter_get_basic(&sub, &s); | |
677 | ||
678 | if (!isempty(s)) | |
679 | i->display = s; | |
680 | } | |
681 | ||
682 | break; | |
683 | } | |
684 | ||
685 | case DBUS_TYPE_ARRAY: { | |
686 | ||
687 | if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { | |
688 | DBusMessageIter sub, sub2; | |
689 | ||
690 | dbus_message_iter_recurse(iter, &sub); | |
691 | while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { | |
692 | const char *id; | |
693 | const char *path; | |
694 | ||
695 | dbus_message_iter_recurse(&sub, &sub2); | |
696 | ||
697 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 && | |
698 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) { | |
699 | char **l; | |
700 | ||
701 | l = strv_append(i->sessions, id); | |
702 | if (!l) | |
703 | return -ENOMEM; | |
704 | ||
705 | strv_free(i->sessions); | |
706 | i->sessions = l; | |
707 | } | |
708 | ||
709 | dbus_message_iter_next(&sub); | |
710 | } | |
711 | ||
712 | return 0; | |
713 | } | |
714 | } | |
715 | } | |
716 | ||
717 | return 0; | |
718 | } | |
719 | ||
720 | static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) { | |
721 | assert(name); | |
722 | assert(iter); | |
723 | assert(i); | |
724 | ||
725 | switch (dbus_message_iter_get_arg_type(iter)) { | |
726 | ||
727 | case DBUS_TYPE_STRING: { | |
728 | const char *s; | |
729 | ||
730 | dbus_message_iter_get_basic(iter, &s); | |
731 | ||
732 | if (!isempty(s)) { | |
733 | if (streq(name, "Id")) | |
734 | i->id = s; | |
735 | } | |
736 | break; | |
737 | } | |
738 | ||
739 | case DBUS_TYPE_STRUCT: { | |
740 | DBusMessageIter sub; | |
741 | ||
742 | dbus_message_iter_recurse(iter, &sub); | |
743 | ||
744 | if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) { | |
745 | const char *s; | |
746 | ||
747 | dbus_message_iter_get_basic(&sub, &s); | |
748 | ||
749 | if (!isempty(s)) | |
750 | i->active_session = s; | |
751 | } | |
752 | ||
753 | break; | |
754 | } | |
755 | ||
756 | case DBUS_TYPE_ARRAY: { | |
757 | ||
758 | if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { | |
759 | DBusMessageIter sub, sub2; | |
760 | ||
761 | dbus_message_iter_recurse(iter, &sub); | |
762 | while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { | |
763 | const char *id; | |
764 | const char *path; | |
765 | ||
766 | dbus_message_iter_recurse(&sub, &sub2); | |
767 | ||
768 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 && | |
769 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) { | |
770 | char **l; | |
771 | ||
772 | l = strv_append(i->sessions, id); | |
773 | if (!l) | |
774 | return -ENOMEM; | |
775 | ||
776 | strv_free(i->sessions); | |
777 | i->sessions = l; | |
778 | } | |
779 | ||
780 | dbus_message_iter_next(&sub); | |
781 | } | |
782 | ||
783 | return 0; | |
784 | } | |
785 | } | |
786 | } | |
787 | ||
788 | return 0; | |
789 | } | |
790 | ||
791 | static int print_property(const char *name, DBusMessageIter *iter) { | |
792 | assert(name); | |
793 | assert(iter); | |
794 | ||
795 | if (arg_property && !strv_find(arg_property, name)) | |
796 | return 0; | |
797 | ||
798 | switch (dbus_message_iter_get_arg_type(iter)) { | |
799 | ||
800 | case DBUS_TYPE_STRUCT: { | |
801 | DBusMessageIter sub; | |
802 | ||
803 | dbus_message_iter_recurse(iter, &sub); | |
804 | ||
805 | if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && | |
806 | (streq(name, "Display") || streq(name, "ActiveSession"))) { | |
807 | const char *s; | |
808 | ||
809 | dbus_message_iter_get_basic(&sub, &s); | |
810 | ||
811 | if (arg_all || !isempty(s)) | |
812 | printf("%s=%s\n", name, s); | |
813 | return 0; | |
814 | } | |
815 | break; | |
816 | } | |
817 | ||
818 | case DBUS_TYPE_ARRAY: | |
819 | ||
820 | if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { | |
821 | DBusMessageIter sub, sub2; | |
822 | bool found = false; | |
823 | ||
824 | dbus_message_iter_recurse(iter, &sub); | |
825 | while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { | |
826 | const char *id; | |
827 | const char *path; | |
828 | ||
829 | dbus_message_iter_recurse(&sub, &sub2); | |
830 | ||
831 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 && | |
832 | bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) { | |
833 | if (found) | |
834 | printf(" %s", id); | |
835 | else { | |
836 | printf("%s=%s", name, id); | |
837 | found = true; | |
838 | } | |
839 | } | |
840 | ||
841 | dbus_message_iter_next(&sub); | |
842 | } | |
843 | ||
844 | if (!found && arg_all) | |
845 | printf("%s=\n", name); | |
846 | else if (found) | |
847 | printf("\n"); | |
848 | ||
849 | return 0; | |
850 | } | |
851 | ||
852 | break; | |
853 | } | |
854 | ||
855 | if (generic_print_property(name, iter, arg_all) > 0) | |
856 | return 0; | |
857 | ||
858 | if (arg_all) | |
859 | printf("%s=[unprintable]\n", name); | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
864 | static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { | |
2a3613b1 | 865 | DBusMessage *reply = NULL; |
a4c279f8 LP |
866 | const char *interface = ""; |
867 | int r; | |
a4c279f8 LP |
868 | DBusMessageIter iter, sub, sub2, sub3; |
869 | SessionStatusInfo session_info; | |
870 | UserStatusInfo user_info; | |
871 | SeatStatusInfo seat_info; | |
872 | ||
a4c279f8 LP |
873 | assert(path); |
874 | assert(new_line); | |
875 | ||
876 | zero(session_info); | |
877 | zero(user_info); | |
878 | zero(seat_info); | |
879 | ||
2a3613b1 SP |
880 | r = bus_method_call_with_reply ( |
881 | bus, | |
a4c279f8 LP |
882 | "org.freedesktop.login1", |
883 | path, | |
884 | "org.freedesktop.DBus.Properties", | |
2a3613b1 SP |
885 | "GetAll", |
886 | &reply, | |
887 | NULL, | |
888 | DBUS_TYPE_STRING, &interface, | |
889 | DBUS_TYPE_INVALID); | |
890 | if (r) | |
a4c279f8 | 891 | goto finish; |
a4c279f8 LP |
892 | |
893 | if (!dbus_message_iter_init(reply, &iter) || | |
894 | dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || | |
895 | dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { | |
896 | log_error("Failed to parse reply."); | |
897 | r = -EIO; | |
898 | goto finish; | |
899 | } | |
900 | ||
901 | dbus_message_iter_recurse(&iter, &sub); | |
902 | ||
903 | if (*new_line) | |
904 | printf("\n"); | |
905 | ||
906 | *new_line = true; | |
907 | ||
908 | while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { | |
909 | const char *name; | |
910 | ||
911 | if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { | |
912 | log_error("Failed to parse reply."); | |
913 | r = -EIO; | |
914 | goto finish; | |
915 | } | |
916 | ||
917 | dbus_message_iter_recurse(&sub, &sub2); | |
918 | ||
919 | if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { | |
920 | log_error("Failed to parse reply."); | |
921 | r = -EIO; | |
922 | goto finish; | |
923 | } | |
924 | ||
925 | if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { | |
926 | log_error("Failed to parse reply."); | |
927 | r = -EIO; | |
928 | goto finish; | |
929 | } | |
930 | ||
931 | dbus_message_iter_recurse(&sub2, &sub3); | |
932 | ||
933 | if (show_properties) | |
934 | r = print_property(name, &sub3); | |
935 | else if (strstr(verb, "session")) | |
936 | r = status_property_session(name, &sub3, &session_info); | |
937 | else if (strstr(verb, "user")) | |
938 | r = status_property_user(name, &sub3, &user_info); | |
939 | else | |
940 | r = status_property_seat(name, &sub3, &seat_info); | |
941 | ||
942 | if (r < 0) { | |
943 | log_error("Failed to parse reply."); | |
944 | r = -EIO; | |
945 | goto finish; | |
946 | } | |
947 | ||
948 | dbus_message_iter_next(&sub); | |
949 | } | |
950 | ||
951 | if (!show_properties) { | |
952 | if (strstr(verb, "session")) | |
953 | print_session_status_info(&session_info); | |
954 | else if (strstr(verb, "user")) | |
955 | print_user_status_info(&user_info); | |
956 | else | |
957 | print_seat_status_info(&seat_info); | |
958 | } | |
959 | ||
960 | strv_free(seat_info.sessions); | |
961 | strv_free(user_info.sessions); | |
962 | ||
963 | r = 0; | |
964 | ||
965 | finish: | |
a4c279f8 LP |
966 | if (reply) |
967 | dbus_message_unref(reply); | |
968 | ||
a4c279f8 LP |
969 | return r; |
970 | } | |
971 | ||
972 | static int show(DBusConnection *bus, char **args, unsigned n) { | |
2a3613b1 | 973 | DBusMessage *reply = NULL; |
a4c279f8 LP |
974 | int r, ret = 0; |
975 | DBusError error; | |
976 | unsigned i; | |
977 | bool show_properties, new_line = false; | |
978 | ||
979 | assert(bus); | |
980 | assert(args); | |
981 | ||
982 | dbus_error_init(&error); | |
983 | ||
984 | show_properties = !strstr(args[0], "status"); | |
985 | ||
986 | if (show_properties) | |
987 | pager_open_if_enabled(); | |
988 | ||
989 | if (show_properties && n <= 1) { | |
990 | /* If not argument is specified inspect the manager | |
991 | * itself */ | |
992 | ||
993 | ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line); | |
994 | goto finish; | |
995 | } | |
996 | ||
997 | for (i = 1; i < n; i++) { | |
998 | const char *path = NULL; | |
999 | ||
1000 | if (strstr(args[0], "session")) { | |
1001 | ||
2a3613b1 SP |
1002 | ret = bus_method_call_with_reply ( |
1003 | bus, | |
a4c279f8 LP |
1004 | "org.freedesktop.login1", |
1005 | "/org/freedesktop/login1", | |
1006 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1007 | "GetSession", |
1008 | &reply, | |
1009 | NULL, | |
1010 | DBUS_TYPE_STRING, &args[i], | |
1011 | DBUS_TYPE_INVALID); | |
a4c279f8 LP |
1012 | |
1013 | } else if (strstr(args[0], "user")) { | |
ddd88763 LP |
1014 | uid_t uid; |
1015 | uint32_t u; | |
a4c279f8 | 1016 | |
d05c5031 | 1017 | ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); |
d77c31f8 | 1018 | if (ret < 0) { |
4b67834e | 1019 | log_error("User %s unknown.", args[i]); |
4b67834e | 1020 | goto finish; |
a4c279f8 LP |
1021 | } |
1022 | ||
2a3613b1 SP |
1023 | u = (uint32_t) uid; |
1024 | ret = bus_method_call_with_reply ( | |
1025 | bus, | |
a4c279f8 LP |
1026 | "org.freedesktop.login1", |
1027 | "/org/freedesktop/login1", | |
1028 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1029 | "GetUser", |
1030 | &reply, | |
1031 | NULL, | |
1032 | DBUS_TYPE_UINT32, &u, | |
1033 | DBUS_TYPE_INVALID); | |
a4c279f8 LP |
1034 | } else { |
1035 | ||
2a3613b1 SP |
1036 | ret = bus_method_call_with_reply ( |
1037 | bus, | |
a4c279f8 LP |
1038 | "org.freedesktop.login1", |
1039 | "/org/freedesktop/login1", | |
1040 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1041 | "GetSeat", |
1042 | &reply, | |
1043 | NULL, | |
1044 | DBUS_TYPE_STRING, &args[i], | |
1045 | DBUS_TYPE_INVALID); | |
a4c279f8 | 1046 | } |
2a3613b1 | 1047 | if (ret) |
a4c279f8 | 1048 | goto finish; |
a4c279f8 LP |
1049 | |
1050 | if (!dbus_message_get_args(reply, &error, | |
1051 | DBUS_TYPE_OBJECT_PATH, &path, | |
1052 | DBUS_TYPE_INVALID)) { | |
1053 | log_error("Failed to parse reply: %s", bus_error_message(&error)); | |
1054 | ret = -EIO; | |
1055 | goto finish; | |
1056 | } | |
1057 | ||
1058 | r = show_one(args[0], bus, path, show_properties, &new_line); | |
1059 | if (r != 0) | |
1060 | ret = r; | |
1061 | ||
a4c279f8 | 1062 | dbus_message_unref(reply); |
2a3613b1 | 1063 | reply = NULL; |
a4c279f8 LP |
1064 | } |
1065 | ||
1066 | finish: | |
a4c279f8 LP |
1067 | if (reply) |
1068 | dbus_message_unref(reply); | |
1069 | ||
1070 | dbus_error_free(&error); | |
1071 | ||
1072 | return ret; | |
1073 | } | |
1074 | ||
1075 | static int activate(DBusConnection *bus, char **args, unsigned n) { | |
24310c11 | 1076 | int ret = 0; |
24310c11 LP |
1077 | unsigned i; |
1078 | ||
24310c11 LP |
1079 | assert(args); |
1080 | ||
24310c11 | 1081 | for (i = 1; i < n; i++) { |
f8440af5 | 1082 | |
2a3613b1 SP |
1083 | ret = bus_method_call_with_reply ( |
1084 | bus, | |
24310c11 LP |
1085 | "org.freedesktop.login1", |
1086 | "/org/freedesktop/login1", | |
1087 | "org.freedesktop.login1.Manager", | |
88e3dc90 LP |
1088 | streq(args[0], "lock-session") ? "LockSession" : |
1089 | streq(args[0], "unlock-session") ? "UnlockSession" : | |
1090 | streq(args[0], "terminate-session") ? "TerminateSession" : | |
2a3613b1 SP |
1091 | "ActivateSession", |
1092 | NULL, | |
1093 | NULL, | |
1094 | DBUS_TYPE_STRING, &args[i], | |
1095 | DBUS_TYPE_INVALID); | |
1096 | if (ret) | |
24310c11 | 1097 | goto finish; |
24310c11 LP |
1098 | } |
1099 | ||
1100 | finish: | |
24310c11 | 1101 | return ret; |
a4c279f8 LP |
1102 | } |
1103 | ||
1104 | static int kill_session(DBusConnection *bus, char **args, unsigned n) { | |
de07ab16 | 1105 | int ret = 0; |
de07ab16 LP |
1106 | unsigned i; |
1107 | ||
de07ab16 LP |
1108 | assert(args); |
1109 | ||
de07ab16 LP |
1110 | if (!arg_kill_who) |
1111 | arg_kill_who = "all"; | |
1112 | ||
1113 | for (i = 1; i < n; i++) { | |
2a3613b1 SP |
1114 | ret = bus_method_call_with_reply ( |
1115 | bus, | |
de07ab16 LP |
1116 | "org.freedesktop.login1", |
1117 | "/org/freedesktop/login1", | |
1118 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1119 | "KillSession", |
1120 | NULL, | |
1121 | NULL, | |
1122 | DBUS_TYPE_STRING, &args[i], | |
1123 | DBUS_TYPE_STRING, &arg_kill_who, | |
1124 | DBUS_TYPE_INT32, &arg_signal, | |
1125 | DBUS_TYPE_INVALID); | |
1126 | if (ret) | |
de07ab16 | 1127 | goto finish; |
de07ab16 LP |
1128 | } |
1129 | ||
1130 | finish: | |
de07ab16 | 1131 | return ret; |
a4c279f8 LP |
1132 | } |
1133 | ||
1134 | static int enable_linger(DBusConnection *bus, char **args, unsigned n) { | |
88e3dc90 | 1135 | int ret = 0; |
88e3dc90 LP |
1136 | unsigned i; |
1137 | dbus_bool_t b, interactive = true; | |
1138 | ||
88e3dc90 LP |
1139 | assert(args); |
1140 | ||
6bb92a16 LP |
1141 | polkit_agent_open_if_enabled(); |
1142 | ||
88e3dc90 LP |
1143 | b = streq(args[0], "enable-linger"); |
1144 | ||
1145 | for (i = 1; i < n; i++) { | |
ddd88763 LP |
1146 | uint32_t u; |
1147 | uid_t uid; | |
88e3dc90 | 1148 | |
d05c5031 | 1149 | ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); |
4b67834e LP |
1150 | if (ret < 0) { |
1151 | log_error("Failed to resolve user %s: %s", args[i], strerror(-ret)); | |
1152 | goto finish; | |
88e3dc90 LP |
1153 | } |
1154 | ||
ddd88763 | 1155 | u = (uint32_t) uid; |
2a3613b1 SP |
1156 | ret = bus_method_call_with_reply ( |
1157 | bus, | |
1158 | "org.freedesktop.login1", | |
1159 | "/org/freedesktop/login1", | |
1160 | "org.freedesktop.login1.Manager", | |
1161 | "SetUserLinger", | |
1162 | NULL, | |
1163 | NULL, | |
1164 | DBUS_TYPE_UINT32, &u, | |
1165 | DBUS_TYPE_BOOLEAN, &b, | |
1166 | DBUS_TYPE_BOOLEAN, &interactive, | |
1167 | DBUS_TYPE_INVALID); | |
1168 | if (ret) | |
88e3dc90 | 1169 | goto finish; |
88e3dc90 LP |
1170 | } |
1171 | ||
1172 | finish: | |
88e3dc90 LP |
1173 | return ret; |
1174 | } | |
1175 | ||
1176 | static int terminate_user(DBusConnection *bus, char **args, unsigned n) { | |
88e3dc90 | 1177 | int ret = 0; |
88e3dc90 LP |
1178 | unsigned i; |
1179 | ||
88e3dc90 LP |
1180 | assert(args); |
1181 | ||
88e3dc90 LP |
1182 | for (i = 1; i < n; i++) { |
1183 | uint32_t u; | |
ddd88763 | 1184 | uid_t uid; |
88e3dc90 | 1185 | |
d05c5031 | 1186 | ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); |
4b67834e LP |
1187 | if (ret < 0) { |
1188 | log_error("Failed to look up user %s: %s", args[i], strerror(-ret)); | |
1189 | goto finish; | |
88e3dc90 LP |
1190 | } |
1191 | ||
ddd88763 | 1192 | u = (uint32_t) uid; |
2a3613b1 SP |
1193 | ret = bus_method_call_with_reply ( |
1194 | bus, | |
1195 | "org.freedesktop.login1", | |
1196 | "/org/freedesktop/login1", | |
1197 | "org.freedesktop.login1.Manager", | |
1198 | "TerminateUser", | |
1199 | NULL, | |
1200 | NULL, | |
1201 | DBUS_TYPE_UINT32, &u, | |
1202 | DBUS_TYPE_INVALID); | |
1203 | if (ret) | |
88e3dc90 | 1204 | goto finish; |
88e3dc90 LP |
1205 | } |
1206 | ||
1207 | finish: | |
88e3dc90 | 1208 | return ret; |
a4c279f8 LP |
1209 | } |
1210 | ||
de07ab16 | 1211 | static int kill_user(DBusConnection *bus, char **args, unsigned n) { |
de07ab16 | 1212 | int ret = 0; |
de07ab16 LP |
1213 | unsigned i; |
1214 | ||
de07ab16 LP |
1215 | assert(args); |
1216 | ||
de07ab16 LP |
1217 | if (!arg_kill_who) |
1218 | arg_kill_who = "all"; | |
1219 | ||
1220 | for (i = 1; i < n; i++) { | |
ddd88763 | 1221 | uid_t uid; |
de07ab16 LP |
1222 | uint32_t u; |
1223 | ||
d05c5031 | 1224 | ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); |
4b67834e LP |
1225 | if (ret < 0) { |
1226 | log_error("Failed to look up user %s: %s", args[i], strerror(-ret)); | |
1227 | goto finish; | |
de07ab16 LP |
1228 | } |
1229 | ||
ddd88763 | 1230 | u = (uint32_t) uid; |
2a3613b1 SP |
1231 | ret = bus_method_call_with_reply ( |
1232 | bus, | |
1233 | "org.freedesktop.login1", | |
1234 | "/org/freedesktop/login1", | |
1235 | "org.freedesktop.login1.Manager", | |
1236 | "KillUser", | |
1237 | NULL, | |
1238 | NULL, | |
1239 | DBUS_TYPE_UINT32, &u, | |
1240 | DBUS_TYPE_INT32, &arg_signal, | |
1241 | DBUS_TYPE_INVALID); | |
1242 | if (ret) | |
de07ab16 | 1243 | goto finish; |
de07ab16 LP |
1244 | } |
1245 | ||
1246 | finish: | |
de07ab16 LP |
1247 | return ret; |
1248 | } | |
1249 | ||
a4c279f8 | 1250 | static int attach(DBusConnection *bus, char **args, unsigned n) { |
88e3dc90 | 1251 | int ret = 0; |
88e3dc90 LP |
1252 | unsigned i; |
1253 | dbus_bool_t interactive = true; | |
1254 | ||
88e3dc90 LP |
1255 | assert(args); |
1256 | ||
6bb92a16 LP |
1257 | polkit_agent_open_if_enabled(); |
1258 | ||
88e3dc90 | 1259 | for (i = 2; i < n; i++) { |
2a3613b1 SP |
1260 | ret = bus_method_call_with_reply ( |
1261 | bus, | |
88e3dc90 LP |
1262 | "org.freedesktop.login1", |
1263 | "/org/freedesktop/login1", | |
1264 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1265 | "AttachDevice", |
1266 | NULL, | |
1267 | NULL, | |
1268 | DBUS_TYPE_STRING, &args[1], | |
1269 | DBUS_TYPE_STRING, &args[i], | |
1270 | DBUS_TYPE_BOOLEAN, &interactive, | |
1271 | DBUS_TYPE_INVALID); | |
1272 | if (ret) | |
88e3dc90 | 1273 | goto finish; |
88e3dc90 LP |
1274 | } |
1275 | ||
1276 | finish: | |
88e3dc90 | 1277 | return ret; |
a4c279f8 LP |
1278 | } |
1279 | ||
1280 | static int flush_devices(DBusConnection *bus, char **args, unsigned n) { | |
88e3dc90 LP |
1281 | dbus_bool_t interactive = true; |
1282 | ||
88e3dc90 LP |
1283 | assert(args); |
1284 | ||
6bb92a16 LP |
1285 | polkit_agent_open_if_enabled(); |
1286 | ||
2a3613b1 SP |
1287 | return bus_method_call_with_reply ( |
1288 | bus, | |
88e3dc90 LP |
1289 | "org.freedesktop.login1", |
1290 | "/org/freedesktop/login1", | |
1291 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1292 | "FlushDevices", |
1293 | NULL, | |
1294 | NULL, | |
1295 | DBUS_TYPE_BOOLEAN, &interactive, | |
1296 | DBUS_TYPE_INVALID); | |
a4c279f8 LP |
1297 | } |
1298 | ||
7212a8a9 | 1299 | static int lock_sessions(DBusConnection *bus, char **args, unsigned n) { |
7212a8a9 LP |
1300 | polkit_agent_open_if_enabled(); |
1301 | ||
2a3613b1 SP |
1302 | return bus_method_call_with_reply ( |
1303 | bus, | |
7212a8a9 LP |
1304 | "org.freedesktop.login1", |
1305 | "/org/freedesktop/login1", | |
1306 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1307 | "LockSessions", |
1308 | NULL, | |
1309 | NULL, | |
1310 | DBUS_TYPE_INVALID); | |
7212a8a9 LP |
1311 | } |
1312 | ||
a4c279f8 | 1313 | static int terminate_seat(DBusConnection *bus, char **args, unsigned n) { |
88e3dc90 | 1314 | int ret = 0; |
88e3dc90 LP |
1315 | unsigned i; |
1316 | ||
88e3dc90 LP |
1317 | assert(args); |
1318 | ||
88e3dc90 | 1319 | for (i = 1; i < n; i++) { |
2a3613b1 SP |
1320 | ret = bus_method_call_with_reply ( |
1321 | bus, | |
88e3dc90 LP |
1322 | "org.freedesktop.login1", |
1323 | "/org/freedesktop/login1", | |
1324 | "org.freedesktop.login1.Manager", | |
2a3613b1 SP |
1325 | "TerminateSeat", |
1326 | NULL, | |
1327 | NULL, | |
1328 | DBUS_TYPE_STRING, &args[i], | |
1329 | DBUS_TYPE_INVALID); | |
1330 | if (ret) | |
88e3dc90 | 1331 | goto finish; |
88e3dc90 LP |
1332 | } |
1333 | ||
1334 | finish: | |
88e3dc90 | 1335 | return ret; |
a4c279f8 LP |
1336 | } |
1337 | ||
abca4822 LP |
1338 | static int help(void) { |
1339 | ||
1340 | printf("%s [OPTIONS...] {COMMAND} ...\n\n" | |
1341 | "Send control commands to or query the login manager.\n\n" | |
1342 | " -h --help Show this help\n" | |
1343 | " --version Show package version\n" | |
a4c279f8 LP |
1344 | " -p --property=NAME Show only properties by this name\n" |
1345 | " -a --all Show all properties, including empty ones\n" | |
1346 | " --kill-who=WHO Who to send signal to\n" | |
1347 | " -s --signal=SIGNAL Which signal to send\n" | |
1348 | " -H --host=[USER@]HOST\n" | |
abca4822 LP |
1349 | " Show information for remote host\n" |
1350 | " -P --privileged Acquire privileges before execution\n" | |
a4c279f8 | 1351 | " --no-pager Do not pipe output into a pager\n\n" |
abca4822 LP |
1352 | "Commands:\n" |
1353 | " list-sessions List sessions\n" | |
a4c279f8 | 1354 | " session-status [ID...] Show session status\n" |
de07ab16 | 1355 | " show-session [ID...] Show properties of one or more sessions\n" |
a4c279f8 LP |
1356 | " activate [ID] Activate a session\n" |
1357 | " lock-session [ID...] Screen lock one or more sessions\n" | |
88e3dc90 | 1358 | " unlock-session [ID...] Screen unlock one or more sessions\n" |
7212a8a9 | 1359 | " lock-sessions Screen lock all current sessions\n" |
a4c279f8 LP |
1360 | " terminate-session [ID...] Terminate one or more sessions\n" |
1361 | " kill-session [ID...] Send signal to processes of a session\n" | |
abca4822 | 1362 | " list-users List users\n" |
a4c279f8 | 1363 | " user-status [USER...] Show user status\n" |
de07ab16 | 1364 | " show-user [USER...] Show properties of one or more users\n" |
a4c279f8 LP |
1365 | " enable-linger [USER...] Enable linger state of one or more users\n" |
1366 | " disable-linger [USER...] Disable linger state of one or more users\n" | |
1367 | " terminate-user [USER...] Terminate all sessions of one or more users\n" | |
1368 | " kill-user [USER...] Send signal to processes of a user\n" | |
1369 | " list-seats List seats\n" | |
1370 | " seat-status [NAME...] Show seat status\n" | |
de07ab16 | 1371 | " show-seat [NAME...] Show properties of one or more seats\n" |
a4c279f8 LP |
1372 | " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n" |
1373 | " flush-devices Flush all device associations\n" | |
de07ab16 | 1374 | " terminate-seat [NAME...] Terminate all sessions on one or more seats\n", |
abca4822 LP |
1375 | program_invocation_short_name); |
1376 | ||
1377 | return 0; | |
1378 | } | |
1379 | ||
1380 | static int parse_argv(int argc, char *argv[]) { | |
1381 | ||
1382 | enum { | |
1383 | ARG_VERSION = 0x100, | |
a4c279f8 | 1384 | ARG_NO_PAGER, |
6bb92a16 LP |
1385 | ARG_KILL_WHO, |
1386 | ARG_NO_ASK_PASSWORD | |
abca4822 LP |
1387 | }; |
1388 | ||
1389 | static const struct option options[] = { | |
1390 | { "help", no_argument, NULL, 'h' }, | |
1391 | { "version", no_argument, NULL, ARG_VERSION }, | |
a4c279f8 LP |
1392 | { "property", required_argument, NULL, 'p' }, |
1393 | { "all", no_argument, NULL, 'a' }, | |
abca4822 | 1394 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, |
a4c279f8 LP |
1395 | { "kill-who", required_argument, NULL, ARG_KILL_WHO }, |
1396 | { "signal", required_argument, NULL, 's' }, | |
abca4822 LP |
1397 | { "host", required_argument, NULL, 'H' }, |
1398 | { "privileged",no_argument, NULL, 'P' }, | |
6bb92a16 | 1399 | { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, |
abca4822 LP |
1400 | { NULL, 0, NULL, 0 } |
1401 | }; | |
1402 | ||
1403 | int c; | |
1404 | ||
1405 | assert(argc >= 0); | |
1406 | assert(argv); | |
1407 | ||
a4c279f8 | 1408 | while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) { |
abca4822 LP |
1409 | |
1410 | switch (c) { | |
1411 | ||
1412 | case 'h': | |
1413 | help(); | |
1414 | return 0; | |
1415 | ||
1416 | case ARG_VERSION: | |
1417 | puts(PACKAGE_STRING); | |
1418 | puts(DISTRIBUTION); | |
1419 | puts(SYSTEMD_FEATURES); | |
1420 | return 0; | |
1421 | ||
a4c279f8 LP |
1422 | case 'p': { |
1423 | char **l; | |
1424 | ||
1425 | l = strv_append(arg_property, optarg); | |
1426 | if (!l) | |
1427 | return -ENOMEM; | |
1428 | ||
1429 | strv_free(arg_property); | |
1430 | arg_property = l; | |
1431 | ||
1432 | /* If the user asked for a particular | |
1433 | * property, show it to him, even if it is | |
1434 | * empty. */ | |
1435 | arg_all = true; | |
1436 | break; | |
1437 | } | |
1438 | ||
1439 | case 'a': | |
1440 | arg_all = true; | |
1441 | break; | |
1442 | ||
abca4822 LP |
1443 | case ARG_NO_PAGER: |
1444 | arg_no_pager = true; | |
1445 | break; | |
1446 | ||
6bb92a16 LP |
1447 | case ARG_NO_ASK_PASSWORD: |
1448 | arg_ask_password = false; | |
5d5e98eb | 1449 | break; |
6bb92a16 | 1450 | |
a4c279f8 LP |
1451 | case ARG_KILL_WHO: |
1452 | arg_kill_who = optarg; | |
1453 | break; | |
1454 | ||
1455 | case 's': | |
1456 | arg_signal = signal_from_string_try_harder(optarg); | |
1457 | if (arg_signal < 0) { | |
1458 | log_error("Failed to parse signal string %s.", optarg); | |
1459 | return -EINVAL; | |
1460 | } | |
1461 | break; | |
1462 | ||
abca4822 LP |
1463 | case 'P': |
1464 | arg_transport = TRANSPORT_POLKIT; | |
1465 | break; | |
1466 | ||
1467 | case 'H': | |
1468 | arg_transport = TRANSPORT_SSH; | |
1469 | arg_host = optarg; | |
1470 | break; | |
1471 | ||
1472 | case '?': | |
1473 | return -EINVAL; | |
1474 | ||
1475 | default: | |
1476 | log_error("Unknown option code %c", c); | |
1477 | return -EINVAL; | |
1478 | } | |
1479 | } | |
1480 | ||
1481 | return 1; | |
1482 | } | |
1483 | ||
1484 | static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { | |
1485 | ||
1486 | static const struct { | |
1487 | const char* verb; | |
1488 | const enum { | |
1489 | MORE, | |
1490 | LESS, | |
1491 | EQUAL | |
1492 | } argc_cmp; | |
1493 | const int argc; | |
1494 | int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); | |
1495 | } verbs[] = { | |
1496 | { "list-sessions", LESS, 1, list_sessions }, | |
a4c279f8 | 1497 | { "session-status", MORE, 2, show }, |
24310c11 | 1498 | { "show-session", MORE, 1, show }, |
a4c279f8 LP |
1499 | { "activate", EQUAL, 2, activate }, |
1500 | { "lock-session", MORE, 2, activate }, | |
88e3dc90 | 1501 | { "unlock-session", MORE, 2, activate }, |
7212a8a9 | 1502 | { "lock-sessions", EQUAL, 1, lock_sessions }, |
a4c279f8 | 1503 | { "terminate-session", MORE, 2, activate }, |
de07ab16 | 1504 | { "kill-session", MORE, 2, kill_session }, |
abca4822 | 1505 | { "list-users", EQUAL, 1, list_users }, |
a4c279f8 | 1506 | { "user-status", MORE, 2, show }, |
24310c11 | 1507 | { "show-user", MORE, 1, show }, |
a4c279f8 LP |
1508 | { "enable-linger", MORE, 2, enable_linger }, |
1509 | { "disable-linger", MORE, 2, enable_linger }, | |
88e3dc90 | 1510 | { "terminate-user", MORE, 2, terminate_user }, |
de07ab16 | 1511 | { "kill-user", MORE, 2, kill_user }, |
abca4822 | 1512 | { "list-seats", EQUAL, 1, list_seats }, |
a4c279f8 | 1513 | { "seat-status", MORE, 2, show }, |
24310c11 | 1514 | { "show-seat", MORE, 1, show }, |
a4c279f8 LP |
1515 | { "attach", MORE, 3, attach }, |
1516 | { "flush-devices", EQUAL, 1, flush_devices }, | |
de07ab16 | 1517 | { "terminate-seat", MORE, 2, terminate_seat }, |
abca4822 LP |
1518 | }; |
1519 | ||
1520 | int left; | |
1521 | unsigned i; | |
1522 | ||
1523 | assert(argc >= 0); | |
1524 | assert(argv); | |
1525 | assert(error); | |
1526 | ||
1527 | left = argc - optind; | |
1528 | ||
1529 | if (left <= 0) | |
1530 | /* Special rule: no arguments means "list-sessions" */ | |
1531 | i = 0; | |
1532 | else { | |
1533 | if (streq(argv[optind], "help")) { | |
1534 | help(); | |
1535 | return 0; | |
1536 | } | |
1537 | ||
1538 | for (i = 0; i < ELEMENTSOF(verbs); i++) | |
1539 | if (streq(argv[optind], verbs[i].verb)) | |
1540 | break; | |
1541 | ||
1542 | if (i >= ELEMENTSOF(verbs)) { | |
1543 | log_error("Unknown operation %s", argv[optind]); | |
1544 | return -EINVAL; | |
1545 | } | |
1546 | } | |
1547 | ||
1548 | switch (verbs[i].argc_cmp) { | |
1549 | ||
1550 | case EQUAL: | |
1551 | if (left != verbs[i].argc) { | |
1552 | log_error("Invalid number of arguments."); | |
1553 | return -EINVAL; | |
1554 | } | |
1555 | ||
1556 | break; | |
1557 | ||
1558 | case MORE: | |
1559 | if (left < verbs[i].argc) { | |
1560 | log_error("Too few arguments."); | |
1561 | return -EINVAL; | |
1562 | } | |
1563 | ||
1564 | break; | |
1565 | ||
1566 | case LESS: | |
1567 | if (left > verbs[i].argc) { | |
1568 | log_error("Too many arguments."); | |
1569 | return -EINVAL; | |
1570 | } | |
1571 | ||
1572 | break; | |
1573 | ||
1574 | default: | |
1575 | assert_not_reached("Unknown comparison operator."); | |
1576 | } | |
1577 | ||
1578 | if (!bus) { | |
1579 | log_error("Failed to get D-Bus connection: %s", error->message); | |
1580 | return -EIO; | |
1581 | } | |
1582 | ||
1583 | return verbs[i].dispatch(bus, argv + optind, left); | |
1584 | } | |
1585 | ||
1586 | int main(int argc, char*argv[]) { | |
1587 | int r, retval = EXIT_FAILURE; | |
1588 | DBusConnection *bus = NULL; | |
1589 | DBusError error; | |
1590 | ||
1591 | dbus_error_init(&error); | |
1592 | ||
1593 | log_parse_environment(); | |
1594 | log_open(); | |
1595 | ||
1596 | r = parse_argv(argc, argv); | |
1597 | if (r < 0) | |
1598 | goto finish; | |
1599 | else if (r == 0) { | |
1600 | retval = EXIT_SUCCESS; | |
1601 | goto finish; | |
1602 | } | |
1603 | ||
1604 | if (arg_transport == TRANSPORT_NORMAL) | |
1605 | bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); | |
1606 | else if (arg_transport == TRANSPORT_POLKIT) | |
1607 | bus_connect_system_polkit(&bus, &error); | |
1608 | else if (arg_transport == TRANSPORT_SSH) | |
1609 | bus_connect_system_ssh(NULL, arg_host, &bus, &error); | |
1610 | else | |
1611 | assert_not_reached("Uh, invalid transport..."); | |
1612 | ||
1613 | r = loginctl_main(bus, argc, argv, &error); | |
1614 | retval = r < 0 ? EXIT_FAILURE : r; | |
1615 | ||
1616 | finish: | |
1617 | if (bus) { | |
1618 | dbus_connection_flush(bus); | |
1619 | dbus_connection_close(bus); | |
1620 | dbus_connection_unref(bus); | |
1621 | } | |
1622 | ||
1623 | dbus_error_free(&error); | |
1624 | dbus_shutdown(); | |
1625 | ||
a4c279f8 LP |
1626 | strv_free(arg_property); |
1627 | ||
abca4822 LP |
1628 | pager_close(); |
1629 | ||
1630 | return retval; | |
1631 | } |