chiark / gitweb /
unit: deduce following unit value dynamically instead of statically, to avoid danglin...
[elogind.git] / src / 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 #include "dbus-unit.h"
27 #include "bus-errors.h"
28
29 const char bus_unit_interface[] = BUS_UNIT_INTERFACE;
30
31 int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
32         char *t;
33         Iterator j;
34         DBusMessageIter sub;
35         Unit *u = data;
36
37         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
38                 return -ENOMEM;
39
40         SET_FOREACH(t, u->meta.names, j)
41                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
42                         return -ENOMEM;
43
44         if (!dbus_message_iter_close_container(i, &sub))
45                 return -ENOMEM;
46
47         return 0;
48 }
49
50 int bus_unit_append_following(Manager *m, DBusMessageIter *i, const char *property, void *data) {
51         Unit *u = data, *f;
52         const char *d;
53
54         assert(m);
55         assert(i);
56         assert(property);
57         assert(u);
58
59         f = unit_following(u);
60         d = f ? f->meta.id : "";
61
62         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
63                 return -ENOMEM;
64
65         return 0;
66 }
67
68 int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data) {
69         Unit *u;
70         Iterator j;
71         DBusMessageIter sub;
72         Set *s = data;
73
74         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
75                 return -ENOMEM;
76
77         SET_FOREACH(u, s, j)
78                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->meta.id))
79                         return -ENOMEM;
80
81         if (!dbus_message_iter_close_container(i, &sub))
82                 return -ENOMEM;
83
84         return 0;
85 }
86
87 int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data) {
88         Unit *u = data;
89         const char *d;
90
91         assert(m);
92         assert(i);
93         assert(property);
94         assert(u);
95
96         d = unit_description(u);
97
98         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
99                 return -ENOMEM;
100
101         return 0;
102 }
103
104 DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
105
106 int bus_unit_append_active_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
107         Unit *u = data;
108         const char *state;
109
110         assert(m);
111         assert(i);
112         assert(property);
113         assert(u);
114
115         state = unit_active_state_to_string(unit_active_state(u));
116
117         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
118                 return -ENOMEM;
119
120         return 0;
121 }
122
123 int bus_unit_append_sub_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
124         Unit *u = data;
125         const char *state;
126
127         assert(m);
128         assert(i);
129         assert(property);
130         assert(u);
131
132         state = unit_sub_state_to_string(u);
133
134         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
135                 return -ENOMEM;
136
137         return 0;
138 }
139
140 int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *property, void *data) {
141         Unit *u = data;
142         dbus_bool_t b;
143
144         assert(m);
145         assert(i);
146         assert(property);
147         assert(u);
148
149         b = unit_can_start(u) &&
150                 !u->meta.only_by_dependency;
151
152         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
153                 return -ENOMEM;
154
155         return 0;
156 }
157
158 int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) {
159         Unit *u = data;
160         dbus_bool_t b;
161
162         assert(m);
163         assert(i);
164         assert(property);
165         assert(u);
166
167         b = unit_can_reload(u);
168
169         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
170                 return -ENOMEM;
171
172         return 0;
173 }
174
175 int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data) {
176         Unit *u = data;
177         DBusMessageIter sub;
178         char *p;
179
180         assert(m);
181         assert(i);
182         assert(property);
183         assert(u);
184
185         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
186                 return -ENOMEM;
187
188         if (u->meta.job) {
189
190                 if (!(p = job_dbus_path(u->meta.job)))
191                         return -ENOMEM;
192
193                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->meta.job->id) ||
194                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
195                         free(p);
196                         return -ENOMEM;
197                 }
198         } else {
199                 uint32_t id = 0;
200
201                 /* No job, so let's fill in some placeholder
202                  * data. Since we need to fill in a valid path we
203                  * simple point to ourselves. */
204
205                 if (!(p = unit_dbus_path(u)))
206                         return -ENOMEM;
207
208                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
209                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
210                         free(p);
211                         return -ENOMEM;
212                 }
213         }
214
215         free(p);
216
217         if (!dbus_message_iter_close_container(i, &sub))
218                 return -ENOMEM;
219
220         return 0;
221 }
222
223 int bus_unit_append_default_cgroup(Manager *m, DBusMessageIter *i, const char *property, void *data) {
224         Unit *u = data;
225         char *t;
226         CGroupBonding *cgb;
227         bool success;
228
229         assert(m);
230         assert(i);
231         assert(property);
232         assert(u);
233
234         if ((cgb = unit_get_default_cgroup(u))) {
235                 if (!(t = cgroup_bonding_to_string(cgb)))
236                         return -ENOMEM;
237         } else
238                 t = (char*) "";
239
240         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
241
242         if (cgb)
243                 free(t);
244
245         return success ? 0 : -ENOMEM;
246 }
247
248 int bus_unit_append_cgroups(Manager *m, DBusMessageIter *i, const char *property, void *data) {
249         Unit *u = data;
250         CGroupBonding *cgb;
251         DBusMessageIter sub;
252
253         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
254                 return -ENOMEM;
255
256         LIST_FOREACH(by_unit, cgb, u->meta.cgroup_bondings) {
257                 char *t;
258                 bool success;
259
260                 if (!(t = cgroup_bonding_to_string(cgb)))
261                         return -ENOMEM;
262
263                 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
264                 free(t);
265
266                 if (!success)
267                         return -ENOMEM;
268         }
269
270         if (!dbus_message_iter_close_container(i, &sub))
271                 return -ENOMEM;
272
273         return 0;
274 }
275
276 int bus_unit_append_need_daemon_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) {
277         Unit *u = data;
278         dbus_bool_t b;
279
280         assert(m);
281         assert(i);
282         assert(property);
283         assert(u);
284
285         b = unit_need_daemon_reload(u);
286
287         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
288                 return -ENOMEM;
289
290         return 0;
291 }
292
293 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
294         DBusMessage *reply = NULL;
295         Manager *m = u->meta.manager;
296         DBusError error;
297         JobType job_type = _JOB_TYPE_INVALID;
298         char *path = NULL;
299         bool reload_if_possible = false;
300
301         dbus_error_init(&error);
302
303         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
304                 job_type = JOB_START;
305         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
306                 job_type = JOB_STOP;
307         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
308                 job_type = JOB_RELOAD;
309         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
310                 job_type = JOB_RESTART;
311         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
312                 job_type = JOB_TRY_RESTART;
313         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
314                 reload_if_possible = true;
315                 job_type = JOB_RESTART;
316         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
317                 reload_if_possible = true;
318                 job_type = JOB_TRY_RESTART;
319         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetMaintenance")) {
320
321                 unit_reset_maintenance(u);
322
323                 if (!(reply = dbus_message_new_method_return(message)))
324                         goto oom;
325
326         } else if (UNIT_VTABLE(u)->bus_message_handler)
327                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
328         else
329                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
330
331         if (job_type != _JOB_TYPE_INVALID) {
332                 const char *smode;
333                 JobMode mode;
334                 Job *j;
335                 int r;
336
337                 if (job_type == JOB_START && u->meta.only_by_dependency) {
338                         dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Unit may be activated by dependency only.");
339                         return bus_send_error_reply(m, connection, message, &error, -EPERM);
340                 }
341
342                 if (!dbus_message_get_args(
343                                     message,
344                                     &error,
345                                     DBUS_TYPE_STRING, &smode,
346                                     DBUS_TYPE_INVALID))
347                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
348
349                 if (reload_if_possible && unit_can_reload(u)) {
350                         if (job_type == JOB_RESTART)
351                                 job_type = JOB_RELOAD_OR_START;
352                         else if (job_type == JOB_TRY_RESTART)
353                                 job_type = JOB_RELOAD;
354                 }
355
356                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
357                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
358                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
359                 }
360
361                 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
362                         return bus_send_error_reply(m, connection, message, &error, r);
363
364                 if (!(reply = dbus_message_new_method_return(message)))
365                         goto oom;
366
367                 if (!(path = job_dbus_path(j)))
368                         goto oom;
369
370                 if (!dbus_message_append_args(
371                                     reply,
372                                     DBUS_TYPE_OBJECT_PATH, &path,
373                                     DBUS_TYPE_INVALID))
374                         goto oom;
375         }
376
377         free(path);
378
379         if (reply) {
380                 if (!dbus_connection_send(connection, reply, NULL))
381                         goto oom;
382
383                 dbus_message_unref(reply);
384         }
385
386         return DBUS_HANDLER_RESULT_HANDLED;
387
388 oom:
389         free(path);
390
391         if (reply)
392                 dbus_message_unref(reply);
393
394         dbus_error_free(&error);
395
396         return DBUS_HANDLER_RESULT_NEED_MEMORY;
397 }
398
399 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
400         Manager *m = data;
401         Unit *u;
402         int r;
403
404         assert(connection);
405         assert(message);
406         assert(m);
407
408         if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
409
410                 if (r == -ENOMEM)
411                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
412
413                 if (r == -ENOENT)
414                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
415
416                 return bus_send_error_reply(m, connection, message, NULL, r);
417         }
418
419         return bus_unit_message_dispatch(u, connection, message);
420 }
421
422 const DBusObjectPathVTable bus_unit_vtable = {
423         .message_function = bus_unit_message_handler
424 };
425
426 void bus_unit_send_change_signal(Unit *u) {
427         char *p = NULL;
428         DBusMessage *m = NULL;
429
430         assert(u);
431
432         if (u->meta.in_dbus_queue) {
433                 LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
434                 u->meta.in_dbus_queue = false;
435         }
436
437         if (!bus_has_subscriber(u->meta.manager)) {
438                 u->meta.sent_dbus_new_signal = true;
439                 return;
440         }
441
442         if (!(p = unit_dbus_path(u)))
443                 goto oom;
444
445         if (u->meta.sent_dbus_new_signal) {
446                 /* Send a change signal */
447
448                 if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed")))
449                         goto oom;
450         } else {
451                 /* Send a new signal */
452
453                 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
454                         goto oom;
455
456                 if (!dbus_message_append_args(m,
457                                               DBUS_TYPE_STRING, &u->meta.id,
458                                               DBUS_TYPE_OBJECT_PATH, &p,
459                                               DBUS_TYPE_INVALID))
460                         goto oom;
461         }
462
463         if (bus_broadcast(u->meta.manager, m) < 0)
464                 goto oom;
465
466         free(p);
467         dbus_message_unref(m);
468
469         u->meta.sent_dbus_new_signal = true;
470
471         return;
472
473 oom:
474         free(p);
475
476         if (m)
477                 dbus_message_unref(m);
478
479         log_error("Failed to allocate unit change/new signal.");
480 }
481
482 void bus_unit_send_removed_signal(Unit *u) {
483         char *p = NULL;
484         DBusMessage *m = NULL;
485
486         assert(u);
487
488         if (!bus_has_subscriber(u->meta.manager))
489                 return;
490
491         if (!u->meta.sent_dbus_new_signal)
492                 bus_unit_send_change_signal(u);
493
494         if (!(p = unit_dbus_path(u)))
495                 goto oom;
496
497         if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
498                 goto oom;
499
500         if (!dbus_message_append_args(m,
501                                       DBUS_TYPE_STRING, &u->meta.id,
502                                       DBUS_TYPE_OBJECT_PATH, &p,
503                                       DBUS_TYPE_INVALID))
504                 goto oom;
505
506         if (bus_broadcast(u->meta.manager, m) < 0)
507                 goto oom;
508
509         free(p);
510         dbus_message_unref(m);
511
512         return;
513
514 oom:
515         free(p);
516
517         if (m)
518                 dbus_message_unref(m);
519
520         log_error("Failed to allocate unit remove signal.");
521 }