]>
Commit | Line | Data |
---|---|---|
3f49d45a LP |
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> | |
a185c5aa | 23 | #include <string.h> |
3f49d45a LP |
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" \ | |
f401e48c LP |
33 | " <method name=\"ActivateSession\">\n" \ |
34 | " <arg name=\"id\" type=\"s\"/>\n" \ | |
35 | " </method>\n" \ | |
3f49d45a | 36 | " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \ |
f401e48c | 37 | " <property name=\"ActiveSession\" type=\"so\" access=\"read\"/>\n" \ |
64559e8b | 38 | " <property name=\"CanMultiSession\" type=\"b\" access=\"read\"/>\n" \ |
3f49d45a | 39 | " <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \ |
a185c5aa LP |
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" \ | |
3f49d45a LP |
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 | ||
dec15e92 | 105 | if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub)) |
3f49d45a LP |
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 | ||
64559e8b | 136 | static int bus_seat_append_multi_session(DBusMessageIter *i, const char *property, void *data) { |
f401e48c LP |
137 | Seat *s = data; |
138 | dbus_bool_t b; | |
139 | ||
140 | assert(i); | |
141 | assert(property); | |
142 | assert(s); | |
143 | ||
addedec4 | 144 | b = seat_can_multi_session(s); |
f401e48c LP |
145 | |
146 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) | |
147 | return -ENOMEM; | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
a185c5aa LP |
152 | static int bus_seat_append_idle_hint(DBusMessageIter *i, const char *property, void *data) { |
153 | Seat *s = data; | |
77527da0 | 154 | dbus_bool_t b; |
a185c5aa LP |
155 | |
156 | assert(i); | |
157 | assert(property); | |
158 | assert(s); | |
159 | ||
77527da0 | 160 | b = seat_get_idle_hint(s, NULL) > 0; |
a185c5aa LP |
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 | ||
3f49d45a LP |
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 | ||
d200735e MS |
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 | ||
3f49d45a LP |
221 | static DBusHandlerResult seat_message_dispatch( |
222 | Seat *s, | |
223 | DBusConnection *connection, | |
224 | DBusMessage *message) { | |
225 | ||
a185c5aa LP |
226 | DBusError error; |
227 | DBusMessage *reply = NULL; | |
228 | int r; | |
229 | ||
3f49d45a LP |
230 | assert(s); |
231 | assert(connection); | |
232 | assert(message); | |
233 | ||
a185c5aa LP |
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; | |
d200735e MS |
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 | } | |
a185c5aa LP |
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; | |
3f49d45a LP |
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 | } | |
da119395 LP |
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 | } | |
9418f147 LP |
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 | ||
ed18b08b LP |
386 | if (!s->started) |
387 | return 0; | |
388 | ||
9418f147 LP |
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 | } |