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