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