]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-client-track.c
core: convert PID 1 to libsystemd-bus
[thirdparty/systemd.git] / src / core / dbus-client-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 "dbus-client-track.h"
23
24 static unsigned tracked_client_hash(const void *a) {
25 const BusTrackedClient *x = a;
26
27 return string_hash_func(x->name) ^ PTR_TO_UINT(x->bus);
28 }
29
30 static int tracked_client_compare(const void *a, const void *b) {
31 const BusTrackedClient *x = a, *y = b;
32 int r;
33
34 r = strcmp(x->name, y->name);
35 if (r != 0)
36 return r;
37
38 if (x->bus < y->bus)
39 return -1;
40 if (x->bus > y->bus)
41 return 1;
42
43 return 0;
44 }
45
46 static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata) {
47 BusTrackedClient *c = userdata;
48 const char *name, *old, *new;
49 int r;
50
51 assert(bus);
52 assert(message);
53
54 r = sd_bus_message_read(message, "sss", &name, &old, &new);
55 if (r < 0) {
56 log_debug("Failed to parse NameOwnerChanged message.");
57 return 0;
58 }
59
60 bus_client_untrack(c->set, bus, name);
61 return 0;
62 }
63
64 static char *build_match(const char *name) {
65
66 return strjoin("type='signal',"
67 "sender='org.freedesktop.DBus',"
68 "path='/org/freedesktop/DBus',"
69 "interface='org.freedesktop.DBus',"
70 "member='NameOwnerChanged',"
71 "arg0='", name, "'", NULL);
72 }
73
74 int bus_client_track(Set **s, sd_bus *bus, const char *name) {
75 BusTrackedClient *c, *found;
76 size_t l;
77 int r;
78
79 assert(s);
80 assert(bus);
81
82 r = set_ensure_allocated(s, tracked_client_hash, tracked_client_compare);
83 if (r < 0)
84 return r;
85
86 name = strempty(name);
87
88 l = strlen(name);
89
90 c = alloca(offsetof(BusTrackedClient, name) + l + 1);
91 c->set = *s;
92 c->bus = bus;
93 strcpy(c->name, name);
94
95 found = set_get(*s, c);
96 if (found)
97 return 0;
98
99 c = memdup(c, offsetof(BusTrackedClient, name) + l + 1);
100 if (!c)
101 return -ENOMEM;
102
103 r = set_put(*s, c);
104 if (r < 0) {
105 free(c);
106 return r;
107 }
108
109 if (!isempty(name)) {
110 _cleanup_free_ char *match = NULL;
111
112 match = build_match(name);
113 if (!match) {
114 set_remove(*s, c);
115 free(c);
116 return -ENOMEM;
117 }
118
119 r = sd_bus_add_match(bus, match, on_name_owner_changed, c);
120 if (r < 0) {
121 set_remove(*s, c);
122 free(c);
123 return r;
124 }
125 }
126
127 sd_bus_ref(c->bus);
128 return 1;
129 }
130
131 static void bus_client_free_one(Set *s, BusTrackedClient *c) {
132 assert(s);
133 assert(c);
134
135 if (!isempty(c->name)) {
136 _cleanup_free_ char *match = NULL;
137
138 match = build_match(c->name);
139 if (match)
140 sd_bus_remove_match(c->bus, match, on_name_owner_changed, c);
141 }
142
143 sd_bus_unref(c->bus);
144 set_remove(s, c);
145 free(c);
146 }
147
148 int bus_client_untrack(Set *s, sd_bus *bus, const char *name) {
149 BusTrackedClient *c, *found;
150 size_t l;
151
152 assert(bus);
153 assert(s);
154 assert(name);
155
156 name = strempty(name);
157
158 l = strlen(name);
159
160 c = alloca(offsetof(BusTrackedClient, name) + l + 1);
161 c->bus = bus;
162 strcpy(c->name, name);
163
164 found = set_get(s, c);
165 if (!found)
166 return 0;
167
168 bus_client_free_one(s, found);
169 return 1;
170 }
171
172 void bus_client_track_free(Set *s) {
173 BusTrackedClient *c;
174
175 while ((c = set_first(s)))
176 bus_client_free_one(s, c);
177
178 set_free(s);
179 }
180
181 int bus_client_untrack_bus(Set *s, sd_bus *bus) {
182 BusTrackedClient *c;
183 Iterator i;
184 int r = 0;
185
186 SET_FOREACH(c, s, i)
187 if (c->bus == bus) {
188 bus_client_free_one(s, c);
189 r++;
190 }
191
192 return r;
193 }
194
195 void bus_client_track_serialize(Manager *m, FILE *f, Set *s) {
196 BusTrackedClient *c;
197 Iterator i;
198
199 assert(m);
200 assert(f);
201
202 SET_FOREACH(c, s, i) {
203 if (c->bus == m->api_bus)
204 fprintf(f, "subscribed=%s", isempty(c->name) ? "*" : c->name);
205 else
206 fprintf(f, "subscribed=%p %s", c->bus, isempty(c->name) ? "*" : c->name);
207 }
208 }
209
210 int bus_client_track_deserialize_item(Manager *m, Set **s, const char *line) {
211 const char *e, *q, *name;
212 sd_bus *bus;
213 void *p;
214 int r;
215
216 e = startswith(line, "subscribed=");
217 if (!e)
218 return 0;
219
220 q = strpbrk(e, WHITESPACE);
221 if (!q) {
222 if (m->api_bus) {
223 bus = m->api_bus;
224 name = e;
225 goto finish;
226 }
227
228 return 1;
229 }
230
231 if (sscanf(e, "%p", &p) != 1) {
232 log_debug("Failed to parse subscription pointer.");
233 return -EINVAL;
234 }
235
236 bus = set_get(m->private_buses, p);
237 if (!bus)
238 return 1;
239
240 name = q + strspn(q, WHITESPACE);
241
242 finish:
243 r = bus_client_track(s, bus, streq(name, "*") ? NULL : name);
244 if (r < 0) {
245 log_debug("Failed to deserialize client subscription: %s", strerror(-r));
246 return r;
247 }
248
249 return 1;
250 }