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