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