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