chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[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=\"ControlGroupPath\" 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 get_user_for_path(Manager *m, const char *path, User **_u) {
193         User *u;
194         unsigned long lu;
195         int r;
196
197         assert(m);
198         assert(path);
199         assert(_u);
200
201         if (!startswith(path, "/org/freedesktop/login1/user/"))
202                 return -EINVAL;
203
204         r = safe_atolu(path + 29, &lu);
205         if (r < 0)
206                 return r;
207
208         u = hashmap_get(m->users, ULONG_TO_PTR(lu));
209         if (!u)
210                 return -ENOENT;
211
212         *_u = u;
213         return 0;
214 }
215
216 static const BusProperty bus_login_user_properties[] = {
217         { "UID",                    bus_property_append_uid,         "u", offsetof(User, uid)                 },
218         { "GID",                    bus_property_append_gid,         "u", offsetof(User, gid)                 },
219         { "Name",                   bus_property_append_string,      "s", offsetof(User, name),               true },
220         { "Timestamp",              bus_property_append_usec,        "t", offsetof(User, timestamp.realtime)  },
221         { "TimestampMonotonic",     bus_property_append_usec,        "t", offsetof(User, timestamp.monotonic) },
222         { "RuntimePath",            bus_property_append_string,      "s", offsetof(User, runtime_path),       true },
223         { "ControlGroupPath",       bus_property_append_string,      "s", offsetof(User, cgroup_path),        true },
224         { "Service",                bus_property_append_string,      "s", offsetof(User, service),            true },
225         { "Display",                bus_user_append_display,      "(so)", 0 },
226         { "State",                  bus_user_append_state,           "s", 0 },
227         { "Sessions",               bus_user_append_sessions,    "a(so)", 0 },
228         { "IdleHint",               bus_user_append_idle_hint,       "b", 0 },
229         { "IdleSinceHint",          bus_user_append_idle_hint_since, "t", 0 },
230         { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
231         { NULL, }
232 };
233
234 static DBusHandlerResult user_message_dispatch(
235                 User *u,
236                 DBusConnection *connection,
237                 DBusMessage *message) {
238
239         DBusError error;
240         DBusMessage *reply = NULL;
241         int r;
242
243         assert(u);
244         assert(connection);
245         assert(message);
246
247         if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
248
249                 r = user_stop(u);
250                 if (r < 0)
251                         return bus_send_error_reply(connection, message, NULL, r);
252
253                 reply = dbus_message_new_method_return(message);
254                 if (!reply)
255                         goto oom;
256         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
257                 int32_t signo;
258
259                 if (!dbus_message_get_args(
260                                     message,
261                                     &error,
262                                     DBUS_TYPE_INT32, &signo,
263                                     DBUS_TYPE_INVALID))
264                         return bus_send_error_reply(connection, message, &error, -EINVAL);
265
266                 if (signo <= 0 || signo >= _NSIG)
267                         return bus_send_error_reply(connection, message, &error, -EINVAL);
268
269                 r = user_kill(u, signo);
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
277         } else {
278                 const BusBoundProperties bps[] = {
279                         { "org.freedesktop.login1.User", bus_login_user_properties, u },
280                         { NULL, }
281                 };
282
283                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
284         }
285
286         if (reply) {
287                 if (!dbus_connection_send(connection, reply, NULL))
288                         goto oom;
289
290                 dbus_message_unref(reply);
291         }
292
293         return DBUS_HANDLER_RESULT_HANDLED;
294
295 oom:
296         if (reply)
297                 dbus_message_unref(reply);
298
299         dbus_error_free(&error);
300
301         return DBUS_HANDLER_RESULT_NEED_MEMORY;
302 }
303
304 static DBusHandlerResult user_message_handler(
305                 DBusConnection *connection,
306                 DBusMessage *message,
307                 void *userdata) {
308
309         Manager *m = userdata;
310         User *u;
311         int r;
312
313         r = get_user_for_path(m, dbus_message_get_path(message), &u);
314         if (r < 0) {
315
316                 if (r == -ENOMEM)
317                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
318
319                 if (r == -ENOENT) {
320                         DBusError e;
321
322                         dbus_error_init(&e);
323                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
324                         return bus_send_error_reply(connection, message, &e, r);
325                 }
326
327                 return bus_send_error_reply(connection, message, NULL, r);
328         }
329
330         return user_message_dispatch(u, connection, message);
331 }
332
333 const DBusObjectPathVTable bus_user_vtable = {
334         .message_function = user_message_handler
335 };
336
337 char *user_bus_path(User *u) {
338         char *s;
339
340         assert(u);
341
342         if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
343                 return NULL;
344
345         return s;
346 }
347
348 int user_send_signal(User *u, bool new_user) {
349         DBusMessage *m;
350         int r = -ENOMEM;
351         char *p = NULL;
352         uint32_t uid;
353
354         assert(u);
355
356         m = dbus_message_new_signal("/org/freedesktop/login1",
357                                     "org.freedesktop.login1.Manager",
358                                     new_user ? "UserNew" : "UserRemoved");
359
360         if (!m)
361                 return -ENOMEM;
362
363         p = user_bus_path(u);
364         if (!p)
365                 goto finish;
366
367         uid = u->uid;
368
369         if (!dbus_message_append_args(
370                             m,
371                             DBUS_TYPE_UINT32, &uid,
372                             DBUS_TYPE_OBJECT_PATH, &p,
373                             DBUS_TYPE_INVALID))
374                 goto finish;
375
376         if (!dbus_connection_send(u->manager->bus, m, NULL))
377                 goto finish;
378
379         r = 0;
380
381 finish:
382         dbus_message_unref(m);
383         free(p);
384
385         return r;
386 }
387
388 int user_send_changed(User *u, const char *properties) {
389         DBusMessage *m;
390         int r = -ENOMEM;
391         char *p = NULL;
392
393         assert(u);
394
395         if (!u->started)
396                 return 0;
397
398         p = user_bus_path(u);
399         if (!p)
400                 return -ENOMEM;
401
402         m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
403         if (!m)
404                 goto finish;
405
406         if (!dbus_connection_send(u->manager->bus, m, NULL))
407                 goto finish;
408
409         r = 0;
410
411 finish:
412         if (m)
413                 dbus_message_unref(m);
414         free(p);
415
416         return r;
417 }