]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-service.c
dbus: expose cgroup properties in introspection everywhere
[thirdparty/systemd.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 }