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