]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat-dbus.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / login / logind-seat-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-seat.h"
27 #include "dbus-common.h"
28 #include "util.h"
29
30 #define BUS_SEAT_INTERFACE \
31 " <interface name=\"org.freedesktop.login1.Seat\">\n" \
32 " <method name=\"Terminate\"/>\n" \
33 " <method name=\"ActivateSession\">\n" \
34 " <arg name=\"id\" type=\"s\"/>\n" \
35 " </method>\n" \
36 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
37 " <property name=\"ActiveSession\" type=\"so\" access=\"read\"/>\n" \
38 " <property name=\"CanMultiSession\" type=\"b\" access=\"read\"/>\n" \
39 " <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
40 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
41 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
42 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
43 " </interface>\n" \
44
45 #define INTROSPECTION \
46 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
47 "<node>\n" \
48 BUS_SEAT_INTERFACE \
49 BUS_PROPERTIES_INTERFACE \
50 BUS_PEER_INTERFACE \
51 BUS_INTROSPECTABLE_INTERFACE \
52 "</node>\n"
53
54 #define INTERFACES_LIST \
55 BUS_GENERIC_INTERFACES_LIST \
56 "org.freedesktop.login1.Seat\0"
57
58 static int bus_seat_append_active(DBusMessageIter *i, const char *property, void *data) {
59 DBusMessageIter sub;
60 Seat *s = data;
61 const char *id, *path;
62 char *p = NULL;
63
64 assert(i);
65 assert(property);
66 assert(s);
67
68 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
69 return -ENOMEM;
70
71 if (s->active) {
72 id = s->active->id;
73 path = p = session_bus_path(s->active);
74
75 if (!p)
76 return -ENOMEM;
77 } else {
78 id = "";
79 path = "/";
80 }
81
82 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
83 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
84 free(p);
85 return -ENOMEM;
86 }
87
88 free(p);
89
90 if (!dbus_message_iter_close_container(i, &sub))
91 return -ENOMEM;
92
93 return 0;
94 }
95
96 static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, void *data) {
97 DBusMessageIter sub, sub2;
98 Seat *s = data;
99 Session *session;
100
101 assert(i);
102 assert(property);
103 assert(s);
104
105 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
106 return -ENOMEM;
107
108 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
109 char *p;
110
111 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
112 return -ENOMEM;
113
114 p = session_bus_path(session);
115 if (!p)
116 return -ENOMEM;
117
118 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
119 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
120 free(p);
121 return -ENOMEM;
122 }
123
124 free(p);
125
126 if (!dbus_message_iter_close_container(&sub, &sub2))
127 return -ENOMEM;
128 }
129
130 if (!dbus_message_iter_close_container(i, &sub))
131 return -ENOMEM;
132
133 return 0;
134 }
135
136 static int bus_seat_append_multi_session(DBusMessageIter *i, const char *property, void *data) {
137 Seat *s = data;
138 dbus_bool_t b;
139
140 assert(i);
141 assert(property);
142 assert(s);
143
144 b = seat_can_multi_session(s);
145
146 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
147 return -ENOMEM;
148
149 return 0;
150 }
151
152 static int bus_seat_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
153 Seat *s = data;
154 dbus_bool_t b;
155
156 assert(i);
157 assert(property);
158 assert(s);
159
160 b = seat_get_idle_hint(s, NULL) > 0;
161 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
162 return -ENOMEM;
163
164 return 0;
165 }
166
167 static int bus_seat_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
168 Seat *s = data;
169 dual_timestamp t;
170 uint64_t k;
171
172 assert(i);
173 assert(property);
174 assert(s);
175
176 seat_get_idle_hint(s, &t);
177 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
178
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
180 return -ENOMEM;
181
182 return 0;
183 }
184
185 static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
186 Seat *s;
187 char *id;
188
189 assert(m);
190 assert(path);
191 assert(_s);
192
193 if (!startswith(path, "/org/freedesktop/login1/seat/"))
194 return -EINVAL;
195
196 id = bus_path_unescape(path + 29);
197 if (!id)
198 return -ENOMEM;
199
200 s = hashmap_get(m->seats, id);
201 free(id);
202
203 if (!s)
204 return -ENOENT;
205
206 *_s = s;
207 return 0;
208 }
209
210 static const BusProperty bus_login_seat_properties[] = {
211 { "Id", bus_property_append_string, "s", offsetof(Seat, id), true },
212 { "ActiveSession", bus_seat_append_active, "(so)", 0 },
213 { "CanMultiSession", bus_seat_append_multi_session, "b", 0 },
214 { "Sessions", bus_seat_append_sessions, "a(so)", 0 },
215 { "IdleHint", bus_seat_append_idle_hint, "b", 0 },
216 { "IdleSinceHint", bus_seat_append_idle_hint_since, "t", 0 },
217 { "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t", 0 },
218 { NULL, }
219 };
220
221 static DBusHandlerResult seat_message_dispatch(
222 Seat *s,
223 DBusConnection *connection,
224 DBusMessage *message) {
225
226 DBusError error;
227 DBusMessage *reply = NULL;
228 int r;
229
230 assert(s);
231 assert(connection);
232 assert(message);
233
234 dbus_error_init(&error);
235
236 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "Terminate")) {
237
238 r = seat_stop_sessions(s);
239 if (r < 0)
240 return bus_send_error_reply(connection, message, NULL, r);
241
242 reply = dbus_message_new_method_return(message);
243 if (!reply)
244 goto oom;
245
246 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "ActivateSession")) {
247 const char *name;
248 Session *session;
249
250 if (!dbus_message_get_args(
251 message,
252 &error,
253 DBUS_TYPE_STRING, &name,
254 DBUS_TYPE_INVALID))
255 return bus_send_error_reply(connection, message, &error, -EINVAL);
256
257 session = hashmap_get(s->manager->sessions, name);
258 if (!session || session->seat != s)
259 return bus_send_error_reply(connection, message, &error, -ENOENT);
260
261 r = session_activate(session);
262 if (r < 0)
263 return bus_send_error_reply(connection, message, NULL, r);
264
265 reply = dbus_message_new_method_return(message);
266 if (!reply)
267 goto oom;
268 } else {
269 const BusBoundProperties bps[] = {
270 { "org.freedesktop.login1.Seat", bus_login_seat_properties, s },
271 { NULL, }
272 };
273 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
274 }
275
276 if (reply) {
277 if (!dbus_connection_send(connection, reply, NULL))
278 goto oom;
279
280 dbus_message_unref(reply);
281 }
282
283 return DBUS_HANDLER_RESULT_HANDLED;
284
285 oom:
286 if (reply)
287 dbus_message_unref(reply);
288
289 dbus_error_free(&error);
290
291 return DBUS_HANDLER_RESULT_NEED_MEMORY;
292 }
293
294 static DBusHandlerResult seat_message_handler(
295 DBusConnection *connection,
296 DBusMessage *message,
297 void *userdata) {
298
299 Manager *m = userdata;
300 Seat *s;
301 int r;
302
303 r = get_seat_for_path(m, dbus_message_get_path(message), &s);
304 if (r < 0) {
305
306 if (r == -ENOMEM)
307 return DBUS_HANDLER_RESULT_NEED_MEMORY;
308
309 if (r == -ENOENT) {
310 DBusError e;
311
312 dbus_error_init(&e);
313 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown seat");
314 return bus_send_error_reply(connection, message, &e, r);
315 }
316
317 return bus_send_error_reply(connection, message, NULL, r);
318 }
319
320 return seat_message_dispatch(s, connection, message);
321 }
322
323 const DBusObjectPathVTable bus_seat_vtable = {
324 .message_function = seat_message_handler
325 };
326
327 char *seat_bus_path(Seat *s) {
328 char *t, *r;
329
330 assert(s);
331
332 t = bus_path_escape(s->id);
333 if (!t)
334 return NULL;
335
336 r = strappend("/org/freedesktop/login1/seat/", t);
337 free(t);
338
339 return r;
340 }
341
342 int seat_send_signal(Seat *s, bool new_seat) {
343 DBusMessage *m;
344 int r = -ENOMEM;
345 char *p = NULL;
346
347 assert(s);
348
349 m = dbus_message_new_signal("/org/freedesktop/login1",
350 "org.freedesktop.login1.Manager",
351 new_seat ? "SeatNew" : "SeatRemoved");
352
353 if (!m)
354 return -ENOMEM;
355
356 p = seat_bus_path(s);
357 if (!p)
358 goto finish;
359
360 if (!dbus_message_append_args(
361 m,
362 DBUS_TYPE_STRING, &s->id,
363 DBUS_TYPE_OBJECT_PATH, &p,
364 DBUS_TYPE_INVALID))
365 goto finish;
366
367 if (!dbus_connection_send(s->manager->bus, m, NULL))
368 goto finish;
369
370 r = 0;
371
372 finish:
373 dbus_message_unref(m);
374 free(p);
375
376 return r;
377 }
378
379 int seat_send_changed(Seat *s, const char *properties) {
380 DBusMessage *m;
381 int r = -ENOMEM;
382 char *p = NULL;
383
384 assert(s);
385
386 if (!s->started)
387 return 0;
388
389 p = seat_bus_path(s);
390 if (!p)
391 return -ENOMEM;
392
393 m = bus_properties_changed_new(p, "org.freedesktop.login1.Seat", properties);
394 if (!m)
395 goto finish;
396
397 if (!dbus_connection_send(s->manager->bus, m, NULL))
398 goto finish;
399
400 r = 0;
401
402 finish:
403 if (m)
404 dbus_message_unref(m);
405 free(p);
406
407 return r;
408 }