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