]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user-dbus.c
udev: move man pages to udev section
[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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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 }