chiark / gitweb /
list: make our list macros a bit easier to use by not requring type spec on each...
[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         { "Leader",                 bus_property_append_pid,           "u", offsetof(Machine, leader)              },
134         { "Class",                  bus_machine_append_class,          "s", offsetof(Machine, class)               },
135         { "State",                  bus_machine_append_state,          "s", 0                                      },
136         { "RootDirectory",          bus_property_append_string,        "s", offsetof(Machine, root_directory),     true },
137         { NULL, }
138 };
139
140 static DBusHandlerResult machine_message_dispatch(
141                 Machine *m,
142                 DBusConnection *connection,
143                 DBusMessage *message) {
144
145         DBusError error;
146         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
147         int r;
148
149         assert(m);
150         assert(connection);
151         assert(message);
152
153         if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Terminate")) {
154
155                 r = machine_stop(m);
156                 if (r < 0)
157                         return bus_send_error_reply(connection, message, NULL, r);
158
159                 reply = dbus_message_new_method_return(message);
160                 if (!reply)
161                         goto oom;
162
163         } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Kill")) {
164                 const char *swho;
165                 int32_t signo;
166                 KillWho who;
167
168                 if (!dbus_message_get_args(
169                                     message,
170                                     &error,
171                                     DBUS_TYPE_STRING, &swho,
172                                     DBUS_TYPE_INT32, &signo,
173                                     DBUS_TYPE_INVALID))
174                         return bus_send_error_reply(connection, message, &error, -EINVAL);
175
176                 if (isempty(swho))
177                         who = KILL_ALL;
178                 else {
179                         who = kill_who_from_string(swho);
180                         if (who < 0)
181                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
182                 }
183
184                 if (signo <= 0 || signo >= _NSIG)
185                         return bus_send_error_reply(connection, message, &error, -EINVAL);
186
187                 r = machine_kill(m, who, signo);
188                 if (r < 0)
189                         return bus_send_error_reply(connection, message, NULL, r);
190
191                 reply = dbus_message_new_method_return(message);
192                 if (!reply)
193                         goto oom;
194
195         } else {
196                 const BusBoundProperties bps[] = {
197                         { "org.freedesktop.machine1.Machine", bus_machine_machine_properties, m },
198                         { NULL, }
199                 };
200
201                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
202         }
203
204         if (reply) {
205                 if (!bus_maybe_send_reply(connection, message, reply))
206                         goto oom;
207         }
208
209         return DBUS_HANDLER_RESULT_HANDLED;
210
211 oom:
212         dbus_error_free(&error);
213
214         return DBUS_HANDLER_RESULT_NEED_MEMORY;
215 }
216
217 static DBusHandlerResult machine_message_handler(
218                 DBusConnection *connection,
219                 DBusMessage *message,
220                 void *userdata) {
221
222         Manager *manager = userdata;
223         Machine *m;
224         int r;
225
226         r = get_machine_for_path(manager, dbus_message_get_path(message), &m);
227         if (r < 0) {
228
229                 if (r == -ENOMEM)
230                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
231
232                 if (r == -ENOENT) {
233                         DBusError e;
234
235                         dbus_error_init(&e);
236                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown machine");
237                         return bus_send_error_reply(connection, message, &e, r);
238                 }
239
240                 return bus_send_error_reply(connection, message, NULL, r);
241         }
242
243         return machine_message_dispatch(m, connection, message);
244 }
245
246 const DBusObjectPathVTable bus_machine_vtable = {
247         .message_function = machine_message_handler
248 };
249
250 char *machine_bus_path(Machine *m) {
251         _cleanup_free_ char *e = NULL;
252
253         assert(m);
254
255         e = bus_path_escape(m->name);
256         if (!e)
257                 return NULL;
258
259         return strappend("/org/freedesktop/machine1/machine/", e);
260 }
261
262 int machine_send_signal(Machine *m, bool new_machine) {
263         _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
264         _cleanup_free_ char *p = NULL;
265
266         assert(m);
267
268         msg = dbus_message_new_signal("/org/freedesktop/machine1",
269                                     "org.freedesktop.machine1.Manager",
270                                     new_machine ? "MachineNew" : "MachineRemoved");
271
272         if (!m)
273                 return -ENOMEM;
274
275         p = machine_bus_path(m);
276         if (!p)
277                 return -ENOMEM;
278
279         if (!dbus_message_append_args(
280                             msg,
281                             DBUS_TYPE_STRING, &m->name,
282                             DBUS_TYPE_OBJECT_PATH, &p,
283                             DBUS_TYPE_INVALID))
284                 return -ENOMEM;
285
286         if (!dbus_connection_send(m->manager->bus, msg, NULL))
287                 return -ENOMEM;
288
289         return 0;
290 }
291
292 int machine_send_changed(Machine *m, const char *properties) {
293         _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
294         _cleanup_free_ char *p = NULL;
295
296         assert(m);
297
298         if (!m->started)
299                 return 0;
300
301         p = machine_bus_path(m);
302         if (!p)
303                 return -ENOMEM;
304
305         msg = bus_properties_changed_new(p, "org.freedesktop.machine1.Machine", properties);
306         if (!msg)
307                 return -ENOMEM;
308
309         if (!dbus_connection_send(m->manager->bus, msg, NULL))
310                 return -ENOMEM;
311
312         return 0;
313 }
314
315 int machine_send_create_reply(Machine *m, DBusError *error) {
316         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
317
318         assert(m);
319
320         if (!m->create_message)
321                 return 0;
322
323         if (error) {
324                 DBusError buffer;
325
326                 dbus_error_init(&buffer);
327
328                 if (!error || !dbus_error_is_set(error)) {
329                         dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
330                         error = &buffer;
331                 }
332
333                 reply = dbus_message_new_error(m->create_message, error->name, error->message);
334                 dbus_error_free(&buffer);
335
336                 if (!reply)
337                         return log_oom();
338         } else {
339                 _cleanup_free_ char *p = NULL;
340
341                 p = machine_bus_path(m);
342                 if (!p)
343                         return log_oom();
344
345                 reply = dbus_message_new_method_return(m->create_message);
346                 if (!reply)
347                         return log_oom();
348
349                 if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID))
350                         return log_oom();
351         }
352
353         /* Update the machine state file before we notify the client
354          * about the result. */
355         machine_save(m);
356
357         if (!dbus_connection_send(m->manager->bus, reply, NULL))
358                 return log_oom();
359
360         dbus_message_unref(m->create_message);
361         m->create_message = NULL;
362
363         return 0;
364 }