chiark / gitweb /
3ccf8de650c6b9724cb79794062b162ac7ea58ef
[elogind.git] / dbus-manager.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <errno.h>
4
5 #include "dbus.h"
6 #include "log.h"
7
8 #define INTROSPECTION_BEGIN                                             \
9         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
10         "<node>"                                                        \
11         " <interface name=\"org.freedesktop.systemd1\">"                \
12         "  <method name=\"GetUnit\">"                                   \
13         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>"           \
14         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
15         "  </method>"                                                   \
16         "  <method name=\"LoadUnit\">"                                  \
17         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>"           \
18         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
19         "  </method>"                                                   \
20         "  <method name=\"GetJob\">"                                    \
21         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>"             \
22         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
23         "  </method>"                                                   \
24         "  <method name=\"ClearJobs\"/>"                                \
25         "  <method name=\"ListUnits\">"                                 \
26         "   <arg name=\"units\" type=\"a(ssssouso)\" direction=\"out\"/>" \
27         "  </method>"                                                   \
28         "  <method name=\"ListJobs\">"                                  \
29         "   <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>"  \
30         "  </method>"                                                   \
31         " </interface>"                                                 \
32         BUS_PROPERTIES_INTERFACE                                        \
33         BUS_INTROSPECTABLE_INTERFACE
34
35 #define INTROSPECTION_END                                               \
36         "</node>"
37
38 DBusHandlerResult bus_manager_message_handler(DBusConnection  *connection, DBusMessage *message, void *data) {
39         int r;
40         Manager *m = data;
41         DBusError error;
42         DBusMessage *reply = NULL;
43         char * path = NULL;
44
45         assert(connection);
46         assert(message);
47         assert(m);
48
49         dbus_error_init(&error);
50
51         log_debug("Got D-Bus request: %s.%s() on %s",
52                   dbus_message_get_interface(message),
53                   dbus_message_get_member(message),
54                   dbus_message_get_path(message));
55
56         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1", "GetUnit")) {
57                 const char *name;
58                 Unit *u;
59
60                 if (!dbus_message_get_args(
61                                     message,
62                                     &error,
63                                     DBUS_TYPE_STRING, &name,
64                                     DBUS_TYPE_INVALID))
65                         return bus_send_error_reply(m, message, &error, -EINVAL);
66
67                 if (!(u = manager_get_unit(m, name)))
68                         return bus_send_error_reply(m, message, NULL, -ENOENT);
69
70                 if (!(reply = dbus_message_new_method_return(message)))
71                         goto oom;
72
73                 if (!(path = unit_dbus_path(u)))
74                         goto oom;
75
76                 if (!dbus_message_append_args(
77                                     reply,
78                                     DBUS_TYPE_OBJECT_PATH, &path,
79                                     DBUS_TYPE_INVALID))
80                         goto oom;
81
82         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1", "LoadUnit")) {
83                 const char *name;
84                 Unit *u;
85
86                 if (!dbus_message_get_args(
87                                     message,
88                                     &error,
89                                     DBUS_TYPE_STRING, &name,
90                                     DBUS_TYPE_INVALID))
91                         return bus_send_error_reply(m, message, &error, -EINVAL);
92
93                 if ((r = manager_load_unit(m, name, &u)) < 0)
94                         return bus_send_error_reply(m, message, NULL, r);
95
96                 if (!(reply = dbus_message_new_method_return(message)))
97                         goto oom;
98
99                 if (!(path = unit_dbus_path(u)))
100                         goto oom;
101
102                 if (!dbus_message_append_args(
103                                     reply,
104                                     DBUS_TYPE_OBJECT_PATH, &path,
105                                     DBUS_TYPE_INVALID))
106                         goto oom;
107
108         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1",  "GetJob")) {
109                 uint32_t id;
110                 Job *j;
111
112                 if (!dbus_message_get_args(
113                                     message,
114                                     &error,
115                                     DBUS_TYPE_UINT32, &id,
116                                     DBUS_TYPE_INVALID))
117                         return bus_send_error_reply(m, message, &error, -EINVAL);
118
119                 if (!(j = manager_get_job(m, id)))
120                         return bus_send_error_reply(m, message, NULL, -ENOENT);
121
122                 if (!(reply = dbus_message_new_method_return(message)))
123                         goto oom;
124
125                 if (!(path = job_dbus_path(j)))
126                         goto oom;
127
128                 if (!dbus_message_append_args(
129                                     reply,
130                                     DBUS_TYPE_OBJECT_PATH, &path,
131                                     DBUS_TYPE_INVALID))
132                         goto oom;
133
134         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1",  "ClearJobs")) {
135
136                 manager_clear_jobs(m);
137
138                 if (!(reply = dbus_message_new_method_return(message)))
139                         goto oom;
140
141         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1",  "ListUnits")) {
142                 DBusMessageIter iter, sub;
143                 Iterator i;
144                 Unit *u;
145                 const char *k;
146
147                 if (!(reply = dbus_message_new_method_return(message)))
148                         goto oom;
149
150                 dbus_message_iter_init_append(reply, &iter);
151
152                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssouso)", &sub))
153                         goto oom;
154
155                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
156                         char *unit_path, *job_path;
157                         const char *id, *description, *load_state, *active_state, *job_type;
158                         DBusMessageIter sub2;
159                         uint32_t job_id;
160
161                         id = unit_id(u);
162                         if (k != id)
163                                 continue;
164
165                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
166                                 goto oom;
167
168                         description = unit_description(u);
169                         load_state = unit_load_state_to_string(u->meta.load_state);
170                         active_state = unit_active_state_to_string(unit_active_state(u));
171
172                         if (!(unit_path = unit_dbus_path(u)))
173                                 goto oom;
174
175                         if (u->meta.job) {
176                                 job_id = (uint32_t) u->meta.job->id;
177
178                                 if (!(job_path = job_dbus_path(u->meta.job))) {
179                                         free(unit_path);
180                                         goto oom;
181                                 }
182
183                                 job_type = job_type_to_string(u->meta.job->state);
184                         } else {
185                                 job_id = 0;
186                                 job_path = unit_path;
187                                 job_type = "";
188                         }
189
190                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &id) ||
191                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
192                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
193                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
194                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path) ||
195                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
196                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &job_type) ||
197                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path)) {
198                                 free(unit_path);
199                                 if (u->meta.job)
200                                         free(job_path);
201                                 goto oom;
202                         }
203
204                         free(unit_path);
205                         if (u->meta.job)
206                                 free(job_path);
207
208                         if (!dbus_message_iter_close_container(&sub, &sub2))
209                                 goto oom;
210                 }
211
212                 if (!dbus_message_iter_close_container(&iter, &sub))
213                         goto oom;
214
215         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1",  "ListJobs")) {
216                 DBusMessageIter iter, sub;
217                 Iterator i;
218                 Job *j;
219
220                 if (!(reply = dbus_message_new_method_return(message)))
221                         goto oom;
222
223                 dbus_message_iter_init_append(reply, &iter);
224
225                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
226                         goto oom;
227
228                 HASHMAP_FOREACH(j, m->jobs, i) {
229                         char *unit_path, *job_path;
230                         const char *unit, *state, *type;
231                         uint32_t id;
232                         DBusMessageIter sub2;
233
234                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
235                                 goto oom;
236
237                         id = (uint32_t) j->id;
238                         unit = unit_id(j->unit);
239                         state = job_state_to_string(j->state);
240                         type = job_type_to_string(j->state);
241
242                         if (!(job_path = job_dbus_path(j)))
243                                 goto oom;
244
245                         if (!(unit_path = unit_dbus_path(j->unit))) {
246                                 free(job_path);
247                                 goto oom;
248                         }
249
250                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
251                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &unit) ||
252                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
253                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
254                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path) ||
255                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path)) {
256                                 free(job_path);
257                                 free(unit_path);
258                                 goto oom;
259                         }
260
261                         free(job_path);
262                         free(unit_path);
263
264                         if (!dbus_message_iter_close_container(&sub, &sub2))
265                                 goto oom;
266                 }
267
268                 if (!dbus_message_iter_close_container(&iter, &sub))
269                         goto oom;
270
271         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
272                 char *introspection = NULL;
273                 FILE *f;
274                 Iterator i;
275                 Unit *u;
276                 Job *j;
277                 const char *k;
278                 size_t size;
279
280                 if (!(reply = dbus_message_new_method_return(message)))
281                         goto oom;
282
283                 /* We roll our own introspection code here, instead of
284                  * relying on bus_default_message_handler() because we
285                  * need to generate our introspection string
286                  * dynamically. */
287
288                 if (!(f = open_memstream(&introspection, &size)))
289                         goto oom;
290
291                 fputs(INTROSPECTION_BEGIN, f);
292
293                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
294                         char *p;
295
296                         if (k != unit_id(u))
297                                 continue;
298
299                         if (!(p = bus_path_escape(k))) {
300                                 fclose(f);
301                                 free(introspection);
302                                 goto oom;
303                         }
304
305                         fprintf(f, "<node name=\"unit/%s\"/>", p);
306                         free(p);
307                 }
308
309                 HASHMAP_FOREACH(j, m->jobs, i)
310                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
311
312                 fputs(INTROSPECTION_END, f);
313
314                 if (ferror(f)) {
315                         fclose(f);
316                         free(introspection);
317                         goto oom;
318                 }
319
320                 fclose(f);
321
322                 if (!introspection)
323                         goto oom;
324
325                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
326                         free(introspection);
327                         goto oom;
328                 }
329
330                 free(introspection);
331
332         } else
333                 return bus_default_message_handler(m, message, NULL, NULL);
334
335         free(path);
336
337         if (reply) {
338                 if (!dbus_connection_send(connection, reply, NULL))
339                         goto oom;
340
341                 dbus_message_unref(reply);
342         }
343
344         return DBUS_HANDLER_RESULT_HANDLED;
345
346 oom:
347         free(path);
348
349         if (reply)
350                 dbus_message_unref(reply);
351
352         dbus_error_free(&error);
353
354         return DBUS_HANDLER_RESULT_NEED_MEMORY;
355 }
356
357 const DBusObjectPathVTable bus_manager_vtable = {
358         .message_function = bus_manager_message_handler
359 };