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