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