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