chiark / gitweb /
core: open up SendSIGHUP property for transient units
[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, "RemainAfterExit")) {
184                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
185                         return -EINVAL;
186
187                 if (mode != UNIT_CHECK) {
188                         dbus_bool_t b;
189
190                         dbus_message_iter_get_basic(i, &b);
191
192                         s->remain_after_exit = b;
193                         unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s\n", yes_no(b));
194                 }
195
196                 return 1;
197
198         } else if (streq(name, "ExecStart")) {
199                 DBusMessageIter sub;
200                 unsigned n = 0;
201
202                 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
203                     dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
204                         return -EINVAL;
205
206                 dbus_message_iter_recurse(i, &sub);
207                 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
208                         _cleanup_strv_free_ char **argv = NULL;
209                         DBusMessageIter sub2;
210                         dbus_bool_t ignore;
211                         const char *path;
212
213                         dbus_message_iter_recurse(&sub, &sub2);
214
215                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
216                                 return -EINVAL;
217
218                         if (!path_is_absolute(path)) {
219                                 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
220                                 return -EINVAL;
221                         }
222
223                         r = bus_parse_strv_iter(&sub2, &argv);
224                         if (r < 0)
225                                 return r;
226
227                         dbus_message_iter_next(&sub2);
228
229                         if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) < 0)
230                                 return -EINVAL;
231
232                         if (mode != UNIT_CHECK) {
233                                 ExecCommand *c;
234
235                                 c = new0(ExecCommand, 1);
236                                 if (!c)
237                                         return -ENOMEM;
238
239                                 c->path = strdup(path);
240                                 if (!c->path) {
241                                         free(c);
242                                         return -ENOMEM;
243                                 }
244
245                                 c->argv = argv;
246                                 argv = NULL;
247
248                                 c->ignore = ignore;
249
250                                 path_kill_slashes(c->path);
251                                 exec_command_append_list(&s->exec_command[SERVICE_EXEC_START], c);
252                         }
253
254                         n++;
255                         dbus_message_iter_next(&sub);
256                 }
257
258                 if (mode != UNIT_CHECK) {
259                         _cleanup_free_ char *buf = NULL;
260                         _cleanup_fclose_ FILE *f = NULL;
261                         ExecCommand *c;
262                         size_t size = 0;
263
264                         if (n == 0) {
265                                 exec_command_free_list(s->exec_command[SERVICE_EXEC_START]);
266                                 s->exec_command[SERVICE_EXEC_START] = NULL;
267                         }
268
269                         f = open_memstream(&buf, &size);
270                         if (!f)
271                                 return -ENOMEM;
272
273                         fputs("ExecStart=\n", f);
274
275                         LIST_FOREACH(command, c, s->exec_command[SERVICE_EXEC_START]) {
276                                 char **a;
277                                 fputs("ExecStart=", f);
278
279                                 if (c->ignore)
280                                         fputc('-', f);
281
282                                 fputc('@', f);
283                                 fputs(c->path, f);
284
285                                 STRV_FOREACH(a, c->argv) {
286                                         fputc(' ', f);
287                                         fputs(*a, f);
288                                 }
289
290                                 fputc('\n', f);
291                         }
292
293                         fflush(f);
294                         unit_write_drop_in_private(UNIT(s), mode, name, buf);
295                 }
296
297                 return 1;
298         }
299
300         return 0;
301 }
302
303 int bus_service_set_property(
304                 Unit *u,
305                 const char *name,
306                 DBusMessageIter *i,
307                 UnitSetPropertiesMode mode,
308                 DBusError *error) {
309
310         Service *s = SERVICE(u);
311         int r;
312
313         assert(name);
314         assert(u);
315         assert(i);
316
317         r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
318         if (r != 0)
319                 return r;
320
321         if (u->transient && u->load_state == UNIT_STUB) {
322                 /* This is a transient unit, let's load a little more */
323
324                 r = bus_service_set_transient_property(s, name, i, mode, error);
325                 if (r != 0)
326                         return r;
327
328                 r = bus_kill_context_set_transient_property(u, &s->kill_context, name, i, mode, error);
329                 if (r != 0)
330                         return r;
331         }
332
333         return 0;
334 }
335
336 int bus_service_commit_properties(Unit *u) {
337         assert(u);
338
339         unit_realize_cgroup(u);
340         return 0;
341 }