]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-track.c
Merge pull request #2717 from keszybz/networkctl-prettification
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-track.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "sd-bus.h"
21
22 #include "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-track.h"
25 #include "bus-util.h"
26
27 struct sd_bus_track {
28 unsigned n_ref;
29 sd_bus *bus;
30 sd_bus_track_handler_t handler;
31 void *userdata;
32 Hashmap *names;
33 LIST_FIELDS(sd_bus_track, queue);
34 Iterator iterator;
35 bool in_queue;
36 bool modified;
37 };
38
39 #define MATCH_PREFIX \
40 "type='signal'," \
41 "sender='org.freedesktop.DBus'," \
42 "path='/org/freedesktop/DBus'," \
43 "interface='org.freedesktop.DBus'," \
44 "member='NameOwnerChanged'," \
45 "arg0='"
46
47 #define MATCH_SUFFIX \
48 "'"
49
50 #define MATCH_FOR_NAME(name) \
51 ({ \
52 char *_x; \
53 size_t _l = strlen(name); \
54 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
55 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
56 _x; \
57 })
58
59 static void bus_track_add_to_queue(sd_bus_track *track) {
60 assert(track);
61
62 if (track->in_queue)
63 return;
64
65 if (!track->handler)
66 return;
67
68 LIST_PREPEND(queue, track->bus->track_queue, track);
69 track->in_queue = true;
70 }
71
72 static void bus_track_remove_from_queue(sd_bus_track *track) {
73 assert(track);
74
75 if (!track->in_queue)
76 return;
77
78 LIST_REMOVE(queue, track->bus->track_queue, track);
79 track->in_queue = false;
80 }
81
82 _public_ int sd_bus_track_new(
83 sd_bus *bus,
84 sd_bus_track **track,
85 sd_bus_track_handler_t handler,
86 void *userdata) {
87
88 sd_bus_track *t;
89
90 assert_return(bus, -EINVAL);
91 assert_return(track, -EINVAL);
92
93 if (!bus->bus_client)
94 return -EINVAL;
95
96 t = new0(sd_bus_track, 1);
97 if (!t)
98 return -ENOMEM;
99
100 t->n_ref = 1;
101 t->handler = handler;
102 t->userdata = userdata;
103 t->bus = sd_bus_ref(bus);
104
105 bus_track_add_to_queue(t);
106
107 *track = t;
108 return 0;
109 }
110
111 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
112
113 if (!track)
114 return NULL;
115
116 assert(track->n_ref > 0);
117
118 track->n_ref++;
119
120 return track;
121 }
122
123 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
124 const char *n;
125
126 if (!track)
127 return NULL;
128
129 assert(track->n_ref > 0);
130
131 if (track->n_ref > 1) {
132 track->n_ref--;
133 return NULL;
134 }
135
136 while ((n = hashmap_first_key(track->names)))
137 sd_bus_track_remove_name(track, n);
138
139 bus_track_remove_from_queue(track);
140 hashmap_free(track->names);
141 sd_bus_unref(track->bus);
142 free(track);
143
144 return NULL;
145 }
146
147 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
148 sd_bus_track *track = userdata;
149 const char *name, *old, *new;
150 int r;
151
152 assert(message);
153 assert(track);
154
155 r = sd_bus_message_read(message, "sss", &name, &old, &new);
156 if (r < 0)
157 return 0;
158
159 sd_bus_track_remove_name(track, name);
160 return 0;
161 }
162
163 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
164 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
165 _cleanup_free_ char *n = NULL;
166 const char *match;
167 int r;
168
169 assert_return(track, -EINVAL);
170 assert_return(service_name_is_valid(name), -EINVAL);
171
172 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
173 if (r < 0)
174 return r;
175
176 n = strdup(name);
177 if (!n)
178 return -ENOMEM;
179
180 /* First, subscribe to this name */
181 match = MATCH_FOR_NAME(n);
182 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
183 if (r < 0)
184 return r;
185
186 r = hashmap_put(track->names, n, slot);
187 if (r == -EEXIST)
188 return 0;
189 if (r < 0)
190 return r;
191
192 /* Second, check if it is currently existing, or maybe
193 * doesn't, or maybe disappeared already. */
194 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
195 if (r < 0) {
196 hashmap_remove(track->names, n);
197 return r;
198 }
199
200 n = NULL;
201 slot = NULL;
202
203 bus_track_remove_from_queue(track);
204 track->modified = true;
205
206 return 1;
207 }
208
209 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
210 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
211 _cleanup_free_ char *n = NULL;
212
213 assert_return(name, -EINVAL);
214
215 if (!track)
216 return 0;
217
218 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
219 if (!slot)
220 return 0;
221
222 if (hashmap_isempty(track->names))
223 bus_track_add_to_queue(track);
224
225 track->modified = true;
226
227 return 1;
228 }
229
230 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
231 if (!track)
232 return 0;
233
234 return hashmap_size(track->names);
235 }
236
237 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
238 assert_return(track, NULL);
239 assert_return(name, NULL);
240
241 return hashmap_get(track->names, (void*) name) ? name : NULL;
242 }
243
244 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
245 const char *n = NULL;
246
247 if (!track)
248 return NULL;
249
250 track->modified = false;
251 track->iterator = ITERATOR_FIRST;
252
253 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
254 return n;
255 }
256
257 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
258 const char *n = NULL;
259
260 if (!track)
261 return NULL;
262
263 if (track->modified)
264 return NULL;
265
266 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
267 return n;
268 }
269
270 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
271 const char *sender;
272
273 assert_return(track, -EINVAL);
274 assert_return(m, -EINVAL);
275
276 sender = sd_bus_message_get_sender(m);
277 if (!sender)
278 return -EINVAL;
279
280 return sd_bus_track_add_name(track, sender);
281 }
282
283 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
284 const char *sender;
285
286 assert_return(track, -EINVAL);
287 assert_return(m, -EINVAL);
288
289 sender = sd_bus_message_get_sender(m);
290 if (!sender)
291 return -EINVAL;
292
293 return sd_bus_track_remove_name(track, sender);
294 }
295
296 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
297 assert_return(track, NULL);
298
299 return track->bus;
300 }
301
302 void bus_track_dispatch(sd_bus_track *track) {
303 int r;
304
305 assert(track);
306 assert(track->in_queue);
307 assert(track->handler);
308
309 bus_track_remove_from_queue(track);
310
311 sd_bus_track_ref(track);
312
313 r = track->handler(track, track->userdata);
314 if (r < 0)
315 log_debug_errno(r, "Failed to process track handler: %m");
316 else if (r == 0)
317 bus_track_add_to_queue(track);
318
319 sd_bus_track_unref(track);
320 }
321
322 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
323 assert_return(track, NULL);
324
325 return track->userdata;
326 }
327
328 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
329 void *ret;
330
331 assert_return(track, NULL);
332
333 ret = track->userdata;
334 track->userdata = userdata;
335
336 return ret;
337 }