• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, "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 
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