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