]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-track.c
core: sysvcompat - avoid repeated function call
[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"
24#include "bus-internal.h"
25#include "bus-track.h"
26
27struct sd_bus_track {
28 unsigned n_ref;
29 sd_bus *bus;
30 sd_bus_track_handler_t handler;
31 void *userdata;
32 Set *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); \
f8294e41 54 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
8f8f05a9
LP
55 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
56 _x; \
57 })
58
59static 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
72static 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 t = new0(sd_bus_track, 1);
94 if (!t)
95 return -ENOMEM;
96
97 t->n_ref = 1;
98 t->handler = handler;
99 t->userdata = userdata;
100 t->bus = sd_bus_ref(bus);
101
102 bus_track_add_to_queue(t);
103
104 *track = t;
105 return 0;
106}
107
108_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
109 assert_return(track, NULL);
110
111 assert(track->n_ref > 0);
112
113 track->n_ref++;
114
115 return track;
116}
117
118_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
119 const char *n;
120
121 if (!track)
122 return NULL;
123
124 assert(track->n_ref > 0);
125
126 if (track->n_ref > 1) {
127 track->n_ref --;
128 return NULL;
129 }
130
131 while ((n = set_first(track->names)))
132 sd_bus_track_remove_name(track, n);
133
134 bus_track_remove_from_queue(track);
135 set_free(track->names);
136 sd_bus_unref(track->bus);
137 free(track);
138
139 return NULL;
140}
141
142static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
143 sd_bus_track *track = userdata;
144 const char *name, *old, *new;
145 int r;
146
147 assert(bus);
148 assert(message);
149 assert(track);
150
151 r = sd_bus_message_read(message, "sss", &name, &old, &new);
152 if (r < 0)
153 return 0;
154
155 sd_bus_track_remove_name(track, name);
156 return 0;
157}
158
159_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
160 _cleanup_free_ char *n = NULL;
161 const char *match;
162 int r;
163
164 assert_return(track, -EINVAL);
165 assert_return(service_name_is_valid(name), -EINVAL);
166
167 r = set_ensure_allocated(&track->names, string_hash_func, string_compare_func);
168 if (r < 0)
169 return r;
170
171 n = strdup(name);
172 if (!n)
173 return -ENOMEM;
174
175 r = set_put(track->names, n);
176 if (r == -EEXIST)
177 return 0;
178 if (r < 0)
179 return r;
180
181 /* First, subscribe to this name */
182 match = MATCH_FOR_NAME(name);
183 r = sd_bus_add_match(track->bus, match, on_name_owner_changed, track);
184 if (r < 0) {
185 set_remove(track->names, n);
186 return r;
187 }
188
189 /* Second, check if it is currently existing, or maybe
190 * doesn't, or maybe disappeared already. */
191 r = sd_bus_get_owner(track->bus, name, 0, NULL);
192 if (r < 0) {
193 set_remove(track->names, n);
194 sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
195 return r;
196 }
197
198 n = 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 const char *match;
4d463961 208 _cleanup_free_ char *n = NULL;;
8f8f05a9
LP
209
210 assert_return(name, -EINVAL);
211
212 if (!track)
213 return 0;
214
215 n = set_remove(track->names, (char*) name);
216 if (!n)
217 return 0;
218
219 match = MATCH_FOR_NAME(n);
220 sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
221
222 if (set_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 set_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 set_get(track->names, (void*) name);
242}
243
244_public_ const char* sd_bus_track_first(sd_bus_track *track) {
245 if (!track)
246 return NULL;
247
248 track->modified = false;
249 track->iterator = NULL;
250
251 return set_iterate(track->names, &track->iterator);
252}
253
254_public_ const char* sd_bus_track_next(sd_bus_track *track) {
255 if (!track)
256 return NULL;
257
258 if (track->modified)
259 return NULL;
260
261 return set_iterate(track->names, &track->iterator);
262}
263
264_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
265 const char *sender;
266
267 assert_return(track, -EINVAL);
268 assert_return(m, -EINVAL);
269
270 sender = sd_bus_message_get_sender(m);
271 if (!sender)
272 return -EINVAL;
273
274 return sd_bus_track_add_name(track, sender);
275}
276
277_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
278 const char *sender;
279
280 assert_return(track, -EINVAL);
281 assert_return(m, -EINVAL);
282
283 sender = sd_bus_message_get_sender(m);
284 if (!sender)
285 return -EINVAL;
286
287 return sd_bus_track_remove_name(track, sender);
288}
289
290_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
291 assert_return(track, NULL);
292
293 return track->bus;
294}
295
296void bus_track_dispatch(sd_bus_track *track) {
297 int r;
298
299 assert(track);
300 assert(track->in_queue);
301 assert(track->handler);
302
303 bus_track_remove_from_queue(track);
304
305 sd_bus_track_ref(track);
306
307 r = track->handler(track, track->userdata);
308 if (r < 0)
309 log_debug("Failed to process track handler: %s", strerror(-r));
310 else if (r == 0)
311 bus_track_add_to_queue(track);
312
313 sd_bus_track_unref(track);
314}