]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a7334b09 | 2 | |
718db961 | 3 | #include "sd-bus.h" |
07630cea | 4 | |
b5efdb8a | 5 | #include "alloc-util.h" |
40af3d02 | 6 | #include "bus-get-properties.h" |
c0f765ca | 7 | #include "bus-util.h" |
b5efdb8a | 8 | #include "dbus-job.h" |
17407bc2 | 9 | #include "dbus-unit.h" |
48b92b37 | 10 | #include "dbus-util.h" |
07630cea | 11 | #include "dbus.h" |
718db961 | 12 | #include "job.h" |
07630cea LP |
13 | #include "log.h" |
14 | #include "selinux-access.h" | |
15 | #include "string-util.h" | |
5cfa33e0 | 16 | #include "strv.h" |
718db961 LP |
17 | |
18 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType); | |
19 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState); | |
20 | ||
21 | static int property_get_unit( | |
22 | sd_bus *bus, | |
23 | const char *path, | |
24 | const char *interface, | |
25 | const char *property, | |
26 | sd_bus_message *reply, | |
ebcf1f97 LP |
27 | void *userdata, |
28 | sd_bus_error *error) { | |
ea430986 | 29 | |
68eda4bd | 30 | _cleanup_free_ char *p = NULL; |
99534007 | 31 | Job *j = ASSERT_PTR(userdata); |
86fbf370 | 32 | |
718db961 LP |
33 | assert(bus); |
34 | assert(reply); | |
86fbf370 | 35 | |
cad45ba1 LP |
36 | p = unit_dbus_path(j->unit); |
37 | if (!p) | |
86fbf370 LP |
38 | return -ENOMEM; |
39 | ||
718db961 | 40 | return sd_bus_message_append(reply, "(so)", j->unit->id, p); |
86fbf370 LP |
41 | } |
42 | ||
19070062 | 43 | int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
99534007 | 44 | Job *j = ASSERT_PTR(userdata); |
ebcf1f97 | 45 | int r; |
ea430986 | 46 | |
ea430986 | 47 | assert(message); |
86fbf370 | 48 | |
8a188de9 | 49 | r = mac_selinux_unit_access_check(j->unit, message, "stop", error); |
ebcf1f97 LP |
50 | if (r < 0) |
51 | return r; | |
52 | ||
1d22e906 | 53 | /* Access is granted to the job owner */ |
1a465207 | 54 | if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) { |
1d22e906 | 55 | |
d35f51ea | 56 | /* And for everybody else consult polkit */ |
1d22e906 LP |
57 | r = bus_verify_manage_units_async(j->unit->manager, message, error); |
58 | if (r < 0) | |
59 | return r; | |
60 | if (r == 0) | |
61 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
62 | } | |
63 | ||
833f92ad | 64 | job_finish_and_invalidate(j, JOB_CANCELED, true, false); |
2cccbca4 | 65 | |
df2d202e | 66 | return sd_bus_reply_method_return(message, NULL); |
ea430986 LP |
67 | } |
68 | ||
15ea79f8 LP |
69 | int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
70 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
71 | _cleanup_free_ Job **list = NULL; | |
72 | Job *j = userdata; | |
fe96c0f8 | 73 | int r, n; |
15ea79f8 LP |
74 | |
75 | if (strstr(sd_bus_message_get_member(message), "After")) | |
76 | n = job_get_after(j, &list); | |
77 | else | |
78 | n = job_get_before(j, &list); | |
79 | if (n < 0) | |
80 | return n; | |
81 | ||
82 | r = sd_bus_message_new_method_return(message, &reply); | |
83 | if (r < 0) | |
84 | return r; | |
85 | ||
86 | r = sd_bus_message_open_container(reply, 'a', "(usssoo)"); | |
87 | if (r < 0) | |
88 | return r; | |
89 | ||
fe96c0f8 | 90 | for (int i = 0; i < n; i ++) { |
15ea79f8 LP |
91 | _cleanup_free_ char *unit_path = NULL, *job_path = NULL; |
92 | ||
93 | job_path = job_dbus_path(list[i]); | |
94 | if (!job_path) | |
95 | return -ENOMEM; | |
96 | ||
97 | unit_path = unit_dbus_path(list[i]->unit); | |
98 | if (!unit_path) | |
99 | return -ENOMEM; | |
100 | ||
101 | r = sd_bus_message_append(reply, "(usssoo)", | |
102 | list[i]->id, | |
103 | list[i]->unit->id, | |
104 | job_type_to_string(list[i]->type), | |
105 | job_state_to_string(list[i]->state), | |
106 | job_path, | |
107 | unit_path); | |
108 | if (r < 0) | |
109 | return r; | |
110 | } | |
111 | ||
112 | r = sd_bus_message_close_container(reply); | |
113 | if (r < 0) | |
114 | return r; | |
115 | ||
116 | return sd_bus_send(NULL, reply, NULL); | |
117 | } | |
118 | ||
718db961 LP |
119 | const sd_bus_vtable bus_job_vtable[] = { |
120 | SD_BUS_VTABLE_START(0), | |
dad97f04 | 121 | |
283868e1 | 122 | SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), |
f4ca32a1 CBI |
123 | SD_BUS_METHOD_WITH_ARGS("GetAfter", |
124 | SD_BUS_NO_ARGS, | |
125 | SD_BUS_RESULT("a(usssoo)", jobs), | |
dad97f04 ZJS |
126 | bus_job_method_get_waiting_jobs, |
127 | SD_BUS_VTABLE_UNPRIVILEGED), | |
f4ca32a1 CBI |
128 | SD_BUS_METHOD_WITH_ARGS("GetBefore", |
129 | SD_BUS_NO_ARGS, | |
130 | SD_BUS_RESULT("a(usssoo)", jobs), | |
dad97f04 ZJS |
131 | bus_job_method_get_waiting_jobs, |
132 | SD_BUS_VTABLE_UNPRIVILEGED), | |
133 | ||
556089dc LP |
134 | SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST), |
135 | SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), | |
136 | SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST), | |
718db961 | 137 | SD_BUS_PROPERTY("State", "s", property_get_state, offsetof(Job, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
48b92b37 | 138 | SD_BUS_PROPERTY("ActivationDetails", "a(ss)", bus_property_get_activation_details, offsetof(Job, activation_details), SD_BUS_VTABLE_PROPERTY_CONST), |
718db961 | 139 | SD_BUS_VTABLE_END |
ea430986 | 140 | }; |
c1e1601e | 141 | |
f6e9aa9e | 142 | static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { |
99534007 | 143 | Manager *m = ASSERT_PTR(userdata); |
f6e9aa9e ZJS |
144 | Job *j; |
145 | int r; | |
146 | ||
147 | assert(bus); | |
148 | assert(path); | |
149 | assert(interface); | |
150 | assert(found); | |
f6e9aa9e ZJS |
151 | |
152 | r = manager_get_job_from_dbus_path(m, path, &j); | |
153 | if (r < 0) | |
154 | return 0; | |
155 | ||
156 | *found = j; | |
157 | return 1; | |
158 | } | |
159 | ||
160 | static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { | |
161 | _cleanup_strv_free_ char **l = NULL; | |
162 | Manager *m = userdata; | |
163 | unsigned k = 0; | |
f6e9aa9e ZJS |
164 | Job *j; |
165 | ||
166 | l = new0(char*, hashmap_size(m->jobs)+1); | |
167 | if (!l) | |
168 | return -ENOMEM; | |
169 | ||
90e74a66 | 170 | HASHMAP_FOREACH(j, m->jobs) { |
f6e9aa9e ZJS |
171 | l[k] = job_dbus_path(j); |
172 | if (!l[k]) | |
173 | return -ENOMEM; | |
174 | ||
175 | k++; | |
176 | } | |
177 | ||
178 | assert(hashmap_size(m->jobs) == k); | |
179 | ||
180 | *nodes = TAKE_PTR(l); | |
181 | ||
182 | return k; | |
183 | } | |
184 | ||
185 | const BusObjectImplementation job_object = { | |
186 | "/org/freedesktop/systemd1/job", | |
187 | "org.freedesktop.systemd1.Job", | |
188 | .fallback_vtables = BUS_FALLBACK_VTABLES({bus_job_vtable, bus_job_find}), | |
189 | .node_enumerator = bus_job_enumerate, | |
190 | }; | |
191 | ||
8f8f05a9 | 192 | static int send_new_signal(sd_bus *bus, void *userdata) { |
4afd3348 | 193 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; |
1508e858 | 194 | _cleanup_free_ char *p = NULL; |
99534007 | 195 | Job *j = ASSERT_PTR(userdata); |
718db961 LP |
196 | int r; |
197 | ||
198 | assert(bus); | |
c1e1601e | 199 | |
97e6a119 MS |
200 | p = job_dbus_path(j); |
201 | if (!p) | |
718db961 | 202 | return -ENOMEM; |
c1e1601e | 203 | |
718db961 LP |
204 | r = sd_bus_message_new_signal( |
205 | bus, | |
151b9b96 | 206 | &m, |
718db961 LP |
207 | "/org/freedesktop/systemd1", |
208 | "org.freedesktop.systemd1.Manager", | |
151b9b96 | 209 | "JobNew"); |
718db961 LP |
210 | if (r < 0) |
211 | return r; | |
212 | ||
213 | r = sd_bus_message_append(m, "uos", j->id, p, j->unit->id); | |
214 | if (r < 0) | |
215 | return r; | |
216 | ||
8f8f05a9 | 217 | return sd_bus_send(bus, m, NULL); |
c1e1601e LP |
218 | } |
219 | ||
8f8f05a9 | 220 | static int send_changed_signal(sd_bus *bus, void *userdata) { |
1508e858 | 221 | _cleanup_free_ char *p = NULL; |
99534007 | 222 | Job *j = ASSERT_PTR(userdata); |
718db961 LP |
223 | |
224 | assert(bus); | |
c1e1601e | 225 | |
97e6a119 MS |
226 | p = job_dbus_path(j); |
227 | if (!p) | |
718db961 | 228 | return -ENOMEM; |
c1e1601e | 229 | |
718db961 | 230 | return sd_bus_emit_properties_changed(bus, p, "org.freedesktop.systemd1.Job", "State", NULL); |
97e6a119 MS |
231 | } |
232 | ||
233 | void bus_job_send_change_signal(Job *j) { | |
718db961 LP |
234 | int r; |
235 | ||
97e6a119 MS |
236 | assert(j); |
237 | ||
17407bc2 LP |
238 | /* Make sure that any change signal on the unit is reflected before we send out the change signal on the job */ |
239 | bus_unit_send_pending_change_signal(j->unit, true); | |
240 | ||
97e6a119 | 241 | if (j->in_dbus_queue) { |
71fda00f | 242 | LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); |
97e6a119 | 243 | j->in_dbus_queue = false; |
af05bb97 LP |
244 | |
245 | /* The job might be good to be GC once its pending signals have been sent */ | |
246 | job_add_to_gc_queue(j); | |
97e6a119 MS |
247 | } |
248 | ||
1a465207 | 249 | r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); |
718db961 | 250 | if (r < 0) |
da927ba9 | 251 | log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id); |
97e6a119 MS |
252 | |
253 | j->sent_dbus_new_signal = true; | |
718db961 LP |
254 | } |
255 | ||
13142276 LP |
256 | void bus_job_send_pending_change_signal(Job *j, bool including_new) { |
257 | assert(j); | |
258 | ||
259 | if (!j->in_dbus_queue) | |
260 | return; | |
261 | ||
262 | if (!j->sent_dbus_new_signal && !including_new) | |
263 | return; | |
264 | ||
265 | if (MANAGER_IS_RELOADING(j->unit->manager)) | |
266 | return; | |
267 | ||
268 | bus_job_send_change_signal(j); | |
269 | } | |
270 | ||
8f8f05a9 | 271 | static int send_removed_signal(sd_bus *bus, void *userdata) { |
4afd3348 | 272 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; |
718db961 | 273 | _cleanup_free_ char *p = NULL; |
99534007 | 274 | Job *j = ASSERT_PTR(userdata); |
718db961 LP |
275 | int r; |
276 | ||
277 | assert(bus); | |
718db961 LP |
278 | |
279 | p = job_dbus_path(j); | |
280 | if (!p) | |
281 | return -ENOMEM; | |
c1e1601e | 282 | |
718db961 LP |
283 | r = sd_bus_message_new_signal( |
284 | bus, | |
151b9b96 | 285 | &m, |
718db961 LP |
286 | "/org/freedesktop/systemd1", |
287 | "org.freedesktop.systemd1.Manager", | |
151b9b96 | 288 | "JobRemoved"); |
718db961 LP |
289 | if (r < 0) |
290 | return r; | |
291 | ||
292 | r = sd_bus_message_append(m, "uoss", j->id, p, j->unit->id, job_result_to_string(j->result)); | |
293 | if (r < 0) | |
294 | return r; | |
295 | ||
8f8f05a9 | 296 | return sd_bus_send(bus, m, NULL); |
97e6a119 | 297 | } |
c1e1601e | 298 | |
97e6a119 | 299 | void bus_job_send_removed_signal(Job *j) { |
718db961 | 300 | int r; |
c1e1601e | 301 | |
718db961 | 302 | assert(j); |
97e6a119 MS |
303 | |
304 | if (!j->sent_dbus_new_signal) | |
305 | bus_job_send_change_signal(j); | |
306 | ||
17407bc2 LP |
307 | /* Make sure that any change signal on the unit is reflected before we send out the change signal on the job */ |
308 | bus_unit_send_pending_change_signal(j->unit, true); | |
309 | ||
1a465207 | 310 | r = bus_foreach_bus(j->manager, j->bus_track, send_removed_signal, j); |
718db961 | 311 | if (r < 0) |
da927ba9 | 312 | log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id); |
c1e1601e | 313 | } |
c5a97ed1 LP |
314 | |
315 | static int bus_job_track_handler(sd_bus_track *t, void *userdata) { | |
99534007 | 316 | Job *j = ASSERT_PTR(userdata); |
c5a97ed1 LP |
317 | |
318 | assert(t); | |
c5a97ed1 LP |
319 | |
320 | j->bus_track = sd_bus_track_unref(j->bus_track); /* make sure we aren't called again */ | |
321 | ||
322 | /* Last client dropped off the bus, maybe we should GC this now? */ | |
323 | job_add_to_gc_queue(j); | |
324 | return 0; | |
325 | } | |
326 | ||
327 | static int bus_job_allocate_bus_track(Job *j) { | |
c5a97ed1 LP |
328 | |
329 | assert(j); | |
330 | ||
331 | if (j->bus_track) | |
332 | return 0; | |
333 | ||
76d8ca22 | 334 | return sd_bus_track_new(j->unit->manager->api_bus, &j->bus_track, bus_job_track_handler, j); |
c5a97ed1 LP |
335 | } |
336 | ||
337 | int bus_job_coldplug_bus_track(Job *j) { | |
c53aafb7 | 338 | int r; |
76d8ca22 | 339 | _cleanup_strv_free_ char **deserialized_clients = NULL; |
c5a97ed1 LP |
340 | |
341 | assert(j); | |
342 | ||
ae2a15bc | 343 | deserialized_clients = TAKE_PTR(j->deserialized_clients); |
76d8ca22 ZJS |
344 | |
345 | if (strv_isempty(deserialized_clients)) | |
346 | return 0; | |
c5a97ed1 LP |
347 | |
348 | if (!j->manager->api_bus) | |
76d8ca22 | 349 | return 0; |
c5a97ed1 LP |
350 | |
351 | r = bus_job_allocate_bus_track(j); | |
352 | if (r < 0) | |
76d8ca22 | 353 | return r; |
c5a97ed1 | 354 | |
76d8ca22 | 355 | return bus_track_add_name_many(j->bus_track, deserialized_clients); |
c5a97ed1 LP |
356 | } |
357 | ||
358 | int bus_job_track_sender(Job *j, sd_bus_message *m) { | |
359 | int r; | |
360 | ||
361 | assert(j); | |
362 | assert(m); | |
363 | ||
364 | if (sd_bus_message_get_bus(m) != j->unit->manager->api_bus) { | |
365 | j->ref_by_private_bus = true; | |
366 | return 0; | |
367 | } | |
368 | ||
369 | r = bus_job_allocate_bus_track(j); | |
370 | if (r < 0) | |
371 | return r; | |
372 | ||
373 | return sd_bus_track_add_sender(j->bus_track, m); | |
374 | } |