]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-session-dbus.c
logind: add infrastructure to keep track of machines, and move to slices
[thirdparty/systemd.git] / src / login / logind-session-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24
25 #include "logind.h"
26 #include "logind-session.h"
27 #include "dbus-common.h"
28 #include "util.h"
29
30 #define BUS_SESSION_INTERFACE \
31 " <interface name=\"org.freedesktop.login1.Session\">\n" \
32 " <method name=\"Terminate\"/>\n" \
33 " <method name=\"Activate\"/>\n" \
34 " <method name=\"Lock\"/>\n" \
35 " <method name=\"Unlock\"/>\n" \
36 " <method name=\"SetIdleHint\">\n" \
37 " <arg name=\"b\" type=\"b\"/>\n" \
38 " </method>\n" \
39 " <method name=\"Kill\">\n" \
40 " <arg name=\"who\" type=\"s\"/>\n" \
41 " <arg name=\"signal\" type=\"s\"/>\n" \
42 " </method>\n" \
43 " <signal name=\"Lock\"/>\n" \
44 " <signal name=\"Unlock\"/>\n" \
45 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
46 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
47 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
48 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
49 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
50 " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
51 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
52 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
53 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
54 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
55 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
56 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
58 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
59 " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
60 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
61 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
62 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
63 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
64 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
65 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
66 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
67 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
68 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
69 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
70 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
71 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
72 " </interface>\n"
73
74 #define INTROSPECTION \
75 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
76 "<node>\n" \
77 BUS_SESSION_INTERFACE \
78 BUS_PROPERTIES_INTERFACE \
79 BUS_PEER_INTERFACE \
80 BUS_INTROSPECTABLE_INTERFACE \
81 "</node>\n"
82
83 #define INTERFACES_LIST \
84 BUS_GENERIC_INTERFACES_LIST \
85 "org.freedesktop.login1.Session\0"
86
87 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
88 DBusMessageIter sub;
89 Session *s = data;
90 const char *id, *path;
91 char *p = NULL;
92
93 assert(i);
94 assert(property);
95 assert(s);
96
97 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
98 return -ENOMEM;
99
100 if (s->seat) {
101 id = s->seat->id;
102 path = p = seat_bus_path(s->seat);
103
104 if (!p)
105 return -ENOMEM;
106 } else {
107 id = "";
108 path = "/";
109 }
110
111 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
112 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
113 return -ENOMEM;
114
115 if (!dbus_message_iter_close_container(i, &sub))
116 return -ENOMEM;
117
118 return 0;
119 }
120
121 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
122 DBusMessageIter sub;
123 User *u = data;
124 _cleanup_free_ char *p = NULL;
125
126 assert(i);
127 assert(property);
128 assert(u);
129
130 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
131 return -ENOMEM;
132
133 p = user_bus_path(u);
134 if (!p)
135 return -ENOMEM;
136
137 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
138 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
139 return -ENOMEM;
140
141 if (!dbus_message_iter_close_container(i, &sub))
142 return -ENOMEM;
143
144 return 0;
145 }
146
147 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
148 Session *s = data;
149 dbus_bool_t b;
150
151 assert(i);
152 assert(property);
153 assert(s);
154
155 b = session_is_active(s);
156 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
157 return -ENOMEM;
158
159 return 0;
160 }
161
162 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
163 Session *s = data;
164 int b;
165
166 assert(i);
167 assert(property);
168 assert(s);
169
170 b = session_get_idle_hint(s, NULL) > 0;
171 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
172 return -ENOMEM;
173
174 return 0;
175 }
176
177 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
178 Session *s = data;
179 dual_timestamp t;
180 uint64_t u;
181 int r;
182
183 assert(i);
184 assert(property);
185 assert(s);
186
187 r = session_get_idle_hint(s, &t);
188 if (r < 0)
189 return r;
190
191 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
192
193 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
194 return -ENOMEM;
195
196 return 0;
197 }
198
199 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
200 Session *s = data;
201 _cleanup_free_ char *t = NULL;
202 int r;
203 bool success;
204
205 assert(i);
206 assert(property);
207 assert(s);
208
209 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
210 if (r < 0)
211 return r;
212
213 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
214 return success ? 0 : -ENOMEM;
215 }
216
217 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
218 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
219
220 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
221 Session *s = data;
222 const char *state;
223
224 assert(i);
225 assert(property);
226 assert(s);
227
228 state = session_state_to_string(session_get_state(s));
229
230 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
231 return -ENOMEM;
232
233 return 0;
234 }
235
236 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
237 _cleanup_free_ char *id = NULL;
238 Session *s;
239
240 assert(m);
241 assert(path);
242 assert(_s);
243
244 if (!startswith(path, "/org/freedesktop/login1/session/"))
245 return -EINVAL;
246
247 id = bus_path_unescape(path + 32);
248 if (!id)
249 return -ENOMEM;
250
251 s = hashmap_get(m->sessions, id);
252 if (!s)
253 return -ENOENT;
254
255 *_s = s;
256 return 0;
257 }
258
259 static const BusProperty bus_login_session_properties[] = {
260 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
261 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
262 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
263 { "DefaultControlGroup", bus_session_append_default_cgroup, "s", 0, },
264 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
265 { "Seat", bus_session_append_seat, "(so)", 0 },
266 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
267 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
268 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
269 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
270 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
271 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
272 { "Slice", bus_property_append_string, "s", offsetof(Session, slice), true },
273 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
274 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
275 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
276 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
277 { "Active", bus_session_append_active, "b", 0 },
278 { "State", bus_session_append_state, "s", 0 },
279 { "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true },
280 { "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true },
281 { "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) },
282 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
283 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
284 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
285 { NULL, }
286 };
287
288 static const BusProperty bus_login_session_user_properties[] = {
289 { "User", bus_session_append_user, "(uo)", 0 },
290 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
291 { NULL, }
292 };
293
294 static DBusHandlerResult session_message_dispatch(
295 Session *s,
296 DBusConnection *connection,
297 DBusMessage *message) {
298
299 DBusError error;
300 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
301 int r;
302
303 assert(s);
304 assert(connection);
305 assert(message);
306
307 dbus_error_init(&error);
308
309 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
310
311 r = session_stop(s);
312 if (r < 0)
313 return bus_send_error_reply(connection, message, NULL, r);
314
315 reply = dbus_message_new_method_return(message);
316 if (!reply)
317 goto oom;
318
319 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
320
321 r = session_activate(s);
322 if (r < 0)
323 return bus_send_error_reply(connection, message, NULL, r);
324
325 reply = dbus_message_new_method_return(message);
326 if (!reply)
327 goto oom;
328
329 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
330 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
331
332 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
333 goto oom;
334
335 reply = dbus_message_new_method_return(message);
336 if (!reply)
337 goto oom;
338
339 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
340 dbus_bool_t b;
341 unsigned long ul;
342
343 if (!dbus_message_get_args(
344 message,
345 &error,
346 DBUS_TYPE_BOOLEAN, &b,
347 DBUS_TYPE_INVALID))
348 return bus_send_error_reply(connection, message, &error, -EINVAL);
349
350 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
351 if (ul == (unsigned long) -1)
352 return bus_send_error_reply(connection, message, &error, -EIO);
353
354 if (ul != 0 && ul != s->user->uid)
355 return bus_send_error_reply(connection, message, NULL, -EPERM);
356
357 session_set_idle_hint(s, b);
358
359 reply = dbus_message_new_method_return(message);
360 if (!reply)
361 goto oom;
362
363 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
364 const char *swho;
365 int32_t signo;
366 KillWho who;
367
368 if (!dbus_message_get_args(
369 message,
370 &error,
371 DBUS_TYPE_STRING, &swho,
372 DBUS_TYPE_INT32, &signo,
373 DBUS_TYPE_INVALID))
374 return bus_send_error_reply(connection, message, &error, -EINVAL);
375
376 if (isempty(swho))
377 who = KILL_ALL;
378 else {
379 who = kill_who_from_string(swho);
380 if (who < 0)
381 return bus_send_error_reply(connection, message, &error, -EINVAL);
382 }
383
384 if (signo <= 0 || signo >= _NSIG)
385 return bus_send_error_reply(connection, message, &error, -EINVAL);
386
387 r = session_kill(s, who, signo);
388 if (r < 0)
389 return bus_send_error_reply(connection, message, NULL, r);
390
391 reply = dbus_message_new_method_return(message);
392 if (!reply)
393 goto oom;
394
395 } else {
396 const BusBoundProperties bps[] = {
397 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
398 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
399 { NULL, }
400 };
401 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
402 }
403
404 if (reply) {
405 if (!bus_maybe_send_reply(connection, message, reply))
406 goto oom;
407 }
408
409 return DBUS_HANDLER_RESULT_HANDLED;
410
411 oom:
412 dbus_error_free(&error);
413
414 return DBUS_HANDLER_RESULT_NEED_MEMORY;
415 }
416
417 static DBusHandlerResult session_message_handler(
418 DBusConnection *connection,
419 DBusMessage *message,
420 void *userdata) {
421
422 Manager *m = userdata;
423 Session *s;
424 int r;
425
426 r = get_session_for_path(m, dbus_message_get_path(message), &s);
427 if (r < 0) {
428
429 if (r == -ENOMEM)
430 return DBUS_HANDLER_RESULT_NEED_MEMORY;
431
432 if (r == -ENOENT) {
433 DBusError e;
434
435 dbus_error_init(&e);
436 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
437 return bus_send_error_reply(connection, message, &e, r);
438 }
439
440 return bus_send_error_reply(connection, message, NULL, r);
441 }
442
443 return session_message_dispatch(s, connection, message);
444 }
445
446 const DBusObjectPathVTable bus_session_vtable = {
447 .message_function = session_message_handler
448 };
449
450 char *session_bus_path(Session *s) {
451 _cleanup_free_ char *t = NULL;
452
453 assert(s);
454
455 t = bus_path_escape(s->id);
456 if (!t)
457 return NULL;
458
459 return strappend("/org/freedesktop/login1/session/", t);
460 }
461
462 int session_send_signal(Session *s, bool new_session) {
463 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
464 _cleanup_free_ char *p = NULL;
465
466 assert(s);
467
468 m = dbus_message_new_signal("/org/freedesktop/login1",
469 "org.freedesktop.login1.Manager",
470 new_session ? "SessionNew" : "SessionRemoved");
471
472 if (!m)
473 return -ENOMEM;
474
475 p = session_bus_path(s);
476 if (!p)
477 return -ENOMEM;
478
479 if (!dbus_message_append_args(
480 m,
481 DBUS_TYPE_STRING, &s->id,
482 DBUS_TYPE_OBJECT_PATH, &p,
483 DBUS_TYPE_INVALID))
484 return -ENOMEM;
485
486 if (!dbus_connection_send(s->manager->bus, m, NULL))
487 return -ENOMEM;
488
489 return 0;
490 }
491
492 int session_send_changed(Session *s, const char *properties) {
493 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
494 _cleanup_free_ char *p = NULL;
495
496 assert(s);
497
498 if (!s->started)
499 return 0;
500
501 p = session_bus_path(s);
502 if (!p)
503 return -ENOMEM;
504
505 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
506 if (!m)
507 return -ENOMEM;
508
509 if (!dbus_connection_send(s->manager->bus, m, NULL))
510 return -ENOMEM;
511
512 return 0;
513 }
514
515 int session_send_lock(Session *s, bool lock) {
516 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
517 bool b;
518 _cleanup_free_ char *p = NULL;
519
520 assert(s);
521
522 p = session_bus_path(s);
523 if (!p)
524 return -ENOMEM;
525
526 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
527
528 if (!m)
529 return -ENOMEM;
530
531 b = dbus_connection_send(s->manager->bus, m, NULL);
532 if (!b)
533 return -ENOMEM;
534
535 return 0;
536 }
537
538 int session_send_lock_all(Manager *m, bool lock) {
539 Session *session;
540 Iterator i;
541 int r = 0;
542
543 assert(m);
544
545 HASHMAP_FOREACH(session, m->sessions, i) {
546 int k;
547
548 k = session_send_lock(session, lock);
549 if (k < 0)
550 r = k;
551 }
552
553 return r;
554 }