chiark / gitweb /
systemadm: display dependencies sorted
[elogind.git] / src / 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 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-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_is_vtconsole(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 DBusHandlerResult seat_message_dispatch(
211                 Seat *s,
212                 DBusConnection *connection,
213                 DBusMessage *message) {
214
215         const BusProperty properties[] = {
216                 { "org.freedesktop.login1.Seat", "Id",                     bus_property_append_string,      "s",     s->id },
217                 { "org.freedesktop.login1.Seat", "ActiveSession",          bus_seat_append_active,          "(so)",  s     },
218                 { "org.freedesktop.login1.Seat", "CanMultiSession",        bus_seat_append_multi_session,   "b",     s     },
219                 { "org.freedesktop.login1.Seat", "Sessions",               bus_seat_append_sessions,        "a(so)", s     },
220                 { "org.freedesktop.login1.Seat", "IdleHint",               bus_seat_append_idle_hint,       "b",     s     },
221                 { "org.freedesktop.login1.Seat", "IdleSinceHint",          bus_seat_append_idle_hint_since, "t",     s     },
222                 { "org.freedesktop.login1.Seat", "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t",     s     },
223                 { NULL, NULL, NULL, NULL, NULL }
224         };
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                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
270
271         if (reply) {
272                 if (!dbus_connection_send(connection, reply, NULL))
273                         goto oom;
274
275                 dbus_message_unref(reply);
276         }
277
278         return DBUS_HANDLER_RESULT_HANDLED;
279
280 oom:
281         if (reply)
282                 dbus_message_unref(reply);
283
284         dbus_error_free(&error);
285
286         return DBUS_HANDLER_RESULT_NEED_MEMORY;
287 }
288
289 static DBusHandlerResult seat_message_handler(
290                 DBusConnection *connection,
291                 DBusMessage *message,
292                 void *userdata) {
293
294         Manager *m = userdata;
295         Seat *s;
296         int r;
297
298         r = get_seat_for_path(m, dbus_message_get_path(message), &s);
299         if (r < 0) {
300
301                 if (r == -ENOMEM)
302                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
303
304                 if (r == -ENOENT) {
305                         DBusError e;
306
307                         dbus_error_init(&e);
308                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown seat");
309                         return bus_send_error_reply(connection, message, &e, r);
310                 }
311
312                 return bus_send_error_reply(connection, message, NULL, r);
313         }
314
315         return seat_message_dispatch(s, connection, message);
316 }
317
318 const DBusObjectPathVTable bus_seat_vtable = {
319         .message_function = seat_message_handler
320 };
321
322 char *seat_bus_path(Seat *s) {
323         char *t, *r;
324
325         assert(s);
326
327         t = bus_path_escape(s->id);
328         if (!t)
329                 return NULL;
330
331         r = strappend("/org/freedesktop/login1/seat/", t);
332         free(t);
333
334         return r;
335 }
336
337 int seat_send_signal(Seat *s, bool new_seat) {
338         DBusMessage *m;
339         int r = -ENOMEM;
340         char *p = NULL;
341
342         assert(s);
343
344         m = dbus_message_new_signal("/org/freedesktop/login1",
345                                     "org.freedesktop.login1.Manager",
346                                     new_seat ? "SeatNew" : "SeatRemoved");
347
348         if (!m)
349                 return -ENOMEM;
350
351         p = seat_bus_path(s);
352         if (!p)
353                 goto finish;
354
355         if (!dbus_message_append_args(
356                             m,
357                             DBUS_TYPE_STRING, &s->id,
358                             DBUS_TYPE_OBJECT_PATH, &p,
359                             DBUS_TYPE_INVALID))
360                 goto finish;
361
362         if (!dbus_connection_send(s->manager->bus, m, NULL))
363                 goto finish;
364
365         r = 0;
366
367 finish:
368         dbus_message_unref(m);
369         free(p);
370
371         return r;
372 }
373
374 int seat_send_changed(Seat *s, const char *properties) {
375         DBusMessage *m;
376         int r = -ENOMEM;
377         char *p = NULL;
378
379         assert(s);
380
381         if (!s->started)
382                 return 0;
383
384         p = seat_bus_path(s);
385         if (!p)
386                 return -ENOMEM;
387
388         m = bus_properties_changed_new(p, "org.freedesktop.login1.Seat", properties);
389         if (!m)
390                 goto finish;
391
392         if (!dbus_connection_send(s->manager->bus, m, NULL))
393                 goto finish;
394
395         r = 0;
396
397 finish:
398         if (m)
399                 dbus_message_unref(m);
400         free(p);
401
402         return r;
403 }