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 <errno.h> 19 #include <stdbool.h> 20 #include <stddef.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/timerfd.h> 25 #include <systemd/sd-bus.h> 26 #include <systemd/sd-event.h> 27 #include <unistd.h> 28 29 #include "internal.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 return NULL; 138 139 for (i = 0; i < count; ++i) 140 { 141 ret[i] = strdup(array[i]); 142 if (!ret[i]) 143 goto error; 144 } 145 146 return ret; 147 148 error: 149 sarrayfree(ret); 150 return NULL; 151 } 152 153 static int async_wait_timeout_callback(_unused_ sd_event_source* s, 154 _unused_ uint64_t usec, void* userdata) 155 { 156 int r; 157 struct async_wait_callback_data* data = userdata; 158 mapper_async_wait* wait = data->wait; 159 160 sd_event_source_unref(data->event_source); 161 r = sd_bus_call_method_async( 162 wait->conn, NULL, "xyz.openbmc_project.ObjectMapper", 163 "/xyz/openbmc_project/object_mapper", 164 "xyz.openbmc_project.ObjectMapper", "GetObject", 165 async_wait_getobject_callback, data, "sas", data->path, 0, NULL); 166 if (r < 0) 167 { 168 async_wait_done(r, wait); 169 free(data); 170 } 171 172 return 0; 173 } 174 175 static int async_wait_getobject_callback(sd_bus_message* m, void* userdata, 176 _unused_ sd_bus_error* e) 177 { 178 size_t i; 179 int r; 180 struct async_wait_callback_data* data = userdata; 181 mapper_async_wait* wait = data->wait; 182 uint64_t next_retry; 183 184 if (wait->finished) 185 goto exit; 186 187 if (sd_bus_message_is_method_error( 188 m, "xyz.openbmc_project.Common.Error.ResourceNotFound")) 189 goto exit; 190 191 r = sd_bus_message_get_errno(m); 192 193 if ((r == EBUSY || r == ENOBUFS) && data->retry < mapper_busy_retries) 194 { 195 r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &next_retry); 196 if (r < 0) 197 { 198 async_wait_done(r, wait); 199 goto exit; 200 } 201 202 next_retry += mapper_busy_delay_interval_usec * (1 << data->retry); 203 r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC, 204 next_retry, 0, async_wait_timeout_callback, data); 205 ++data->retry; 206 if (r < 0) 207 { 208 async_wait_done(r, wait); 209 goto exit; 210 } 211 212 return 0; 213 } 214 215 if (r) 216 { 217 async_wait_done(-r, wait); 218 goto exit; 219 } 220 221 for (i = 0; i < wait->count; ++i) 222 { 223 if (!strcmp(data->path, wait->objs[i])) 224 { 225 wait->status[i] = 1; 226 } 227 } 228 229 if (async_wait_check_done(wait)) 230 async_wait_done(0, wait); 231 232 exit: 233 free(data); 234 return 0; 235 } 236 237 static int async_wait_get_objects(mapper_async_wait* wait) 238 { 239 size_t i; 240 int r; 241 struct async_wait_callback_data* data = NULL; 242 243 for (i = 0; i < wait->count; ++i) 244 { 245 if (wait->status[i]) 246 continue; 247 data = malloc(sizeof(*data)); 248 data->wait = wait; 249 data->path = wait->objs[i]; 250 data->retry = 0; 251 data->event_source = NULL; 252 r = sd_bus_call_method_async( 253 wait->conn, NULL, "xyz.openbmc_project.ObjectMapper", 254 "/xyz/openbmc_project/object_mapper", 255 "xyz.openbmc_project.ObjectMapper", "GetObject", 256 async_wait_getobject_callback, data, "sas", wait->objs[i], 0, NULL); 257 if (r < 0) 258 { 259 free(data); 260 fprintf(stderr, "Error invoking method: %s\n", strerror(-r)); 261 return r; 262 } 263 } 264 265 return 0; 266 } 267 268 static int async_wait_match_introspection_complete(_unused_ sd_bus_message* m, 269 void* w, 270 _unused_ sd_bus_error* e) 271 { 272 int r; 273 274 mapper_async_wait* wait = w; 275 if (wait->finished) 276 return 0; 277 278 r = async_wait_get_objects(wait); 279 if (r < 0) 280 async_wait_done(r, wait); 281 282 return 0; 283 } 284 285 static void async_wait_done(int r, mapper_async_wait* w) 286 { 287 if (w->finished) 288 return; 289 290 w->finished = 1; 291 sd_bus_slot_unref(w->introspection_slot); 292 sd_bus_slot_unref(w->intf_slot); 293 294 if (w->callback) 295 w->callback(r, w->userdata); 296 } 297 298 static int async_wait_check_done(mapper_async_wait* w) 299 { 300 size_t i; 301 302 if (w->finished) 303 return 1; 304 305 for (i = 0; i < w->count; ++i) 306 if (!w->status[i]) 307 return 0; 308 309 return 1; 310 } 311 312 _public_ void mapper_wait_async_free(mapper_async_wait* w) 313 { 314 free(w->status); 315 sarrayfree(w->objs); 316 free(w); 317 } 318 319 _public_ int mapper_wait_async(sd_bus* conn, sd_event* loop, char* objs[], 320 void (*callback)(int, void*), void* userdata, 321 mapper_async_wait** w) 322 { 323 int r; 324 mapper_async_wait* wait = NULL; 325 326 wait = malloc(sizeof(*wait)); 327 if (!wait) 328 return -ENOMEM; 329 330 memset(wait, 0, sizeof(*wait)); 331 wait->conn = conn; 332 wait->loop = loop; 333 wait->callback = callback; 334 wait->userdata = userdata; 335 wait->count = sarraylen(objs); 336 if (!wait->count) 337 { 338 r = 0; 339 goto free_wait; 340 } 341 342 wait->objs = sarraydup(objs); 343 if (!wait->objs) 344 { 345 r = -ENOMEM; 346 goto free_wait; 347 } 348 349 wait->status = malloc(sizeof(*wait->status) * wait->count); 350 if (!wait->status) 351 { 352 r = -ENOMEM; 353 goto free_objs; 354 } 355 memset(wait->status, 0, sizeof(*wait->status) * wait->count); 356 357 r = sd_bus_add_match(conn, &wait->introspection_slot, 358 async_wait_introspection_match, 359 async_wait_match_introspection_complete, wait); 360 if (r < 0) 361 { 362 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 363 goto free_status; 364 } 365 366 r = sd_bus_add_match(conn, &wait->intf_slot, 367 async_wait_interfaces_added_match, 368 async_wait_match_introspection_complete, wait); 369 if (r < 0) 370 { 371 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 372 goto unref_name_slot; 373 } 374 375 r = async_wait_get_objects(wait); 376 if (r < 0) 377 { 378 fprintf(stderr, "Error calling method: %s\n", strerror(-r)); 379 goto unref_intf_slot; 380 } 381 382 *w = wait; 383 384 return 0; 385 386 unref_intf_slot: 387 sd_bus_slot_unref(wait->intf_slot); 388 unref_name_slot: 389 sd_bus_slot_unref(wait->introspection_slot); 390 free_status: 391 free(wait->status); 392 free_objs: 393 sarrayfree(wait->objs); 394 free_wait: 395 free(wait); 396 397 return r; 398 } 399 400 static int async_subtree_timeout_callback(_unused_ sd_event_source* s, 401 _unused_ uint64_t usec, 402 void* userdata) 403 { 404 int r; 405 struct mapper_async_subtree* subtree = userdata; 406 407 sd_event_source_unref(subtree->event_source); 408 r = sd_bus_call_method_async( 409 subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper", 410 "/xyz/openbmc_project/object_mapper", 411 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 412 async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0, 413 1, subtree->interface); 414 if (r < 0) 415 async_subtree_done(r, subtree); 416 417 return 0; 418 } 419 420 static int async_subtree_getpaths_callback(sd_bus_message* m, void* userdata, 421 _unused_ sd_bus_error* e) 422 { 423 int r; 424 struct mapper_async_subtree* subtree = userdata; 425 uint64_t next_retry; 426 427 if (subtree->finished) 428 goto exit; 429 430 r = sd_bus_message_get_errno(m); 431 432 if (sd_bus_message_is_method_error( 433 m, "xyz.openbmc_project.Common.Error.ResourceNotFound")) 434 { 435 if (subtree->op == MAPPER_OP_REMOVE) 436 r = 0; 437 else 438 goto exit; 439 } 440 441 if ((r == EBUSY || r == ENOBUFS) && subtree->retry < mapper_busy_retries) 442 { 443 r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &next_retry); 444 if (r < 0) 445 { 446 async_subtree_done(r, subtree); 447 goto exit; 448 } 449 450 next_retry += mapper_busy_delay_interval_usec * (1 << subtree->retry); 451 r = sd_event_add_time(subtree->loop, &subtree->event_source, 452 CLOCK_MONOTONIC, next_retry, 0, 453 async_subtree_timeout_callback, subtree); 454 ++subtree->retry; 455 if (r < 0) 456 { 457 async_subtree_done(r, subtree); 458 goto exit; 459 } 460 461 return 0; 462 } 463 464 if (r) 465 { 466 async_subtree_done(-r, subtree); 467 goto exit; 468 } 469 470 if (subtree->op == MAPPER_OP_REMOVE) 471 { 472 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); 473 if (r < 0) 474 { 475 async_subtree_done(r, subtree); 476 goto exit; 477 } 478 479 r = sd_bus_message_at_end(m, false); 480 if (r < 0) 481 { 482 async_subtree_done(r, subtree); 483 goto exit; 484 } 485 486 /* For remove, operation is complete when the interface is not present 487 * we know it is empty if the returned array is empty 488 */ 489 if (r) 490 async_subtree_done(0, subtree); 491 } 492 493 exit: 494 return 0; 495 } 496 497 static int async_subtree_getpaths(mapper_async_subtree* subtree) 498 { 499 int r = 0; 500 501 subtree->retry = 0; 502 subtree->event_source = NULL; 503 r = sd_bus_call_method_async( 504 subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper", 505 "/xyz/openbmc_project/object_mapper", 506 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 507 async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0, 508 1, subtree->interface); 509 if (r < 0) 510 { 511 fprintf(stderr, "Error invoking method: %s\n", strerror(-r)); 512 return r; 513 } 514 515 return 0; 516 } 517 518 static int async_subtree_match_callback(_unused_ sd_bus_message* m, void* t, 519 _unused_ sd_bus_error* e) 520 { 521 int r; 522 523 mapper_async_subtree* subtree = t; 524 if (subtree->finished) 525 return 0; 526 527 r = async_subtree_getpaths(subtree); 528 if (r < 0) 529 async_subtree_done(r, subtree); 530 531 return 0; 532 } 533 534 static void async_subtree_done(int r, mapper_async_subtree* t) 535 { 536 if (t->finished) 537 return; 538 539 t->finished = 1; 540 sd_bus_slot_unref(t->slot); 541 542 if (t->callback) 543 t->callback(r, t->userdata); 544 } 545 546 _public_ int mapper_subtree_async(sd_bus* conn, sd_event* loop, char* namespace, 547 char* interface, void (*callback)(int, void*), 548 void* userdata, mapper_async_subtree** t, 549 int op) 550 { 551 int r = 0; 552 mapper_async_subtree* subtree = NULL; 553 554 subtree = malloc(sizeof(*subtree)); 555 if (!subtree) 556 return -ENOMEM; 557 558 memset(subtree, 0, sizeof(*subtree)); 559 subtree->conn = conn; 560 subtree->loop = loop; 561 subtree->namespace = namespace; 562 subtree->interface = interface; 563 subtree->callback = callback; 564 subtree->userdata = userdata; 565 subtree->op = op; 566 567 if (subtree->op == MAPPER_OP_REMOVE) 568 { 569 r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match, 570 async_subtree_match_callback, subtree); 571 if (r < 0) 572 { 573 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 574 goto unref_slot; 575 } 576 } 577 else 578 { 579 /* Operation not supported */ 580 r = -EINVAL; 581 goto free_subtree; 582 } 583 584 r = async_subtree_getpaths(subtree); 585 if (r < 0) 586 { 587 fprintf(stderr, "Error calling method: %s\n", strerror(-r)); 588 goto unref_slot; 589 } 590 591 *t = subtree; 592 593 return 0; 594 595 unref_slot: 596 sd_bus_slot_unref(subtree->slot); 597 free_subtree: 598 free(subtree); 599 600 return r; 601 } 602 603 _public_ int mapper_get_object(sd_bus* conn, const char* obj, 604 sd_bus_message** reply) 605 { 606 sd_bus_message* request = NULL; 607 int r, retry = 0; 608 609 r = sd_bus_message_new_method_call( 610 conn, &request, "xyz.openbmc_project.ObjectMapper", 611 "/xyz/openbmc_project/object_mapper", 612 "xyz.openbmc_project.ObjectMapper", "GetObject"); 613 if (r < 0) 614 goto exit; 615 616 r = sd_bus_message_append(request, "s", obj); 617 if (r < 0) 618 goto exit; 619 r = sd_bus_message_append(request, "as", 0, NULL); 620 if (r < 0) 621 goto exit; 622 623 while (true) 624 { 625 r = sd_bus_call(conn, request, 0, NULL, reply); 626 if (r == -EBUSY || r == -ENOBUFS) 627 { 628 if (retry >= mapper_busy_retries) 629 break; 630 631 usleep(mapper_busy_delay_interval_usec * (1 << retry)); 632 ++retry; 633 continue; 634 } 635 break; 636 } 637 638 if (r < 0) 639 goto exit; 640 641 exit: 642 sd_bus_message_unref(request); 643 644 return r; 645 } 646 647 _public_ int mapper_get_service(sd_bus* conn, const char* obj, char** service) 648 { 649 sd_bus_message* reply = NULL; 650 const char* tmp; 651 int r; 652 653 r = mapper_get_object(conn, obj, &reply); 654 if (r < 0) 655 goto exit; 656 657 r = sd_bus_message_enter_container(reply, 0, NULL); 658 if (r < 0) 659 goto exit; 660 661 r = sd_bus_message_enter_container(reply, 0, NULL); 662 if (r < 0) 663 goto exit; 664 665 r = sd_bus_message_read(reply, "s", &tmp); 666 if (r < 0) 667 goto exit; 668 669 *service = strdup(tmp); 670 671 exit: 672 sd_bus_message_unref(reply); 673 674 return r; 675 } 676