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