]>
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 | |
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> | |
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" \ |
3f49d45a | 41 | " <property name=\"ControlGroupPath\" 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 | ||
3f49d45a LP |
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 DBusHandlerResult user_message_dispatch( | |
217 | User *u, | |
218 | DBusConnection *connection, | |
219 | DBusMessage *message) { | |
220 | ||
221 | const BusProperty properties[] = { | |
a185c5aa LP |
222 | { "org.freedesktop.login1.User", "UID", bus_property_append_uid, "u", &u->uid }, |
223 | { "org.freedesktop.login1.User", "GID", bus_property_append_gid, "u", &u->gid }, | |
224 | { "org.freedesktop.login1.User", "Name", bus_property_append_string, "s", u->name }, | |
225 | { "org.freedesktop.login1.User", "Timestamp", bus_property_append_usec, "t", &u->timestamp.realtime }, | |
226 | { "org.freedesktop.login1.User", "TimestampMonotonic", bus_property_append_usec, "t", &u->timestamp.monotonic }, | |
227 | { "org.freedesktop.login1.User", "RuntimePath", bus_property_append_string, "s", u->runtime_path }, | |
228 | { "org.freedesktop.login1.User", "ControlGroupPath", bus_property_append_string, "s", u->cgroup_path }, | |
229 | { "org.freedesktop.login1.User", "Service", bus_property_append_string, "s", u->service }, | |
230 | { "org.freedesktop.login1.User", "Display", bus_user_append_display, "(so)", u }, | |
231 | { "org.freedesktop.login1.User", "State", bus_user_append_state, "s", u }, | |
232 | { "org.freedesktop.login1.User", "Sessions", bus_user_append_sessions, "a(so)", u }, | |
233 | { "org.freedesktop.login1.User", "IdleHint", bus_user_append_idle_hint, "b", u }, | |
234 | { "org.freedesktop.login1.User", "IdleSinceHint", bus_user_append_idle_hint_since, "t", u }, | |
235 | { "org.freedesktop.login1.User", "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", u }, | |
3f49d45a LP |
236 | { NULL, NULL, NULL, NULL, NULL } |
237 | }; | |
238 | ||
a185c5aa LP |
239 | DBusError error; |
240 | DBusMessage *reply = NULL; | |
241 | int r; | |
242 | ||
3f49d45a LP |
243 | assert(u); |
244 | assert(connection); | |
245 | assert(message); | |
246 | ||
a185c5aa LP |
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; | |
de07ab16 LP |
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 | ||
a185c5aa LP |
277 | } else |
278 | return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); | |
279 | ||
280 | if (reply) { | |
281 | if (!dbus_connection_send(connection, reply, NULL)) | |
282 | goto oom; | |
283 | ||
284 | dbus_message_unref(reply); | |
285 | } | |
286 | ||
287 | return DBUS_HANDLER_RESULT_HANDLED; | |
288 | ||
289 | oom: | |
290 | if (reply) | |
291 | dbus_message_unref(reply); | |
292 | ||
293 | dbus_error_free(&error); | |
294 | ||
295 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
3f49d45a LP |
296 | } |
297 | ||
298 | static DBusHandlerResult user_message_handler( | |
299 | DBusConnection *connection, | |
300 | DBusMessage *message, | |
301 | void *userdata) { | |
302 | ||
303 | Manager *m = userdata; | |
304 | User *u; | |
305 | int r; | |
306 | ||
307 | r = get_user_for_path(m, dbus_message_get_path(message), &u); | |
308 | if (r < 0) { | |
309 | ||
310 | if (r == -ENOMEM) | |
311 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
312 | ||
313 | if (r == -ENOENT) { | |
314 | DBusError e; | |
315 | ||
316 | dbus_error_init(&e); | |
317 | dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user"); | |
318 | return bus_send_error_reply(connection, message, &e, r); | |
319 | } | |
320 | ||
321 | return bus_send_error_reply(connection, message, NULL, r); | |
322 | } | |
323 | ||
324 | return user_message_dispatch(u, connection, message); | |
325 | } | |
326 | ||
327 | const DBusObjectPathVTable bus_user_vtable = { | |
328 | .message_function = user_message_handler | |
329 | }; | |
330 | ||
331 | char *user_bus_path(User *u) { | |
332 | char *s; | |
333 | ||
334 | assert(u); | |
335 | ||
336 | if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0) | |
337 | return NULL; | |
338 | ||
339 | return s; | |
340 | } | |
da119395 LP |
341 | |
342 | int user_send_signal(User *u, bool new_user) { | |
343 | DBusMessage *m; | |
344 | int r = -ENOMEM; | |
345 | char *p = NULL; | |
346 | uint32_t uid; | |
347 | ||
348 | assert(u); | |
349 | ||
350 | m = dbus_message_new_signal("/org/freedesktop/login1", | |
351 | "org.freedesktop.login1.Manager", | |
352 | new_user ? "UserNew" : "UserRemoved"); | |
353 | ||
354 | if (!m) | |
355 | return -ENOMEM; | |
356 | ||
357 | p = user_bus_path(u); | |
358 | if (!p) | |
359 | goto finish; | |
360 | ||
361 | uid = u->uid; | |
362 | ||
363 | if (!dbus_message_append_args( | |
364 | m, | |
365 | DBUS_TYPE_UINT32, &uid, | |
366 | DBUS_TYPE_OBJECT_PATH, &p, | |
367 | DBUS_TYPE_INVALID)) | |
368 | goto finish; | |
369 | ||
370 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
371 | goto finish; | |
372 | ||
373 | r = 0; | |
374 | ||
375 | finish: | |
376 | dbus_message_unref(m); | |
377 | free(p); | |
378 | ||
379 | return r; | |
380 | } | |
9418f147 LP |
381 | |
382 | int user_send_changed(User *u, const char *properties) { | |
383 | DBusMessage *m; | |
384 | int r = -ENOMEM; | |
385 | char *p = NULL; | |
386 | ||
387 | assert(u); | |
388 | ||
ed18b08b LP |
389 | if (!u->started) |
390 | return 0; | |
391 | ||
9418f147 LP |
392 | p = user_bus_path(u); |
393 | if (!p) | |
394 | return -ENOMEM; | |
395 | ||
396 | m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties); | |
397 | if (!m) | |
398 | goto finish; | |
399 | ||
400 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
401 | goto finish; | |
402 | ||
403 | r = 0; | |
404 | ||
405 | finish: | |
406 | if (m) | |
407 | dbus_message_unref(m); | |
408 | free(p); | |
409 | ||
410 | return r; | |
411 | } |