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