]> code.delx.au - pulseaudio/blob - src/modules/reserve-monitor.c
Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
[pulseaudio] / src / modules / reserve-monitor.c
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
36 struct rm_monitor {
37 int ref;
38
39 char *device_name;
40 char *service_name;
41
42 DBusConnection *connection;
43
44 unsigned busy:1;
45 unsigned filtering:1;
46 unsigned matching:1;
47
48 rm_change_cb_t change_cb;
49 void *userdata;
50 };
51
52 #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
53
54 static DBusHandlerResult filter_handler(
55 DBusConnection *c,
56 DBusMessage *s,
57 void *userdata) {
58
59 DBusMessage *reply;
60 rm_monitor *m;
61 DBusError error;
62
63 dbus_error_init(&error);
64
65 m = userdata;
66 assert(m->ref >= 1);
67
68 if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) {
69 const char *name, *old, *new;
70
71 if (!dbus_message_get_args(
72 s,
73 &error,
74 DBUS_TYPE_STRING, &name,
75 DBUS_TYPE_STRING, &old,
76 DBUS_TYPE_STRING, &new,
77 DBUS_TYPE_INVALID))
78 goto invalid;
79
80 if (strcmp(name, m->service_name) == 0) {
81 m->busy = !!(new && *new);
82
83 /* If we ourselves own the device, then don't consider this 'busy' */
84 if (m->busy) {
85 const char *un;
86
87 if ((un = dbus_bus_get_unique_name(c)))
88 if (strcmp(new, un) == 0)
89 m->busy = FALSE;
90 }
91
92 if (m->change_cb) {
93 m->ref++;
94 m->change_cb(m);
95 rm_release(m);
96 }
97 }
98 }
99
100 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
101
102 invalid:
103 if (!(reply = dbus_message_new_error(
104 s,
105 DBUS_ERROR_INVALID_ARGS,
106 "Invalid arguments")))
107 goto oom;
108
109 if (!dbus_connection_send(c, reply, NULL))
110 goto oom;
111
112 dbus_message_unref(reply);
113
114 dbus_error_free(&error);
115
116 return DBUS_HANDLER_RESULT_HANDLED;
117
118 oom:
119 if (reply)
120 dbus_message_unref(reply);
121
122 dbus_error_free(&error);
123
124 return DBUS_HANDLER_RESULT_NEED_MEMORY;
125 }
126
127 int rm_watch(
128 rm_monitor **_m,
129 DBusConnection *connection,
130 const char*device_name,
131 rm_change_cb_t change_cb,
132 DBusError *error) {
133
134 rm_monitor *m = NULL;
135 int r;
136 DBusError _error;
137
138 if (!error)
139 error = &_error;
140
141 dbus_error_init(error);
142
143 if (!_m)
144 return -EINVAL;
145
146 if (!connection)
147 return -EINVAL;
148
149 if (!device_name)
150 return -EINVAL;
151
152 if (!(m = calloc(sizeof(rm_monitor), 1)))
153 return -ENOMEM;
154
155 m->ref = 1;
156
157 if (!(m->device_name = strdup(device_name))) {
158 r = -ENOMEM;
159 goto fail;
160 }
161
162 m->connection = dbus_connection_ref(connection);
163 m->change_cb = change_cb;
164
165 if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
166 r = -ENOMEM;
167 goto fail;
168 }
169 sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name);
170
171 if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) {
172 r = -ENOMEM;
173 goto fail;
174 }
175
176 m->filtering = 1;
177
178 dbus_bus_add_match(m->connection,
179 "type='signal',"
180 "sender='" DBUS_SERVICE_DBUS "',"
181 "interface='" DBUS_INTERFACE_DBUS "',"
182 "member='NameOwnerChanged'", error);
183
184 if (dbus_error_is_set(error)) {
185 r = -EIO;
186 goto fail;
187 }
188
189 m->matching = 1;
190
191 m->busy = dbus_bus_name_has_owner(m->connection, m->service_name, error);
192
193 if (dbus_error_is_set(error)) {
194 r = -EIO;
195 goto fail;
196 }
197
198 *_m = m;
199 return 0;
200
201 fail:
202 if (&_error == error)
203 dbus_error_free(&_error);
204
205 if (m)
206 rm_release(m);
207
208 return r;
209 }
210
211 void rm_release(rm_monitor *m) {
212 if (!m)
213 return;
214
215 assert(m->ref > 0);
216
217 if (--m->ref > 0)
218 return;
219
220 if (m->matching)
221 dbus_bus_remove_match(
222 m->connection,
223 "type='signal',"
224 "sender='" DBUS_SERVICE_DBUS "',"
225 "interface='" DBUS_INTERFACE_DBUS "',"
226 "member='NameOwnerChanged'", NULL);
227
228 if (m->filtering)
229 dbus_connection_remove_filter(
230 m->connection,
231 filter_handler,
232 m);
233
234 free(m->device_name);
235 free(m->service_name);
236
237 if (m->connection)
238 dbus_connection_unref(m->connection);
239
240 free(m);
241 }
242
243 int rm_busy(rm_monitor *m) {
244 if (!m)
245 return -EINVAL;
246
247 assert(m->ref > 0);
248
249 return m->busy;
250 }
251
252 void rm_set_userdata(rm_monitor *m, void *userdata) {
253
254 if (!m)
255 return;
256
257 assert(m->ref > 0);
258 m->userdata = userdata;
259 }
260
261 void* rm_get_userdata(rm_monitor *m) {
262
263 if (!m)
264 return NULL;
265
266 assert(m->ref > 0);
267
268 return m->userdata;
269 }