]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/login/logind-session-dbus.c
logind: Make more use of cleanup macros
[thirdparty/systemd.git] / src / login / logind-session-dbus.c
... / ...
CommitLineData
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=\"Leader\" type=\"u\" access=\"read\"/>\n" \
60 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
61 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
62 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
63 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
64 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
65 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
66 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
67 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
68 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
69 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
70 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
71 " </interface>\n"
72
73#define INTROSPECTION \
74 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
75 "<node>\n" \
76 BUS_SESSION_INTERFACE \
77 BUS_PROPERTIES_INTERFACE \
78 BUS_PEER_INTERFACE \
79 BUS_INTROSPECTABLE_INTERFACE \
80 "</node>\n"
81
82#define INTERFACES_LIST \
83 BUS_GENERIC_INTERFACES_LIST \
84 "org.freedesktop.login1.Session\0"
85
86static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
87 DBusMessageIter sub;
88 Session *s = data;
89 const char *id, *path;
90 char *p = NULL;
91
92 assert(i);
93 assert(property);
94 assert(s);
95
96 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
97 return -ENOMEM;
98
99 if (s->seat) {
100 id = s->seat->id;
101 path = p = seat_bus_path(s->seat);
102
103 if (!p)
104 return -ENOMEM;
105 } else {
106 id = "";
107 path = "/";
108 }
109
110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
111 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
112 free(p);
113 return -ENOMEM;
114 }
115
116 free(p);
117
118 if (!dbus_message_iter_close_container(i, &sub))
119 return -ENOMEM;
120
121 return 0;
122}
123
124static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
125 DBusMessageIter sub;
126 User *u = data;
127 char *p = NULL;
128
129 assert(i);
130 assert(property);
131 assert(u);
132
133 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
134 return -ENOMEM;
135
136 p = user_bus_path(u);
137 if (!p)
138 return -ENOMEM;
139
140 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
141 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
142 free(p);
143 return -ENOMEM;
144 }
145
146 free(p);
147
148 if (!dbus_message_iter_close_container(i, &sub))
149 return -ENOMEM;
150
151 return 0;
152}
153
154static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
155 Session *s = data;
156 dbus_bool_t b;
157
158 assert(i);
159 assert(property);
160 assert(s);
161
162 b = session_is_active(s);
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
164 return -ENOMEM;
165
166 return 0;
167}
168
169static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
170 Session *s = data;
171 int b;
172
173 assert(i);
174 assert(property);
175 assert(s);
176
177 b = session_get_idle_hint(s, NULL) > 0;
178 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
179 return -ENOMEM;
180
181 return 0;
182}
183
184static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
185 Session *s = data;
186 dual_timestamp t;
187 uint64_t u;
188 int r;
189
190 assert(i);
191 assert(property);
192 assert(s);
193
194 r = session_get_idle_hint(s, &t);
195 if (r < 0)
196 return r;
197
198 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
199
200 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
201 return -ENOMEM;
202
203 return 0;
204}
205
206static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
207 Session *s = data;
208 char *t;
209 int r;
210 bool success;
211
212 assert(i);
213 assert(property);
214 assert(s);
215
216 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
217 if (r < 0)
218 return r;
219
220 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
221 free(t);
222
223 return success ? 0 : -ENOMEM;
224}
225
226static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
227static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
228
229static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
230 Session *s = data;
231 const char *state;
232
233 assert(i);
234 assert(property);
235 assert(s);
236
237 state = session_state_to_string(session_get_state(s));
238
239 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
240 return -ENOMEM;
241
242 return 0;
243}
244
245static int get_session_for_path(Manager *m, const char *path, Session **_s) {
246 Session *s;
247 char *id;
248
249 assert(m);
250 assert(path);
251 assert(_s);
252
253 if (!startswith(path, "/org/freedesktop/login1/session/"))
254 return -EINVAL;
255
256 id = bus_path_unescape(path + 32);
257 if (!id)
258 return -ENOMEM;
259
260 s = hashmap_get(m->sessions, id);
261 free(id);
262
263 if (!s)
264 return -ENOENT;
265
266 *_s = s;
267 return 0;
268}
269
270static const BusProperty bus_login_session_properties[] = {
271 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
272 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
273 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
274 { "DefaultControlGroup", bus_session_append_default_cgroup, "s", 0, },
275 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
276 { "Seat", bus_session_append_seat, "(so)", 0 },
277 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
278 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
279 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
280 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
281 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
282 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
283 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
284 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
285 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
286 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
287 { "Active", bus_session_append_active, "b", 0 },
288 { "State", bus_session_append_state, "s", 0 },
289 { "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true },
290 { "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true },
291 { "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) },
292 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
293 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
294 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
295 { NULL, }
296};
297
298static const BusProperty bus_login_session_user_properties[] = {
299 { "User", bus_session_append_user, "(uo)", 0 },
300 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
301 { NULL, }
302};
303
304static DBusHandlerResult session_message_dispatch(
305 Session *s,
306 DBusConnection *connection,
307 DBusMessage *message) {
308
309 DBusError error;
310 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
311 int r;
312
313 assert(s);
314 assert(connection);
315 assert(message);
316
317 dbus_error_init(&error);
318
319 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
320
321 r = session_stop(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", "Activate")) {
330
331 r = session_activate(s);
332 if (r < 0)
333 return bus_send_error_reply(connection, message, NULL, r);
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", "Lock") ||
340 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
341
342 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
343 goto oom;
344
345 reply = dbus_message_new_method_return(message);
346 if (!reply)
347 goto oom;
348
349 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
350 dbus_bool_t b;
351 unsigned long ul;
352
353 if (!dbus_message_get_args(
354 message,
355 &error,
356 DBUS_TYPE_BOOLEAN, &b,
357 DBUS_TYPE_INVALID))
358 return bus_send_error_reply(connection, message, &error, -EINVAL);
359
360 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
361 if (ul == (unsigned long) -1)
362 return bus_send_error_reply(connection, message, &error, -EIO);
363
364 if (ul != 0 && ul != s->user->uid)
365 return bus_send_error_reply(connection, message, NULL, -EPERM);
366
367 session_set_idle_hint(s, b);
368
369 reply = dbus_message_new_method_return(message);
370 if (!reply)
371 goto oom;
372
373 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
374 const char *swho;
375 int32_t signo;
376 KillWho who;
377
378 if (!dbus_message_get_args(
379 message,
380 &error,
381 DBUS_TYPE_STRING, &swho,
382 DBUS_TYPE_INT32, &signo,
383 DBUS_TYPE_INVALID))
384 return bus_send_error_reply(connection, message, &error, -EINVAL);
385
386 if (isempty(swho))
387 who = KILL_ALL;
388 else {
389 who = kill_who_from_string(swho);
390 if (who < 0)
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
392 }
393
394 if (signo <= 0 || signo >= _NSIG)
395 return bus_send_error_reply(connection, message, &error, -EINVAL);
396
397 r = session_kill(s, who, signo);
398 if (r < 0)
399 return bus_send_error_reply(connection, message, NULL, r);
400
401 reply = dbus_message_new_method_return(message);
402 if (!reply)
403 goto oom;
404
405 } else {
406 const BusBoundProperties bps[] = {
407 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
408 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
409 { NULL, }
410 };
411 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
412 }
413
414 if (reply) {
415 if (!bus_maybe_send_reply(connection, message, reply))
416 goto oom;
417 }
418
419 return DBUS_HANDLER_RESULT_HANDLED;
420
421oom:
422 dbus_error_free(&error);
423
424 return DBUS_HANDLER_RESULT_NEED_MEMORY;
425}
426
427static DBusHandlerResult session_message_handler(
428 DBusConnection *connection,
429 DBusMessage *message,
430 void *userdata) {
431
432 Manager *m = userdata;
433 Session *s;
434 int r;
435
436 r = get_session_for_path(m, dbus_message_get_path(message), &s);
437 if (r < 0) {
438
439 if (r == -ENOMEM)
440 return DBUS_HANDLER_RESULT_NEED_MEMORY;
441
442 if (r == -ENOENT) {
443 DBusError e;
444
445 dbus_error_init(&e);
446 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
447 return bus_send_error_reply(connection, message, &e, r);
448 }
449
450 return bus_send_error_reply(connection, message, NULL, r);
451 }
452
453 return session_message_dispatch(s, connection, message);
454}
455
456const DBusObjectPathVTable bus_session_vtable = {
457 .message_function = session_message_handler
458};
459
460char *session_bus_path(Session *s) {
461 char *t, *r;
462
463 assert(s);
464
465 t = bus_path_escape(s->id);
466 if (!t)
467 return NULL;
468
469 r = strappend("/org/freedesktop/login1/session/", t);
470 free(t);
471
472 return r;
473}
474
475int session_send_signal(Session *s, bool new_session) {
476 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
477 int r = -ENOMEM;
478 _cleanup_free_ char *p = NULL;
479
480 assert(s);
481
482 m = dbus_message_new_signal("/org/freedesktop/login1",
483 "org.freedesktop.login1.Manager",
484 new_session ? "SessionNew" : "SessionRemoved");
485
486 if (!m)
487 return -ENOMEM;
488
489 p = session_bus_path(s);
490 if (!p)
491 goto finish;
492
493 if (!dbus_message_append_args(
494 m,
495 DBUS_TYPE_STRING, &s->id,
496 DBUS_TYPE_OBJECT_PATH, &p,
497 DBUS_TYPE_INVALID))
498 goto finish;
499
500 if (!dbus_connection_send(s->manager->bus, m, NULL))
501 goto finish;
502
503 r = 0;
504
505finish:
506 return r;
507}
508
509int session_send_changed(Session *s, const char *properties) {
510 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
511 int r = -ENOMEM;
512 _cleanup_free_ char *p = NULL;
513
514 assert(s);
515
516 if (!s->started)
517 return 0;
518
519 p = session_bus_path(s);
520 if (!p)
521 return -ENOMEM;
522
523 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
524 if (!m)
525 goto finish;
526
527 if (!dbus_connection_send(s->manager->bus, m, NULL))
528 goto finish;
529
530 r = 0;
531
532finish:
533 return r;
534}
535
536int session_send_lock(Session *s, bool lock) {
537 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
538 bool b;
539 _cleanup_free_ char *p = NULL;
540
541 assert(s);
542
543 p = session_bus_path(s);
544 if (!p)
545 return -ENOMEM;
546
547 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
548
549 if (!m)
550 return -ENOMEM;
551
552 b = dbus_connection_send(s->manager->bus, m, NULL);
553 if (!b)
554 return -ENOMEM;
555
556 return 0;
557}
558
559int session_send_lock_all(Manager *m, bool lock) {
560 Session *session;
561 Iterator i;
562 int r = 0;
563
564 assert(m);
565
566 HASHMAP_FOREACH(session, m->sessions, i) {
567 int k;
568
569 k = session_send_lock(session, lock);
570 if (k < 0)
571 r = k;
572 }
573
574 return r;
575}