chiark / gitweb /
src/core/selinux-access: #include <sys/socket.h>
[elogind.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 }