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