1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "bus-get-properties.h"
10 #include "dbus-util.h"
14 #include "selinux-access.h"
15 #include "string-util.h"
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
);
21 static int property_get_unit(
24 const char *interface
,
26 sd_bus_message
*reply
,
28 sd_bus_error
*error
) {
30 _cleanup_free_
char *p
= NULL
;
31 Job
*j
= ASSERT_PTR(userdata
);
36 p
= unit_dbus_path(j
->unit
);
40 return sd_bus_message_append(reply
, "(so)", j
->unit
->id
, p
);
43 int bus_job_method_cancel(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
44 Job
*j
= ASSERT_PTR(userdata
);
49 r
= mac_selinux_unit_access_check(j
->unit
, message
, "stop", error
);
53 /* Access is granted to the job owner */
54 if (!sd_bus_track_contains(j
->bus_track
, sd_bus_message_get_sender(message
))) {
56 /* And for everybody else consult polkit */
57 r
= bus_verify_manage_units_async(j
->unit
->manager
, message
, error
);
61 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
64 job_finish_and_invalidate(j
, JOB_CANCELED
, true, false);
66 return sd_bus_reply_method_return(message
, NULL
);
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
;
75 if (strstr(sd_bus_message_get_member(message
), "After"))
76 n
= job_get_after(j
, &list
);
78 n
= job_get_before(j
, &list
);
82 r
= sd_bus_message_new_method_return(message
, &reply
);
86 r
= sd_bus_message_open_container(reply
, 'a', "(usssoo)");
90 for (int i
= 0; i
< n
; i
++) {
91 _cleanup_free_
char *unit_path
= NULL
, *job_path
= NULL
;
93 job_path
= job_dbus_path(list
[i
]);
97 unit_path
= unit_dbus_path(list
[i
]->unit
);
101 r
= sd_bus_message_append(reply
, "(usssoo)",
104 job_type_to_string(list
[i
]->type
),
105 job_state_to_string(list
[i
]->state
),
112 r
= sd_bus_message_close_container(reply
);
116 return sd_bus_send(NULL
, reply
, NULL
);
119 const sd_bus_vtable bus_job_vtable
[] = {
120 SD_BUS_VTABLE_START(0),
122 SD_BUS_METHOD("Cancel", NULL
, NULL
, bus_job_method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
123 SD_BUS_METHOD_WITH_ARGS("GetAfter",
125 SD_BUS_RESULT("a(usssoo)", jobs
),
126 bus_job_method_get_waiting_jobs
,
127 SD_BUS_VTABLE_UNPRIVILEGED
),
128 SD_BUS_METHOD_WITH_ARGS("GetBefore",
130 SD_BUS_RESULT("a(usssoo)", jobs
),
131 bus_job_method_get_waiting_jobs
,
132 SD_BUS_VTABLE_UNPRIVILEGED
),
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
),
137 SD_BUS_PROPERTY("State", "s", property_get_state
, offsetof(Job
, state
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
138 SD_BUS_PROPERTY("ActivationDetails", "a(ss)", bus_property_get_activation_details
, offsetof(Job
, activation_details
), SD_BUS_VTABLE_PROPERTY_CONST
),
142 static int bus_job_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
143 Manager
*m
= ASSERT_PTR(userdata
);
152 r
= manager_get_job_from_dbus_path(m
, path
, &j
);
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
;
166 l
= new0(char*, hashmap_size(m
->jobs
)+1);
170 HASHMAP_FOREACH(j
, m
->jobs
) {
171 l
[k
] = job_dbus_path(j
);
178 assert(hashmap_size(m
->jobs
) == k
);
180 *nodes
= TAKE_PTR(l
);
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
,
192 static int send_new_signal(sd_bus
*bus
, void *userdata
) {
193 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
194 _cleanup_free_
char *p
= NULL
;
195 Job
*j
= ASSERT_PTR(userdata
);
200 p
= job_dbus_path(j
);
204 r
= sd_bus_message_new_signal(
207 "/org/freedesktop/systemd1",
208 "org.freedesktop.systemd1.Manager",
213 r
= sd_bus_message_append(m
, "uos", j
->id
, p
, j
->unit
->id
);
217 return sd_bus_send(bus
, m
, NULL
);
220 static int send_changed_signal(sd_bus
*bus
, void *userdata
) {
221 _cleanup_free_
char *p
= NULL
;
222 Job
*j
= ASSERT_PTR(userdata
);
226 p
= job_dbus_path(j
);
230 return sd_bus_emit_properties_changed(bus
, p
, "org.freedesktop.systemd1.Job", "State", NULL
);
233 void bus_job_send_change_signal(Job
*j
) {
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);
241 if (j
->in_dbus_queue
) {
242 LIST_REMOVE(dbus_queue
, j
->manager
->dbus_job_queue
, j
);
243 j
->in_dbus_queue
= false;
245 /* The job might be good to be GC once its pending signals have been sent */
246 job_add_to_gc_queue(j
);
249 r
= bus_foreach_bus(j
->manager
, j
->bus_track
, j
->sent_dbus_new_signal
? send_changed_signal
: send_new_signal
, j
);
251 log_debug_errno(r
, "Failed to send job change signal for %u: %m", j
->id
);
253 j
->sent_dbus_new_signal
= true;
256 void bus_job_send_pending_change_signal(Job
*j
, bool including_new
) {
259 if (!j
->in_dbus_queue
)
262 if (!j
->sent_dbus_new_signal
&& !including_new
)
265 if (MANAGER_IS_RELOADING(j
->unit
->manager
))
268 bus_job_send_change_signal(j
);
271 static int send_removed_signal(sd_bus
*bus
, void *userdata
) {
272 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
273 _cleanup_free_
char *p
= NULL
;
274 Job
*j
= ASSERT_PTR(userdata
);
279 p
= job_dbus_path(j
);
283 r
= sd_bus_message_new_signal(
286 "/org/freedesktop/systemd1",
287 "org.freedesktop.systemd1.Manager",
292 r
= sd_bus_message_append(m
, "uoss", j
->id
, p
, j
->unit
->id
, job_result_to_string(j
->result
));
296 return sd_bus_send(bus
, m
, NULL
);
299 void bus_job_send_removed_signal(Job
*j
) {
304 if (!j
->sent_dbus_new_signal
)
305 bus_job_send_change_signal(j
);
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);
310 r
= bus_foreach_bus(j
->manager
, j
->bus_track
, send_removed_signal
, j
);
312 log_debug_errno(r
, "Failed to send job remove signal for %u: %m", j
->id
);
315 static int bus_job_track_handler(sd_bus_track
*t
, void *userdata
) {
316 Job
*j
= ASSERT_PTR(userdata
);
320 j
->bus_track
= sd_bus_track_unref(j
->bus_track
); /* make sure we aren't called again */
322 /* Last client dropped off the bus, maybe we should GC this now? */
323 job_add_to_gc_queue(j
);
327 static int bus_job_allocate_bus_track(Job
*j
) {
334 return sd_bus_track_new(j
->unit
->manager
->api_bus
, &j
->bus_track
, bus_job_track_handler
, j
);
337 int bus_job_coldplug_bus_track(Job
*j
) {
339 _cleanup_strv_free_
char **deserialized_clients
= NULL
;
343 deserialized_clients
= TAKE_PTR(j
->deserialized_clients
);
345 if (strv_isempty(deserialized_clients
))
348 if (!j
->manager
->api_bus
)
351 r
= bus_job_allocate_bus_track(j
);
355 return bus_track_add_name_many(j
->bus_track
, deserialized_clients
);
358 int bus_job_track_sender(Job
*j
, sd_bus_message
*m
) {
364 if (sd_bus_message_get_bus(m
) != j
->unit
->manager
->api_bus
) {
365 j
->ref_by_private_bus
= true;
369 r
= bus_job_allocate_bus_track(j
);
373 return sd_bus_track_add_sender(j
->bus_track
, m
);