chiark / gitweb /
license: add GPLv2+ license blurbs everwhere
[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         "  <property name=\"Id\" type=\"s\" access=\"read\"/>"
48         "  <property name=\"Description\" type=\"s\" access=\"read\"/>"
49         "  <property name=\"LoadState\" type=\"s\" access=\"read\"/>"
50         "  <property name=\"ActiveState\" type=\"s\" access=\"read\"/>"
51         "  <property name=\"LoadPath\" type=\"s\" access=\"read\"/>"
52         "  <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>"
53         "  <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>"
54         "  <property name=\"CanReload\" type=\"b\" access=\"read\"/>"
55         "  <property name=\"CanStart\" type=\"b\" access=\"read\"/>"
56         "  <property name=\"Job\" type=\"(uo)\" access=\"read\"/>"
57         " </interface>"
58         BUS_PROPERTIES_INTERFACE
59         BUS_INTROSPECTABLE_INTERFACE
60         "</node>";
61
62 static int bus_unit_append_id(Manager *m, DBusMessageIter *i, const char *property, void *data) {
63         Unit *u = data;
64         const char *id;
65
66         assert(m);
67         assert(i);
68         assert(property);
69         assert(u);
70
71         id = unit_id(u);
72
73         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id))
74                 return -ENOMEM;
75
76         return 0;
77 }
78
79 static int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data) {
80         Unit *u = data;
81         const char *d;
82
83         assert(m);
84         assert(i);
85         assert(property);
86         assert(u);
87
88         d = unit_description(u);
89
90         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
91                 return -ENOMEM;
92
93         return 0;
94 }
95
96 static int bus_unit_append_load_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
97         Unit *u = data;
98         const char *state;
99
100         assert(m);
101         assert(i);
102         assert(property);
103         assert(u);
104
105         state = unit_load_state_to_string(u->meta.load_state);
106
107         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
108                 return -ENOMEM;
109
110         return 0;
111 }
112
113 static int bus_unit_append_active_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
114         Unit *u = data;
115         const char *state;
116
117         assert(m);
118         assert(i);
119         assert(property);
120         assert(u);
121
122         state = unit_active_state_to_string(unit_active_state(u));
123
124         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
125                 return -ENOMEM;
126
127         return 0;
128 }
129
130 static int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) {
131         Unit *u = data;
132         dbus_bool_t b;
133
134         assert(m);
135         assert(i);
136         assert(property);
137         assert(u);
138
139         b = unit_can_reload(u);
140
141         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
142                 return -ENOMEM;
143
144         return 0;
145 }
146
147 static int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *property, void *data) {
148         Unit *u = data;
149         dbus_bool_t b;
150
151         assert(m);
152         assert(i);
153         assert(property);
154         assert(u);
155
156         b = unit_can_start(u);
157
158         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
159                 return -ENOMEM;
160
161         return 0;
162 }
163
164 static int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data) {
165         Unit *u = data;
166         DBusMessageIter sub;
167         char *p;
168
169         assert(m);
170         assert(i);
171         assert(property);
172         assert(u);
173
174         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
175                 return -ENOMEM;
176
177         if (u->meta.job) {
178
179                 if (!(p = job_dbus_path(u->meta.job)))
180                         return -ENOMEM;
181
182                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->meta.job->id) ||
183                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
184                         free(p);
185                         return -ENOMEM;
186                 }
187         } else {
188                 uint32_t id = 0;
189
190                 /* No job, so let's fill in some placeholder
191                  * data. Since we need to fill in a valid path we
192                  * simple point to ourselves. */
193
194                 if (!(p = unit_dbus_path(u)))
195                         return -ENOMEM;
196
197                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
198                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
199                         free(p);
200                         return -ENOMEM;
201                 }
202         }
203
204         free(p);
205
206         if (!dbus_message_iter_close_container(i, &sub))
207                 return -ENOMEM;
208
209         return 0;
210 }
211
212 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message) {
213
214         const BusProperty properties[] = {
215                 { "org.freedesktop.systemd1.Unit", "Id",                   bus_unit_append_id,           "s",    u                               },
216                 { "org.freedesktop.systemd1.Unit", "Description",          bus_unit_append_description,  "s",    u                               },
217                 { "org.freedesktop.systemd1.Unit", "LoadState",            bus_unit_append_load_state,   "s",    u                               },
218                 { "org.freedesktop.systemd1.Unit", "ActiveState",          bus_unit_append_active_state, "s",    u                               },
219                 { "org.freedesktop.systemd1.Unit", "LoadPath",             bus_property_append_string,   "s",    u->meta.load_path               },
220                 { "org.freedesktop.systemd1.Unit", "ActiveEnterTimestamp", bus_property_append_uint64,   "t",    &u->meta.active_enter_timestamp },
221                 { "org.freedesktop.systemd1.Unit", "ActiveExitTimestamp",  bus_property_append_uint64,   "t",    &u->meta.active_exit_timestamp  },
222                 { "org.freedesktop.systemd1.Unit", "CanReload",            bus_unit_append_can_reload,   "b",    u                               },
223                 { "org.freedesktop.systemd1.Unit", "CanStart",             bus_unit_append_can_start,    "b",    u                               },
224                 { "org.freedesktop.systemd1.Unit", "Job",                  bus_unit_append_job,          "(uo)", u                               },
225                 { NULL, NULL, NULL, NULL, NULL }
226         };
227
228         DBusMessage *reply = NULL;
229         Manager *m = u->meta.manager;
230         DBusError error;
231         JobType job_type = _JOB_TYPE_INVALID;
232
233         dbus_error_init(&error);
234
235         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
236                 job_type = JOB_START;
237         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
238                 job_type = JOB_STOP;
239         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
240                 job_type = JOB_RELOAD;
241         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
242                 job_type = JOB_RESTART;
243         else
244                 return bus_default_message_handler(u->meta.manager, message, introspection, properties);
245
246         if (job_type != _JOB_TYPE_INVALID) {
247                 const char *smode;
248                 JobMode mode;
249                 Job *j;
250                 int r;
251                 char *path;
252
253                 if (!dbus_message_get_args(
254                                     message,
255                                     &error,
256                                     DBUS_TYPE_STRING, &smode,
257                                     DBUS_TYPE_INVALID))
258                         return bus_send_error_reply(m, message, &error, -EINVAL);
259
260                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID)
261                         return bus_send_error_reply(m, message, NULL, -EINVAL);
262
263                 if ((r = manager_add_job(m, job_type, u, mode, true, &j)) < 0)
264                         return bus_send_error_reply(m, message, NULL, r);
265
266                 if (!(reply = dbus_message_new_method_return(message)))
267                         goto oom;
268
269                 if (!(path = job_dbus_path(j)))
270                         goto oom;
271
272                 if (!dbus_message_append_args(
273                                     reply,
274                                     DBUS_TYPE_OBJECT_PATH, &path,
275                                     DBUS_TYPE_INVALID))
276                         goto oom;
277         }
278
279         if (reply) {
280                 if (!dbus_connection_send(m->bus, reply, NULL))
281                         goto oom;
282
283                 dbus_message_unref(reply);
284         }
285
286         return DBUS_HANDLER_RESULT_HANDLED;
287
288 oom:
289         if (reply)
290                 dbus_message_unref(reply);
291
292         dbus_error_free(&error);
293
294         return DBUS_HANDLER_RESULT_NEED_MEMORY;
295 }
296
297 static DBusHandlerResult bus_unit_message_handler(DBusConnection  *connection, DBusMessage  *message, void *data) {
298         Manager *m = data;
299         Unit *u;
300         int r;
301
302         assert(connection);
303         assert(message);
304         assert(m);
305
306         log_debug("Got D-Bus request: %s.%s() on %s",
307                   dbus_message_get_interface(message),
308                   dbus_message_get_member(message),
309                   dbus_message_get_path(message));
310
311         if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
312
313                 if (r == -ENOMEM)
314                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
315
316                 if (r == -ENOENT)
317                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
318
319                 return bus_send_error_reply(m, message, NULL, r);
320         }
321
322         return bus_unit_message_dispatch(u, message);
323 }
324
325 const DBusObjectPathVTable bus_unit_vtable = {
326         .message_function = bus_unit_message_handler
327 };