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