]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/logind-session-dbus.c
logind: make idle hint logic work
[thirdparty/systemd.git] / src / 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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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 " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
40 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
41 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
43 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
44 " <property name=\"ControlGroupPath\" type=\"s\" access=\"read\"/>\n" \
45 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
46 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
47 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
48 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
49 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
50 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
51 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
52 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
53 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
54 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
55 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
56 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
57 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
58 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
59 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
60 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
61 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
62 " </interface>\n"
63
64 #define INTROSPECTION \
65 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
66 "<node>\n" \
67 BUS_SESSION_INTERFACE \
68 BUS_PROPERTIES_INTERFACE \
69 BUS_PEER_INTERFACE \
70 BUS_INTROSPECTABLE_INTERFACE \
71 "</node>\n"
72
73 #define INTERFACES_LIST \
74 BUS_GENERIC_INTERFACES_LIST \
75 "org.freedesktop.login1.Session\0"
76
77 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
78 DBusMessageIter sub;
79 Session *s = data;
80 const char *id, *path;
81 char *p = NULL;
82
83 assert(i);
84 assert(property);
85 assert(s);
86
87 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
88 return -ENOMEM;
89
90 if (s->seat) {
91 id = s->seat->id;
92 path = p = seat_bus_path(s->seat);
93
94 if (!p)
95 return -ENOMEM;
96 } else {
97 id = "";
98 path = "/";
99 }
100
101 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
102 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
103 free(p);
104 return -ENOMEM;
105 }
106
107 free(p);
108
109 if (!dbus_message_iter_close_container(i, &sub))
110 return -ENOMEM;
111
112 return 0;
113 }
114
115 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
116 DBusMessageIter sub;
117 Session *s = data;
118 char *p = NULL;
119
120 assert(i);
121 assert(property);
122 assert(s);
123
124 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
125 return -ENOMEM;
126
127 p = user_bus_path(s->user);
128 if (!p)
129 return -ENOMEM;
130
131 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &s->user->uid) ||
132 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
133 free(p);
134 return -ENOMEM;
135 }
136
137 free(p);
138
139 if (!dbus_message_iter_close_container(i, &sub))
140 return -ENOMEM;
141
142 return 0;
143 }
144
145 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
146 Session *s = data;
147 dbus_bool_t b;
148
149 assert(i);
150 assert(property);
151 assert(s);
152
153 b = session_is_active(s);
154 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
155 return -ENOMEM;
156
157 return 0;
158 }
159
160 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
161 Session *s = data;
162 int b;
163
164 assert(i);
165 assert(property);
166 assert(s);
167
168 b = session_get_idle_hint(s, NULL) > 0;
169 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
170 return -ENOMEM;
171
172 return 0;
173 }
174
175 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
176 Session *s = data;
177 dual_timestamp t;
178 uint64_t u;
179
180 assert(i);
181 assert(property);
182 assert(s);
183
184 session_get_idle_hint(s, &t);
185 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
186
187 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
188 return -ENOMEM;
189
190 return 0;
191 }
192
193 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
194
195 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
196 Session *s;
197 char *id;
198
199 assert(m);
200 assert(path);
201 assert(_s);
202
203 if (!startswith(path, "/org/freedesktop/login1/session/"))
204 return -EINVAL;
205
206 id = bus_path_unescape(path + 32);
207 if (!id)
208 return -ENOMEM;
209
210 s = hashmap_get(m->sessions, id);
211 free(id);
212
213 if (!s)
214 return -ENOENT;
215
216 *_s = s;
217 return 0;
218 }
219
220 static DBusHandlerResult session_message_dispatch(
221 Session *s,
222 DBusConnection *connection,
223 DBusMessage *message) {
224
225 const BusProperty properties[] = {
226 { "org.freedesktop.login1.Session", "Id", bus_property_append_string, "s", s->id },
227 { "org.freedesktop.login1.Session", "User", bus_session_append_user, "(uo)", s },
228 { "org.freedesktop.login1.Session", "Name", bus_property_append_string, "s", s->user->name },
229 { "org.freedesktop.login1.Session", "Timestamp", bus_property_append_usec, "t", &s->timestamp.realtime },
230 { "org.freedesktop.login1.Session", "TimestampMonotonic", bus_property_append_usec, "t", &s->timestamp.monotonic },
231 { "org.freedesktop.login1.Session", "ControlGroupPath", bus_property_append_string, "s", s->cgroup_path },
232 { "org.freedesktop.login1.Session", "VTNr", bus_property_append_uint32, "u", &s->vtnr },
233 { "org.freedesktop.login1.Session", "Seat", bus_session_append_seat, "(so)", s },
234 { "org.freedesktop.login1.Session", "TTY", bus_property_append_string, "s", s->tty },
235 { "org.freedesktop.login1.Session", "Display", bus_property_append_string, "s", s->display },
236 { "org.freedesktop.login1.Session", "Remote", bus_property_append_bool, "b", &s->remote },
237 { "org.freedesktop.login1.Session", "RemoteUser", bus_property_append_string, "s", s->remote_user },
238 { "org.freedesktop.login1.Session", "RemoteHost", bus_property_append_string, "s", s->remote_host },
239 { "org.freedesktop.login1.Session", "Leader", bus_property_append_pid, "u", &s->leader },
240 { "org.freedesktop.login1.Session", "Audit", bus_property_append_uint32, "u", &s->audit_id },
241 { "org.freedesktop.login1.Session", "Type", bus_session_append_type, "s", &s->type },
242 { "org.freedesktop.login1.Session", "Active", bus_session_append_active, "b", s },
243 { "org.freedesktop.login1.Session", "Controllers", bus_property_append_strv, "as", s->controllers },
244 { "org.freedesktop.login1.Session", "ResetControllers", bus_property_append_strv, "as", s->reset_controllers },
245 { "org.freedesktop.login1.Session", "KillProcesses", bus_property_append_bool, "b", &s->kill_processes },
246 { "org.freedesktop.login1.Session", "IdleHint", bus_session_append_idle_hint, "b", s },
247 { "org.freedesktop.login1.Session", "IdleSinceHint", bus_session_append_idle_hint_since, "t", s },
248 { "org.freedesktop.login1.Session", "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", s },
249 { NULL, NULL, NULL, NULL, NULL }
250 };
251
252 DBusError error;
253 DBusMessage *reply = NULL;
254 int r;
255
256 assert(s);
257 assert(connection);
258 assert(message);
259
260 dbus_error_init(&error);
261
262 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
263
264 r = session_stop(s);
265 if (r < 0)
266 return bus_send_error_reply(connection, message, NULL, r);
267
268 reply = dbus_message_new_method_return(message);
269 if (!reply)
270 goto oom;
271
272 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
273
274 r = session_activate(s);
275 if (r < 0)
276 return bus_send_error_reply(connection, message, NULL, r);
277
278 reply = dbus_message_new_method_return(message);
279 if (!reply)
280 goto oom;
281
282 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
283 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
284 bool b;
285 DBusMessage *sig;
286
287 sig = dbus_message_new_signal(dbus_message_get_path(message), "org.freedesktop.login1.Session", dbus_message_get_member(message));
288 if (!sig)
289 goto oom;
290
291 b = dbus_connection_send(connection, sig, NULL);
292 dbus_message_unref(sig);
293
294 if (!b)
295 goto oom;
296
297 reply = dbus_message_new_method_return(message);
298 if (!reply)
299 goto oom;
300
301 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
302 dbus_bool_t b;
303
304 if (!dbus_message_get_args(
305 message,
306 &error,
307 DBUS_TYPE_BOOLEAN, &b,
308 DBUS_TYPE_INVALID))
309 return bus_send_error_reply(connection, message, &error, -EINVAL);
310
311 session_set_idle_hint(s, b);
312
313 reply = dbus_message_new_method_return(message);
314 if (!reply)
315 goto oom;
316
317 } else
318 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
319
320 if (reply) {
321 if (!dbus_connection_send(connection, reply, NULL))
322 goto oom;
323
324 dbus_message_unref(reply);
325 }
326
327 return DBUS_HANDLER_RESULT_HANDLED;
328
329 oom:
330 if (reply)
331 dbus_message_unref(reply);
332
333 dbus_error_free(&error);
334
335 return DBUS_HANDLER_RESULT_NEED_MEMORY;
336 }
337
338 static DBusHandlerResult session_message_handler(
339 DBusConnection *connection,
340 DBusMessage *message,
341 void *userdata) {
342
343 Manager *m = userdata;
344 Session *s;
345 int r;
346
347 r = get_session_for_path(m, dbus_message_get_path(message), &s);
348 if (r < 0) {
349
350 if (r == -ENOMEM)
351 return DBUS_HANDLER_RESULT_NEED_MEMORY;
352
353 if (r == -ENOENT) {
354 DBusError e;
355
356 dbus_error_init(&e);
357 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
358 return bus_send_error_reply(connection, message, &e, r);
359 }
360
361 return bus_send_error_reply(connection, message, NULL, r);
362 }
363
364 return session_message_dispatch(s, connection, message);
365 }
366
367 const DBusObjectPathVTable bus_session_vtable = {
368 .message_function = session_message_handler
369 };
370
371 char *session_bus_path(Session *s) {
372 char *t, *r;
373
374 assert(s);
375
376 t = bus_path_escape(s->id);
377 if (!t)
378 return NULL;
379
380 r = strappend("/org/freedesktop/login1/session/", t);
381 free(t);
382
383 return r;
384 }
385
386 int session_send_signal(Session *s, bool new_session) {
387 DBusMessage *m;
388 int r = -ENOMEM;
389 char *p = NULL;
390
391 assert(s);
392
393 m = dbus_message_new_signal("/org/freedesktop/login1",
394 "org.freedesktop.login1.Manager",
395 new_session ? "SessionNew" : "SessionRemoved");
396
397 if (!m)
398 return -ENOMEM;
399
400 p = session_bus_path(s);
401 if (!p)
402 goto finish;
403
404 if (!dbus_message_append_args(
405 m,
406 DBUS_TYPE_STRING, &s->id,
407 DBUS_TYPE_OBJECT_PATH, &p,
408 DBUS_TYPE_INVALID))
409 goto finish;
410
411 if (!dbus_connection_send(s->manager->bus, m, NULL))
412 goto finish;
413
414 r = 0;
415
416 finish:
417 dbus_message_unref(m);
418 free(p);
419
420 return r;
421 }
422
423 int session_send_changed(Session *s, const char *properties) {
424 DBusMessage *m;
425 int r = -ENOMEM;
426 char *p = NULL;
427
428 assert(s);
429
430 p = session_bus_path(s);
431 if (!p)
432 return -ENOMEM;
433
434 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
435 if (!m)
436 goto finish;
437
438 if (!dbus_connection_send(s->manager->bus, m, NULL))
439 goto finish;
440
441 r = 0;
442
443 finish:
444 if (m)
445 dbus_message_unref(m);
446 free(p);
447
448 return r;
449 }