]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-track.c
tmpfiles: accurately report creation results
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-track.c
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"
24 #include "bus-util.h"
25 #include "bus-internal.h"
26 #include "bus-track.h"
27
28 struct sd_bus_track {
29 unsigned n_ref;
30 sd_bus *bus;
31 sd_bus_track_handler_t handler;
32 void *userdata;
33 Hashmap *names;
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); \
55 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
56 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
57 _x; \
58 })
59
60 static 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
73 static 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
132 while ((n = hashmap_first_key(track->names)))
133 sd_bus_track_remove_name(track, n);
134
135 bus_track_remove_from_queue(track);
136 hashmap_free(track->names);
137 sd_bus_unref(track->bus);
138 free(track);
139
140 return NULL;
141 }
142
143 static 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) {
161 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
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
169 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
170 if (r < 0)
171 return r;
172
173 n = strdup(name);
174 if (!n)
175 return -ENOMEM;
176
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);
184 if (r == -EEXIST)
185 return 0;
186 if (r < 0)
187 return r;
188
189 /* Second, check if it is currently existing, or maybe
190 * doesn't, or maybe disappeared already. */
191 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
192 if (r < 0) {
193 hashmap_remove(track->names, n);
194 return r;
195 }
196
197 n = NULL;
198 slot = NULL;
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) {
207 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
208 _cleanup_free_ char *n = NULL;
209
210 assert_return(name, -EINVAL);
211
212 if (!track)
213 return 0;
214
215 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
216 if (!slot)
217 return 0;
218
219 if (hashmap_isempty(track->names))
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
231 return hashmap_size(track->names);
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
238 return hashmap_get(track->names, (void*) name) ? name : NULL;
239 }
240
241 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
242 const char *n = NULL;
243
244 if (!track)
245 return NULL;
246
247 track->modified = false;
248 track->iterator = ITERATOR_FIRST;
249
250 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
251 return n;
252 }
253
254 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
255 const char *n = NULL;
256
257 if (!track)
258 return NULL;
259
260 if (track->modified)
261 return NULL;
262
263 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
264 return n;
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
299 void 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)
312 log_debug_errno(r, "Failed to process track handler: %m");
313 else if (r == 0)
314 bus_track_add_to_queue(track);
315
316 sd_bus_track_unref(track);
317 }
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 }