chiark / gitweb /
core/smack: downgrade info to debug
[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         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 (!dbus_connection_send(connection, reply, NULL))
308                         goto oom;
309
310                 dbus_message_unref(reply);
311         }
312
313         return DBUS_HANDLER_RESULT_HANDLED;
314
315 oom:
316         if (reply)
317                 dbus_message_unref(reply);
318
319         dbus_error_free(&error);
320
321         return DBUS_HANDLER_RESULT_NEED_MEMORY;
322 }
323
324 static DBusHandlerResult user_message_handler(
325                 DBusConnection *connection,
326                 DBusMessage *message,
327                 void *userdata) {
328
329         Manager *m = userdata;
330         User *u;
331         int r;
332
333         r = get_user_for_path(m, dbus_message_get_path(message), &u);
334         if (r < 0) {
335
336                 if (r == -ENOMEM)
337                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
338
339                 if (r == -ENOENT) {
340                         DBusError e;
341
342                         dbus_error_init(&e);
343                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
344                         return bus_send_error_reply(connection, message, &e, r);
345                 }
346
347                 return bus_send_error_reply(connection, message, NULL, r);
348         }
349
350         return user_message_dispatch(u, connection, message);
351 }
352
353 const DBusObjectPathVTable bus_user_vtable = {
354         .message_function = user_message_handler
355 };
356
357 char *user_bus_path(User *u) {
358         char *s;
359
360         assert(u);
361
362         if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
363                 return NULL;
364
365         return s;
366 }
367
368 int user_send_signal(User *u, bool new_user) {
369         DBusMessage *m;
370         int r = -ENOMEM;
371         char *p = NULL;
372         uint32_t uid;
373
374         assert(u);
375
376         m = dbus_message_new_signal("/org/freedesktop/login1",
377                                     "org.freedesktop.login1.Manager",
378                                     new_user ? "UserNew" : "UserRemoved");
379
380         if (!m)
381                 return -ENOMEM;
382
383         p = user_bus_path(u);
384         if (!p)
385                 goto finish;
386
387         uid = u->uid;
388
389         if (!dbus_message_append_args(
390                             m,
391                             DBUS_TYPE_UINT32, &uid,
392                             DBUS_TYPE_OBJECT_PATH, &p,
393                             DBUS_TYPE_INVALID))
394                 goto finish;
395
396         if (!dbus_connection_send(u->manager->bus, m, NULL))
397                 goto finish;
398
399         r = 0;
400
401 finish:
402         dbus_message_unref(m);
403         free(p);
404
405         return r;
406 }
407
408 int user_send_changed(User *u, const char *properties) {
409         DBusMessage *m;
410         int r = -ENOMEM;
411         char *p = NULL;
412
413         assert(u);
414
415         if (!u->started)
416                 return 0;
417
418         p = user_bus_path(u);
419         if (!p)
420                 return -ENOMEM;
421
422         m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
423         if (!m)
424                 goto finish;
425
426         if (!dbus_connection_send(u->manager->bus, m, NULL))
427                 goto finish;
428
429         r = 0;
430
431 finish:
432         if (m)
433                 dbus_message_unref(m);
434         free(p);
435
436         return r;
437 }