chiark / gitweb /
shared: add terminal-util.[ch]
[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         r = sd_bus_message_close_container(reply);
107         if (r < 0)
108                 return r;
109
110         return 1;
111 }
112
113 static int property_get_idle_hint(
114                 sd_bus *bus,
115                 const char *path,
116                 const char *interface,
117                 const char *property,
118                 sd_bus_message *reply,
119                 void *userdata,
120                 sd_bus_error *error) {
121
122         User *u = userdata;
123
124         assert(bus);
125         assert(reply);
126         assert(u);
127
128         return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0);
129 }
130
131 static int property_get_idle_since_hint(
132                 sd_bus *bus,
133                 const char *path,
134                 const char *interface,
135                 const char *property,
136                 sd_bus_message *reply,
137                 void *userdata,
138                 sd_bus_error *error) {
139
140         User *u = userdata;
141         dual_timestamp t;
142         uint64_t k;
143
144         assert(bus);
145         assert(reply);
146         assert(u);
147
148         user_get_idle_hint(u, &t);
149         k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
150
151         return sd_bus_message_append(reply, "t", k);
152 }
153
154 static int property_get_linger(
155                 sd_bus *bus,
156                 const char *path,
157                 const char *interface,
158                 const char *property,
159                 sd_bus_message *reply,
160                 void *userdata,
161                 sd_bus_error *error) {
162
163         User *u = userdata;
164         int r;
165
166         assert(bus);
167         assert(reply);
168         assert(u);
169
170         r = user_check_linger_file(u);
171
172         return sd_bus_message_append(reply, "b", r > 0);
173 }
174
175 int bus_user_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
176         User *u = userdata;
177         int r;
178
179         assert(bus);
180         assert(message);
181         assert(u);
182
183         r = bus_verify_polkit_async(
184                         message,
185                         CAP_KILL,
186                         "org.freedesktop.login1.manage",
187                         false,
188                         u->uid,
189                         &u->manager->polkit_registry,
190                         error);
191         if (r < 0)
192                 return r;
193         if (r == 0)
194                 return 1; /* Will call us back */
195
196         r = user_stop(u, true);
197         if (r < 0)
198                 return r;
199
200         return sd_bus_reply_method_return(message, NULL);
201 }
202
203 int bus_user_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
204         User *u = userdata;
205         int32_t signo;
206         int r;
207
208         assert(bus);
209         assert(message);
210         assert(u);
211
212         r = bus_verify_polkit_async(
213                         message,
214                         CAP_KILL,
215                         "org.freedesktop.login1.manage",
216                         false,
217                         u->uid,
218                         &u->manager->polkit_registry,
219                         error);
220         if (r < 0)
221                 return r;
222         if (r == 0)
223                 return 1; /* Will call us back */
224
225         r = sd_bus_message_read(message, "i", &signo);
226         if (r < 0)
227                 return r;
228
229         if (signo <= 0 || signo >= _NSIG)
230                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
231
232         r = user_kill(u, signo);
233         if (r < 0)
234                 return r;
235
236         return sd_bus_reply_method_return(message, NULL);
237 }
238
239 const sd_bus_vtable user_vtable[] = {
240         SD_BUS_VTABLE_START(0),
241
242         SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST),
243         SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST),
244         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST),
245         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
246         SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
247         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
248         SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST),
249         SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
250         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
251         SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
252         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
253         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
254         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
255         SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
256
257         SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
258         SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
259
260         SD_BUS_VTABLE_END
261 };
262
263 int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
264         Manager *m = userdata;
265         uid_t uid;
266         User *user;
267         int r;
268
269         assert(bus);
270         assert(path);
271         assert(interface);
272         assert(found);
273         assert(m);
274
275         if (streq(path, "/org/freedesktop/login1/user/self")) {
276                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
277                 sd_bus_message *message;
278
279                 message = sd_bus_get_current_message(bus);
280                 if (!message)
281                         return 0;
282
283                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
284                 if (r < 0)
285                         return r;
286
287                 r = sd_bus_creds_get_owner_uid(creds, &uid);
288         } else {
289                 const char *p;
290
291                 p = startswith(path, "/org/freedesktop/login1/user/_");
292                 if (!p)
293                         return 0;
294
295                 r = parse_uid(p, &uid);
296         }
297         if (r < 0)
298                 return 0;
299
300         user = hashmap_get(m->users, UID_TO_PTR(uid));
301         if (!user)
302                 return 0;
303
304         *found = user;
305         return 1;
306 }
307
308 char *user_bus_path(User *u) {
309         char *s;
310
311         assert(u);
312
313         if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0)
314                 return NULL;
315
316         return s;
317 }
318
319 int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
320         _cleanup_strv_free_ char **l = NULL;
321         sd_bus_message *message;
322         Manager *m = userdata;
323         User *user;
324         Iterator i;
325         int r;
326
327         assert(bus);
328         assert(path);
329         assert(nodes);
330
331         HASHMAP_FOREACH(user, m->users, i) {
332                 char *p;
333
334                 p = user_bus_path(user);
335                 if (!p)
336                         return -ENOMEM;
337
338                 r = strv_consume(&l, p);
339                 if (r < 0)
340                         return r;
341         }
342
343         message = sd_bus_get_current_message(bus);
344         if (message) {
345                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
346                 uid_t uid;
347
348                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
349                 if (r >= 0) {
350                         r = sd_bus_creds_get_owner_uid(creds, &uid);
351                         if (r >= 0) {
352                                 user = hashmap_get(m->users, UID_TO_PTR(uid));
353                                 if (user) {
354                                         r = strv_extend(&l, "/org/freedesktop/login1/user/self");
355                                         if (r < 0)
356                                                 return r;
357                                 }
358                         }
359                 }
360         }
361
362         *nodes = l;
363         l = NULL;
364
365         return 1;
366 }
367
368 int user_send_signal(User *u, bool new_user) {
369         _cleanup_free_ char *p = NULL;
370
371         assert(u);
372
373         p = user_bus_path(u);
374         if (!p)
375                 return -ENOMEM;
376
377         return sd_bus_emit_signal(
378                         u->manager->bus,
379                         "/org/freedesktop/login1",
380                         "org.freedesktop.login1.Manager",
381                         new_user ? "UserNew" : "UserRemoved",
382                         "uo", (uint32_t) u->uid, p);
383 }
384
385 int user_send_changed(User *u, const char *properties, ...) {
386         _cleanup_free_ char *p = NULL;
387         char **l;
388
389         assert(u);
390
391         if (!u->started)
392                 return 0;
393
394         p = user_bus_path(u);
395         if (!p)
396                 return -ENOMEM;
397
398         l = strv_from_stdarg_alloca(properties);
399
400         return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l);
401 }