chiark / gitweb /
7897b857b67585b8e01b595be590ddafe3d90cd3
[elogind.git] / dbus-unit.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 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
24 #include "dbus.h"
25 #include "log.h"
26
27 static const char introspection[] =
28         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
29         "<node>"
30         " <interface name=\"org.freedesktop.systemd1.Unit\">"
31         "  <method name=\"Start\">"
32         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>"
33         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>"
34         "  </method>"
35         "  <method name=\"Stop\">"
36         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>"
37         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>"
38         "  </method>"
39         "  <method name=\"Restart\">"
40         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>"
41         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>"
42         "  </method>"
43         "  <method name=\"Reload\">"
44         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>"
45         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>"
46         "  </method>"
47         "  <signal name=\"Changed\"/>"
48         "  <property name=\"Id\" type=\"s\" access=\"read\"/>"
49         "  <property name=\"Description\" type=\"s\" access=\"read\"/>"
50         "  <property name=\"LoadState\" type=\"s\" access=\"read\"/>"
51         "  <property name=\"ActiveState\" type=\"s\" access=\"read\"/>"
52         "  <property name=\"SubState\" type=\"s\" access=\"read\"/>"
53         "  <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>"
54         "  <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>"
55         "  <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>"
56         "  <property name=\"CanReload\" type=\"b\" access=\"read\"/>"
57         "  <property name=\"CanStart\" type=\"b\" access=\"read\"/>"
58         "  <property name=\"Job\" type=\"(uo)\" access=\"read\"/>"
59         " </interface>"
60         BUS_PROPERTIES_INTERFACE
61         BUS_INTROSPECTABLE_INTERFACE
62         "</node>";
63
64 static int bus_unit_append_id(Manager *m, DBusMessageIter *i, const char *property, void *data) {
65         Unit *u = data;
66         const char *id;
67
68         assert(m);
69         assert(i);
70         assert(property);
71         assert(u);
72
73         id = unit_id(u);
74
75         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id))
76                 return -ENOMEM;
77
78         return 0;
79 }
80
81 static int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data) {
82         Unit *u = data;
83         const char *d;
84
85         assert(m);
86         assert(i);
87         assert(property);
88         assert(u);
89
90         d = unit_description(u);
91
92         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
93                 return -ENOMEM;
94
95         return 0;
96 }
97
98 DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
99
100 static int bus_unit_append_active_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
101         Unit *u = data;
102         const char *state;
103
104         assert(m);
105         assert(i);
106         assert(property);
107         assert(u);
108
109         state = unit_active_state_to_string(unit_active_state(u));
110
111         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
112                 return -ENOMEM;
113
114         return 0;
115 }
116
117 static int bus_unit_append_sub_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
118         Unit *u = data;
119         const char *state;
120
121         assert(m);
122         assert(i);
123         assert(property);
124         assert(u);
125
126         state = unit_sub_state_to_string(u);
127
128         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
129                 return -ENOMEM;
130
131         return 0;
132 }
133
134 static int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) {
135         Unit *u = data;
136         dbus_bool_t b;
137
138         assert(m);
139         assert(i);
140         assert(property);
141         assert(u);
142
143         b = unit_can_reload(u);
144
145         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
146                 return -ENOMEM;
147
148         return 0;
149 }
150
151 static int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *property, void *data) {
152         Unit *u = data;
153         dbus_bool_t b;
154
155         assert(m);
156         assert(i);
157         assert(property);
158         assert(u);
159
160         b = unit_can_start(u);
161
162         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
163                 return -ENOMEM;
164
165         return 0;
166 }
167
168 static int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data) {
169         Unit *u = data;
170         DBusMessageIter sub;
171         char *p;
172
173         assert(m);
174         assert(i);
175         assert(property);
176         assert(u);
177
178         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
179                 return -ENOMEM;
180
181         if (u->meta.job) {
182
183                 if (!(p = job_dbus_path(u->meta.job)))
184                         return -ENOMEM;
185
186                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->meta.job->id) ||
187                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
188                         free(p);
189                         return -ENOMEM;
190                 }
191         } else {
192                 uint32_t id = 0;
193
194                 /* No job, so let's fill in some placeholder
195                  * data. Since we need to fill in a valid path we
196                  * simple point to ourselves. */
197
198                 if (!(p = unit_dbus_path(u)))
199                         return -ENOMEM;
200
201                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
202                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
203                         free(p);
204                         return -ENOMEM;
205                 }
206         }
207
208         free(p);
209
210         if (!dbus_message_iter_close_container(i, &sub))
211                 return -ENOMEM;
212
213         return 0;
214 }
215
216 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message) {
217
218         const BusProperty properties[] = {
219                 { "org.freedesktop.systemd1.Unit", "Id",                   bus_unit_append_id,           "s",    u                               },
220                 { "org.freedesktop.systemd1.Unit", "Description",          bus_unit_append_description,  "s",    u                               },
221                 { "org.freedesktop.systemd1.Unit", "LoadState",            bus_unit_append_load_state,   "s",    &u->meta.load_state             },
222                 { "org.freedesktop.systemd1.Unit", "ActiveState",          bus_unit_append_active_state, "s",    u                               },
223                 { "org.freedesktop.systemd1.Unit", "SubState",             bus_unit_append_sub_state,    "s",    u                               },
224                 { "org.freedesktop.systemd1.Unit", "FragmentPath",         bus_property_append_string,   "s",    u->meta.fragment_path           },
225                 { "org.freedesktop.systemd1.Unit", "ActiveEnterTimestamp", bus_property_append_uint64,   "t",    &u->meta.active_enter_timestamp },
226                 { "org.freedesktop.systemd1.Unit", "ActiveExitTimestamp",  bus_property_append_uint64,   "t",    &u->meta.active_exit_timestamp  },
227                 { "org.freedesktop.systemd1.Unit", "CanReload",            bus_unit_append_can_reload,   "b",    u                               },
228                 { "org.freedesktop.systemd1.Unit", "CanStart",             bus_unit_append_can_start,    "b",    u                               },
229                 { "org.freedesktop.systemd1.Unit", "Job",                  bus_unit_append_job,          "(uo)", u                               },
230                 { NULL, NULL, NULL, NULL, NULL }
231         };
232
233         DBusMessage *reply = NULL;
234         Manager *m = u->meta.manager;
235         DBusError error;
236         JobType job_type = _JOB_TYPE_INVALID;
237
238         dbus_error_init(&error);
239
240         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
241                 job_type = JOB_START;
242         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
243                 job_type = JOB_STOP;
244         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
245                 job_type = JOB_RELOAD;
246         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
247                 job_type = JOB_RESTART;
248         else
249                 return bus_default_message_handler(u->meta.manager, message, introspection, properties);
250
251         if (job_type != _JOB_TYPE_INVALID) {
252                 const char *smode;
253                 JobMode mode;
254                 Job *j;
255                 int r;
256                 char *path;
257
258                 if (!dbus_message_get_args(
259                                     message,
260                                     &error,
261                                     DBUS_TYPE_STRING, &smode,
262                                     DBUS_TYPE_INVALID))
263                         return bus_send_error_reply(m, message, &error, -EINVAL);
264
265                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID)
266                         return bus_send_error_reply(m, message, NULL, -EINVAL);
267
268                 if ((r = manager_add_job(m, job_type, u, mode, true, &j)) < 0)
269                         return bus_send_error_reply(m, message, NULL, r);
270
271                 if (!(reply = dbus_message_new_method_return(message)))
272                         goto oom;
273
274                 if (!(path = job_dbus_path(j)))
275                         goto oom;
276
277                 if (!dbus_message_append_args(
278                                     reply,
279                                     DBUS_TYPE_OBJECT_PATH, &path,
280                                     DBUS_TYPE_INVALID))
281                         goto oom;
282         }
283
284         if (reply) {
285                 if (!dbus_connection_send(m->api_bus, reply, NULL))
286                         goto oom;
287
288                 dbus_message_unref(reply);
289         }
290
291         return DBUS_HANDLER_RESULT_HANDLED;
292
293 oom:
294         if (reply)
295                 dbus_message_unref(reply);
296
297         dbus_error_free(&error);
298
299         return DBUS_HANDLER_RESULT_NEED_MEMORY;
300 }
301
302 static DBusHandlerResult bus_unit_message_handler(DBusConnection  *connection, DBusMessage  *message, void *data) {
303         Manager *m = data;
304         Unit *u;
305         int r;
306
307         assert(connection);
308         assert(message);
309         assert(m);
310
311         log_debug("Got D-Bus request: %s.%s() on %s",
312                   dbus_message_get_interface(message),
313                   dbus_message_get_member(message),
314                   dbus_message_get_path(message));
315
316         if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
317
318                 if (r == -ENOMEM)
319                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
320
321                 if (r == -ENOENT)
322                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
323
324                 return bus_send_error_reply(m, message, NULL, r);
325         }
326
327         return bus_unit_message_dispatch(u, message);
328 }
329
330 const DBusObjectPathVTable bus_unit_vtable = {
331         .message_function = bus_unit_message_handler
332 };
333
334 void bus_unit_send_change_signal(Unit *u) {
335         char *p = NULL;
336         DBusMessage *m = NULL;
337
338         assert(u);
339         assert(u->meta.in_dbus_queue);
340
341         LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
342         u->meta.in_dbus_queue = false;
343
344         if (set_isempty(u->meta.manager->subscribed))
345                 return;
346
347         if (!(p = unit_dbus_path(u)))
348                 goto oom;
349
350         if (u->meta.sent_dbus_new_signal) {
351                 /* Send a change signal */
352
353                 if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed")))
354                         goto oom;
355         } else {
356                 const char *id;
357                 /* Send a new signal */
358
359                 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitNew")))
360                         goto oom;
361
362                 id = unit_id(u);
363                 if (!dbus_message_append_args(m,
364                                               DBUS_TYPE_STRING, &id,
365                                               DBUS_TYPE_OBJECT_PATH, &p,
366                                               DBUS_TYPE_INVALID))
367                         goto oom;
368         }
369
370         if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL))
371                 goto oom;
372
373         free(p);
374         dbus_message_unref(m);
375
376         u->meta.sent_dbus_new_signal = true;
377
378         return;
379
380 oom:
381         free(p);
382
383         if (m)
384                 dbus_message_unref(m);
385
386         log_error("Failed to allocate unit change/new signal.");
387 }
388
389 void bus_unit_send_removed_signal(Unit *u) {
390         char *p = NULL;
391         DBusMessage *m = NULL;
392         const char *id;
393
394         assert(u);
395
396         if (set_isempty(u->meta.manager->subscribed) || !u->meta.sent_dbus_new_signal)
397                 return;
398
399         if (!(p = unit_dbus_path(u)))
400                 goto oom;
401
402         if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitRemoved")))
403                 goto oom;
404
405         id = unit_id(u);
406         if (!dbus_message_append_args(m,
407                                       DBUS_TYPE_STRING, &id,
408                                       DBUS_TYPE_OBJECT_PATH, &p,
409                                       DBUS_TYPE_INVALID))
410                 goto oom;
411
412         if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL))
413                 goto oom;
414
415         free(p);
416         dbus_message_unref(m);
417
418         return;
419
420 oom:
421         free(p);
422
423         if (m)
424                 dbus_message_unref(m);
425
426         log_error("Failed to allocate unit remove signal.");
427 }