chiark / gitweb /
logind: port over to use scopes+slices for all cgroup stuff
[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=\"Service\" type=\"s\" access=\"read\"/>\n"   \
42         "  <property name=\"Slice\" 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         _cleanup_free_ 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                 return -ENOMEM;
91
92         if (!dbus_message_iter_close_container(i, &sub))
93                 return -ENOMEM;
94
95         return 0;
96 }
97
98 static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
99         User *u = data;
100         const char *state;
101
102         assert(i);
103         assert(property);
104         assert(u);
105
106         state = user_state_to_string(user_get_state(u));
107
108         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
109                 return -ENOMEM;
110
111         return 0;
112 }
113
114 static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
115         DBusMessageIter sub, sub2;
116         User *u = data;
117         Session *session;
118
119         assert(i);
120         assert(property);
121         assert(u);
122
123         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
124                 return -ENOMEM;
125
126         LIST_FOREACH(sessions_by_user, session, u->sessions) {
127                 char *p;
128
129                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
130                         return -ENOMEM;
131
132                 p = session_bus_path(session);
133                 if (!p)
134                         return -ENOMEM;
135
136                 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
137                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
138                         free(p);
139                         return -ENOMEM;
140                 }
141
142                 free(p);
143
144                 if (!dbus_message_iter_close_container(&sub, &sub2))
145                         return -ENOMEM;
146         }
147
148         if (!dbus_message_iter_close_container(i, &sub))
149                 return -ENOMEM;
150
151         return 0;
152 }
153
154 static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
155         User *u = data;
156         dbus_bool_t b;
157
158         assert(i);
159         assert(property);
160         assert(u);
161
162         b = user_get_idle_hint(u, NULL) > 0;
163
164         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
165                 return -ENOMEM;
166
167         return 0;
168 }
169
170 static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
171         User *u = data;
172         dual_timestamp t;
173         uint64_t k;
174
175         assert(i);
176         assert(property);
177         assert(u);
178
179         user_get_idle_hint(u, &t);
180         k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
181
182         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
183                 return -ENOMEM;
184
185         return 0;
186 }
187
188 static int get_user_for_path(Manager *m, const char *path, User **_u) {
189         User *u;
190         unsigned long lu;
191         int r;
192
193         assert(m);
194         assert(path);
195         assert(_u);
196
197         if (!startswith(path, "/org/freedesktop/login1/user/_"))
198                 return -EINVAL;
199
200         r = safe_atolu(path + 30, &lu);
201         if (r < 0)
202                 return r;
203
204         u = hashmap_get(m->users, ULONG_TO_PTR(lu));
205         if (!u)
206                 return -ENOENT;
207
208         *_u = u;
209         return 0;
210 }
211
212 static const BusProperty bus_login_user_properties[] = {
213         { "UID",                    bus_property_append_uid,         "u", offsetof(User, uid)                 },
214         { "GID",                    bus_property_append_gid,         "u", offsetof(User, gid)                 },
215         { "Name",                   bus_property_append_string,      "s", offsetof(User, name),               true },
216         { "Timestamp",              bus_property_append_usec,        "t", offsetof(User, timestamp.realtime)  },
217         { "TimestampMonotonic",     bus_property_append_usec,        "t", offsetof(User, timestamp.monotonic) },
218         { "RuntimePath",            bus_property_append_string,      "s", offsetof(User, runtime_path),       true },
219         { "Service",                bus_property_append_string,      "s", offsetof(User, service),            true },
220         { "Slice",                  bus_property_append_string,      "s", offsetof(User, slice),              true },
221         { "Display",                bus_user_append_display,      "(so)", 0 },
222         { "State",                  bus_user_append_state,           "s", 0 },
223         { "Sessions",               bus_user_append_sessions,    "a(so)", 0 },
224         { "IdleHint",               bus_user_append_idle_hint,       "b", 0 },
225         { "IdleSinceHint",          bus_user_append_idle_hint_since, "t", 0 },
226         { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
227         { NULL, }
228 };
229
230 static DBusHandlerResult user_message_dispatch(
231                 User *u,
232                 DBusConnection *connection,
233                 DBusMessage *message) {
234
235         DBusError error;
236         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
237         int r;
238
239         assert(u);
240         assert(connection);
241         assert(message);
242
243         if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
244
245                 r = user_stop(u);
246                 if (r < 0)
247                         return bus_send_error_reply(connection, message, NULL, r);
248
249                 reply = dbus_message_new_method_return(message);
250                 if (!reply)
251                         goto oom;
252         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
253                 int32_t signo;
254
255                 if (!dbus_message_get_args(
256                                     message,
257                                     &error,
258                                     DBUS_TYPE_INT32, &signo,
259                                     DBUS_TYPE_INVALID))
260                         return bus_send_error_reply(connection, message, &error, -EINVAL);
261
262                 if (signo <= 0 || signo >= _NSIG)
263                         return bus_send_error_reply(connection, message, &error, -EINVAL);
264
265                 r = user_kill(u, signo);
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
273         } else {
274                 const BusBoundProperties bps[] = {
275                         { "org.freedesktop.login1.User", bus_login_user_properties, u },
276                         { NULL, }
277                 };
278
279                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
280         }
281
282         if (reply) {
283                 if (!bus_maybe_send_reply(connection, message, reply))
284                         goto oom;
285         }
286
287         return DBUS_HANDLER_RESULT_HANDLED;
288
289 oom:
290         dbus_error_free(&error);
291
292         return DBUS_HANDLER_RESULT_NEED_MEMORY;
293 }
294
295 static DBusHandlerResult user_message_handler(
296                 DBusConnection *connection,
297                 DBusMessage *message,
298                 void *userdata) {
299
300         Manager *m = userdata;
301         User *u;
302         int r;
303
304         r = get_user_for_path(m, dbus_message_get_path(message), &u);
305         if (r < 0) {
306
307                 if (r == -ENOMEM)
308                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
309
310                 if (r == -ENOENT) {
311                         DBusError e;
312
313                         dbus_error_init(&e);
314                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
315                         return bus_send_error_reply(connection, message, &e, r);
316                 }
317
318                 return bus_send_error_reply(connection, message, NULL, r);
319         }
320
321         return user_message_dispatch(u, connection, message);
322 }
323
324 const DBusObjectPathVTable bus_user_vtable = {
325         .message_function = user_message_handler
326 };
327
328 char *user_bus_path(User *u) {
329         char *s;
330
331         assert(u);
332
333         if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0)
334                 return NULL;
335
336         return s;
337 }
338
339 int user_send_signal(User *u, bool new_user) {
340         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
341         _cleanup_free_ char *p = NULL;
342         uint32_t uid;
343
344         assert(u);
345
346         m = dbus_message_new_signal("/org/freedesktop/login1",
347                                     "org.freedesktop.login1.Manager",
348                                     new_user ? "UserNew" : "UserRemoved");
349
350         if (!m)
351                 return -ENOMEM;
352
353         p = user_bus_path(u);
354         if (!p)
355                 return -ENOMEM;
356
357         uid = u->uid;
358
359         if (!dbus_message_append_args(
360                             m,
361                             DBUS_TYPE_UINT32, &uid,
362                             DBUS_TYPE_OBJECT_PATH, &p,
363                             DBUS_TYPE_INVALID))
364                 return -ENOMEM;
365
366         if (!dbus_connection_send(u->manager->bus, m, NULL))
367                 return -ENOMEM;
368
369         return 0;
370 }
371
372 int user_send_changed(User *u, const char *properties) {
373         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
374         _cleanup_free_ char *p = NULL;
375
376         assert(u);
377
378         if (!u->started)
379                 return 0;
380
381         p = user_bus_path(u);
382         if (!p)
383                 return -ENOMEM;
384
385         m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
386         if (!m)
387                 return -ENOMEM;
388
389         if (!dbus_connection_send(u->manager->bus, m, NULL))
390                 return -ENOMEM;
391
392         return 0;
393 }