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