]>
Commit | Line | Data |
---|---|---|
ea430986 LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
a7334b09 LP |
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 | ||
ea430986 LP |
22 | #include <errno.h> |
23 | ||
24 | #include "dbus.h" | |
25 | #include "log.h" | |
4139c1b2 | 26 | #include "dbus-manager.h" |
1137a57c | 27 | #include "strv.h" |
398ef8ba | 28 | #include "bus-errors.h" |
ea430986 | 29 | |
4288f619 LP |
30 | #define BUS_MANAGER_INTERFACE \ |
31 | " <interface name=\"org.freedesktop.systemd1.Manager\">\n" \ | |
32 | " <method name=\"GetUnit\">\n" \ | |
33 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
34 | " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ | |
35 | " </method>\n" \ | |
598b557b LP |
36 | " <method name=\"GetUnitByPID\">\n" \ |
37 | " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \ | |
38 | " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ | |
39 | " </method>\n" \ | |
4288f619 LP |
40 | " <method name=\"LoadUnit\">\n" \ |
41 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
42 | " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ | |
43 | " </method>\n" \ | |
c87eba54 LP |
44 | " <method name=\"StartUnit\">\n" \ |
45 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
46 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
47 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
48 | " </method>\n" \ | |
49 | " <method name=\"StopUnit\">\n" \ | |
50 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
51 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
52 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
53 | " </method>\n" \ | |
54 | " <method name=\"ReloadUnit\">\n" \ | |
55 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
56 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
57 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
58 | " </method>\n" \ | |
59 | " <method name=\"RestartUnit\">\n" \ | |
60 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
61 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
62 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
63 | " </method>\n" \ | |
9a1ac7b9 LP |
64 | " <method name=\"TryRestartUnit\">\n" \ |
65 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
66 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
67 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
68 | " </method>\n" \ | |
6f28c033 LP |
69 | " <method name=\"ReloadOrRestartUnit\">\n" \ |
70 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
71 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
72 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
73 | " </method>\n" \ | |
74 | " <method name=\"ReloadOrTryRestartUnit\">\n" \ | |
75 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
76 | " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ | |
77 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
78 | " </method>\n" \ | |
5632e374 LP |
79 | " <method name=\"ResetMaintenanceUnit\">\n" \ |
80 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
81 | " </method>\n" \ | |
4288f619 LP |
82 | " <method name=\"GetJob\">\n" \ |
83 | " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \ | |
84 | " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ | |
85 | " </method>\n" \ | |
86 | " <method name=\"ClearJobs\"/>\n" \ | |
5632e374 | 87 | " <method name=\"ResetMaintenance\"/>\n" \ |
4288f619 | 88 | " <method name=\"ListUnits\">\n" \ |
8fe914ec | 89 | " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \ |
4288f619 LP |
90 | " </method>\n" \ |
91 | " <method name=\"ListJobs\">\n" \ | |
92 | " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \ | |
93 | " </method>\n" \ | |
94 | " <method name=\"Subscribe\"/>\n" \ | |
95 | " <method name=\"Unsubscribe\"/>\n" \ | |
96 | " <method name=\"Dump\"/>\n" \ | |
97 | " <method name=\"CreateSnapshot\">\n" \ | |
98 | " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ | |
99 | " <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \ | |
100 | " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ | |
101 | " </method>\n" \ | |
102 | " <method name=\"Reload\"/>\n" \ | |
103 | " <method name=\"Reexecute\"/>\n" \ | |
104 | " <method name=\"Exit\"/>\n" \ | |
105 | " <method name=\"SetEnvironment\">\n" \ | |
106 | " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \ | |
107 | " </method>\n" \ | |
108 | " <method name=\"UnsetEnvironment\">\n" \ | |
109 | " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \ | |
110 | " </method>\n" \ | |
111 | " <signal name=\"UnitNew\">\n" \ | |
112 | " <arg name=\"id\" type=\"s\"/>\n" \ | |
113 | " <arg name=\"unit\" type=\"o\"/>\n" \ | |
114 | " </signal>\n" \ | |
115 | " <signal name=\"UnitRemoved\">\n" \ | |
116 | " <arg name=\"id\" type=\"s\"/>\n" \ | |
117 | " <arg name=\"unit\" type=\"o\"/>\n" \ | |
118 | " </signal>\n" \ | |
119 | " <signal name=\"JobNew\">\n" \ | |
120 | " <arg name=\"id\" type=\"u\"/>\n" \ | |
121 | " <arg name=\"job\" type=\"o\"/>\n" \ | |
122 | " </signal>\n" \ | |
123 | " <signal name=\"JobRemoved\">\n" \ | |
124 | " <arg name=\"id\" type=\"u\"/>\n" \ | |
125 | " <arg name=\"job\" type=\"o\"/>\n" \ | |
126 | " <arg name=\"success\" type=\"b\"/>\n" \ | |
127 | " </signal>" \ | |
128 | " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \ | |
129 | " <property name=\"RunningAs\" type=\"s\" access=\"read\"/>\n" \ | |
0442c13b | 130 | " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \ |
4288f619 LP |
131 | " <property name=\"LogLevel\" type=\"s\" access=\"read\"/>\n" \ |
132 | " <property name=\"LogTarget\" type=\"s\" access=\"read\"/>\n" \ | |
133 | " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \ | |
134 | " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \ | |
135 | " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \ | |
f295f5c0 | 136 | " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \ |
9e58ff9c | 137 | " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \ |
6e98720f | 138 | " <property name=\"SysVConsole\" type=\"b\" access=\"read\"/>\n" \ |
f295f5c0 LP |
139 | " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \ |
140 | " <property name=\"SysVInitPath\" type=\"as\" access=\"read\"/>\n" \ | |
141 | " <property name=\"SysVRcndPath\" type=\"as\" access=\"read\"/>\n" \ | |
142 | " <property name=\"NotifySocket\" type=\"s\" access=\"read\"/>\n" \ | |
c6c18be3 | 143 | " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \ |
4288f619 LP |
144 | " </interface>\n" |
145 | ||
ea430986 LP |
146 | #define INTROSPECTION_BEGIN \ |
147 | DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ | |
4288f619 LP |
148 | "<node>\n" \ |
149 | BUS_MANAGER_INTERFACE \ | |
ea430986 LP |
150 | BUS_PROPERTIES_INTERFACE \ |
151 | BUS_INTROSPECTABLE_INTERFACE | |
152 | ||
153 | #define INTROSPECTION_END \ | |
4288f619 LP |
154 | "</node>\n" |
155 | ||
156 | const char bus_manager_interface[] = BUS_MANAGER_INTERFACE; | |
ea430986 | 157 | |
4139c1b2 | 158 | static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs); |
1adf1049 LP |
159 | |
160 | static int bus_manager_append_log_target(Manager *m, DBusMessageIter *i, const char *property, void *data) { | |
161 | const char *t; | |
162 | ||
163 | assert(m); | |
164 | assert(i); | |
165 | assert(property); | |
166 | ||
167 | t = log_target_to_string(log_get_target()); | |
168 | ||
169 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) | |
170 | return -ENOMEM; | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | static int bus_manager_append_log_level(Manager *m, DBusMessageIter *i, const char *property, void *data) { | |
176 | const char *t; | |
177 | ||
178 | assert(m); | |
179 | assert(i); | |
180 | assert(property); | |
181 | ||
182 | t = log_level_to_string(log_get_max_level()); | |
183 | ||
184 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) | |
185 | return -ENOMEM; | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
4f0f902f LP |
190 | static int bus_manager_append_n_names(Manager *m, DBusMessageIter *i, const char *property, void *data) { |
191 | uint32_t u; | |
192 | ||
193 | assert(m); | |
194 | assert(i); | |
195 | assert(property); | |
196 | ||
197 | u = hashmap_size(m->units); | |
198 | ||
199 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u)) | |
200 | return -ENOMEM; | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | static int bus_manager_append_n_jobs(Manager *m, DBusMessageIter *i, const char *property, void *data) { | |
206 | uint32_t u; | |
207 | ||
208 | assert(m); | |
209 | assert(i); | |
210 | assert(property); | |
211 | ||
212 | u = hashmap_size(m->jobs); | |
213 | ||
214 | if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u)) | |
215 | return -ENOMEM; | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
894ba510 LP |
220 | static const char *message_get_sender_with_fallback(DBusMessage *m) { |
221 | const char *s; | |
222 | ||
223 | assert(m); | |
224 | ||
225 | if ((s = dbus_message_get_sender(m))) | |
226 | return s; | |
227 | ||
228 | /* When the message came in from a direct connection the | |
229 | * message will have no sender. We fix that here. */ | |
230 | ||
231 | return ":no-sender"; | |
232 | } | |
233 | ||
5e8d1c9a | 234 | static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { |
ea430986 | 235 | Manager *m = data; |
1adf1049 LP |
236 | |
237 | const BusProperty properties[] = { | |
f295f5c0 LP |
238 | { "org.freedesktop.systemd1.Manager", "Version", bus_property_append_string, "s", PACKAGE_STRING }, |
239 | { "org.freedesktop.systemd1.Manager", "RunningAs", bus_manager_append_running_as, "s", &m->running_as }, | |
240 | { "org.freedesktop.systemd1.Manager", "StartupTimestamp", bus_property_append_uint64, "t", &m->startup_timestamp.realtime }, | |
241 | { "org.freedesktop.systemd1.Manager", "LogLevel", bus_manager_append_log_level, "s", NULL }, | |
242 | { "org.freedesktop.systemd1.Manager", "LogTarget", bus_manager_append_log_target, "s", NULL }, | |
243 | { "org.freedesktop.systemd1.Manager", "NNames", bus_manager_append_n_names, "u", NULL }, | |
244 | { "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", NULL }, | |
245 | { "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment }, | |
246 | { "org.freedesktop.systemd1.Manager", "ConfirmSpawn", bus_property_append_bool, "b", &m->confirm_spawn }, | |
9e58ff9c | 247 | { "org.freedesktop.systemd1.Manager", "ShowStatus", bus_property_append_bool, "b", &m->show_status }, |
6e98720f | 248 | { "org.freedesktop.systemd1.Manager", "SysVConsole", bus_property_append_bool, "b", &m->sysv_console }, |
f295f5c0 LP |
249 | { "org.freedesktop.systemd1.Manager", "UnitPath", bus_property_append_strv, "as", m->lookup_paths.unit_path }, |
250 | { "org.freedesktop.systemd1.Manager", "SysVInitPath", bus_property_append_strv, "as", m->lookup_paths.sysvinit_path }, | |
251 | { "org.freedesktop.systemd1.Manager", "SysVRcndPath", bus_property_append_strv, "as", m->lookup_paths.sysvrcnd_path }, | |
252 | { "org.freedesktop.systemd1.Manager", "NotifySocket", bus_property_append_string, "s", m->notify_socket }, | |
c6c18be3 | 253 | { "org.freedesktop.systemd1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_hierarchy }, |
1adf1049 LP |
254 | { NULL, NULL, NULL, NULL, NULL } |
255 | }; | |
256 | ||
257 | int r; | |
ea430986 LP |
258 | DBusError error; |
259 | DBusMessage *reply = NULL; | |
260 | char * path = NULL; | |
c87eba54 | 261 | JobType job_type = _JOB_TYPE_INVALID; |
6f28c033 | 262 | bool reload_if_possible = false; |
ea430986 LP |
263 | |
264 | assert(connection); | |
265 | assert(message); | |
266 | assert(m); | |
267 | ||
268 | dbus_error_init(&error); | |
269 | ||
4139c1b2 | 270 | if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) { |
ea430986 LP |
271 | const char *name; |
272 | Unit *u; | |
273 | ||
274 | if (!dbus_message_get_args( | |
275 | message, | |
276 | &error, | |
277 | DBUS_TYPE_STRING, &name, | |
278 | DBUS_TYPE_INVALID)) | |
5e8d1c9a | 279 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); |
ea430986 | 280 | |
398ef8ba LP |
281 | if (!(u = manager_get_unit(m, name))) { |
282 | dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); | |
283 | return bus_send_error_reply(m, connection, message, &error, -ENOENT); | |
284 | } | |
ea430986 LP |
285 | |
286 | if (!(reply = dbus_message_new_method_return(message))) | |
287 | goto oom; | |
288 | ||
289 | if (!(path = unit_dbus_path(u))) | |
290 | goto oom; | |
291 | ||
292 | if (!dbus_message_append_args( | |
293 | reply, | |
294 | DBUS_TYPE_OBJECT_PATH, &path, | |
295 | DBUS_TYPE_INVALID)) | |
296 | goto oom; | |
598b557b | 297 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) { |
598b557b LP |
298 | Unit *u; |
299 | uint32_t pid; | |
300 | ||
301 | if (!dbus_message_get_args( | |
302 | message, | |
303 | &error, | |
304 | DBUS_TYPE_UINT32, &pid, | |
305 | DBUS_TYPE_INVALID)) | |
306 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); | |
307 | ||
308 | if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) { | |
309 | dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid); | |
310 | return bus_send_error_reply(m, connection, message, &error, -ENOENT); | |
311 | } | |
312 | ||
313 | if (!(reply = dbus_message_new_method_return(message))) | |
314 | goto oom; | |
315 | ||
316 | if (!(path = unit_dbus_path(u))) | |
317 | goto oom; | |
ea430986 | 318 | |
598b557b LP |
319 | if (!dbus_message_append_args( |
320 | reply, | |
321 | DBUS_TYPE_OBJECT_PATH, &path, | |
322 | DBUS_TYPE_INVALID)) | |
323 | goto oom; | |
4139c1b2 | 324 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) { |
ea430986 LP |
325 | const char *name; |
326 | Unit *u; | |
327 | ||
328 | if (!dbus_message_get_args( | |
329 | message, | |
330 | &error, | |
331 | DBUS_TYPE_STRING, &name, | |
332 | DBUS_TYPE_INVALID)) | |
5e8d1c9a | 333 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); |
ea430986 | 334 | |
398ef8ba LP |
335 | if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0) |
336 | return bus_send_error_reply(m, connection, message, &error, r); | |
ea430986 LP |
337 | |
338 | if (!(reply = dbus_message_new_method_return(message))) | |
339 | goto oom; | |
340 | ||
341 | if (!(path = unit_dbus_path(u))) | |
342 | goto oom; | |
343 | ||
344 | if (!dbus_message_append_args( | |
345 | reply, | |
346 | DBUS_TYPE_OBJECT_PATH, &path, | |
347 | DBUS_TYPE_INVALID)) | |
348 | goto oom; | |
349 | ||
c87eba54 LP |
350 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit")) |
351 | job_type = JOB_START; | |
352 | else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit")) | |
353 | job_type = JOB_STOP; | |
354 | else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit")) | |
355 | job_type = JOB_RELOAD; | |
356 | else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit")) | |
357 | job_type = JOB_RESTART; | |
9a1ac7b9 LP |
358 | else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit")) |
359 | job_type = JOB_TRY_RESTART; | |
6f28c033 LP |
360 | else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) { |
361 | reload_if_possible = true; | |
362 | job_type = JOB_RESTART; | |
363 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) { | |
364 | reload_if_possible = true; | |
365 | job_type = JOB_TRY_RESTART; | |
366 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) { | |
ea430986 LP |
367 | uint32_t id; |
368 | Job *j; | |
369 | ||
370 | if (!dbus_message_get_args( | |
371 | message, | |
372 | &error, | |
373 | DBUS_TYPE_UINT32, &id, | |
374 | DBUS_TYPE_INVALID)) | |
5e8d1c9a | 375 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); |
ea430986 | 376 | |
398ef8ba LP |
377 | if (!(j = manager_get_job(m, id))) { |
378 | dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); | |
379 | return bus_send_error_reply(m, connection, message, &error, -ENOENT); | |
380 | } | |
ea430986 LP |
381 | |
382 | if (!(reply = dbus_message_new_method_return(message))) | |
383 | goto oom; | |
384 | ||
385 | if (!(path = job_dbus_path(j))) | |
386 | goto oom; | |
387 | ||
388 | if (!dbus_message_append_args( | |
389 | reply, | |
390 | DBUS_TYPE_OBJECT_PATH, &path, | |
391 | DBUS_TYPE_INVALID)) | |
392 | goto oom; | |
393 | ||
4139c1b2 | 394 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) { |
ea430986 LP |
395 | |
396 | manager_clear_jobs(m); | |
397 | ||
398 | if (!(reply = dbus_message_new_method_return(message))) | |
399 | goto oom; | |
400 | ||
5632e374 LP |
401 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetMaintenance")) { |
402 | ||
403 | manager_reset_maintenance(m); | |
404 | ||
405 | if (!(reply = dbus_message_new_method_return(message))) | |
406 | goto oom; | |
407 | ||
408 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetMaintenanceUnit")) { | |
409 | const char *name; | |
410 | Unit *u; | |
411 | ||
412 | if (!dbus_message_get_args( | |
413 | message, | |
414 | &error, | |
415 | DBUS_TYPE_STRING, &name, | |
416 | DBUS_TYPE_INVALID)) | |
417 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); | |
418 | ||
419 | if (!(u = manager_get_unit(m, name))) { | |
420 | dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); | |
421 | return bus_send_error_reply(m, connection, message, &error, -ENOENT); | |
422 | } | |
423 | ||
424 | unit_reset_maintenance(u); | |
425 | ||
426 | if (!(reply = dbus_message_new_method_return(message))) | |
427 | goto oom; | |
428 | ||
4139c1b2 | 429 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) { |
ea430986 LP |
430 | DBusMessageIter iter, sub; |
431 | Iterator i; | |
432 | Unit *u; | |
433 | const char *k; | |
434 | ||
435 | if (!(reply = dbus_message_new_method_return(message))) | |
436 | goto oom; | |
437 | ||
438 | dbus_message_iter_init_append(reply, &iter); | |
439 | ||
8fe914ec | 440 | if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub)) |
ea430986 LP |
441 | goto oom; |
442 | ||
443 | HASHMAP_FOREACH_KEY(u, k, m->units, i) { | |
47be870b | 444 | char *u_path, *j_path; |
8fe914ec | 445 | const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following; |
ea430986 LP |
446 | DBusMessageIter sub2; |
447 | uint32_t job_id; | |
a7f241db | 448 | Unit *f; |
ea430986 | 449 | |
9e2f7c11 | 450 | if (k != u->meta.id) |
ea430986 LP |
451 | continue; |
452 | ||
453 | if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) | |
454 | goto oom; | |
455 | ||
456 | description = unit_description(u); | |
457 | load_state = unit_load_state_to_string(u->meta.load_state); | |
458 | active_state = unit_active_state_to_string(unit_active_state(u)); | |
10a94420 | 459 | sub_state = unit_sub_state_to_string(u); |
a7f241db LP |
460 | |
461 | f = unit_following(u); | |
462 | following = f ? f->meta.id : ""; | |
ea430986 | 463 | |
47be870b | 464 | if (!(u_path = unit_dbus_path(u))) |
ea430986 LP |
465 | goto oom; |
466 | ||
467 | if (u->meta.job) { | |
468 | job_id = (uint32_t) u->meta.job->id; | |
469 | ||
47be870b LP |
470 | if (!(j_path = job_dbus_path(u->meta.job))) { |
471 | free(u_path); | |
ea430986 LP |
472 | goto oom; |
473 | } | |
474 | ||
c87eba54 | 475 | sjob_type = job_type_to_string(u->meta.job->type); |
ea430986 LP |
476 | } else { |
477 | job_id = 0; | |
47be870b | 478 | j_path = u_path; |
c87eba54 | 479 | sjob_type = ""; |
ea430986 LP |
480 | } |
481 | ||
9e2f7c11 | 482 | if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) || |
ea430986 LP |
483 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) || |
484 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || | |
485 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || | |
10a94420 | 486 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) || |
8fe914ec | 487 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) || |
47be870b | 488 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) || |
ea430986 | 489 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) || |
c87eba54 | 490 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) || |
47be870b LP |
491 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) { |
492 | free(u_path); | |
ea430986 | 493 | if (u->meta.job) |
47be870b | 494 | free(j_path); |
ea430986 LP |
495 | goto oom; |
496 | } | |
497 | ||
47be870b | 498 | free(u_path); |
ea430986 | 499 | if (u->meta.job) |
47be870b | 500 | free(j_path); |
ea430986 LP |
501 | |
502 | if (!dbus_message_iter_close_container(&sub, &sub2)) | |
503 | goto oom; | |
504 | } | |
505 | ||
506 | if (!dbus_message_iter_close_container(&iter, &sub)) | |
507 | goto oom; | |
508 | ||
4139c1b2 | 509 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) { |
ea430986 LP |
510 | DBusMessageIter iter, sub; |
511 | Iterator i; | |
512 | Job *j; | |
513 | ||
514 | if (!(reply = dbus_message_new_method_return(message))) | |
515 | goto oom; | |
516 | ||
517 | dbus_message_iter_init_append(reply, &iter); | |
518 | ||
519 | if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub)) | |
520 | goto oom; | |
521 | ||
522 | HASHMAP_FOREACH(j, m->jobs, i) { | |
47be870b | 523 | char *u_path, *j_path; |
9e2f7c11 | 524 | const char *state, *type; |
ea430986 LP |
525 | uint32_t id; |
526 | DBusMessageIter sub2; | |
527 | ||
528 | if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) | |
529 | goto oom; | |
530 | ||
531 | id = (uint32_t) j->id; | |
ea430986 | 532 | state = job_state_to_string(j->state); |
2b53c70b | 533 | type = job_type_to_string(j->type); |
ea430986 | 534 | |
47be870b | 535 | if (!(j_path = job_dbus_path(j))) |
ea430986 LP |
536 | goto oom; |
537 | ||
47be870b LP |
538 | if (!(u_path = unit_dbus_path(j->unit))) { |
539 | free(j_path); | |
ea430986 LP |
540 | goto oom; |
541 | } | |
542 | ||
543 | if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) || | |
9e2f7c11 | 544 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) || |
ea430986 LP |
545 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || |
546 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || | |
47be870b LP |
547 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) || |
548 | !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) { | |
549 | free(j_path); | |
550 | free(u_path); | |
ea430986 LP |
551 | goto oom; |
552 | } | |
553 | ||
47be870b LP |
554 | free(j_path); |
555 | free(u_path); | |
ea430986 LP |
556 | |
557 | if (!dbus_message_iter_close_container(&sub, &sub2)) | |
558 | goto oom; | |
559 | } | |
560 | ||
561 | if (!dbus_message_iter_close_container(&iter, &sub)) | |
562 | goto oom; | |
563 | ||
4139c1b2 | 564 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) { |
c1e1601e | 565 | char *client; |
a567261a LP |
566 | Set *s; |
567 | ||
568 | if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) { | |
569 | if (!(s = set_new(string_hash_func, string_compare_func))) | |
570 | goto oom; | |
571 | ||
572 | if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) { | |
573 | set_free(s); | |
574 | goto oom; | |
575 | } | |
576 | } | |
c1e1601e | 577 | |
894ba510 | 578 | if (!(client = strdup(message_get_sender_with_fallback(message)))) |
c1e1601e LP |
579 | goto oom; |
580 | ||
a567261a LP |
581 | if ((r = set_put(s, client)) < 0) { |
582 | free(client); | |
5e8d1c9a | 583 | return bus_send_error_reply(m, connection, message, NULL, r); |
a567261a | 584 | } |
c1e1601e LP |
585 | |
586 | if (!(reply = dbus_message_new_method_return(message))) | |
587 | goto oom; | |
588 | ||
4139c1b2 | 589 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) { |
c1e1601e LP |
590 | char *client; |
591 | ||
398ef8ba LP |
592 | if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) { |
593 | dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed."); | |
594 | return bus_send_error_reply(m, connection, message, &error, -ENOENT); | |
595 | } | |
c1e1601e LP |
596 | |
597 | free(client); | |
598 | ||
599 | if (!(reply = dbus_message_new_method_return(message))) | |
600 | goto oom; | |
601 | ||
4139c1b2 | 602 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) { |
b152adec LP |
603 | FILE *f; |
604 | char *dump = NULL; | |
605 | size_t size; | |
606 | ||
607 | if (!(reply = dbus_message_new_method_return(message))) | |
608 | goto oom; | |
609 | ||
610 | if (!(f = open_memstream(&dump, &size))) | |
611 | goto oom; | |
612 | ||
613 | manager_dump_units(m, f, NULL); | |
614 | manager_dump_jobs(m, f, NULL); | |
615 | ||
616 | if (ferror(f)) { | |
617 | fclose(f); | |
618 | free(dump); | |
619 | goto oom; | |
620 | } | |
621 | ||
622 | fclose(f); | |
623 | ||
624 | if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) { | |
625 | free(dump); | |
626 | goto oom; | |
627 | } | |
628 | ||
629 | free(dump); | |
4139c1b2 LP |
630 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) { |
631 | const char *name; | |
632 | dbus_bool_t cleanup; | |
633 | Snapshot *s; | |
634 | ||
635 | if (!dbus_message_get_args( | |
636 | message, | |
637 | &error, | |
638 | DBUS_TYPE_STRING, &name, | |
639 | DBUS_TYPE_BOOLEAN, &cleanup, | |
640 | DBUS_TYPE_INVALID)) | |
5e8d1c9a | 641 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); |
4139c1b2 LP |
642 | |
643 | if (name && name[0] == 0) | |
644 | name = NULL; | |
645 | ||
398ef8ba LP |
646 | if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0) |
647 | return bus_send_error_reply(m, connection, message, &error, r); | |
4139c1b2 LP |
648 | |
649 | if (!(reply = dbus_message_new_method_return(message))) | |
650 | goto oom; | |
651 | ||
652 | if (!(path = unit_dbus_path(UNIT(s)))) | |
653 | goto oom; | |
654 | ||
655 | if (!dbus_message_append_args( | |
656 | reply, | |
657 | DBUS_TYPE_OBJECT_PATH, &path, | |
658 | DBUS_TYPE_INVALID)) | |
659 | goto oom; | |
b152adec | 660 | |
ea430986 LP |
661 | } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { |
662 | char *introspection = NULL; | |
663 | FILE *f; | |
664 | Iterator i; | |
665 | Unit *u; | |
666 | Job *j; | |
667 | const char *k; | |
668 | size_t size; | |
669 | ||
670 | if (!(reply = dbus_message_new_method_return(message))) | |
671 | goto oom; | |
672 | ||
673 | /* We roll our own introspection code here, instead of | |
674 | * relying on bus_default_message_handler() because we | |
675 | * need to generate our introspection string | |
676 | * dynamically. */ | |
677 | ||
678 | if (!(f = open_memstream(&introspection, &size))) | |
679 | goto oom; | |
680 | ||
681 | fputs(INTROSPECTION_BEGIN, f); | |
682 | ||
683 | HASHMAP_FOREACH_KEY(u, k, m->units, i) { | |
684 | char *p; | |
685 | ||
9e2f7c11 | 686 | if (k != u->meta.id) |
ea430986 LP |
687 | continue; |
688 | ||
689 | if (!(p = bus_path_escape(k))) { | |
690 | fclose(f); | |
691 | free(introspection); | |
692 | goto oom; | |
693 | } | |
694 | ||
695 | fprintf(f, "<node name=\"unit/%s\"/>", p); | |
696 | free(p); | |
697 | } | |
698 | ||
699 | HASHMAP_FOREACH(j, m->jobs, i) | |
700 | fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id); | |
701 | ||
702 | fputs(INTROSPECTION_END, f); | |
703 | ||
704 | if (ferror(f)) { | |
705 | fclose(f); | |
706 | free(introspection); | |
707 | goto oom; | |
708 | } | |
709 | ||
710 | fclose(f); | |
711 | ||
712 | if (!introspection) | |
713 | goto oom; | |
714 | ||
715 | if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { | |
716 | free(introspection); | |
717 | goto oom; | |
718 | } | |
719 | ||
720 | free(introspection); | |
721 | ||
a16e1123 LP |
722 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) { |
723 | ||
724 | assert(!m->queued_message); | |
725 | ||
726 | /* Instead of sending the reply back right away, we | |
727 | * just remember that we need to and then send it | |
728 | * after the reload is finished. That way the caller | |
729 | * knows when the reload finished. */ | |
730 | ||
731 | if (!(m->queued_message = dbus_message_new_method_return(message))) | |
732 | goto oom; | |
733 | ||
7b97f477 | 734 | m->queued_message_connection = connection; |
a16e1123 LP |
735 | m->exit_code = MANAGER_RELOAD; |
736 | ||
737 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) { | |
738 | ||
739 | if (!(reply = dbus_message_new_method_return(message))) | |
740 | goto oom; | |
741 | ||
742 | m->exit_code = MANAGER_REEXECUTE; | |
743 | ||
744 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) { | |
745 | ||
398ef8ba LP |
746 | if (m->running_as == MANAGER_SYSTEM) { |
747 | dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for session managers."); | |
748 | return bus_send_error_reply(m, connection, message, &error, -ENOTSUP); | |
749 | } | |
a16e1123 LP |
750 | |
751 | if (!(reply = dbus_message_new_method_return(message))) | |
752 | goto oom; | |
753 | ||
754 | m->exit_code = MANAGER_EXIT; | |
755 | ||
1137a57c LP |
756 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) { |
757 | char **l = NULL, **e = NULL; | |
758 | ||
759 | if ((r = bus_parse_strv(message, &l)) < 0) { | |
760 | if (r == -ENOMEM) | |
761 | goto oom; | |
762 | ||
5e8d1c9a | 763 | return bus_send_error_reply(m, connection, message, NULL, r); |
1137a57c LP |
764 | } |
765 | ||
5b6319dc | 766 | e = strv_env_merge(2, m->environment, l); |
1137a57c LP |
767 | strv_free(l); |
768 | ||
769 | if (!e) | |
770 | goto oom; | |
771 | ||
772 | if (!(reply = dbus_message_new_method_return(message))) { | |
773 | strv_free(e); | |
774 | goto oom; | |
775 | } | |
776 | ||
777 | strv_free(m->environment); | |
778 | m->environment = e; | |
779 | ||
780 | } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) { | |
781 | char **l = NULL, **e = NULL; | |
782 | ||
783 | if ((r = bus_parse_strv(message, &l)) < 0) { | |
784 | if (r == -ENOMEM) | |
785 | goto oom; | |
786 | ||
5e8d1c9a | 787 | return bus_send_error_reply(m, connection, message, NULL, r); |
1137a57c LP |
788 | } |
789 | ||
5b6319dc | 790 | e = strv_env_delete(m->environment, 1, l); |
1137a57c LP |
791 | strv_free(l); |
792 | ||
793 | if (!e) | |
794 | goto oom; | |
795 | ||
796 | if (!(reply = dbus_message_new_method_return(message))) | |
797 | goto oom; | |
798 | ||
799 | strv_free(m->environment); | |
800 | m->environment = e; | |
801 | ||
ea430986 | 802 | } else |
5e8d1c9a | 803 | return bus_default_message_handler(m, connection, message, NULL, properties); |
ea430986 | 804 | |
c87eba54 LP |
805 | if (job_type != _JOB_TYPE_INVALID) { |
806 | const char *name, *smode; | |
807 | JobMode mode; | |
808 | Job *j; | |
809 | Unit *u; | |
810 | ||
811 | if (!dbus_message_get_args( | |
812 | message, | |
813 | &error, | |
814 | DBUS_TYPE_STRING, &name, | |
815 | DBUS_TYPE_STRING, &smode, | |
816 | DBUS_TYPE_INVALID)) | |
5e8d1c9a | 817 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); |
c87eba54 | 818 | |
398ef8ba LP |
819 | if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) { |
820 | dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode); | |
821 | return bus_send_error_reply(m, connection, message, &error, -EINVAL); | |
822 | } | |
c87eba54 | 823 | |
398ef8ba LP |
824 | if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0) |
825 | return bus_send_error_reply(m, connection, message, &error, r); | |
c87eba54 | 826 | |
6f28c033 LP |
827 | if (reload_if_possible && unit_can_reload(u)) { |
828 | if (job_type == JOB_RESTART) | |
829 | job_type = JOB_RELOAD_OR_START; | |
830 | else if (job_type == JOB_TRY_RESTART) | |
831 | job_type = JOB_RELOAD; | |
832 | } | |
833 | ||
b5e9dba8 LP |
834 | if ((job_type == JOB_START && u->meta.refuse_manual_start) || |
835 | (job_type == JOB_STOP && u->meta.refuse_manual_stop) || | |
836 | ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && | |
837 | (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) { | |
838 | dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); | |
398ef8ba LP |
839 | return bus_send_error_reply(m, connection, message, &error, -EPERM); |
840 | } | |
c87eba54 | 841 | |
398ef8ba LP |
842 | if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0) |
843 | return bus_send_error_reply(m, connection, message, &error, r); | |
c87eba54 | 844 | |
894ba510 | 845 | if (!(j->bus_client = strdup(message_get_sender_with_fallback(message)))) |
a567261a LP |
846 | goto oom; |
847 | ||
848 | j->bus = connection; | |
849 | ||
c87eba54 LP |
850 | if (!(reply = dbus_message_new_method_return(message))) |
851 | goto oom; | |
852 | ||
853 | if (!(path = job_dbus_path(j))) | |
854 | goto oom; | |
855 | ||
856 | if (!dbus_message_append_args( | |
857 | reply, | |
858 | DBUS_TYPE_OBJECT_PATH, &path, | |
859 | DBUS_TYPE_INVALID)) | |
860 | goto oom; | |
861 | } | |
862 | ||
ea430986 LP |
863 | free(path); |
864 | ||
865 | if (reply) { | |
866 | if (!dbus_connection_send(connection, reply, NULL)) | |
867 | goto oom; | |
868 | ||
869 | dbus_message_unref(reply); | |
870 | } | |
871 | ||
872 | return DBUS_HANDLER_RESULT_HANDLED; | |
873 | ||
874 | oom: | |
875 | free(path); | |
876 | ||
877 | if (reply) | |
878 | dbus_message_unref(reply); | |
879 | ||
880 | dbus_error_free(&error); | |
881 | ||
882 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
883 | } | |
884 | ||
885 | const DBusObjectPathVTable bus_manager_vtable = { | |
886 | .message_function = bus_manager_message_handler | |
887 | }; |