]>
Commit | Line | Data |
---|---|---|
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 | ||
65 | static 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 | ||
99 | static 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 | ||
115 | static 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 |
155 | static 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 | ||
171 | static 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 | 189 | static 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 |
207 | static 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 |
231 | static 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 |
250 | static 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 | ||
309 | oom: | |
a185c5aa LP |
310 | dbus_error_free(&error); |
311 | ||
312 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
3f49d45a LP |
313 | } |
314 | ||
315 | static 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 | ||
344 | const DBusObjectPathVTable bus_user_vtable = { | |
345 | .message_function = user_message_handler | |
346 | }; | |
347 | ||
348 | char *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 | |
359 | int 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 | |
392 | int 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 | } |