]>
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" \ |
3f49d45a LP |
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" \ | |
a185c5aa LP |
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" \ | |
3f49d45a LP |
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 | ||
dec15e92 | 127 | if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub)) |
3f49d45a LP |
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 | ||
a185c5aa LP |
158 | static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) { |
159 | User *u = data; | |
77527da0 | 160 | dbus_bool_t b; |
a185c5aa LP |
161 | |
162 | assert(i); | |
163 | assert(property); | |
164 | assert(u); | |
165 | ||
77527da0 LP |
166 | b = user_get_idle_hint(u, NULL) > 0; |
167 | ||
a185c5aa LP |
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 | ||
9e13dbae | 192 | static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) { |
b69d29ce LP |
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 | ||
3f49d45a LP |
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 | ||
d200735e MS |
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 }, | |
b69d29ce | 243 | { "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 }, |
d200735e MS |
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 | ||
3f49d45a LP |
254 | static DBusHandlerResult user_message_dispatch( |
255 | User *u, | |
256 | DBusConnection *connection, | |
257 | DBusMessage *message) { | |
258 | ||
a185c5aa | 259 | DBusError error; |
ce0fc5f5 | 260 | _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; |
a185c5aa LP |
261 | int r; |
262 | ||
3f49d45a LP |
263 | assert(u); |
264 | assert(connection); | |
265 | assert(message); | |
266 | ||
a185c5aa LP |
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; | |
de07ab16 LP |
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 | ||
d200735e MS |
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 | } | |
a185c5aa LP |
305 | |
306 | if (reply) { | |
c6a818c8 | 307 | if (!bus_maybe_send_reply(connection, message, reply)) |
a185c5aa | 308 | goto oom; |
a185c5aa LP |
309 | } |
310 | ||
311 | return DBUS_HANDLER_RESULT_HANDLED; | |
312 | ||
313 | oom: | |
a185c5aa LP |
314 | dbus_error_free(&error); |
315 | ||
316 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
3f49d45a LP |
317 | } |
318 | ||
319 | static DBusHandlerResult user_message_handler( | |
320 | DBusConnection *connection, | |
321 | DBusMessage *message, | |
322 | void *userdata) { | |
323 | ||
324 | Manager *m = userdata; | |
325 | User *u; | |
326 | int r; | |
327 | ||
328 | r = get_user_for_path(m, dbus_message_get_path(message), &u); | |
329 | if (r < 0) { | |
330 | ||
331 | if (r == -ENOMEM) | |
332 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
333 | ||
334 | if (r == -ENOENT) { | |
335 | DBusError e; | |
336 | ||
337 | dbus_error_init(&e); | |
338 | dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user"); | |
339 | return bus_send_error_reply(connection, message, &e, r); | |
340 | } | |
341 | ||
342 | return bus_send_error_reply(connection, message, NULL, r); | |
343 | } | |
344 | ||
345 | return user_message_dispatch(u, connection, message); | |
346 | } | |
347 | ||
348 | const DBusObjectPathVTable bus_user_vtable = { | |
349 | .message_function = user_message_handler | |
350 | }; | |
351 | ||
352 | char *user_bus_path(User *u) { | |
353 | char *s; | |
354 | ||
355 | assert(u); | |
356 | ||
357 | if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0) | |
358 | return NULL; | |
359 | ||
360 | return s; | |
361 | } | |
da119395 LP |
362 | |
363 | int user_send_signal(User *u, bool new_user) { | |
ce0fc5f5 | 364 | _cleanup_dbus_message_unref_ DBusMessage *m = NULL; |
da119395 | 365 | int r = -ENOMEM; |
ce0fc5f5 | 366 | _cleanup_free_ char *p = NULL; |
da119395 LP |
367 | uint32_t uid; |
368 | ||
369 | assert(u); | |
370 | ||
371 | m = dbus_message_new_signal("/org/freedesktop/login1", | |
372 | "org.freedesktop.login1.Manager", | |
373 | new_user ? "UserNew" : "UserRemoved"); | |
374 | ||
375 | if (!m) | |
376 | return -ENOMEM; | |
377 | ||
378 | p = user_bus_path(u); | |
379 | if (!p) | |
380 | goto finish; | |
381 | ||
382 | uid = u->uid; | |
383 | ||
384 | if (!dbus_message_append_args( | |
385 | m, | |
386 | DBUS_TYPE_UINT32, &uid, | |
387 | DBUS_TYPE_OBJECT_PATH, &p, | |
388 | DBUS_TYPE_INVALID)) | |
389 | goto finish; | |
390 | ||
391 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
392 | goto finish; | |
393 | ||
394 | r = 0; | |
395 | ||
396 | finish: | |
da119395 LP |
397 | return r; |
398 | } | |
9418f147 LP |
399 | |
400 | int user_send_changed(User *u, const char *properties) { | |
ce0fc5f5 | 401 | _cleanup_dbus_message_unref_ DBusMessage *m = NULL; |
9418f147 | 402 | int r = -ENOMEM; |
ce0fc5f5 | 403 | _cleanup_free_ char *p = NULL; |
9418f147 LP |
404 | |
405 | assert(u); | |
406 | ||
ed18b08b LP |
407 | if (!u->started) |
408 | return 0; | |
409 | ||
9418f147 LP |
410 | p = user_bus_path(u); |
411 | if (!p) | |
412 | return -ENOMEM; | |
413 | ||
414 | m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties); | |
415 | if (!m) | |
416 | goto finish; | |
417 | ||
418 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
419 | goto finish; | |
420 | ||
421 | r = 0; | |
422 | ||
423 | finish: | |
9418f147 LP |
424 | return r; |
425 | } |