]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-job.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / core / dbus-job.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "sd-bus.h"
22
23 #include "alloc-util.h"
24 #include "dbus-job.h"
25 #include "dbus.h"
26 #include "job.h"
27 #include "log.h"
28 #include "selinux-access.h"
29 #include "string-util.h"
30
31 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType);
32 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState);
33
34 static int property_get_unit(
35 sd_bus *bus,
36 const char *path,
37 const char *interface,
38 const char *property,
39 sd_bus_message *reply,
40 void *userdata,
41 sd_bus_error *error) {
42
43 _cleanup_free_ char *p = NULL;
44 Job *j = userdata;
45
46 assert(bus);
47 assert(reply);
48 assert(j);
49
50 p = unit_dbus_path(j->unit);
51 if (!p)
52 return -ENOMEM;
53
54 return sd_bus_message_append(reply, "(so)", j->unit->id, p);
55 }
56
57 int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error *error) {
58 Job *j = userdata;
59 int r;
60
61 assert(message);
62 assert(j);
63
64 r = mac_selinux_unit_access_check(j->unit, message, "stop", error);
65 if (r < 0)
66 return r;
67
68 /* Access is granted to the job owner */
69 if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) {
70
71 /* And for everybody else consult PolicyKit */
72 r = bus_verify_manage_units_async(j->unit->manager, message, error);
73 if (r < 0)
74 return r;
75 if (r == 0)
76 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
77 }
78
79 job_finish_and_invalidate(j, JOB_CANCELED, true, false);
80
81 return sd_bus_reply_method_return(message, NULL);
82 }
83
84 int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
85 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
86 _cleanup_free_ Job **list = NULL;
87 Job *j = userdata;
88 int r, i, n;
89
90 if (strstr(sd_bus_message_get_member(message), "After"))
91 n = job_get_after(j, &list);
92 else
93 n = job_get_before(j, &list);
94 if (n < 0)
95 return n;
96
97 r = sd_bus_message_new_method_return(message, &reply);
98 if (r < 0)
99 return r;
100
101 r = sd_bus_message_open_container(reply, 'a', "(usssoo)");
102 if (r < 0)
103 return r;
104
105 for (i = 0; i < n; i ++) {
106 _cleanup_free_ char *unit_path = NULL, *job_path = NULL;
107
108 job_path = job_dbus_path(list[i]);
109 if (!job_path)
110 return -ENOMEM;
111
112 unit_path = unit_dbus_path(list[i]->unit);
113 if (!unit_path)
114 return -ENOMEM;
115
116 r = sd_bus_message_append(reply, "(usssoo)",
117 list[i]->id,
118 list[i]->unit->id,
119 job_type_to_string(list[i]->type),
120 job_state_to_string(list[i]->state),
121 job_path,
122 unit_path);
123 if (r < 0)
124 return r;
125 }
126
127 r = sd_bus_message_close_container(reply);
128 if (r < 0)
129 return r;
130
131 return sd_bus_send(NULL, reply, NULL);
132 }
133
134 const sd_bus_vtable bus_job_vtable[] = {
135 SD_BUS_VTABLE_START(0),
136 SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
137 SD_BUS_METHOD("GetAfter", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
138 SD_BUS_METHOD("GetBefore", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
139 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST),
140 SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
141 SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST),
142 SD_BUS_PROPERTY("State", "s", property_get_state, offsetof(Job, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
143 SD_BUS_VTABLE_END
144 };
145
146 static int send_new_signal(sd_bus *bus, void *userdata) {
147 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
148 _cleanup_free_ char *p = NULL;
149 Job *j = userdata;
150 int r;
151
152 assert(bus);
153 assert(j);
154
155 p = job_dbus_path(j);
156 if (!p)
157 return -ENOMEM;
158
159 r = sd_bus_message_new_signal(
160 bus,
161 &m,
162 "/org/freedesktop/systemd1",
163 "org.freedesktop.systemd1.Manager",
164 "JobNew");
165 if (r < 0)
166 return r;
167
168 r = sd_bus_message_append(m, "uos", j->id, p, j->unit->id);
169 if (r < 0)
170 return r;
171
172 return sd_bus_send(bus, m, NULL);
173 }
174
175 static int send_changed_signal(sd_bus *bus, void *userdata) {
176 _cleanup_free_ char *p = NULL;
177 Job *j = userdata;
178
179 assert(bus);
180 assert(j);
181
182 p = job_dbus_path(j);
183 if (!p)
184 return -ENOMEM;
185
186 return sd_bus_emit_properties_changed(bus, p, "org.freedesktop.systemd1.Job", "State", NULL);
187 }
188
189 void bus_job_send_change_signal(Job *j) {
190 int r;
191
192 assert(j);
193
194 if (j->in_dbus_queue) {
195 LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
196 j->in_dbus_queue = false;
197 }
198
199 r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j);
200 if (r < 0)
201 log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id);
202
203 j->sent_dbus_new_signal = true;
204 }
205
206 static int send_removed_signal(sd_bus *bus, void *userdata) {
207 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
208 _cleanup_free_ char *p = NULL;
209 Job *j = userdata;
210 int r;
211
212 assert(bus);
213 assert(j);
214
215 p = job_dbus_path(j);
216 if (!p)
217 return -ENOMEM;
218
219 r = sd_bus_message_new_signal(
220 bus,
221 &m,
222 "/org/freedesktop/systemd1",
223 "org.freedesktop.systemd1.Manager",
224 "JobRemoved");
225 if (r < 0)
226 return r;
227
228 r = sd_bus_message_append(m, "uoss", j->id, p, j->unit->id, job_result_to_string(j->result));
229 if (r < 0)
230 return r;
231
232 return sd_bus_send(bus, m, NULL);
233 }
234
235 void bus_job_send_removed_signal(Job *j) {
236 int r;
237
238 assert(j);
239
240 if (!j->sent_dbus_new_signal)
241 bus_job_send_change_signal(j);
242
243 r = bus_foreach_bus(j->manager, j->bus_track, send_removed_signal, j);
244 if (r < 0)
245 log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id);
246 }
247
248 static int bus_job_track_handler(sd_bus_track *t, void *userdata) {
249 Job *j = userdata;
250
251 assert(t);
252 assert(j);
253
254 j->bus_track = sd_bus_track_unref(j->bus_track); /* make sure we aren't called again */
255
256 /* Last client dropped off the bus, maybe we should GC this now? */
257 job_add_to_gc_queue(j);
258 return 0;
259 }
260
261 static int bus_job_allocate_bus_track(Job *j) {
262
263 assert(j);
264
265 if (j->bus_track)
266 return 0;
267
268 return sd_bus_track_new(j->unit->manager->api_bus, &j->bus_track, bus_job_track_handler, j);
269 }
270
271 int bus_job_coldplug_bus_track(Job *j) {
272 int r = 0;
273 _cleanup_strv_free_ char **deserialized_clients = NULL;
274
275 assert(j);
276
277 deserialized_clients = j->deserialized_clients;
278 j->deserialized_clients = NULL;
279
280 if (strv_isempty(deserialized_clients))
281 return 0;
282
283 if (!j->manager->api_bus)
284 return 0;
285
286 r = bus_job_allocate_bus_track(j);
287 if (r < 0)
288 return r;
289
290 return bus_track_add_name_many(j->bus_track, deserialized_clients);
291 }
292
293 int bus_job_track_sender(Job *j, sd_bus_message *m) {
294 int r;
295
296 assert(j);
297 assert(m);
298
299 if (sd_bus_message_get_bus(m) != j->unit->manager->api_bus) {
300 j->ref_by_private_bus = true;
301 return 0;
302 }
303
304 r = bus_job_allocate_bus_track(j);
305 if (r < 0)
306 return r;
307
308 return sd_bus_track_add_sender(j->bus_track, m);
309 }