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 18 #include "mapper.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 #include "internal.h" 32 33 #define _public_ __attribute__((__visibility__("default"))) 34 #define _unused_ __attribute__((unused)) 35 36 static const char* async_wait_introspection_match = 37 "type='signal'," 38 "sender='xyz.openbmc_project.ObjectMapper'," 39 "interface='xyz.openbmc_project.ObjectMapper.Private'," 40 "member='IntrospectionComplete'"; 41 42 static const char* async_wait_interfaces_added_match = 43 "type='signal'," 44 "interface='org.freedesktop.DBus.ObjectManager'," 45 "member='InterfacesAdded'"; 46 47 static const char* interfaces_removed_match = 48 "type='signal'," 49 "interface='org.freedesktop.DBus.ObjectManager'," 50 "member='InterfacesRemoved'"; 51 52 static const int mapper_busy_retries = 5; 53 static const uint64_t mapper_busy_delay_interval_usec = 1000000; 54 55 struct mapper_async_wait 56 { 57 char** objs; 58 void (*callback)(int, void*); 59 void* userdata; 60 sd_event* loop; 61 sd_bus* conn; 62 sd_bus_slot* introspection_slot; 63 sd_bus_slot* intf_slot; 64 int* status; 65 size_t count; 66 int finished; 67 int r; 68 }; 69 70 struct async_wait_callback_data 71 { 72 mapper_async_wait* wait; 73 const char* path; 74 sd_event_source* event_source; 75 int retry; 76 }; 77 78 struct mapper_async_subtree 79 { 80 char* namespace; 81 char* interface; 82 void (*callback)(int, void*); 83 void* userdata; 84 sd_event* loop; 85 sd_bus* conn; 86 sd_bus_slot* slot; 87 sd_event_source* event_source; 88 int finished; 89 int op; 90 int retry; 91 }; 92 93 static int async_wait_match_introspection_complete(sd_bus_message*, void*, 94 sd_bus_error*); 95 static int async_wait_check_done(mapper_async_wait*); 96 static void async_wait_done(int r, mapper_async_wait*); 97 static int async_wait_get_objects(mapper_async_wait*); 98 static int async_wait_getobject_callback(sd_bus_message*, void*, sd_bus_error*); 99 100 static int async_subtree_match_callback(sd_bus_message*, void*, sd_bus_error*); 101 static void async_subtree_done(int r, mapper_async_subtree*); 102 static int async_subtree_getpaths(mapper_async_subtree*); 103 static int async_subtree_getpaths_callback(sd_bus_message*, void*, 104 sd_bus_error*); 105 106 size_t sarraylen(char* array[]) 107 { 108 size_t count = 0; 109 char** p = array; 110 111 while (*p != NULL) 112 { 113 ++count; 114 ++p; 115 } 116 117 return count; 118 } 119 120 void sarrayfree(char* array[]) 121 { 122 char** p = array; 123 while (*p != NULL) 124 { 125 free(*p); 126 ++p; 127 } 128 free(array); 129 } 130 131 char** sarraydup(char* array[]) 132 { 133 size_t count = sarraylen(array); 134 size_t i; 135 char** ret = NULL; 136 137 ret = calloc(count + 1, sizeof(*ret)); 138 if (!ret) 139 return NULL; 140 141 for (i = 0; i < count; ++i) 142 { 143 ret[i] = strdup(array[i]); 144 if (!ret[i]) 145 goto error; 146 } 147 148 return ret; 149 150 error: 151 sarrayfree(ret); 152 return NULL; 153 } 154 155 static int async_wait_timeout_callback(_unused_ sd_event_source* s, 156 _unused_ uint64_t usec, void* userdata) 157 { 158 int r; 159 struct async_wait_callback_data* data = userdata; 160 mapper_async_wait* wait = data->wait; 161 162 sd_event_source_unref(data->event_source); 163 r = sd_bus_call_method_async(wait->conn, NULL, MAPPER_BUSNAME, MAPPER_PATH, 164 MAPPER_INTERFACE, "GetObject", 165 async_wait_getobject_callback, data, "sas", 166 data->path, 0, NULL); 167 if (r < 0) 168 { 169 async_wait_done(r, wait); 170 free(data); 171 } 172 173 return 0; 174 } 175 176 static int async_wait_getobject_callback(sd_bus_message* m, void* userdata, 177 _unused_ sd_bus_error* e) 178 { 179 size_t i; 180 int r; 181 struct async_wait_callback_data* data = userdata; 182 mapper_async_wait* wait = data->wait; 183 uint64_t next_retry; 184 185 if (wait->finished) 186 goto exit; 187 188 if (sd_bus_message_is_method_error( 189 m, "xyz.openbmc_project.Common.Error.ResourceNotFound")) 190 goto exit; 191 192 r = sd_bus_message_get_errno(m); 193 194 if ((r == EBUSY || r == ENOBUFS) && data->retry < mapper_busy_retries) 195 { 196 r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &next_retry); 197 if (r < 0) 198 { 199 async_wait_done(r, wait); 200 goto exit; 201 } 202 203 next_retry += mapper_busy_delay_interval_usec * (1 << data->retry); 204 r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC, 205 next_retry, 0, async_wait_timeout_callback, data); 206 ++data->retry; 207 if (r < 0) 208 { 209 async_wait_done(r, wait); 210 goto exit; 211 } 212 213 return 0; 214 } 215 216 if (r) 217 { 218 async_wait_done(-r, wait); 219 goto exit; 220 } 221 222 for (i = 0; i < wait->count; ++i) 223 { 224 if (!strcmp(data->path, wait->objs[i])) 225 { 226 wait->status[i] = 1; 227 } 228 } 229 230 if (async_wait_check_done(wait)) 231 async_wait_done(0, wait); 232 233 exit: 234 free(data); 235 return 0; 236 } 237 238 static int async_wait_get_objects(mapper_async_wait* wait) 239 { 240 size_t i; 241 int r; 242 struct async_wait_callback_data* data = NULL; 243 244 for (i = 0; i < wait->count; ++i) 245 { 246 if (wait->status[i]) 247 continue; 248 data = malloc(sizeof(*data)); 249 data->wait = wait; 250 data->path = wait->objs[i]; 251 data->retry = 0; 252 data->event_source = NULL; 253 r = sd_bus_call_method_async(wait->conn, NULL, MAPPER_BUSNAME, 254 MAPPER_PATH, MAPPER_INTERFACE, "GetObject", 255 async_wait_getobject_callback, data, "sas", 256 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, MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, 410 "GetSubTreePaths", async_subtree_getpaths_callback, subtree, "sias", 411 subtree->namespace, 0, 1, subtree->interface); 412 if (r < 0) 413 async_subtree_done(r, subtree); 414 415 return 0; 416 } 417 418 static int async_subtree_getpaths_callback(sd_bus_message* m, void* userdata, 419 _unused_ sd_bus_error* e) 420 { 421 int r; 422 struct mapper_async_subtree* subtree = userdata; 423 uint64_t next_retry; 424 425 if (subtree->finished) 426 goto exit; 427 428 r = sd_bus_message_get_errno(m); 429 430 if (sd_bus_message_is_method_error( 431 m, "xyz.openbmc_project.Common.Error.ResourceNotFound")) 432 { 433 if (subtree->op == MAPPER_OP_REMOVE) 434 r = 0; 435 else 436 goto exit; 437 } 438 439 if ((r == EBUSY || r == ENOBUFS) && subtree->retry < mapper_busy_retries) 440 { 441 r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &next_retry); 442 if (r < 0) 443 { 444 async_subtree_done(r, subtree); 445 goto exit; 446 } 447 448 next_retry += mapper_busy_delay_interval_usec * (1 << subtree->retry); 449 r = sd_event_add_time(subtree->loop, &subtree->event_source, 450 CLOCK_MONOTONIC, next_retry, 0, 451 async_subtree_timeout_callback, subtree); 452 ++subtree->retry; 453 if (r < 0) 454 { 455 async_subtree_done(r, subtree); 456 goto exit; 457 } 458 459 return 0; 460 } 461 462 if (r) 463 { 464 async_subtree_done(-r, subtree); 465 goto exit; 466 } 467 468 if (subtree->op == MAPPER_OP_REMOVE) 469 { 470 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); 471 if (r < 0) 472 { 473 async_subtree_done(r, subtree); 474 goto exit; 475 } 476 477 r = sd_bus_message_at_end(m, false); 478 if (r < 0) 479 { 480 async_subtree_done(r, subtree); 481 goto exit; 482 } 483 484 /* For remove, operation is complete when the interface is not present 485 * we know it is empty if the returned array is empty 486 */ 487 if (r) 488 async_subtree_done(0, subtree); 489 } 490 491 exit: 492 return 0; 493 } 494 495 static int async_subtree_getpaths(mapper_async_subtree* subtree) 496 { 497 int r = 0; 498 499 subtree->retry = 0; 500 subtree->event_source = NULL; 501 r = sd_bus_call_method_async( 502 subtree->conn, NULL, MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, 503 "GetSubTreePaths", async_subtree_getpaths_callback, subtree, "sias", 504 subtree->namespace, 0, 1, subtree->interface); 505 if (r < 0) 506 { 507 fprintf(stderr, "Error invoking method: %s\n", strerror(-r)); 508 return r; 509 } 510 511 return 0; 512 } 513 514 static int async_subtree_match_callback(_unused_ sd_bus_message* m, void* t, 515 _unused_ sd_bus_error* e) 516 { 517 int r; 518 519 mapper_async_subtree* subtree = t; 520 if (subtree->finished) 521 return 0; 522 523 r = async_subtree_getpaths(subtree); 524 if (r < 0) 525 async_subtree_done(r, subtree); 526 527 return 0; 528 } 529 530 static void async_subtree_done(int r, mapper_async_subtree* t) 531 { 532 if (t->finished) 533 return; 534 535 t->finished = 1; 536 sd_bus_slot_unref(t->slot); 537 538 if (t->callback) 539 t->callback(r, t->userdata); 540 } 541 542 _public_ int mapper_subtree_async(sd_bus* conn, sd_event* loop, char* namespace, 543 char* interface, void (*callback)(int, void*), 544 void* userdata, mapper_async_subtree** t, 545 int op) 546 { 547 int r = 0; 548 mapper_async_subtree* subtree = NULL; 549 550 subtree = malloc(sizeof(*subtree)); 551 if (!subtree) 552 return -ENOMEM; 553 554 memset(subtree, 0, sizeof(*subtree)); 555 subtree->conn = conn; 556 subtree->loop = loop; 557 subtree->namespace = namespace; 558 subtree->interface = interface; 559 subtree->callback = callback; 560 subtree->userdata = userdata; 561 subtree->op = op; 562 563 if (subtree->op == MAPPER_OP_REMOVE) 564 { 565 r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match, 566 async_subtree_match_callback, subtree); 567 if (r < 0) 568 { 569 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r)); 570 goto unref_slot; 571 } 572 } 573 else 574 { 575 /* Operation not supported */ 576 r = -EINVAL; 577 goto free_subtree; 578 } 579 580 r = async_subtree_getpaths(subtree); 581 if (r < 0) 582 { 583 fprintf(stderr, "Error calling method: %s\n", strerror(-r)); 584 goto unref_slot; 585 } 586 587 *t = subtree; 588 589 return 0; 590 591 unref_slot: 592 sd_bus_slot_unref(subtree->slot); 593 free_subtree: 594 free(subtree); 595 596 return r; 597 } 598 599 _public_ int mapper_get_object(sd_bus* conn, const char* obj, 600 sd_bus_message** reply) 601 { 602 sd_bus_message* request = NULL; 603 int r, retry = 0; 604 605 r = sd_bus_message_new_method_call(conn, &request, MAPPER_BUSNAME, 606 MAPPER_PATH, MAPPER_INTERFACE, 607 "GetObject"); 608 if (r < 0) 609 goto exit; 610 611 r = sd_bus_message_append(request, "s", obj); 612 if (r < 0) 613 goto exit; 614 r = sd_bus_message_append(request, "as", 0, NULL); 615 if (r < 0) 616 goto exit; 617 618 while (true) 619 { 620 r = sd_bus_call(conn, request, 0, NULL, reply); 621 if (r == -EBUSY || r == -ENOBUFS) 622 { 623 if (retry >= mapper_busy_retries) 624 break; 625 626 usleep(mapper_busy_delay_interval_usec * (1 << retry)); 627 ++retry; 628 continue; 629 } 630 break; 631 } 632 633 if (r < 0) 634 goto exit; 635 636 exit: 637 sd_bus_message_unref(request); 638 639 return r; 640 } 641 642 _public_ int mapper_get_service(sd_bus* conn, const char* obj, char** service) 643 { 644 sd_bus_message* reply = NULL; 645 const char* tmp; 646 int r; 647 648 r = mapper_get_object(conn, obj, &reply); 649 if (r < 0) 650 goto exit; 651 652 r = sd_bus_message_enter_container(reply, 0, NULL); 653 if (r < 0) 654 goto exit; 655 656 r = sd_bus_message_enter_container(reply, 0, NULL); 657 if (r < 0) 658 goto exit; 659 660 r = sd_bus_message_read(reply, "s", &tmp); 661 if (r < 0) 662 goto exit; 663 664 *service = strdup(tmp); 665 666 exit: 667 sd_bus_message_unref(reply); 668 669 return r; 670 } 671