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 37struct 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 63static 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 80static 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 118invalid: 119 dbus_error_free(&error); 120 121 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 122} 123 124int 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 200fail: 201 if (&_error == error) 202 dbus_error_free(&_error); 203 204 if (m) 205 rm_release(m); 206 207 return r; 208} 209 210void 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 241int rm_busy(rm_monitor *m) { 242 if (!m) 243 return -EINVAL; 244 245 assert(m->ref > 0); 246 247 return m->busy; 248} 249 250void 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 259void* 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