]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user-dbus.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.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 }