chiark / gitweb /
dbus: expose cgroup properties in introspection everywhere
[elogind.git] / src / core / dbus-service.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23
24 #include "strv.h"
25 #include "path-util.h"
26 #include "dbus-unit.h"
27 #include "dbus-execute.h"
28 #include "dbus-kill.h"
29 #include "dbus-cgroup.h"
30 #include "dbus-common.h"
31 #include "selinux-access.h"
32 #include "dbus-service.h"
33
34 #define BUS_SERVICE_INTERFACE                                           \
35         " <interface name=\"org.freedesktop.systemd1.Service\">\n"      \
36         "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
37         "  <property name=\"Restart\" type=\"s\" access=\"read\"/>\n"   \
38         "  <property name=\"PIDFile\" type=\"s\" access=\"read\"/>\n"   \
39         "  <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \
40         "  <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \
41         "  <property name=\"TimeoutStartUSec\" type=\"t\" access=\"read\"/>\n" \
42         "  <property name=\"TimeoutStopUSec\" type=\"t\" access=\"read\"/>\n" \
43         "  <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \
44         "  <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \
45         "  <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
46         "  <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \
47         "  <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \
48         "  <property name=\"StartLimitAction\" type=\"s\" access=\"readwrite\"/>\n" \
49         BUS_EXEC_COMMAND_INTERFACE("ExecStartPre")                      \
50         BUS_EXEC_COMMAND_INTERFACE("ExecStart")                         \
51         BUS_EXEC_COMMAND_INTERFACE("ExecStartPost")                     \
52         BUS_EXEC_COMMAND_INTERFACE("ExecReload")                        \
53         BUS_EXEC_COMMAND_INTERFACE("ExecStop")                          \
54         BUS_EXEC_COMMAND_INTERFACE("ExecStopPost")                      \
55         BUS_EXEC_CONTEXT_INTERFACE                                      \
56         BUS_KILL_CONTEXT_INTERFACE                                      \
57         BUS_CGROUP_CONTEXT_INTERFACE                                    \
58         "  <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
59         "  <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
60         "  <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
61         BUS_EXEC_STATUS_INTERFACE("ExecMain")                           \
62         "  <property name=\"MainPID\" type=\"u\" access=\"read\"/>\n"   \
63         "  <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
64         "  <property name=\"BusName\" type=\"s\" access=\"read\"/>\n"   \
65         "  <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \
66         "  <property name=\"Result\" type=\"s\" access=\"read\"/>\n"    \
67        " </interface>\n"
68
69 #define INTROSPECTION                                                   \
70         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
71         "<node>\n"                                                      \
72         BUS_UNIT_INTERFACE                                              \
73         BUS_SERVICE_INTERFACE                                           \
74         BUS_PROPERTIES_INTERFACE                                        \
75         BUS_PEER_INTERFACE                                              \
76         BUS_INTROSPECTABLE_INTERFACE                                    \
77         "</node>\n"
78
79 #define INTERFACES_LIST                              \
80         BUS_UNIT_INTERFACES_LIST                     \
81         "org.freedesktop.systemd1.Service\0"
82
83 const char bus_service_interface[] _introspect_("Service") = BUS_SERVICE_INTERFACE;
84
85 const char bus_service_invalidating_properties[] =
86         "ExecStartPre\0"
87         "ExecStart\0"
88         "ExecStartPost\0"
89         "ExecReload\0"
90         "ExecStop\0"
91         "ExecStopPost\0"
92         "ExecMain\0"
93         "WatchdogTimestamp\0"
94         "WatchdogTimestampMonotonic\0"
95         "MainPID\0"
96         "ControlPID\0"
97         "StatusText\0"
98         "Result\0";
99
100 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType);
101 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart);
102 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess);
103 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult);
104 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction);
105 static DEFINE_BUS_PROPERTY_SET_ENUM(bus_service_set_start_limit_action, start_limit_action, StartLimitAction);
106
107 static const BusProperty bus_exec_main_status_properties[] = {
108         { "ExecMainStartTimestamp",         bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime)  },
109         { "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
110         { "ExecMainExitTimestamp",          bus_property_append_usec, "t", offsetof(ExecStatus, exit_timestamp.realtime)   },
111         { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, exit_timestamp.monotonic)  },
112         { "ExecMainPID",                    bus_property_append_pid,  "u", offsetof(ExecStatus, pid)                       },
113         { "ExecMainCode",                   bus_property_append_int,  "i", offsetof(ExecStatus, code)                      },
114         { "ExecMainStatus",                 bus_property_append_int,  "i", offsetof(ExecStatus, status)                    },
115         {}
116 };
117
118 static const BusProperty bus_service_properties[] = {
119         { "Type",                   bus_service_append_type,          "s", offsetof(Service, type)                         },
120         { "Restart",                bus_service_append_restart,       "s", offsetof(Service, restart)                      },
121         { "PIDFile",                bus_property_append_string,       "s", offsetof(Service, pid_file),               true },
122         { "NotifyAccess",           bus_service_append_notify_access, "s", offsetof(Service, notify_access)                },
123         { "RestartUSec",            bus_property_append_usec,         "t", offsetof(Service, restart_usec)                 },
124         { "TimeoutStartUSec",       bus_property_append_usec,         "t", offsetof(Service, timeout_start_usec)           },
125         { "TimeoutStopUSec",        bus_property_append_usec,         "t", offsetof(Service, timeout_stop_usec)            },
126         { "WatchdogUSec",           bus_property_append_usec,         "t", offsetof(Service, watchdog_usec)                },
127         { "WatchdogTimestamp",      bus_property_append_usec,         "t", offsetof(Service, watchdog_timestamp.realtime)  },
128         { "WatchdogTimestampMonotonic",bus_property_append_usec,      "t", offsetof(Service, watchdog_timestamp.monotonic) },
129         { "StartLimitInterval",     bus_property_append_usec,         "t", offsetof(Service, start_limit.interval)         },
130         { "StartLimitBurst",        bus_property_append_uint32,       "u", offsetof(Service, start_limit.burst)            },
131         { "StartLimitAction",       bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action), false, bus_service_set_start_limit_action},
132         BUS_EXEC_COMMAND_PROPERTY("ExecStartPre",  offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]),  true ),
133         BUS_EXEC_COMMAND_PROPERTY("ExecStart",     offsetof(Service, exec_command[SERVICE_EXEC_START]),      true ),
134         BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ),
135         BUS_EXEC_COMMAND_PROPERTY("ExecReload",    offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]),     true ),
136         BUS_EXEC_COMMAND_PROPERTY("ExecStop",      offsetof(Service, exec_command[SERVICE_EXEC_STOP]),       true ),
137         BUS_EXEC_COMMAND_PROPERTY("ExecStopPost",  offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]),  true ),
138         { "PermissionsStartOnly",   bus_property_append_bool,         "b", offsetof(Service, permissions_start_only)       },
139         { "RootDirectoryStartOnly", bus_property_append_bool,         "b", offsetof(Service, root_directory_start_only)    },
140         { "RemainAfterExit",        bus_property_append_bool,         "b", offsetof(Service, remain_after_exit)            },
141         { "GuessMainPID",           bus_property_append_bool,         "b", offsetof(Service, guess_main_pid)               },
142         { "MainPID",                bus_property_append_pid,          "u", offsetof(Service, main_pid)                     },
143         { "ControlPID",             bus_property_append_pid,          "u", offsetof(Service, control_pid)                  },
144         { "BusName",                bus_property_append_string,       "s", offsetof(Service, bus_name),               true },
145         { "StatusText",             bus_property_append_string,       "s", offsetof(Service, status_text),            true },
146         { "Result",                 bus_service_append_service_result,"s", offsetof(Service, result)                       },
147         {}
148 };
149
150 DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) {
151         Service *s = SERVICE(u);
152
153         const BusBoundProperties bps[] = {
154                 { "org.freedesktop.systemd1.Unit",    bus_unit_properties,             u },
155                 { "org.freedesktop.systemd1.Service", bus_service_properties,          s },
156                 { "org.freedesktop.systemd1.Service", bus_exec_context_properties,     &s->exec_context },
157                 { "org.freedesktop.systemd1.Service", bus_kill_context_properties,     &s->kill_context },
158                 { "org.freedesktop.systemd1.Service", bus_cgroup_context_properties,   &s->cgroup_context },
159                 { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
160                 {}
161         };
162
163         SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
164
165         return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
166 }
167
168 static int bus_service_set_transient_properties(
169                 Service *s,
170                 const char *name,
171                 DBusMessageIter *i,
172                 UnitSetPropertiesMode mode,
173                 DBusError *error) {
174
175         int r;
176
177         assert(name);
178         assert(s);
179         assert(i);
180
181         if (streq(name, "ExecStart")) {
182                 DBusMessageIter sub;
183                 unsigned n = 0;
184
185                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
186                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
187                         return -EINVAL;
188
189                 dbus_message_iter_recurse(i, &sub);
190                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
191                         _cleanup_strv_free_ char **argv = NULL;
192                         DBusMessageIter sub2;
193                         dbus_bool_t ignore;
194                         const char *path;
195
196                         dbus_message_iter_recurse(&sub, &sub2);
197
198                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
199                                 return -EINVAL;
200
201                         if (!path_is_absolute(path)) {
202                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
203                                 return -EINVAL;
204                         }
205
206                         r = bus_parse_strv_iter(&sub2, &argv);
207                         if (r < 0)
208                                 return r;
209
210                         dbus_message_iter_next(&sub2);
211
212                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) < 0)
213                                 return -EINVAL;
214
215                         if (mode != UNIT_CHECK) {
216                                 ExecCommand *c;
217
218                                 c = new0(ExecCommand, 1);
219                                 if (!c)
220                                         return -ENOMEM;
221
222                                 c->path = strdup(path);
223                                 if (!c->path) {
224                                         free(c);
225                                         return -ENOMEM;
226                                 }
227
228                                 c->argv = argv;
229                                 argv = NULL;
230
231                                 c->ignore = ignore;
232
233                                 path_kill_slashes(c->path);
234                                 exec_command_append_list(&s->exec_command[SERVICE_EXEC_START], c);
235                         }
236
237                         n++;
238                         dbus_message_iter_next(&sub);
239                 }
240
241                 if (mode != UNIT_CHECK) {
242                         _cleanup_free_ char *buf = NULL;
243                         _cleanup_fclose_ FILE *f = NULL;
244                         ExecCommand *c;
245                         size_t size = 0;
246
247                         if (n == 0) {
248                                 exec_command_free_list(s->exec_command[SERVICE_EXEC_START]);
249                                 s->exec_command[SERVICE_EXEC_START] = NULL;
250                         }
251
252                         f = open_memstream(&buf, &size);
253                         if (!f)
254                                 return -ENOMEM;
255
256                         fputs("ExecStart=\n", f);
257
258                         LIST_FOREACH(command, c, s->exec_command[SERVICE_EXEC_START]) {
259                                 char **a;
260                                 fputs("ExecStart=", f);
261
262                                 if (c->ignore)
263                                         fputc('-', f);
264
265                                 fputc('@', f);
266                                 fputs(c->path, f);
267
268                                 STRV_FOREACH(a, c->argv) {
269                                         fputc(' ', f);
270                                         fputs(*a, f);
271                                 }
272
273                                 fputc('\n', f);
274                         }
275
276                         fflush(f);
277                         unit_write_drop_in_private_section(UNIT(s), mode, "exec-start", buf);
278                 }
279
280                 return 1;
281         }
282
283         return 0;
284 }
285
286 int bus_service_set_property(
287                 Unit *u,
288                 const char *name,
289                 DBusMessageIter *i,
290                 UnitSetPropertiesMode mode,
291                 DBusError *error) {
292
293         Service *s = SERVICE(u);
294         int r;
295
296         assert(name);
297         assert(u);
298         assert(i);
299
300         r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
301         if (r != 0)
302                 return r;
303
304         if (u->transient && u->load_state == UNIT_STUB) {
305                 /* This is a transient unit, let's load a little more */
306
307                 r = bus_service_set_transient_properties(s, name, i, mode, error);
308                 if (r != 0)
309                         return r;
310         }
311
312         return 0;
313 }
314
315 int bus_service_commit_properties(Unit *u) {
316         assert(u);
317
318         unit_realize_cgroup(u);
319         return 0;
320 }