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