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