chiark / gitweb /
loginctl: restore cgroup display for status output
[elogind.git] / src / machine / machine-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 "machined.h"
26 #include "machine.h"
27 #include "dbus-common.h"
28
29 #define BUS_MACHINE_INTERFACE \
30         " <interface name=\"org.freedesktop.machine1.Machine\">\n"        \
31         "  <method name=\"Terminate\"/>\n"                              \
32         "  <method name=\"Kill\">\n"                                    \
33         "   <arg name=\"who\" type=\"s\"/>\n"                           \
34         "   <arg name=\"signal\" type=\"s\"/>\n"                        \
35         "  </method>\n"                                                 \
36         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"      \
37         "  <property name=\"Id\" type=\"ay\" access=\"read\"/>\n"        \
38         "  <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
39         "  <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
40         "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
41         "  <property name=\"Scope\" type=\"s\" access=\"read\"/>\n"     \
42         "  <property name=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
43         "  <property name=\"Class\" type=\"s\" access=\"read\"/>\n"     \
44         "  <property name=\"State\" type=\"s\" access=\"read\"/>\n"     \
45         "  <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \
46         " </interface>\n"
47
48 #define INTROSPECTION                                                   \
49         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
50         "<node>\n"                                                      \
51         BUS_MACHINE_INTERFACE                                           \
52         BUS_PROPERTIES_INTERFACE                                        \
53         BUS_PEER_INTERFACE                                              \
54         BUS_INTROSPECTABLE_INTERFACE                                    \
55         "</node>\n"
56
57 #define INTERFACES_LIST                              \
58         BUS_GENERIC_INTERFACES_LIST                  \
59         "org.freedesktop.machine1.Machine\0"
60
61 static int bus_machine_append_id(DBusMessageIter *i, const char *property, void *data) {
62         DBusMessageIter sub;
63         Machine *m = data;
64         dbus_bool_t b;
65         void *p;
66
67         assert(i);
68         assert(property);
69         assert(m);
70
71         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub))
72                 return -ENOMEM;
73
74         p = &m->id;
75         b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, 16);
76         if (!b)
77                 return -ENOMEM;
78
79         if (!dbus_message_iter_close_container(i, &sub))
80                 return -ENOMEM;
81
82         return 0;
83 }
84
85 static int bus_machine_append_state(DBusMessageIter *i, const char *property, void *data) {
86         Machine *m = data;
87         const char *state;
88
89         assert(i);
90         assert(property);
91         assert(m);
92
93         state = machine_state_to_string(machine_get_state(m));
94
95         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
96                 return -ENOMEM;
97
98         return 0;
99 }
100
101 static int get_machine_for_path(Manager *m, const char *path, Machine **_machine) {
102         _cleanup_free_ char *e = NULL;
103         Machine *machine;
104
105         assert(m);
106         assert(path);
107         assert(_machine);
108
109         if (!startswith(path, "/org/freedesktop/machine1/machine/"))
110                 return -EINVAL;
111
112         e = bus_path_unescape(path + 34);
113         if (!e)
114                 return -ENOMEM;
115
116         machine = hashmap_get(m->machines, e);
117         if (!machine)
118                 return -ENOENT;
119
120         *_machine = machine;
121         return 0;
122 }
123
124 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_machine_append_class, machine_class, MachineClass);
125
126 static const BusProperty bus_machine_machine_properties[] = {
127         { "Name",                   bus_property_append_string,        "s", offsetof(Machine, name),               true },
128         { "Id",                     bus_machine_append_id,            "ay", 0 },
129         { "Timestamp",              bus_property_append_usec,          "t", offsetof(Machine, timestamp.realtime)  },
130         { "TimestampMonotonic",     bus_property_append_usec,          "t", offsetof(Machine, timestamp.monotonic) },
131         { "Service",                bus_property_append_string,        "s", offsetof(Machine, service),            true },
132         { "Scope",                  bus_property_append_string,        "s", offsetof(Machine, scope),              true },
133         { "Class",                  bus_machine_append_class,          "s", offsetof(Machine, class)               },
134         { "State",                  bus_machine_append_state,          "s", 0                                      },
135         { "RootDirectory",          bus_property_append_string,        "s", offsetof(Machine, root_directory),     true },
136         { NULL, }
137 };
138
139 static DBusHandlerResult machine_message_dispatch(
140                 Machine *m,
141                 DBusConnection *connection,
142                 DBusMessage *message) {
143
144         DBusError error;
145         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
146         int r;
147
148         assert(m);
149         assert(connection);
150         assert(message);
151
152         if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Terminate")) {
153
154                 r = machine_stop(m);
155                 if (r < 0)
156                         return bus_send_error_reply(connection, message, NULL, r);
157
158                 reply = dbus_message_new_method_return(message);
159                 if (!reply)
160                         goto oom;
161
162         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Kill")) {
163                 const char *swho;
164                 int32_t signo;
165                 KillWho who;
166
167                 if (!dbus_message_get_args(
168                                     message,
169                                     &error,
170                                     DBUS_TYPE_STRING, &swho,
171                                     DBUS_TYPE_INT32, &signo,
172                                     DBUS_TYPE_INVALID))
173                         return bus_send_error_reply(connection, message, &error, -EINVAL);
174
175                 if (isempty(swho))
176                         who = KILL_ALL;
177                 else {
178                         who = kill_who_from_string(swho);
179                         if (who < 0)
180                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
181                 }
182
183                 if (signo <= 0 || signo >= _NSIG)
184                         return bus_send_error_reply(connection, message, &error, -EINVAL);
185
186                 r = machine_kill(m, who, signo);
187                 if (r < 0)
188                         return bus_send_error_reply(connection, message, NULL, r);
189
190                 reply = dbus_message_new_method_return(message);
191                 if (!reply)
192                         goto oom;
193
194         } else {
195                 const BusBoundProperties bps[] = {
196                         { "org.freedesktop.machine1.Machine", bus_machine_machine_properties, m },
197                         { NULL, }
198                 };
199
200                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
201         }
202
203         if (reply) {
204                 if (!bus_maybe_send_reply(connection, message, reply))
205                         goto oom;
206         }
207
208         return DBUS_HANDLER_RESULT_HANDLED;
209
210 oom:
211         dbus_error_free(&error);
212
213         return DBUS_HANDLER_RESULT_NEED_MEMORY;
214 }
215
216 static DBusHandlerResult machine_message_handler(
217                 DBusConnection *connection,
218                 DBusMessage *message,
219                 void *userdata) {
220
221         Manager *manager = userdata;
222         Machine *m;
223         int r;
224
225         r = get_machine_for_path(manager, dbus_message_get_path(message), &m);
226         if (r < 0) {
227
228                 if (r == -ENOMEM)
229                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
230
231                 if (r == -ENOENT) {
232                         DBusError e;
233
234                         dbus_error_init(&e);
235                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown machine");
236                         return bus_send_error_reply(connection, message, &e, r);
237                 }
238
239                 return bus_send_error_reply(connection, message, NULL, r);
240         }
241
242         return machine_message_dispatch(m, connection, message);
243 }
244
245 const DBusObjectPathVTable bus_machine_vtable = {
246         .message_function = machine_message_handler
247 };
248
249 char *machine_bus_path(Machine *m) {
250         _cleanup_free_ char *e = NULL;
251
252         assert(m);
253
254         e = bus_path_escape(m->name);
255         if (!e)
256                 return NULL;
257
258         return strappend("/org/freedesktop/machine1/machine/", e);
259 }
260
261 int machine_send_signal(Machine *m, bool new_machine) {
262         _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
263         _cleanup_free_ char *p = NULL;
264
265         assert(m);
266
267         msg = dbus_message_new_signal("/org/freedesktop/machine1",
268                                     "org.freedesktop.machine1.Manager",
269                                     new_machine ? "MachineNew" : "MachineRemoved");
270
271         if (!m)
272                 return -ENOMEM;
273
274         p = machine_bus_path(m);
275         if (!p)
276                 return -ENOMEM;
277
278         if (!dbus_message_append_args(
279                             msg,
280                             DBUS_TYPE_STRING, &m->name,
281                             DBUS_TYPE_OBJECT_PATH, &p,
282                             DBUS_TYPE_INVALID))
283                 return -ENOMEM;
284
285         if (!dbus_connection_send(m->manager->bus, msg, NULL))
286                 return -ENOMEM;
287
288         return 0;
289 }
290
291 int machine_send_changed(Machine *m, const char *properties) {
292         _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
293         _cleanup_free_ char *p = NULL;
294
295         assert(m);
296
297         if (!m->started)
298                 return 0;
299
300         p = machine_bus_path(m);
301         if (!p)
302                 return -ENOMEM;
303
304         msg = bus_properties_changed_new(p, "org.freedesktop.machine1.Machine", properties);
305         if (!msg)
306                 return -ENOMEM;
307
308         if (!dbus_connection_send(m->manager->bus, msg, NULL))
309                 return -ENOMEM;
310
311         return 0;
312 }
313
314 int machine_send_create_reply(Machine *m, DBusError *error) {
315         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
316
317         assert(m);
318
319         if (!m->create_message)
320                 return 0;
321
322         if (error) {
323                 DBusError buffer;
324
325                 dbus_error_init(&buffer);
326
327                 if (!error || !dbus_error_is_set(error)) {
328                         dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
329                         error = &buffer;
330                 }
331
332                 reply = dbus_message_new_error(m->create_message, error->name, error->message);
333                 dbus_error_free(&buffer);
334
335                 if (!reply)
336                         return log_oom();
337         } else {
338                 _cleanup_free_ char *p = NULL;
339
340                 p = machine_bus_path(m);
341                 if (!p)
342                         return log_oom();
343
344                 reply = dbus_message_new_method_return(m->create_message);
345                 if (!reply)
346                         return log_oom();
347
348                 if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID))
349                         return log_oom();
350         }
351
352         if (!dbus_connection_send(m->manager->bus, reply, NULL))
353                 return log_oom();
354
355         dbus_message_unref(m->create_message);
356         m->create_message = NULL;
357
358         return 0;
359 }