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 "config.h" 17 #include <stdlib.h> 18 #include <string.h> 19 #include <stdio.h> 20 #include <errno.h> 21 #include <unistd.h> 22 #include <sys/timerfd.h> 23 #include <systemd/sd-bus.h> 24 #include <systemd/sd-event.h> 25 #include "mapper.h" 26 27 static const char *async_wait_introspection_match = 28 "type='signal'," 29 "sender='xyz.openbmc_project.ObjectMapper'," 30 "interface='xyz.openbmc_project.ObjectMapper.Private'," 31 "member='IntrospectionComplete'"; 32 33 static const char *async_wait_interfaces_added_match = 34 "type='signal'," 35 "interface='org.freedesktop.DBus.ObjectManager'," 36 "member='InterfacesAdded'"; 37 38 static const char *interfaces_removed_match = 39 "type='signal'," 40 "interface='org.freedesktop.DBus.ObjectManager'," 41 "member='InterfacesRemoved'"; 42 43 static const int mapper_busy_retries = 5; 44 static const uint64_t mapper_busy_delay_interval_usec = 1000000; 45 46 struct mapper_async_wait 47 { 48 char **objs; 49 void (*callback)(int, void *); 50 void *userdata; 51 sd_event *loop; 52 sd_bus *conn; 53 sd_bus_slot *introspection_slot; 54 sd_bus_slot *intf_slot; 55 int *status; 56 int count; 57 int finished; 58 int r; 59 }; 60 61 struct async_wait_callback_data 62 { 63 mapper_async_wait *wait; 64 const char *path; 65 sd_event_source *event_source; 66 int retry; 67 }; 68 69 struct mapper_async_subtree 70 { 71 char *namespace; 72 char *interface; 73 void (*callback)(int, void *); 74 void *userdata; 75 sd_event *loop; 76 sd_bus *conn; 77 sd_bus_slot *slot; 78 sd_event_source *event_source; 79 int finished; 80 int op; 81 int retry; 82 }; 83 84 static int async_wait_match_introspection_complete(sd_bus_message *, void *, 85 sd_bus_error *); 86 static int async_wait_check_done(mapper_async_wait *); 87 static void async_wait_done(int r, mapper_async_wait *); 88 static int async_wait_get_objects(mapper_async_wait *); 89 static int async_wait_getobject_callback(sd_bus_message *, void *, 90 sd_bus_error *); 91 92 static int async_subtree_match_callback(sd_bus_message *, void *, 93 sd_bus_error *); 94 static void async_subtree_done(int r, mapper_async_subtree *); 95 static int async_subtree_getpaths(mapper_async_subtree *); 96 static int async_subtree_getpaths_callback(sd_bus_message *, void *, 97 sd_bus_error *); 98 99 static int sarraylen(char *array[]) 100 { 101 int count = 0; 102 char **p = array; 103 104 while (*p != NULL) 105 { 106 ++count; 107 ++p; 108 } 109 110 return count; 111 } 112 113 static void sarrayfree(char *array[]) 114 { 115 char **p = array; 116 while (*p != NULL) 117 { 118 free(*p); 119 ++p; 120 } 121 free(array); 122 } 123 124 static char **sarraydup(char *array[]) 125 { 126 int count = sarraylen(array); 127 int i; 128 char **ret = NULL; 129 130 ret = malloc(sizeof(*ret) * count); 131 if (!ret) 132 return NULL; 133 134 for (i = 0; i < count; ++i) 135 { 136 ret[i] = strdup(array[i]); 137 if (!ret[i]) 138 goto error; 139 } 140 141 return ret; 142 143 error: 144 sarrayfree(ret); 145 return NULL; 146 } 147 148 static int async_wait_timeout_callback(sd_event_source *s, uint64_t usec, 149 void *userdata) 150 { 151 int r; 152 struct async_wait_callback_data *data = userdata; 153 mapper_async_wait *wait = data->wait; 154 155 sd_event_source_unref(data->event_source); 156 r = sd_bus_call_method_async(wait->conn, NULL, MAPPER_BUSNAME, MAPPER_PATH, 157 MAPPER_INTERFACE, "GetObject", 158 async_wait_getobject_callback, data, "sas", 159 data->path, 0, NULL); 160 if (r < 0) 161 { 162 async_wait_done(r, wait); 163 free(data); 164 } 165 166 return 0; 167 } 168 169 static int async_wait_getobject_callback(sd_bus_message *m, void *userdata, 170 sd_bus_error *e) 171 { 172 int i, r; 173 struct async_wait_callback_data *data = userdata; 174 mapper_async_wait *wait = data->wait; 175 uint64_t now; 176 177 if (wait->finished) 178 goto exit; 179 180 r = sd_bus_message_get_errno(m); 181 if (r == ENOENT) 182 goto exit; 183 184 if (r == EBUSY && data->retry < mapper_busy_retries) 185 { 186 r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &now); 187 if (r < 0) 188 { 189 async_wait_done(r, wait); 190 goto exit; 191 } 192 193 ++data->retry; 194 r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC, 195 now + mapper_busy_delay_interval_usec, 0, 196 async_wait_timeout_callback, data); 197 if (r < 0) 198 { 199 async_wait_done(r, wait); 200 goto exit; 201 } 202 203 return 0; 204 } 205 206 if (r) 207 { 208 async_wait_done(-r, wait); 209 goto exit; 210 } 211 212 for (i = 0; i < wait->count; ++i) 213 { 214 if (!strcmp(data->path, wait->objs[i])) 215 { 216 wait->status[i] = 1; 217 } 218 } 219 220 if (async_wait_check_done(wait)) 221 async_wait_done(0, wait); 222 223 exit: 224 free(data); 225 return 0; 226 } 227 228 static int async_wait_get_objects(mapper_async_wait *wait) 229 { 230 int i, r; 231 struct async_wait_callback_data *data = NULL; 232 233 for (i = 0; i < wait->count; ++i) 234 { 235 if (wait->status[i]) 236 continue; 237 data = malloc(sizeof(*data)); 238 data->wait = wait; 239 data->path = wait->objs[i]; 240 data->retry = 0; 241 data->event_source = NULL; 242 r = sd_bus_call_method_async(wait->conn, NULL, MAPPER_BUSNAME, 243 MAPPER_PATH, MAPPER_INTERFACE, "GetObject", 244 async_wait_getobject_callback, data, "sas", 245 wait->objs[i], 0, NULL); 246 if (r < 0) 247 { 248 free(data); 249 fprintf(stderr, "Error invoking method: %s\n", strerror(-r)); 250 return r; 251 } 252 } 253 254 return 0; 255 } 256 257 static int async_wait_match_introspection_complete(sd_bus_message *m, void *w, 258 sd_bus_error *e) 259 { 260 int r; 261 262 mapper_async_wait *wait = w; 263 if (wait->finished) 264 return 0; 265 266 r = async_wait_get_objects(wait); 267 if (r < 0) 268 async_wait_done(r, wait); 269 270 return 0; 271 } 272 273 static void async_wait_done(int r, mapper_async_wait *w) 274 { 275 if (w->finished) 276 return; 277 278 w->finished = 1; 279 sd_bus_slot_unref(w->introspection_slot); 280 sd_bus_slot_unref(w->intf_slot); 281 282 if (w->callback) 283 w->callback(r, w->userdata); 284 } 285 286 static int async_wait_check_done(mapper_async_wait *w) 287 { 288 int i; 289 290 if (w->finished) 291 return 1; 292 293 for (i = 0; i < w->count; ++i) 294 if (!w->status[i]) 295 return 0; 296 297 return 1; 298 } 299 300 void mapper_wait_async_free(mapper_async_wait *w) 301 { 302 free(w->status); 303 sarrayfree(w->objs); 304 free(w); 305 } 306 307 int mapper_wait_async(sd_bus *conn, sd_event *loop, char *objs[], 308 void (*callback)(int, void *), void *userdata, 309 mapper_async_wait **w) 310 { 311 int r; 312 mapper_async_wait *wait = NULL; 313 314 wait = malloc(sizeof(*wait)); 315 if (!wait) 316 return -ENOMEM; 317 318 memset(wait, 0, sizeof(*wait)); 319 wait->conn = conn; 320 wait->loop = loop; 321 wait->callback = callback; 322 wait->userdata = userdata; 323 wait->count = sarraylen(objs); 324 if (!wait->count) 325 return 0; 326 327 wait->objs = sarraydup(objs); 328 if (!wait->objs) 329 { 330 r = -ENOMEM; 331 goto free_wait; 332 } 333 334 wait->status = malloc(sizeof(*wait->status) * wait->count); 335 if (!wait->status) 336 { 337 r = -ENOMEM; 338 goto free_objs; 339 } 340 memset(wait->status, 0, sizeof(*wait->status) * wait->count); 341 342 r = sd_bus_add_match(conn, &wait->introspection_slot, 343 async_wait_introspection_match, 344 async_wait_match_introspection_complete, wait); 345 if (r < 0) 346 { 347 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 348 goto free_status; 349 } 350 351 r = sd_bus_add_match(conn, &wait->intf_slot, 352 async_wait_interfaces_added_match, 353 async_wait_match_introspection_complete, wait); 354 if (r < 0) 355 { 356 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 357 goto unref_name_slot; 358 } 359 360 r = async_wait_get_objects(wait); 361 if (r < 0) 362 { 363 fprintf(stderr, "Error calling method: %s\n", strerror(-r)); 364 goto unref_intf_slot; 365 } 366 367 *w = wait; 368 369 return 0; 370 371 unref_intf_slot: 372 sd_bus_slot_unref(wait->intf_slot); 373 unref_name_slot: 374 sd_bus_slot_unref(wait->introspection_slot); 375 free_status: 376 free(wait->status); 377 free_objs: 378 sarrayfree(wait->objs); 379 free_wait: 380 free(wait); 381 382 return r; 383 } 384 385 static int async_subtree_timeout_callback(sd_event_source *s, uint64_t usec, 386 void *userdata) 387 { 388 int r; 389 struct mapper_async_subtree *subtree = userdata; 390 391 sd_event_source_unref(subtree->event_source); 392 r = sd_bus_call_method_async( 393 subtree->conn, NULL, MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, 394 "GetSubTreePaths", async_subtree_getpaths_callback, subtree, "sias", 395 subtree->namespace, 0, 1, subtree->interface); 396 if (r < 0) 397 async_subtree_done(r, subtree); 398 399 return 0; 400 } 401 402 static int async_subtree_getpaths_callback(sd_bus_message *m, void *userdata, 403 sd_bus_error *e) 404 { 405 int r; 406 char *intf = NULL; 407 struct mapper_async_subtree *subtree = userdata; 408 uint64_t now; 409 410 if (subtree->finished) 411 goto exit; 412 413 r = sd_bus_message_get_errno(m); 414 415 if (r == ENOENT) 416 { 417 if (subtree->op == MAPPER_OP_REMOVE) 418 r = 0; 419 else 420 goto exit; 421 } 422 423 if (r == EBUSY && subtree->retry < mapper_busy_retries) 424 { 425 r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &now); 426 if (r < 0) 427 { 428 async_subtree_done(r, subtree); 429 goto exit; 430 } 431 432 ++subtree->retry; 433 r = sd_event_add_time(subtree->loop, &subtree->event_source, 434 CLOCK_MONOTONIC, 435 now + mapper_busy_delay_interval_usec, 0, 436 async_subtree_timeout_callback, subtree); 437 if (r < 0) 438 { 439 async_subtree_done(r, subtree); 440 goto exit; 441 } 442 443 return 0; 444 } 445 446 if (r) 447 { 448 async_subtree_done(-r, subtree); 449 goto exit; 450 } 451 452 sd_bus_message_read(m, "as", 1, &intf); 453 if (subtree->op == MAPPER_OP_REMOVE) 454 { 455 /* For remove, operation is complete when the interface is not present 456 */ 457 if (intf == NULL) 458 async_subtree_done(0, subtree); 459 } 460 461 exit: 462 return 0; 463 } 464 465 static int async_subtree_getpaths(mapper_async_subtree *subtree) 466 { 467 int r = 0; 468 469 subtree->retry = 0; 470 subtree->event_source = NULL; 471 r = sd_bus_call_method_async( 472 subtree->conn, NULL, MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, 473 "GetSubTreePaths", async_subtree_getpaths_callback, subtree, "sias", 474 subtree->namespace, 0, 1, subtree->interface); 475 if (r < 0) 476 { 477 fprintf(stderr, "Error invoking method: %s\n", strerror(-r)); 478 return r; 479 } 480 481 return 0; 482 } 483 484 static int async_subtree_match_callback(sd_bus_message *m, void *t, 485 sd_bus_error *e) 486 { 487 int r; 488 489 mapper_async_subtree *subtree = t; 490 if (subtree->finished) 491 return 0; 492 493 r = async_subtree_getpaths(subtree); 494 if (r < 0) 495 async_subtree_done(r, subtree); 496 497 return 0; 498 } 499 500 static void async_subtree_done(int r, mapper_async_subtree *t) 501 { 502 if (t->finished) 503 return; 504 505 t->finished = 1; 506 sd_bus_slot_unref(t->slot); 507 508 if (t->callback) 509 t->callback(r, t->userdata); 510 } 511 512 int mapper_subtree_async(sd_bus *conn, sd_event *loop, char *namespace, 513 char *interface, void (*callback)(int, void *), 514 void *userdata, mapper_async_subtree **t, int op) 515 { 516 int r = 0; 517 mapper_async_subtree *subtree = NULL; 518 519 subtree = malloc(sizeof(*subtree)); 520 if (!subtree) 521 return -ENOMEM; 522 523 memset(subtree, 0, sizeof(*subtree)); 524 subtree->conn = conn; 525 subtree->loop = loop; 526 subtree->namespace = namespace; 527 subtree->interface = interface; 528 subtree->callback = callback; 529 subtree->userdata = userdata; 530 subtree->op = op; 531 532 if (subtree->op == MAPPER_OP_REMOVE) 533 { 534 r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match, 535 async_subtree_match_callback, subtree); 536 if (r < 0) 537 { 538 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 539 goto unref_slot; 540 } 541 } 542 else 543 { 544 /* Operation not supported */ 545 r = -EINVAL; 546 goto free_subtree; 547 } 548 549 r = async_subtree_getpaths(subtree); 550 if (r < 0) 551 { 552 fprintf(stderr, "Error calling method: %s\n", strerror(-r)); 553 goto unref_slot; 554 } 555 556 *t = subtree; 557 558 return 0; 559 560 unref_slot: 561 sd_bus_slot_unref(subtree->slot); 562 free_subtree: 563 free(subtree); 564 565 return r; 566 } 567 568 int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply) 569 { 570 sd_bus_error error = SD_BUS_ERROR_NULL; 571 sd_bus_message *request = NULL; 572 int r, retry = 0; 573 574 r = sd_bus_message_new_method_call(conn, &request, MAPPER_BUSNAME, 575 MAPPER_PATH, MAPPER_INTERFACE, 576 "GetObject"); 577 if (r < 0) 578 goto exit; 579 580 r = sd_bus_message_append(request, "s", obj); 581 if (r < 0) 582 goto exit; 583 r = sd_bus_message_append(request, "as", 0, NULL); 584 if (r < 0) 585 goto exit; 586 587 while (retry < mapper_busy_retries) 588 { 589 sd_bus_error_free(&error); 590 r = sd_bus_call(conn, request, 0, &error, reply); 591 if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) 592 { 593 ++retry; 594 595 if (retry != mapper_busy_retries) 596 usleep(mapper_busy_delay_interval_usec); 597 continue; 598 } 599 break; 600 } 601 602 if (r < 0) 603 goto exit; 604 605 exit: 606 sd_bus_error_free(&error); 607 sd_bus_message_unref(request); 608 609 return r; 610 } 611 612 int mapper_get_service(sd_bus *conn, const char *obj, char **service) 613 { 614 sd_bus_message *reply = NULL; 615 const char *tmp; 616 int r; 617 618 r = mapper_get_object(conn, obj, &reply); 619 if (r < 0) 620 goto exit; 621 622 r = sd_bus_message_enter_container(reply, 0, NULL); 623 if (r < 0) 624 goto exit; 625 626 r = sd_bus_message_enter_container(reply, 0, NULL); 627 if (r < 0) 628 goto exit; 629 630 r = sd_bus_message_read(reply, "s", &tmp); 631 if (r < 0) 632 goto exit; 633 634 *service = strdup(tmp); 635 636 exit: 637 sd_bus_message_unref(reply); 638 639 return r; 640 } 641