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