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