1 /** 2 * Copyright © 2016 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include <stdlib.h> 17 #include <string.h> 18 #include <stdio.h> 19 #include <errno.h> 20 #include <systemd/sd-bus.h> 21 #include "mapper.h" 22 23 static const char *async_wait_name_owner_match = 24 "type='signal'," 25 "sender='org.freedesktop.DBus'," 26 "interface='org.freedesktop.DBus'," 27 "member='NameOwnerChanged'," 28 "path='/org/freedesktop/DBus'"; 29 30 static const char *async_wait_interfaces_added_match = 31 "type='signal'," 32 "interface='org.freedesktop.DBus.ObjectManager'," 33 "member='InterfacesAdded'"; 34 35 struct mapper_async_wait 36 { 37 char **objs; 38 void (*callback)(int, void *); 39 void *userdata; 40 sd_bus *conn; 41 sd_bus_slot *name_owner_slot; 42 sd_bus_slot *intf_slot; 43 int *status; 44 int count; 45 int finished; 46 int r; 47 }; 48 49 struct async_wait_callback_data 50 { 51 mapper_async_wait *wait; 52 const char *path; 53 }; 54 55 static int async_wait_match_name_owner_changed(sd_bus_message *, void *, 56 sd_bus_error *); 57 static int async_wait_match_interfaces_added(sd_bus_message *, void *, 58 sd_bus_error *); 59 static int async_wait_check_done(mapper_async_wait *); 60 static void async_wait_done(int r, mapper_async_wait *); 61 static int async_wait_get_objects(mapper_async_wait *); 62 63 static int sarraylen(char *array[]) 64 { 65 int count = 0; 66 char **p = array; 67 68 while(*p != NULL) { 69 ++count; 70 ++p; 71 } 72 73 return count; 74 } 75 76 static void sarrayfree(char *array[]) 77 { 78 char **p = array; 79 while(*p != NULL) { 80 free(*p); 81 ++p; 82 } 83 free(array); 84 } 85 86 static char **sarraydup(char *array[]) 87 { 88 int count = sarraylen(array); 89 int i; 90 char **ret = NULL; 91 92 ret = malloc(sizeof(*ret) * count); 93 if(!ret) 94 return NULL; 95 96 for(i=0; i<count; ++i) { 97 ret[i] = strdup(array[i]); 98 if(!ret[i]) 99 goto error; 100 } 101 102 return ret; 103 104 error: 105 sarrayfree(ret); 106 return NULL; 107 } 108 109 static int async_wait_getobject_callback(sd_bus_message *m, 110 void *userdata, 111 sd_bus_error *e) 112 { 113 int i; 114 struct async_wait_callback_data *data = userdata; 115 mapper_async_wait *wait = data->wait; 116 117 if(wait->finished) 118 return 0; 119 if(sd_bus_message_get_errno(m)) 120 return 0; 121 122 for(i=0; i<wait->count; ++i) { 123 if(!strcmp(data->path, wait->objs[i])) { 124 wait->status[i] = 1; 125 } 126 } 127 128 free(data); 129 if(async_wait_check_done(wait)) 130 async_wait_done(0, wait); 131 132 return 0; 133 } 134 135 static int async_wait_get_objects(mapper_async_wait *wait) 136 { 137 int i, r; 138 struct async_wait_callback_data *data = NULL; 139 140 for(i=0; i<wait->count; ++i) { 141 if(wait->status[i]) 142 continue; 143 data = malloc(sizeof(*data)); 144 data->wait = wait; 145 data->path = wait->objs[i]; 146 r = sd_bus_call_method_async( 147 wait->conn, 148 NULL, 149 "org.openbmc.ObjectMapper", 150 "/org/openbmc/ObjectMapper", 151 "org.openbmc.ObjectMapper", 152 "GetObject", 153 async_wait_getobject_callback, 154 data, 155 "s", 156 wait->objs[i]); 157 if(r < 0) { 158 free(data); 159 fprintf(stderr, "Error invoking method: %s\n", 160 strerror(-r)); 161 return r; 162 } 163 } 164 165 return 0; 166 } 167 168 static int async_wait_match_name_owner_changed(sd_bus_message *m, void *w, 169 sd_bus_error *e) 170 { 171 int r; 172 173 mapper_async_wait *wait = w; 174 if(wait->finished) 175 return 0; 176 177 r = async_wait_get_objects(wait); 178 if(r < 0) 179 async_wait_done(r, wait); 180 181 return 0; 182 } 183 184 static int async_wait_match_interfaces_added(sd_bus_message *m, void *w, 185 sd_bus_error *e) 186 { 187 int i, r; 188 mapper_async_wait *wait = w; 189 const char *path; 190 191 if(wait->finished) 192 return 0; 193 194 r = sd_bus_message_read(m, "o", &path); 195 if (r < 0) { 196 fprintf(stderr, "Error reading message: %s\n", 197 strerror(-r)); 198 goto finished; 199 } 200 201 for(i=0; i<wait->count; ++i) { 202 if(!strcmp(path, wait->objs[i])) 203 wait->status[i] = 1; 204 } 205 206 finished: 207 if(r < 0 || async_wait_check_done(wait)) 208 async_wait_done(r < 0 ? r : 0, wait); 209 210 return 0; 211 } 212 213 static void async_wait_done(int r, mapper_async_wait *w) 214 { 215 if(w->finished) 216 return; 217 218 w->finished = 1; 219 sd_bus_slot_unref(w->name_owner_slot); 220 sd_bus_slot_unref(w->intf_slot); 221 222 if(w->callback) 223 w->callback(r, w->userdata); 224 } 225 226 static int async_wait_check_done(mapper_async_wait *w) 227 { 228 int i; 229 230 if(w->finished) 231 return 1; 232 233 for(i=0; i<w->count; ++i) 234 if(!w->status[i]) 235 return 0; 236 237 return 1; 238 } 239 240 void mapper_wait_async_free(mapper_async_wait *w) 241 { 242 free(w->status); 243 sarrayfree(w->objs); 244 free(w); 245 } 246 247 int mapper_wait_async(sd_bus *conn, 248 char *objs[], 249 void (*callback)(int, void *), 250 void *userdata, 251 mapper_async_wait **w) 252 { 253 int r; 254 mapper_async_wait *wait = NULL; 255 256 wait = malloc(sizeof(*wait)); 257 if(!wait) 258 return -ENOMEM; 259 260 memset(wait, 0, sizeof(*wait)); 261 wait->conn = conn; 262 wait->callback = callback; 263 wait->userdata = userdata; 264 wait->count = sarraylen(objs); 265 if(!wait->count) 266 return 0; 267 268 wait->objs = sarraydup(objs); 269 if(!wait->objs) { 270 r = -ENOMEM; 271 goto free_wait; 272 } 273 274 wait->status = malloc(sizeof(*wait->status) * wait->count); 275 if(!wait->status) { 276 r = -ENOMEM; 277 goto free_objs; 278 } 279 memset(wait->status, 0, sizeof(*wait->status) * wait->count); 280 281 r = sd_bus_add_match(conn, 282 &wait->name_owner_slot, 283 async_wait_name_owner_match, 284 async_wait_match_name_owner_changed, 285 wait); 286 if(r < 0) { 287 fprintf(stderr, "Error adding match rule: %s\n", 288 strerror(-r)); 289 goto free_status; 290 } 291 292 r = sd_bus_add_match(conn, 293 &wait->intf_slot, 294 async_wait_interfaces_added_match, 295 async_wait_match_interfaces_added, 296 wait); 297 if(r < 0) { 298 fprintf(stderr, "Error adding match rule: %s\n", 299 strerror(-r)); 300 goto unref_name_slot; 301 } 302 303 r = async_wait_get_objects(wait); 304 if(r < 0) { 305 fprintf(stderr, "Error calling method: %s\n", 306 strerror(-r)); 307 goto unref_intf_slot; 308 } 309 310 *w = wait; 311 312 return 0; 313 314 unref_intf_slot: 315 sd_bus_slot_unref(wait->intf_slot); 316 unref_name_slot: 317 sd_bus_slot_unref(wait->name_owner_slot); 318 free_status: 319 free(wait->status); 320 free_objs: 321 sarrayfree(wait->objs); 322 free_wait: 323 free(wait); 324 325 return r; 326 } 327 328 int mapper_get_service(sd_bus *conn, const char *obj, char **service) 329 { 330 sd_bus_error error = SD_BUS_ERROR_NULL; 331 sd_bus_message *request = NULL, *reply = NULL; 332 const char *tmp; 333 int r; 334 335 r = sd_bus_message_new_method_call( 336 conn, 337 &request, 338 "org.openbmc.ObjectMapper", 339 "/org/openbmc/ObjectMapper", 340 "org.openbmc.ObjectMapper", 341 "GetObject"); 342 if (r < 0) 343 goto exit; 344 345 r = sd_bus_message_append(request, "s", obj); 346 if (r < 0) 347 goto exit; 348 349 r = sd_bus_call(conn, request, 0, &error, &reply); 350 if (r < 0) 351 goto exit; 352 353 r = sd_bus_message_enter_container(reply, 0, NULL); 354 if (r < 0) 355 goto exit; 356 357 r = sd_bus_message_enter_container(reply, 0, NULL); 358 if (r < 0) 359 goto exit; 360 361 r = sd_bus_message_read(reply, "s", &tmp); 362 if (r < 0) 363 goto exit; 364 365 *service = strdup(tmp); 366 367 exit: 368 sd_bus_error_free(&error); 369 sd_bus_message_unref(request); 370 sd_bus_message_unref(reply); 371 372 return r; 373 } 374