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