hashmap: Add pa_hashmap_remove_all()
[platform/upstream/pulseaudio.git] / 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 #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
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
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, "org.freedesktop.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
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
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
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
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
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 }