1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
2
3 /***
4 Copyright 2009 Lennart Poettering
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 ***/
26
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <assert.h>
33
34 #include "reserve-monitor.h"
35 #include "reserve.h"
36
37 struct rm_monitor {
38 int ref;
39
40 char *device_name;
41 char *service_name;
42 char *match;
43
44 DBusConnection *connection;
45
46 unsigned busy:1;
47 unsigned filtering:1;
48 unsigned matching:1;
49
50 rm_change_cb_t change_cb;
51 void *userdata;
52 };
53
54 #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
55
56 #define SERVICE_FILTER \
57 "type='signal'," \
58 "sender='" DBUS_SERVICE_DBUS "'," \
59 "interface='" DBUS_INTERFACE_DBUS "'," \
60 "member='NameOwnerChanged'," \
61 "arg0='%s'"
62
get_busy(DBusConnection * c,const char * name_owner)63 static unsigned get_busy(
64 DBusConnection *c,
65 const char *name_owner) {
66
67 const char *un;
68
69 if (!name_owner || !*name_owner)
70 return FALSE;
71
72 /* If we ourselves own the device, then don't consider this 'busy' */
73 if ((un = dbus_bus_get_unique_name(c)))
74 if (strcmp(name_owner, un) == 0)
75 return FALSE;
76
77 return TRUE;
78 }
79
filter_handler(DBusConnection * c,DBusMessage * s,void * userdata)80 static DBusHandlerResult filter_handler(
81 DBusConnection *c,
82 DBusMessage *s,
83 void *userdata) {
84
85 rm_monitor *m;
86 DBusError error;
87
88 dbus_error_init(&error);
89
90 m = userdata;
91 assert(m->ref >= 1);
92
93 if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
94 const char *name, *old, *new;
95
96 if (!dbus_message_get_args(
97 s,
98 &error,
99 DBUS_TYPE_STRING, &name,
100 DBUS_TYPE_STRING, &old,
101 DBUS_TYPE_STRING, &new,
102 DBUS_TYPE_INVALID))
103 goto invalid;
104
105 if (strcmp(name, m->service_name) == 0) {
106 unsigned old_busy = m->busy;
107
108 m->busy = get_busy(c, new);
109
110 if (m->busy != old_busy && m->change_cb) {
111 m->ref++;
112 m->change_cb(m);
113 rm_release(m);
114 }
115 }
116 }
117
118 invalid:
119 dbus_error_free(&error);
120
121 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
122 }
123
rm_watch(rm_monitor ** _m,DBusConnection * connection,const char * device_name,rm_change_cb_t change_cb,DBusError * error)124 int rm_watch(
125 rm_monitor **_m,
126 DBusConnection *connection,
127 const char *device_name,
128 rm_change_cb_t change_cb,
129 DBusError *error) {
130
131 rm_monitor *m = NULL;
132 char *name_owner;
133 int r;
134 DBusError _error;
135
136 if (!error)
137 error = &_error;
138
139 dbus_error_init(error);
140
141 if (!_m)
142 return -EINVAL;
143
144 if (!connection)
145 return -EINVAL;
146
147 if (!device_name)
148 return -EINVAL;
149
150 if (!(m = calloc(sizeof(rm_monitor), 1)))
151 return -ENOMEM;
152
153 m->ref = 1;
154
155 if (!(m->device_name = strdup(device_name))) {
156 r = -ENOMEM;
157 goto fail;
158 }
159
160 m->connection = dbus_connection_ref(connection);
161 m->change_cb = change_cb;
162
163 if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
164 r = -ENOMEM;
165 goto fail;
166 }
167 sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name);
168
169 if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) {
170 r = -ENOMEM;
171 goto fail;
172 }
173
174 m->filtering = 1;
175
176 if (!(m->match = malloc(sizeof(SERVICE_FILTER) - 2 + strlen(m->service_name)))) {
177 r = -ENOMEM;
178 goto fail;
179 }
180
181 sprintf(m->match, SERVICE_FILTER, m->service_name);
182 dbus_bus_add_match(m->connection, m->match, error);
183
184 if (dbus_error_is_set(error)) {
185 r = -EIO;
186 goto fail;
187 }
188
189 m->matching = 1;
190
191 if ((r = rd_dbus_get_name_owner(m->connection, m->service_name, &name_owner, error)) < 0)
192 goto fail;
193
194 m->busy = get_busy(m->connection, name_owner);
195 free(name_owner);
196
197 *_m = m;
198 return 0;
199
200 fail:
201 if (&_error == error)
202 dbus_error_free(&_error);
203
204 if (m)
205 rm_release(m);
206
207 return r;
208 }
209
rm_release(rm_monitor * m)210 void rm_release(rm_monitor *m) {
211 if (!m)
212 return;
213
214 assert(m->ref > 0);
215
216 if (--m->ref > 0)
217 return;
218
219 if (m->matching)
220 dbus_bus_remove_match(
221 m->connection,
222 m->match,
223 NULL);
224
225 if (m->filtering)
226 dbus_connection_remove_filter(
227 m->connection,
228 filter_handler,
229 m);
230
231 free(m->device_name);
232 free(m->service_name);
233 free(m->match);
234
235 if (m->connection)
236 dbus_connection_unref(m->connection);
237
238 free(m);
239 }
240
rm_busy(rm_monitor * m)241 int rm_busy(rm_monitor *m) {
242 if (!m)
243 return -EINVAL;
244
245 assert(m->ref > 0);
246
247 return m->busy;
248 }
249
rm_set_userdata(rm_monitor * m,void * userdata)250 void rm_set_userdata(rm_monitor *m, void *userdata) {
251
252 if (!m)
253 return;
254
255 assert(m->ref > 0);
256 m->userdata = userdata;
257 }
258
rm_get_userdata(rm_monitor * m)259 void* rm_get_userdata(rm_monitor *m) {
260
261 if (!m)
262 return NULL;
263
264 assert(m->ref > 0);
265
266 return m->userdata;
267 }
268