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 #include <stdbool.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <stdio.h> 21 #include <errno.h> 22 #include <unistd.h> 23 #include <sys/timerfd.h> 24 #include <systemd/sd-bus.h> 25 #include <systemd/sd-event.h> 26 #include "mapper.h" 27 28 static const char *async_wait_introspection_match = 29 "type='signal'," 30 "sender='xyz.openbmc_project.ObjectMapper'," 31 "interface='xyz.openbmc_project.ObjectMapper.Private'," 32 "member='IntrospectionComplete'"; 33 34 static const char *async_wait_interfaces_added_match = 35 "type='signal'," 36 "interface='org.freedesktop.DBus.ObjectManager'," 37 "member='InterfacesAdded'"; 38 39 static const char *interfaces_removed_match = 40 "type='signal'," 41 "interface='org.freedesktop.DBus.ObjectManager'," 42 "member='InterfacesRemoved'"; 43 44 static const int mapper_busy_retries = 5; 45 static const uint64_t mapper_busy_delay_interval_usec = 1000000; 46 47 struct mapper_async_wait 48 { 49 char **objs; 50 void (*callback)(int, void *); 51 void *userdata; 52 sd_event *loop; 53 sd_bus *conn; 54 sd_bus_slot *introspection_slot; 55 sd_bus_slot *intf_slot; 56 int *status; 57 int count; 58 int finished; 59 int r; 60 }; 61 62 struct async_wait_callback_data 63 { 64 mapper_async_wait *wait; 65 const char *path; 66 sd_event_source *event_source; 67 int retry; 68 }; 69 70 struct mapper_async_subtree 71 { 72 char *namespace; 73 char *interface; 74 void (*callback)(int, void *); 75 void *userdata; 76 sd_event *loop; 77 sd_bus *conn; 78 sd_bus_slot *slot; 79 sd_event_source *event_source; 80 int finished; 81 int op; 82 int retry; 83 }; 84 85 static int async_wait_match_introspection_complete(sd_bus_message *, void *, 86 sd_bus_error *); 87 static int async_wait_check_done(mapper_async_wait *); 88 static void async_wait_done(int r, mapper_async_wait *); 89 static int async_wait_get_objects(mapper_async_wait *); 90 static int async_wait_getobject_callback(sd_bus_message *, void *, 91 sd_bus_error *); 92 93 static int async_subtree_match_callback(sd_bus_message *, void *, 94 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 && 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 && 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_error error = SD_BUS_ERROR_NULL; 585 sd_bus_message *request = NULL; 586 int r, retry = 0; 587 588 r = sd_bus_message_new_method_call(conn, &request, MAPPER_BUSNAME, 589 MAPPER_PATH, MAPPER_INTERFACE, 590 "GetObject"); 591 if (r < 0) 592 goto exit; 593 594 r = sd_bus_message_append(request, "s", obj); 595 if (r < 0) 596 goto exit; 597 r = sd_bus_message_append(request, "as", 0, NULL); 598 if (r < 0) 599 goto exit; 600 601 while (true) 602 { 603 sd_bus_error_free(&error); 604 r = sd_bus_call(conn, request, 0, &error, reply); 605 if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) 606 { 607 if (retry >= mapper_busy_retries) 608 break; 609 610 usleep(mapper_busy_delay_interval_usec * (1 << retry)); 611 ++retry; 612 continue; 613 } 614 break; 615 } 616 617 if (r < 0) 618 goto exit; 619 620 exit: 621 sd_bus_error_free(&error); 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