]>
Commit | Line | Data |
---|---|---|
9444b1f2 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2011 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 | #include <string.h> | |
24 | ||
25 | #include "logind.h" | |
26 | #include "logind-machine.h" | |
27 | #include "dbus-common.h" | |
28 | ||
29 | #define BUS_MACHINE_INTERFACE \ | |
30 | " <interface name=\"org.freedesktop.login1.Machine\">\n" \ | |
31 | " <method name=\"Terminate\"/>\n" \ | |
32 | " <method name=\"Kill\">\n" \ | |
33 | " <arg name=\"who\" type=\"s\"/>\n" \ | |
34 | " <arg name=\"signal\" type=\"s\"/>\n" \ | |
35 | " </method>\n" \ | |
36 | " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \ | |
37 | " <property name=\"Id\" type=\"ay\" access=\"read\"/>\n" \ | |
38 | " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \ | |
39 | " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ | |
40 | " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ | |
41 | " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \ | |
42 | " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \ | |
43 | " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \ | |
44 | " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \ | |
45 | " <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \ | |
46 | " </interface>\n" | |
47 | ||
48 | #define INTROSPECTION \ | |
49 | DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ | |
50 | "<node>\n" \ | |
51 | BUS_MACHINE_INTERFACE \ | |
52 | BUS_PROPERTIES_INTERFACE \ | |
53 | BUS_PEER_INTERFACE \ | |
54 | BUS_INTROSPECTABLE_INTERFACE \ | |
55 | "</node>\n" | |
56 | ||
57 | #define INTERFACES_LIST \ | |
58 | BUS_GENERIC_INTERFACES_LIST \ | |
59 | "org.freedesktop.login1.Machine\0" | |
60 | ||
61 | static int bus_machine_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) { | |
62 | _cleanup_free_ char *t = NULL; | |
63 | Machine *m = data; | |
64 | int r; | |
65 | bool success; | |
66 | ||
67 | assert(i); | |
68 | assert(property); | |
69 | assert(m); | |
70 | ||
71 | r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &t); | |
72 | if (r < 0) | |
73 | return r; | |
74 | ||
75 | success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t); | |
76 | return success ? 0 : -ENOMEM; | |
77 | } | |
78 | ||
79 | static int bus_machine_append_id(DBusMessageIter *i, const char *property, void *data) { | |
80 | DBusMessageIter sub; | |
81 | Machine *m = data; | |
82 | dbus_bool_t b; | |
83 | void *p; | |
84 | ||
85 | assert(i); | |
86 | assert(property); | |
87 | assert(m); | |
88 | ||
89 | if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub)) | |
90 | return -ENOMEM; | |
91 | ||
92 | p = &m->id; | |
93 | b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, 16); | |
94 | if (!b) | |
95 | return -ENOMEM; | |
96 | ||
97 | if (!dbus_message_iter_close_container(i, &sub)) | |
98 | return -ENOMEM; | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static int get_machine_for_path(Manager *m, const char *path, Machine **_machine) { | |
104 | _cleanup_free_ char *e = NULL; | |
105 | Machine *machine; | |
106 | ||
107 | assert(m); | |
108 | assert(path); | |
109 | assert(_machine); | |
110 | ||
111 | if (!startswith(path, "/org/freedesktop/login1/machine/")) | |
112 | return -EINVAL; | |
113 | ||
114 | e = bus_path_unescape(path + 32); | |
115 | if (!e) | |
116 | return -ENOMEM; | |
117 | ||
118 | machine = hashmap_get(m->machines, e); | |
119 | if (!machine) | |
120 | return -ENOENT; | |
121 | ||
122 | *_machine = machine; | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_machine_append_class, machine_class, MachineClass); | |
127 | ||
128 | static const BusProperty bus_login_machine_properties[] = { | |
129 | { "Name", bus_property_append_string, "s", offsetof(Machine, name), true }, | |
130 | { "Id", bus_machine_append_id, "ay", 0 }, | |
131 | { "Timestamp", bus_property_append_usec, "t", offsetof(Machine, timestamp.realtime) }, | |
132 | { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Machine, timestamp.monotonic) }, | |
133 | { "DefaultControlGroup", bus_machine_append_default_cgroup, "s", 0 }, | |
134 | { "Service", bus_property_append_string, "s", offsetof(Machine, service), true }, | |
135 | { "Slice", bus_property_append_string, "s", offsetof(Machine, slice), true }, | |
136 | { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) }, | |
137 | { "Class", bus_machine_append_class, "s", offsetof(Machine, class) }, | |
138 | { "RootDirectory", bus_property_append_string, "s", offsetof(Machine, root_directory), true }, | |
139 | { NULL, } | |
140 | }; | |
141 | ||
142 | static DBusHandlerResult machine_message_dispatch( | |
143 | Machine *m, | |
144 | DBusConnection *connection, | |
145 | DBusMessage *message) { | |
146 | ||
147 | DBusError error; | |
148 | _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; | |
149 | int r; | |
150 | ||
151 | assert(m); | |
152 | assert(connection); | |
153 | assert(message); | |
154 | ||
155 | if (dbus_message_is_method_call(message, "org.freedesktop.login1.Machine", "Terminate")) { | |
156 | ||
157 | r = machine_stop(m); | |
158 | if (r < 0) | |
159 | return bus_send_error_reply(connection, message, NULL, r); | |
160 | ||
161 | reply = dbus_message_new_method_return(message); | |
162 | if (!reply) | |
163 | goto oom; | |
164 | ||
165 | } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Machine", "Kill")) { | |
166 | const char *swho; | |
167 | int32_t signo; | |
168 | KillWho who; | |
169 | ||
170 | if (!dbus_message_get_args( | |
171 | message, | |
172 | &error, | |
173 | DBUS_TYPE_STRING, &swho, | |
174 | DBUS_TYPE_INT32, &signo, | |
175 | DBUS_TYPE_INVALID)) | |
176 | return bus_send_error_reply(connection, message, &error, -EINVAL); | |
177 | ||
178 | if (isempty(swho)) | |
179 | who = KILL_ALL; | |
180 | else { | |
181 | who = kill_who_from_string(swho); | |
182 | if (who < 0) | |
183 | return bus_send_error_reply(connection, message, &error, -EINVAL); | |
184 | } | |
185 | ||
186 | if (signo <= 0 || signo >= _NSIG) | |
187 | return bus_send_error_reply(connection, message, &error, -EINVAL); | |
188 | ||
189 | r = machine_kill(m, who, signo); | |
190 | if (r < 0) | |
191 | return bus_send_error_reply(connection, message, NULL, r); | |
192 | ||
193 | reply = dbus_message_new_method_return(message); | |
194 | if (!reply) | |
195 | goto oom; | |
196 | ||
197 | } else { | |
198 | const BusBoundProperties bps[] = { | |
199 | { "org.freedesktop.login1.Machine", bus_login_machine_properties, m }, | |
200 | { NULL, } | |
201 | }; | |
202 | ||
203 | return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); | |
204 | } | |
205 | ||
206 | if (reply) { | |
207 | if (!bus_maybe_send_reply(connection, message, reply)) | |
208 | goto oom; | |
209 | } | |
210 | ||
211 | return DBUS_HANDLER_RESULT_HANDLED; | |
212 | ||
213 | oom: | |
214 | dbus_error_free(&error); | |
215 | ||
216 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
217 | } | |
218 | ||
219 | static DBusHandlerResult machine_message_handler( | |
220 | DBusConnection *connection, | |
221 | DBusMessage *message, | |
222 | void *userdata) { | |
223 | ||
224 | Manager *manager = userdata; | |
225 | Machine *m; | |
226 | int r; | |
227 | ||
228 | r = get_machine_for_path(manager, dbus_message_get_path(message), &m); | |
229 | if (r < 0) { | |
230 | ||
231 | if (r == -ENOMEM) | |
232 | return DBUS_HANDLER_RESULT_NEED_MEMORY; | |
233 | ||
234 | if (r == -ENOENT) { | |
235 | DBusError e; | |
236 | ||
237 | dbus_error_init(&e); | |
238 | dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown machine"); | |
239 | return bus_send_error_reply(connection, message, &e, r); | |
240 | } | |
241 | ||
242 | return bus_send_error_reply(connection, message, NULL, r); | |
243 | } | |
244 | ||
245 | return machine_message_dispatch(m, connection, message); | |
246 | } | |
247 | ||
248 | const DBusObjectPathVTable bus_machine_vtable = { | |
249 | .message_function = machine_message_handler | |
250 | }; | |
251 | ||
252 | char *machine_bus_path(Machine *m) { | |
253 | _cleanup_free_ char *e = NULL; | |
254 | ||
255 | assert(m); | |
256 | ||
257 | e = bus_path_escape(m->name); | |
258 | if (!e) | |
259 | return NULL; | |
260 | ||
261 | return strappend("/org/freedesktop/login1/machine/", e); | |
262 | } | |
263 | ||
264 | int machine_send_signal(Machine *m, bool new_machine) { | |
265 | _cleanup_dbus_message_unref_ DBusMessage *msg = NULL; | |
266 | _cleanup_free_ char *p = NULL; | |
267 | ||
268 | assert(m); | |
269 | ||
270 | msg = dbus_message_new_signal("/org/freedesktop/login1", | |
271 | "org.freedesktop.login1.Manager", | |
272 | new_machine ? "MachineNew" : "MachineRemoved"); | |
273 | ||
274 | if (!m) | |
275 | return -ENOMEM; | |
276 | ||
277 | p = machine_bus_path(m); | |
278 | if (!p) | |
279 | return -ENOMEM; | |
280 | ||
281 | if (!dbus_message_append_args( | |
282 | msg, | |
283 | DBUS_TYPE_STRING, &m->name, | |
284 | DBUS_TYPE_OBJECT_PATH, &p, | |
285 | DBUS_TYPE_INVALID)) | |
286 | return -ENOMEM; | |
287 | ||
288 | if (!dbus_connection_send(m->manager->bus, msg, NULL)) | |
289 | return -ENOMEM; | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | int machine_send_changed(Machine *m, const char *properties) { | |
295 | _cleanup_dbus_message_unref_ DBusMessage *msg = NULL; | |
296 | _cleanup_free_ char *p = NULL; | |
297 | ||
298 | assert(m); | |
299 | ||
300 | if (!m->started) | |
301 | return 0; | |
302 | ||
303 | p = machine_bus_path(m); | |
304 | if (!p) | |
305 | return -ENOMEM; | |
306 | ||
307 | msg = bus_properties_changed_new(p, "org.freedesktop.login1.Machine", properties); | |
308 | if (!msg) | |
309 | return -ENOMEM; | |
310 | ||
311 | if (!dbus_connection_send(m->manager->bus, msg, NULL)) | |
312 | return -ENOMEM; | |
313 | ||
314 | return 0; | |
315 | } |