]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-track.c
tmpfiles: accurately report creation results
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-track.c
CommitLineData
8f8f05a9
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 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 "sd-bus.h"
23#include "set.h"
19befb2d 24#include "bus-util.h"
8f8f05a9
LP
25#include "bus-internal.h"
26#include "bus-track.h"
27
28struct sd_bus_track {
29 unsigned n_ref;
30 sd_bus *bus;
31 sd_bus_track_handler_t handler;
32 void *userdata;
19befb2d 33 Hashmap *names;
8f8f05a9
LP
34 LIST_FIELDS(sd_bus_track, queue);
35 Iterator iterator;
36 bool in_queue;
37 bool modified;
38};
39
40#define MATCH_PREFIX \
41 "type='signal'," \
42 "sender='org.freedesktop.DBus'," \
43 "path='/org/freedesktop/DBus'," \
44 "interface='org.freedesktop.DBus'," \
45 "member='NameOwnerChanged'," \
46 "arg0='"
47
48#define MATCH_SUFFIX \
49 "'"
50
51#define MATCH_FOR_NAME(name) \
52 ({ \
53 char *_x; \
54 size_t _l = strlen(name); \
f8294e41 55 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
8f8f05a9
LP
56 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
57 _x; \
58 })
59
60static void bus_track_add_to_queue(sd_bus_track *track) {
61 assert(track);
62
63 if (track->in_queue)
64 return;
65
66 if (!track->handler)
67 return;
68
69 LIST_PREPEND(queue, track->bus->track_queue, track);
70 track->in_queue = true;
71}
72
73static void bus_track_remove_from_queue(sd_bus_track *track) {
74 assert(track);
75
76 if (!track->in_queue)
77 return;
78
79 LIST_REMOVE(queue, track->bus->track_queue, track);
80 track->in_queue = false;
81}
82
83_public_ int sd_bus_track_new(
84 sd_bus *bus,
85 sd_bus_track **track,
86 sd_bus_track_handler_t handler,
87 void *userdata) {
88
89 sd_bus_track *t;
90
91 assert_return(bus, -EINVAL);
92 assert_return(track, -EINVAL);
93
94 t = new0(sd_bus_track, 1);
95 if (!t)
96 return -ENOMEM;
97
98 t->n_ref = 1;
99 t->handler = handler;
100 t->userdata = userdata;
101 t->bus = sd_bus_ref(bus);
102
103 bus_track_add_to_queue(t);
104
105 *track = t;
106 return 0;
107}
108
109_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
110 assert_return(track, NULL);
111
112 assert(track->n_ref > 0);
113
114 track->n_ref++;
115
116 return track;
117}
118
119_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
120 const char *n;
121
122 if (!track)
123 return NULL;
124
125 assert(track->n_ref > 0);
126
127 if (track->n_ref > 1) {
128 track->n_ref --;
129 return NULL;
130 }
131
19befb2d 132 while ((n = hashmap_first_key(track->names)))
8f8f05a9
LP
133 sd_bus_track_remove_name(track, n);
134
135 bus_track_remove_from_queue(track);
19befb2d 136 hashmap_free(track->names);
8f8f05a9
LP
137 sd_bus_unref(track->bus);
138 free(track);
139
140 return NULL;
141}
142
143static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
144 sd_bus_track *track = userdata;
145 const char *name, *old, *new;
146 int r;
147
148 assert(bus);
149 assert(message);
150 assert(track);
151
152 r = sd_bus_message_read(message, "sss", &name, &old, &new);
153 if (r < 0)
154 return 0;
155
156 sd_bus_track_remove_name(track, name);
157 return 0;
158}
159
160_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
19befb2d 161 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
8f8f05a9
LP
162 _cleanup_free_ char *n = NULL;
163 const char *match;
164 int r;
165
166 assert_return(track, -EINVAL);
167 assert_return(service_name_is_valid(name), -EINVAL);
168
d5099efc 169 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
8f8f05a9
LP
170 if (r < 0)
171 return r;
172
173 n = strdup(name);
174 if (!n)
175 return -ENOMEM;
176
19befb2d
LP
177 /* First, subscribe to this name */
178 match = MATCH_FOR_NAME(n);
179 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
180 if (r < 0)
181 return r;
182
183 r = hashmap_put(track->names, n, slot);
8f8f05a9
LP
184 if (r == -EEXIST)
185 return 0;
186 if (r < 0)
187 return r;
188
8f8f05a9
LP
189 /* Second, check if it is currently existing, or maybe
190 * doesn't, or maybe disappeared already. */
056f95d0 191 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
8f8f05a9 192 if (r < 0) {
19befb2d 193 hashmap_remove(track->names, n);
8f8f05a9
LP
194 return r;
195 }
196
197 n = NULL;
19befb2d 198 slot = NULL;
8f8f05a9
LP
199
200 bus_track_remove_from_queue(track);
201 track->modified = true;
202
203 return 1;
204}
205
206_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
19befb2d
LP
207 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
208 _cleanup_free_ char *n = NULL;
8f8f05a9
LP
209
210 assert_return(name, -EINVAL);
211
212 if (!track)
213 return 0;
214
19befb2d
LP
215 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
216 if (!slot)
8f8f05a9
LP
217 return 0;
218
19befb2d 219 if (hashmap_isempty(track->names))
8f8f05a9
LP
220 bus_track_add_to_queue(track);
221
222 track->modified = true;
223
224 return 1;
225}
226
227_public_ unsigned sd_bus_track_count(sd_bus_track *track) {
228 if (!track)
229 return 0;
230
19befb2d 231 return hashmap_size(track->names);
8f8f05a9
LP
232}
233
234_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
235 assert_return(track, NULL);
236 assert_return(name, NULL);
237
19befb2d 238 return hashmap_get(track->names, (void*) name) ? name : NULL;
8f8f05a9
LP
239}
240
241_public_ const char* sd_bus_track_first(sd_bus_track *track) {
19befb2d
LP
242 const char *n = NULL;
243
8f8f05a9
LP
244 if (!track)
245 return NULL;
246
247 track->modified = false;
4dd6c572 248 track->iterator = ITERATOR_FIRST;
8f8f05a9 249
19befb2d
LP
250 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
251 return n;
8f8f05a9
LP
252}
253
254_public_ const char* sd_bus_track_next(sd_bus_track *track) {
19befb2d
LP
255 const char *n = NULL;
256
8f8f05a9
LP
257 if (!track)
258 return NULL;
259
260 if (track->modified)
261 return NULL;
262
19befb2d
LP
263 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
264 return n;
8f8f05a9
LP
265}
266
267_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
268 const char *sender;
269
270 assert_return(track, -EINVAL);
271 assert_return(m, -EINVAL);
272
273 sender = sd_bus_message_get_sender(m);
274 if (!sender)
275 return -EINVAL;
276
277 return sd_bus_track_add_name(track, sender);
278}
279
280_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
281 const char *sender;
282
283 assert_return(track, -EINVAL);
284 assert_return(m, -EINVAL);
285
286 sender = sd_bus_message_get_sender(m);
287 if (!sender)
288 return -EINVAL;
289
290 return sd_bus_track_remove_name(track, sender);
291}
292
293_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
294 assert_return(track, NULL);
295
296 return track->bus;
297}
298
299void bus_track_dispatch(sd_bus_track *track) {
300 int r;
301
302 assert(track);
303 assert(track->in_queue);
304 assert(track->handler);
305
306 bus_track_remove_from_queue(track);
307
308 sd_bus_track_ref(track);
309
310 r = track->handler(track, track->userdata);
311 if (r < 0)
da927ba9 312 log_debug_errno(r, "Failed to process track handler: %m");
8f8f05a9
LP
313 else if (r == 0)
314 bus_track_add_to_queue(track);
315
316 sd_bus_track_unref(track);
317}
04552566
LP
318
319_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
320 assert_return(track, NULL);
321
322 return track->userdata;
323}
324
325_public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
326 void *ret;
327
328 assert_return(track, NULL);
329
330 ret = track->userdata;
331 track->userdata = userdata;
332
333 return ret;
334}