]>
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" \ |
a185c5aa | 41 | " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \ |
9444b1f2 | 42 | " <property name=\"Slice\" 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" \ | |
9444b1f2 | 49 | " </interface>\n" |
3f49d45a LP |
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; | |
7fd1b19b | 68 | _cleanup_free_ char *p = NULL; |
3f49d45a LP |
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) || | |
4654e558 | 89 | !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) |
3f49d45a | 90 | return -ENOMEM; |
3f49d45a LP |
91 | |
92 | if (!dbus_message_iter_close_container(i, &sub)) | |
93 | return -ENOMEM; | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) { | |
99 | User *u = data; | |
100 | const char *state; | |
101 | ||
102 | assert(i); | |
103 | assert(property); | |
104 | assert(u); | |
105 | ||
106 | state = user_state_to_string(user_get_state(u)); | |
107 | ||
108 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) | |
109 | return -ENOMEM; | |
110 | ||
111 | return 0; | |
112 | } | |
113 | ||
114 | static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) { | |
115 | DBusMessageIter sub, sub2; | |
116 | User *u = data; | |
117 | Session *session; | |
118 | ||
119 | assert(i); | |
120 | assert(property); | |
121 | assert(u); | |
122 | ||
dec15e92 | 123 | if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub)) |
3f49d45a LP |
124 | return -ENOMEM; |
125 | ||
126 | LIST_FOREACH(sessions_by_user, session, u->sessions) { | |
127 | char *p; | |
128 | ||
129 | if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) | |
130 | return -ENOMEM; | |
131 | ||
132 | p = session_bus_path(session); | |
133 | if (!p) | |
134 | return -ENOMEM; | |
135 | ||
136 | if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) || | |
137 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) { | |
138 | free(p); | |
139 | return -ENOMEM; | |
140 | } | |
141 | ||
142 | free(p); | |
143 | ||
144 | if (!dbus_message_iter_close_container(&sub, &sub2)) | |
145 | return -ENOMEM; | |
146 | } | |
147 | ||
148 | if (!dbus_message_iter_close_container(i, &sub)) | |
149 | return -ENOMEM; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
a185c5aa LP |
154 | static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) { |
155 | User *u = data; | |
77527da0 | 156 | dbus_bool_t b; |
a185c5aa LP |
157 | |
158 | assert(i); | |
159 | assert(property); | |
160 | assert(u); | |
161 | ||
77527da0 LP |
162 | b = user_get_idle_hint(u, NULL) > 0; |
163 | ||
a185c5aa LP |
164 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) |
165 | return -ENOMEM; | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
170 | static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) { | |
171 | User *u = data; | |
172 | dual_timestamp t; | |
173 | uint64_t k; | |
174 | ||
175 | assert(i); | |
176 | assert(property); | |
177 | assert(u); | |
178 | ||
179 | user_get_idle_hint(u, &t); | |
180 | k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; | |
181 | ||
182 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k)) | |
183 | return -ENOMEM; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
3f49d45a LP |
188 | static int get_user_for_path(Manager *m, const char *path, User **_u) { |
189 | User *u; | |
190 | unsigned long lu; | |
191 | int r; | |
192 | ||
193 | assert(m); | |
194 | assert(path); | |
195 | assert(_u); | |
196 | ||
9444b1f2 | 197 | if (!startswith(path, "/org/freedesktop/login1/user/_")) |
3f49d45a LP |
198 | return -EINVAL; |
199 | ||
9444b1f2 | 200 | r = safe_atolu(path + 30, &lu); |
3f49d45a LP |
201 | if (r < 0) |
202 | return r; | |
203 | ||
204 | u = hashmap_get(m->users, ULONG_TO_PTR(lu)); | |
205 | if (!u) | |
206 | return -ENOENT; | |
207 | ||
208 | *_u = u; | |
209 | return 0; | |
210 | } | |
211 | ||
d200735e MS |
212 | static const BusProperty bus_login_user_properties[] = { |
213 | { "UID", bus_property_append_uid, "u", offsetof(User, uid) }, | |
214 | { "GID", bus_property_append_gid, "u", offsetof(User, gid) }, | |
215 | { "Name", bus_property_append_string, "s", offsetof(User, name), true }, | |
216 | { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) }, | |
217 | { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) }, | |
218 | { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true }, | |
d200735e | 219 | { "Service", bus_property_append_string, "s", offsetof(User, service), true }, |
9444b1f2 | 220 | { "Slice", bus_property_append_string, "s", offsetof(User, slice), true }, |
d200735e MS |
221 | { "Display", bus_user_append_display, "(so)", 0 }, |
222 | { "State", bus_user_append_state, "s", 0 }, | |
223 | { "Sessions", bus_user_append_sessions, "a(so)", 0 }, | |
224 | { "IdleHint", bus_user_append_idle_hint, "b", 0 }, | |
225 | { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 }, | |
226 | { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 }, | |
227 | { NULL, } | |
228 | }; | |
229 | ||
3f49d45a LP |
230 | static DBusHandlerResult user_message_dispatch( |
231 | User *u, | |
232 | DBusConnection *connection, | |
233 | DBusMessage *message) { | |
234 | ||
a185c5aa | 235 | DBusError error; |
ce0fc5f5 | 236 | _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; |
a185c5aa LP |
237 | int r; |
238 | ||
3f49d45a LP |
239 | assert(u); |
240 | assert(connection); | |
241 | assert(message); | |
242 | ||
a185c5aa LP |
243 | if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) { |
244 | ||
245 | r = user_stop(u); | |
246 | if (r < 0) | |
247 | return bus_send_error_reply(connection, message, NULL, r); | |
248 | ||
249 | reply = dbus_message_new_method_return(message); | |
250 | if (!reply) | |
251 | goto oom; | |
de07ab16 LP |
252 | } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) { |
253 | int32_t signo; | |
254 | ||
255 | if (!dbus_message_get_args( | |
256 | message, | |
257 | &error, | |
258 | DBUS_TYPE_INT32, &signo, | |
259 | DBUS_TYPE_INVALID)) | |
260 | return bus_send_error_reply(connection, message, &error, -EINVAL); | |
261 | ||
262 | if (signo <= 0 || signo >= _NSIG) | |
263 | return bus_send_error_reply(connection, message, &error, -EINVAL); | |
264 | ||
265 | r = user_kill(u, signo); | |
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; | |
272 | ||
d200735e MS |
273 | } else { |
274 | const BusBoundProperties bps[] = { | |
275 | { "org.freedesktop.login1.User", bus_login_user_properties, u }, | |
276 | { NULL, } | |
277 | }; | |
278 | ||
279 | return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); | |
280 | } | |
a185c5aa LP |
281 | |
282 | if (reply) { | |
c6a818c8 | 283 | if (!bus_maybe_send_reply(connection, message, reply)) |
a185c5aa | 284 | goto oom; |
a185c5aa LP |
285 | } |
286 | ||
287 | return DBUS_HANDLER_RESULT_HANDLED; | |
288 | ||
289 | oom: | |
a185c5aa LP |
290 | dbus_error_free(&error); |
291 | ||
292 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
3f49d45a LP |
293 | } |
294 | ||
295 | static DBusHandlerResult user_message_handler( | |
296 | DBusConnection *connection, | |
297 | DBusMessage *message, | |
298 | void *userdata) { | |
299 | ||
300 | Manager *m = userdata; | |
301 | User *u; | |
302 | int r; | |
303 | ||
304 | r = get_user_for_path(m, dbus_message_get_path(message), &u); | |
305 | if (r < 0) { | |
306 | ||
307 | if (r == -ENOMEM) | |
308 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
309 | ||
310 | if (r == -ENOENT) { | |
311 | DBusError e; | |
312 | ||
313 | dbus_error_init(&e); | |
314 | dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user"); | |
315 | return bus_send_error_reply(connection, message, &e, r); | |
316 | } | |
317 | ||
318 | return bus_send_error_reply(connection, message, NULL, r); | |
319 | } | |
320 | ||
321 | return user_message_dispatch(u, connection, message); | |
322 | } | |
323 | ||
324 | const DBusObjectPathVTable bus_user_vtable = { | |
325 | .message_function = user_message_handler | |
326 | }; | |
327 | ||
328 | char *user_bus_path(User *u) { | |
329 | char *s; | |
330 | ||
331 | assert(u); | |
332 | ||
9444b1f2 | 333 | if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0) |
3f49d45a LP |
334 | return NULL; |
335 | ||
336 | return s; | |
337 | } | |
da119395 LP |
338 | |
339 | int user_send_signal(User *u, bool new_user) { | |
ce0fc5f5 | 340 | _cleanup_dbus_message_unref_ DBusMessage *m = NULL; |
ce0fc5f5 | 341 | _cleanup_free_ char *p = NULL; |
da119395 LP |
342 | uint32_t uid; |
343 | ||
344 | assert(u); | |
345 | ||
346 | m = dbus_message_new_signal("/org/freedesktop/login1", | |
347 | "org.freedesktop.login1.Manager", | |
348 | new_user ? "UserNew" : "UserRemoved"); | |
349 | ||
350 | if (!m) | |
351 | return -ENOMEM; | |
352 | ||
353 | p = user_bus_path(u); | |
354 | if (!p) | |
4654e558 | 355 | return -ENOMEM; |
da119395 LP |
356 | |
357 | uid = u->uid; | |
358 | ||
359 | if (!dbus_message_append_args( | |
360 | m, | |
361 | DBUS_TYPE_UINT32, &uid, | |
362 | DBUS_TYPE_OBJECT_PATH, &p, | |
363 | DBUS_TYPE_INVALID)) | |
4654e558 | 364 | return -ENOMEM; |
da119395 LP |
365 | |
366 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
4654e558 | 367 | return -ENOMEM; |
da119395 | 368 | |
4654e558 | 369 | return 0; |
da119395 | 370 | } |
9418f147 LP |
371 | |
372 | int user_send_changed(User *u, const char *properties) { | |
ce0fc5f5 | 373 | _cleanup_dbus_message_unref_ DBusMessage *m = NULL; |
ce0fc5f5 | 374 | _cleanup_free_ char *p = NULL; |
9418f147 LP |
375 | |
376 | assert(u); | |
377 | ||
ed18b08b LP |
378 | if (!u->started) |
379 | return 0; | |
380 | ||
9418f147 LP |
381 | p = user_bus_path(u); |
382 | if (!p) | |
383 | return -ENOMEM; | |
384 | ||
385 | m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties); | |
386 | if (!m) | |
4654e558 | 387 | return -ENOMEM; |
9418f147 LP |
388 | |
389 | if (!dbus_connection_send(u->manager->bus, m, NULL)) | |
4654e558 | 390 | return -ENOMEM; |
9418f147 | 391 | |
4654e558 | 392 | return 0; |
9418f147 | 393 | } |