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