]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |
2 | ||
3 | #include <errno.h> | |
4 | #include <string.h> | |
5 | ||
6 | #include "alloc-util.h" | |
7 | #include "bus-util.h" | |
8 | #include "format-util.h" | |
9 | #include "logind-dbus.h" | |
10 | #include "logind-session-dbus.h" | |
11 | #include "logind-user-dbus.h" | |
12 | #include "logind-user.h" | |
13 | #include "logind.h" | |
14 | #include "missing_capability.h" | |
15 | #include "signal-util.h" | |
16 | #include "strv.h" | |
17 | #include "user-util.h" | |
18 | ||
19 | static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", User, user_get_state, user_state_to_string); | |
20 | ||
21 | static int property_get_display( | |
22 | sd_bus *bus, | |
23 | const char *path, | |
24 | const char *interface, | |
25 | const char *property, | |
26 | sd_bus_message *reply, | |
27 | void *userdata, | |
28 | sd_bus_error *error) { | |
29 | ||
30 | _cleanup_free_ char *p = NULL; | |
31 | User *u = userdata; | |
32 | ||
33 | assert(bus); | |
34 | assert(reply); | |
35 | assert(u); | |
36 | ||
37 | p = u->display ? session_bus_path(u->display) : strdup("/"); | |
38 | if (!p) | |
39 | return -ENOMEM; | |
40 | ||
41 | return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p); | |
42 | } | |
43 | ||
44 | static int property_get_sessions( | |
45 | sd_bus *bus, | |
46 | const char *path, | |
47 | const char *interface, | |
48 | const char *property, | |
49 | sd_bus_message *reply, | |
50 | void *userdata, | |
51 | sd_bus_error *error) { | |
52 | ||
53 | User *u = userdata; | |
54 | Session *session; | |
55 | int r; | |
56 | ||
57 | assert(bus); | |
58 | assert(reply); | |
59 | assert(u); | |
60 | ||
61 | r = sd_bus_message_open_container(reply, 'a', "(so)"); | |
62 | if (r < 0) | |
63 | return r; | |
64 | ||
65 | LIST_FOREACH(sessions_by_user, session, u->sessions) { | |
66 | _cleanup_free_ char *p = NULL; | |
67 | ||
68 | p = session_bus_path(session); | |
69 | if (!p) | |
70 | return -ENOMEM; | |
71 | ||
72 | r = sd_bus_message_append(reply, "(so)", session->id, p); | |
73 | if (r < 0) | |
74 | return r; | |
75 | ||
76 | } | |
77 | ||
78 | return sd_bus_message_close_container(reply); | |
79 | } | |
80 | ||
81 | static int property_get_idle_hint( | |
82 | sd_bus *bus, | |
83 | const char *path, | |
84 | const char *interface, | |
85 | const char *property, | |
86 | sd_bus_message *reply, | |
87 | void *userdata, | |
88 | sd_bus_error *error) { | |
89 | ||
90 | User *u = userdata; | |
91 | ||
92 | assert(bus); | |
93 | assert(reply); | |
94 | assert(u); | |
95 | ||
96 | return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0); | |
97 | } | |
98 | ||
99 | static int property_get_idle_since_hint( | |
100 | sd_bus *bus, | |
101 | const char *path, | |
102 | const char *interface, | |
103 | const char *property, | |
104 | sd_bus_message *reply, | |
105 | void *userdata, | |
106 | sd_bus_error *error) { | |
107 | ||
108 | User *u = userdata; | |
109 | dual_timestamp t = DUAL_TIMESTAMP_NULL; | |
110 | uint64_t k; | |
111 | ||
112 | assert(bus); | |
113 | assert(reply); | |
114 | assert(u); | |
115 | ||
116 | (void) user_get_idle_hint(u, &t); | |
117 | k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; | |
118 | ||
119 | return sd_bus_message_append(reply, "t", k); | |
120 | } | |
121 | ||
122 | static int property_get_linger( | |
123 | sd_bus *bus, | |
124 | const char *path, | |
125 | const char *interface, | |
126 | const char *property, | |
127 | sd_bus_message *reply, | |
128 | void *userdata, | |
129 | sd_bus_error *error) { | |
130 | ||
131 | User *u = userdata; | |
132 | int r; | |
133 | ||
134 | assert(bus); | |
135 | assert(reply); | |
136 | assert(u); | |
137 | ||
138 | r = user_check_linger_file(u); | |
139 | ||
140 | return sd_bus_message_append(reply, "b", r > 0); | |
141 | } | |
142 | ||
143 | int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
144 | User *u = userdata; | |
145 | int r; | |
146 | ||
147 | assert(message); | |
148 | assert(u); | |
149 | ||
150 | r = bus_verify_polkit_async( | |
151 | message, | |
152 | CAP_KILL, | |
153 | "org.freedesktop.login1.manage", | |
154 | NULL, | |
155 | false, | |
156 | u->uid, | |
157 | &u->manager->polkit_registry, | |
158 | error); | |
159 | if (r < 0) | |
160 | return r; | |
161 | if (r == 0) | |
162 | return 1; /* Will call us back */ | |
163 | ||
164 | r = user_stop(u, true); | |
165 | if (r < 0) | |
166 | return r; | |
167 | ||
168 | return sd_bus_reply_method_return(message, NULL); | |
169 | } | |
170 | ||
171 | int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
172 | User *u = userdata; | |
173 | int32_t signo; | |
174 | int r; | |
175 | ||
176 | assert(message); | |
177 | assert(u); | |
178 | ||
179 | r = bus_verify_polkit_async( | |
180 | message, | |
181 | CAP_KILL, | |
182 | "org.freedesktop.login1.manage", | |
183 | NULL, | |
184 | false, | |
185 | u->uid, | |
186 | &u->manager->polkit_registry, | |
187 | error); | |
188 | if (r < 0) | |
189 | return r; | |
190 | if (r == 0) | |
191 | return 1; /* Will call us back */ | |
192 | ||
193 | r = sd_bus_message_read(message, "i", &signo); | |
194 | if (r < 0) | |
195 | return r; | |
196 | ||
197 | if (!SIGNAL_VALID(signo)) | |
198 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); | |
199 | ||
200 | r = user_kill(u, signo); | |
201 | if (r < 0) | |
202 | return r; | |
203 | ||
204 | return sd_bus_reply_method_return(message, NULL); | |
205 | } | |
206 | ||
207 | const sd_bus_vtable user_vtable[] = { | |
208 | SD_BUS_VTABLE_START(0), | |
209 | ||
210 | SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST), | |
211 | SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST), | |
212 | SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST), | |
213 | BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), | |
214 | SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), | |
215 | SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), | |
216 | SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), | |
217 | SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
218 | SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), | |
219 | SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), | |
220 | SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
221 | SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
222 | SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
223 | SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), | |
224 | ||
225 | SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), | |
226 | SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), | |
227 | ||
228 | SD_BUS_VTABLE_END | |
229 | }; | |
230 | ||
231 | int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { | |
232 | Manager *m = userdata; | |
233 | uid_t uid; | |
234 | User *user; | |
235 | int r; | |
236 | ||
237 | assert(bus); | |
238 | assert(path); | |
239 | assert(interface); | |
240 | assert(found); | |
241 | assert(m); | |
242 | ||
243 | if (streq(path, "/org/freedesktop/login1/user/self")) { | |
244 | sd_bus_message *message; | |
245 | ||
246 | message = sd_bus_get_current_message(bus); | |
247 | if (!message) | |
248 | return 0; | |
249 | ||
250 | r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); | |
251 | if (r == -ENXIO) { | |
252 | sd_bus_error_free(error); | |
253 | return 0; | |
254 | } | |
255 | if (r < 0) | |
256 | return r; | |
257 | } else { | |
258 | const char *p; | |
259 | ||
260 | p = startswith(path, "/org/freedesktop/login1/user/_"); | |
261 | if (!p) | |
262 | return 0; | |
263 | ||
264 | r = parse_uid(p, &uid); | |
265 | if (r < 0) | |
266 | return 0; | |
267 | ||
268 | user = hashmap_get(m->users, UID_TO_PTR(uid)); | |
269 | if (!user) | |
270 | return 0; | |
271 | } | |
272 | ||
273 | *found = user; | |
274 | return 1; | |
275 | } | |
276 | ||
277 | char *user_bus_path(User *u) { | |
278 | char *s; | |
279 | ||
280 | assert(u); | |
281 | ||
282 | if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0) | |
283 | return NULL; | |
284 | ||
285 | return s; | |
286 | } | |
287 | ||
288 | int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { | |
289 | _cleanup_strv_free_ char **l = NULL; | |
290 | sd_bus_message *message; | |
291 | Manager *m = userdata; | |
292 | User *user; | |
293 | Iterator i; | |
294 | int r; | |
295 | ||
296 | assert(bus); | |
297 | assert(path); | |
298 | assert(nodes); | |
299 | ||
300 | HASHMAP_FOREACH(user, m->users, i) { | |
301 | char *p; | |
302 | ||
303 | p = user_bus_path(user); | |
304 | if (!p) | |
305 | return -ENOMEM; | |
306 | ||
307 | r = strv_consume(&l, p); | |
308 | if (r < 0) | |
309 | return r; | |
310 | } | |
311 | ||
312 | message = sd_bus_get_current_message(bus); | |
313 | if (message) { | |
314 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; | |
315 | ||
316 | r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); | |
317 | if (r >= 0) { | |
318 | uid_t uid; | |
319 | ||
320 | r = sd_bus_creds_get_owner_uid(creds, &uid); | |
321 | if (r >= 0) { | |
322 | user = hashmap_get(m->users, UID_TO_PTR(uid)); | |
323 | if (user) { | |
324 | r = strv_extend(&l, "/org/freedesktop/login1/user/self"); | |
325 | if (r < 0) | |
326 | return r; | |
327 | } | |
328 | } | |
329 | } | |
330 | } | |
331 | ||
332 | *nodes = TAKE_PTR(l); | |
333 | ||
334 | return 1; | |
335 | } | |
336 | ||
337 | int user_send_signal(User *u, bool new_user) { | |
338 | _cleanup_free_ char *p = NULL; | |
339 | ||
340 | assert(u); | |
341 | ||
342 | p = user_bus_path(u); | |
343 | if (!p) | |
344 | return -ENOMEM; | |
345 | ||
346 | return sd_bus_emit_signal( | |
347 | u->manager->bus, | |
348 | "/org/freedesktop/login1", | |
349 | "org.freedesktop.login1.Manager", | |
350 | new_user ? "UserNew" : "UserRemoved", | |
351 | "uo", (uint32_t) u->uid, p); | |
352 | } | |
353 | ||
354 | int user_send_changed(User *u, const char *properties, ...) { | |
355 | _cleanup_free_ char *p = NULL; | |
356 | char **l; | |
357 | ||
358 | assert(u); | |
359 | ||
360 | if (!u->started) | |
361 | return 0; | |
362 | ||
363 | p = user_bus_path(u); | |
364 | if (!p) | |
365 | return -ENOMEM; | |
366 | ||
367 | l = strv_from_stdarg_alloca(properties); | |
368 | ||
369 | return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l); | |
370 | } |