cache.c revision c8a0a5cdfba51f7de5d203aa13a97377b215515a
1/* 2 * lib/cache.c Caching Module 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> 10 */ 11 12/** 13 * @ingroup cache_mngt 14 * @defgroup cache Cache 15 * 16 * @code 17 * Cache Management | | Type Specific Cache Operations 18 * 19 * | | +----------------+ +------------+ 20 * | request update | | msg_parser | 21 * | | +----------------+ +------------+ 22 * +- - - - -^- - - - - - - -^- -|- - - - 23 * nl_cache_update: | | | | 24 * 1) --------- co_request_update ------+ | | 25 * | | | 26 * 2) destroy old cache +----------- pp_cb ---------|---+ 27 * | | | 28 * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ 29 * +--------------+ | | | | 30 * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - 31 * +--------------+ | | +-------------+ 32 * | nl_recvmsgs | 33 * | | +-----|-^-----+ 34 * +---v-|---+ 35 * | | | nl_recv | 36 * +---------+ 37 * | | Core Netlink 38 * @endcode 39 * 40 * @{ 41 */ 42 43#include <netlink-local.h> 44#include <netlink/netlink.h> 45#include <netlink/cache.h> 46#include <netlink/object.h> 47#include <netlink/utils.h> 48 49/** 50 * @name Access Functions 51 * @{ 52 */ 53 54/** 55 * Return the number of items in the cache 56 * @arg cache cache handle 57 */ 58int nl_cache_nitems(struct nl_cache *cache) 59{ 60 return cache->c_nitems; 61} 62 63/** 64 * Return the number of items matching a filter in the cache 65 * @arg cache Cache object. 66 * @arg filter Filter object. 67 */ 68int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) 69{ 70 struct nl_object_ops *ops; 71 struct nl_object *obj; 72 int nitems = 0; 73 74 if (cache->c_ops == NULL) 75 BUG(); 76 77 ops = cache->c_ops->co_obj_ops; 78 79 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 80 if (filter && !nl_object_match_filter(obj, filter)) 81 continue; 82 83 nitems++; 84 } 85 86 return nitems; 87} 88 89/** 90 * Returns \b true if the cache is empty. 91 * @arg cache Cache to check 92 * @return \a true if the cache is empty, otherwise \b false is returned. 93 */ 94int nl_cache_is_empty(struct nl_cache *cache) 95{ 96 return nl_list_empty(&cache->c_items); 97} 98 99/** 100 * Return the operations set of the cache 101 * @arg cache cache handle 102 */ 103struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache) 104{ 105 return cache->c_ops; 106} 107 108/** 109 * Return the first element in the cache 110 * @arg cache cache handle 111 */ 112struct nl_object *nl_cache_get_first(struct nl_cache *cache) 113{ 114 if (nl_list_empty(&cache->c_items)) 115 return NULL; 116 117 return nl_list_entry(cache->c_items.next, 118 struct nl_object, ce_list); 119} 120 121/** 122 * Return the last element in the cache 123 * @arg cache cache handle 124 */ 125struct nl_object *nl_cache_get_last(struct nl_cache *cache) 126{ 127 if (nl_list_empty(&cache->c_items)) 128 return NULL; 129 130 return nl_list_entry(cache->c_items.prev, 131 struct nl_object, ce_list); 132} 133 134/** 135 * Return the next element in the cache 136 * @arg obj current object 137 */ 138struct nl_object *nl_cache_get_next(struct nl_object *obj) 139{ 140 if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list)) 141 return NULL; 142 else 143 return nl_list_entry(obj->ce_list.next, 144 struct nl_object, ce_list); 145} 146 147/** 148 * Return the previous element in the cache 149 * @arg obj current object 150 */ 151struct nl_object *nl_cache_get_prev(struct nl_object *obj) 152{ 153 if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list)) 154 return NULL; 155 else 156 return nl_list_entry(obj->ce_list.prev, 157 struct nl_object, ce_list); 158} 159 160/** @} */ 161 162/** 163 * @name Cache Creation/Deletion 164 * @{ 165 */ 166 167/** 168 * Allocate an empty cache 169 * @arg ops cache operations to base the cache on 170 * 171 * @return A newly allocated and initialized cache. 172 */ 173struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) 174{ 175 struct nl_cache *cache; 176 177 cache = calloc(1, sizeof(*cache)); 178 if (!cache) { 179 nl_errno(ENOMEM); 180 return NULL; 181 } 182 183 nl_init_list_head(&cache->c_items); 184 cache->c_ops = ops; 185 186 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); 187 188 return cache; 189} 190 191/** 192 * Allocate an empty cache based on type name 193 * @arg kind Name of cache type 194 * @return A newly allocated and initialized cache. 195 */ 196struct nl_cache *nl_cache_alloc_name(const char *kind) 197{ 198 struct nl_cache_ops *ops; 199 200 ops = nl_cache_ops_lookup(kind); 201 if (!ops) { 202 nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind); 203 return NULL; 204 } 205 206 return nl_cache_alloc(ops); 207} 208 209/** 210 * Allocate a new cache containing a subset of a cache 211 * @arg orig Original cache to be based on 212 * @arg filter Filter defining the subset to be filled into new cache 213 * @return A newly allocated cache or NULL. 214 */ 215struct nl_cache *nl_cache_subset(struct nl_cache *orig, 216 struct nl_object *filter) 217{ 218 struct nl_cache *cache; 219 struct nl_object_ops *ops; 220 struct nl_object *obj; 221 222 if (!filter) 223 BUG(); 224 225 cache = nl_cache_alloc(orig->c_ops); 226 if (!cache) 227 return NULL; 228 229 ops = orig->c_ops->co_obj_ops; 230 231 nl_list_for_each_entry(obj, &orig->c_items, ce_list) { 232 if (!nl_object_match_filter(obj, filter)) 233 continue; 234 235 nl_cache_add(cache, obj); 236 } 237 238 return cache; 239} 240 241/** 242 * Clear a cache. 243 * @arg cache cache to clear 244 * 245 * Removes all elements of a cache. 246 */ 247void nl_cache_clear(struct nl_cache *cache) 248{ 249 struct nl_object *obj, *tmp; 250 251 NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); 252 253 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) 254 nl_cache_remove(obj); 255} 256 257/** 258 * Free a cache. 259 * @arg cache Cache to free. 260 * 261 * Removes all elements of a cache and frees all memory. 262 * 263 * @note Use this function if you are working with allocated caches. 264 */ 265void nl_cache_free(struct nl_cache *cache) 266{ 267 nl_cache_clear(cache); 268 NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); 269 free(cache); 270} 271 272/** @} */ 273 274/** 275 * @name Cache Modifications 276 * @{ 277 */ 278 279static int __cache_add(struct nl_cache *cache, struct nl_object *obj) 280{ 281 obj->ce_cache = cache; 282 283 nl_list_add_tail(&obj->ce_list, &cache->c_items); 284 cache->c_nitems++; 285 286 NL_DBG(1, "Added %p to cache %p <%s>.\n", 287 obj, cache, nl_cache_name(cache)); 288 289 return 0; 290} 291 292/** 293 * Add object to a cache. 294 * @arg cache Cache to add object to 295 * @arg obj Object to be added to the cache 296 * 297 * Adds the given object to the specified cache. The object is cloned 298 * if it has been added to another cache already. 299 * 300 * @return 0 or a negative error code. 301 */ 302int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) 303{ 304 struct nl_object *new; 305 306 if (cache->c_ops->co_obj_ops != obj->ce_ops) 307 return nl_error(EINVAL, "Object mismatches cache type"); 308 309 if (!nl_list_empty(&obj->ce_list)) { 310 new = nl_object_clone(obj); 311 if (!new) 312 return nl_errno(ENOMEM); 313 } else { 314 nl_object_get(obj); 315 new = obj; 316 } 317 318 return __cache_add(cache, new); 319} 320 321/** 322 * Move object from one cache to another 323 * @arg cache Cache to move object to. 324 * @arg obj Object subject to be moved 325 * 326 * Removes the given object from its associated cache if needed 327 * and adds it to the new cache. 328 * 329 * @return 0 on success or a negative error code. 330 */ 331int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) 332{ 333 if (cache->c_ops->co_obj_ops != obj->ce_ops) 334 return nl_error(EINVAL, "Object mismatches cache type"); 335 336 NL_DBG(3, "Moving object %p to cache %p\n", obj, cache); 337 338 /* Acquire reference, if already in a cache this will be 339 * reverted during removal */ 340 nl_object_get(obj); 341 342 if (!nl_list_empty(&obj->ce_list)) 343 nl_cache_remove(obj); 344 345 return __cache_add(cache, obj); 346} 347 348/** 349 * Removes an object from a cache. 350 * @arg obj Object to remove from its cache 351 * 352 * Removes the object \c obj from the cache it is assigned to, since 353 * an object can only be assigned to one cache at a time, the cache 354 * must ne be passed along with it. 355 */ 356void nl_cache_remove(struct nl_object *obj) 357{ 358 struct nl_cache *cache = obj->ce_cache; 359 360 if (cache == NULL) 361 return; 362 363 nl_list_del(&obj->ce_list); 364 obj->ce_cache = NULL; 365 nl_object_put(obj); 366 cache->c_nitems--; 367 368 NL_DBG(1, "Deleted %p from cache %p <%s>.\n", 369 obj, cache, nl_cache_name(cache)); 370} 371 372/** 373 * Search for an object in a cache 374 * @arg cache Cache to search in. 375 * @arg needle Object to look for. 376 * 377 * Iterates over the cache and looks for an object with identical 378 * identifiers as the needle. 379 * 380 * @return Reference to object or NULL if not found. 381 * @note The returned object must be returned via nl_object_put(). 382 */ 383struct nl_object *nl_cache_search(struct nl_cache *cache, 384 struct nl_object *needle) 385{ 386 struct nl_object *obj; 387 388 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 389 if (nl_object_identical(obj, needle)) { 390 nl_object_get(obj); 391 return obj; 392 } 393 } 394 395 return NULL; 396} 397 398 399/** @} */ 400 401/** 402 * @name Synchronization 403 * @{ 404 */ 405 406/** 407 * Request a full dump from the kernel to fill a cache 408 * @arg handle Netlink handle 409 * @arg cache Cache subjected to be filled. 410 * 411 * Send a dumping request to the kernel causing it to dump all objects 412 * related to the specified cache to the netlink socket. 413 * 414 * Use nl_cache_pickup() to read the objects from the socket and fill them 415 * into a cache. 416 */ 417int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache) 418{ 419 NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", 420 cache, nl_cache_name(cache)); 421 422 if (cache->c_ops->co_request_update == NULL) 423 return nl_error(EOPNOTSUPP, "Operation not supported"); 424 425 return cache->c_ops->co_request_update(cache, handle); 426} 427 428/** @cond SKIP */ 429struct update_xdata { 430 struct nl_cache_ops *ops; 431 struct nl_parser_param *params; 432}; 433 434static int update_msg_parser(struct nl_msg *msg, void *arg) 435{ 436 struct update_xdata *x = arg; 437 438 return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); 439} 440/** @endcond */ 441 442int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache, 443 struct nl_parser_param *param) 444{ 445 int err; 446 struct nl_cb *cb; 447 struct update_xdata x = { 448 .ops = cache->c_ops, 449 .params = param, 450 }; 451 452 NL_DBG(1, "Picking up answer for cache %p <%s>...\n", 453 cache, nl_cache_name(cache)); 454 455 cb = nl_cb_clone(handle->h_cb); 456 if (cb == NULL) 457 return nl_get_errno(); 458 459 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); 460 461 err = nl_recvmsgs(handle, cb); 462 if (err < 0) 463 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ 464 "%d: %s", cache, nl_cache_name(cache), 465 err, nl_geterror()); 466 467 nl_cb_put(cb); 468 469 return err; 470} 471 472static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) 473{ 474 return nl_cache_add((struct nl_cache *) p->pp_arg, c); 475} 476 477/** 478 * Pickup a netlink dump response and put it into a cache. 479 * @arg handle Netlink handle. 480 * @arg cache Cache to put items into. 481 * 482 * Waits for netlink messages to arrive, parses them and puts them into 483 * the specified cache. 484 * 485 * @return 0 on success or a negative error code. 486 */ 487int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache) 488{ 489 struct nl_parser_param p = { 490 .pp_cb = pickup_cb, 491 .pp_arg = cache, 492 }; 493 494 return __cache_pickup(handle, cache, &p); 495} 496 497static int cache_include(struct nl_cache *cache, struct nl_object *obj, 498 struct nl_msgtype *type, change_func_t cb) 499{ 500 struct nl_object *old; 501 502 switch (type->mt_act) { 503 case NL_ACT_NEW: 504 case NL_ACT_DEL: 505 old = nl_cache_search(cache, obj); 506 if (old) { 507 nl_cache_remove(old); 508 if (type->mt_act == NL_ACT_DEL && cb) 509 cb(cache, old, NL_ACT_DEL); 510 } 511 512 if (type->mt_act == NL_ACT_NEW) { 513 nl_cache_move(cache, obj); 514 if (old == NULL && cb) 515 cb(cache, obj, NL_ACT_NEW); 516 else if (old) { 517 if (nl_object_diff(old, obj) && cb) 518 cb(cache, obj, NL_ACT_CHANGE); 519 520 nl_object_put(old); 521 } 522 } 523 break; 524 default: 525 NL_DBG(2, "Unknown action associated to object %p\n", obj); 526 return 0; 527 } 528 529 return 0; 530} 531 532int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, 533 change_func_t change_cb) 534{ 535 struct nl_cache_ops *ops = cache->c_ops; 536 int i; 537 538 if (ops->co_obj_ops != obj->ce_ops) 539 return nl_error(EINVAL, "Object mismatches cache type"); 540 541 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) 542 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) 543 return cache_include(cache, obj, &ops->co_msgtypes[i], 544 change_cb); 545 546 return nl_errno(EINVAL); 547} 548 549static int resync_cb(struct nl_object *c, struct nl_parser_param *p) 550{ 551 struct nl_cache_assoc *ca = p->pp_arg; 552 553 return nl_cache_include(ca->ca_cache, c, ca->ca_change); 554} 555 556int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache, 557 change_func_t change_cb) 558{ 559 struct nl_object *obj, *next; 560 struct nl_cache_assoc ca = { 561 .ca_cache = cache, 562 .ca_change = change_cb, 563 }; 564 struct nl_parser_param p = { 565 .pp_cb = resync_cb, 566 .pp_arg = &ca, 567 }; 568 int err; 569 570 NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); 571 572 /* Mark all objects so we can see if some of them are obsolete */ 573 nl_cache_mark_all(cache); 574 575 err = nl_cache_request_full_dump(handle, cache); 576 if (err < 0) 577 goto errout; 578 579 err = __cache_pickup(handle, cache, &p); 580 if (err < 0) 581 goto errout; 582 583 nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) 584 if (nl_object_is_marked(obj)) 585 nl_cache_remove(obj); 586 587 NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); 588 589 err = 0; 590errout: 591 return err; 592} 593 594/** @} */ 595 596/** 597 * @name Parsing 598 * @{ 599 */ 600 601/** @cond SKIP */ 602int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, 603 struct nlmsghdr *nlh, struct nl_parser_param *params) 604{ 605 int i, err; 606 607 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) { 608 err = nl_error(EINVAL, "netlink message too short to be " 609 "of kind %s", ops->co_name); 610 goto errout; 611 } 612 613 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { 614 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { 615 err = ops->co_msg_parser(ops, who, nlh, params); 616 if (err != -ENOENT) 617 goto errout; 618 } 619 } 620 621 622 err = nl_error(EINVAL, "Unsupported netlink message type %d", 623 nlh->nlmsg_type); 624errout: 625 return err; 626} 627/** @endcond */ 628 629/** 630 * Parse a netlink message and add it to the cache. 631 * @arg cache cache to add element to 632 * @arg msg netlink message 633 * 634 * Parses a netlink message by calling the cache specific message parser 635 * and adds the new element to the cache. 636 * 637 * @return 0 or a negative error code. 638 */ 639int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) 640{ 641 struct nl_parser_param p = { 642 .pp_cb = pickup_cb, 643 .pp_arg = cache, 644 }; 645 646 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); 647} 648 649/** 650 * (Re)fill a cache with the contents in the kernel. 651 * @arg handle netlink handle 652 * @arg cache cache to update 653 * 654 * Clears the specified cache and fills it with the current state in 655 * the kernel. 656 * 657 * @return 0 or a negative error code. 658 */ 659int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache) 660{ 661 int err; 662 663 err = nl_cache_request_full_dump(handle, cache); 664 if (err < 0) 665 return err; 666 667 NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", 668 cache, nl_cache_name(cache)); 669 nl_cache_clear(cache); 670 671 return nl_cache_pickup(handle, cache); 672} 673 674/** @} */ 675 676/** 677 * @name Utillities 678 * @{ 679 */ 680 681/** 682 * Mark all objects in a cache 683 * @arg cache Cache to mark all objects in 684 */ 685void nl_cache_mark_all(struct nl_cache *cache) 686{ 687 struct nl_object *obj; 688 689 NL_DBG(2, "Marking all objects in cache %p <%s>...\n", 690 cache, nl_cache_name(cache)); 691 692 nl_list_for_each_entry(obj, &cache->c_items, ce_list) 693 nl_object_mark(obj); 694} 695 696/** @} */ 697 698/** 699 * @name Dumping 700 * @{ 701 */ 702 703/** 704 * Dump all elements of a cache. 705 * @arg cache cache to dump 706 * @arg params dumping parameters 707 * 708 * Dumps all elements of the \a cache to the file descriptor \a fd. 709 */ 710void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) 711{ 712 nl_cache_dump_filter(cache, params, NULL); 713} 714 715/** 716 * Dump all elements of a cache (filtered). 717 * @arg cache cache to dump 718 * @arg params dumping parameters (optional) 719 * @arg filter filter object 720 * 721 * Dumps all elements of the \a cache to the file descriptor \a fd 722 * given they match the given filter \a filter. 723 */ 724void nl_cache_dump_filter(struct nl_cache *cache, 725 struct nl_dump_params *params, 726 struct nl_object *filter) 727{ 728 int type = params ? params->dp_type : NL_DUMP_FULL; 729 struct nl_object_ops *ops; 730 struct nl_object *obj; 731 732 NL_DBG(2, "Dumping cache %p <%s> filter %p\n", 733 cache, nl_cache_name(cache), filter); 734 735 if (type > NL_DUMP_MAX || type < 0) 736 BUG(); 737 738 if (cache->c_ops == NULL) 739 BUG(); 740 741 ops = cache->c_ops->co_obj_ops; 742 if (!ops->oo_dump[type]) 743 return; 744 745 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 746 if (filter && !nl_object_match_filter(obj, filter)) 747 continue; 748 749 NL_DBG(4, "Dumping object %p...\n", obj); 750 dump_from_ops(obj, params); 751 } 752} 753 754/** @} */ 755 756/** 757 * @name Iterators 758 * @{ 759 */ 760 761/** 762 * Call a callback on each element of the cache. 763 * @arg cache cache to iterate on 764 * @arg cb callback function 765 * @arg arg argument passed to callback function 766 * 767 * Calls a callback function \a cb on each element of the \a cache. 768 * The argument \a arg is passed on the callback function. 769 */ 770void nl_cache_foreach(struct nl_cache *cache, 771 void (*cb)(struct nl_object *, void *), void *arg) 772{ 773 nl_cache_foreach_filter(cache, NULL, cb, arg); 774} 775 776/** 777 * Call a callback on each element of the cache (filtered). 778 * @arg cache cache to iterate on 779 * @arg filter filter object 780 * @arg cb callback function 781 * @arg arg argument passed to callback function 782 * 783 * Calls a callback function \a cb on each element of the \a cache 784 * that matches the \a filter. The argument \a arg is passed on 785 * to the callback function. 786 */ 787void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, 788 void (*cb)(struct nl_object *, void *), void *arg) 789{ 790 struct nl_object *obj, *tmp; 791 struct nl_object_ops *ops; 792 793 if (cache->c_ops == NULL) 794 BUG(); 795 796 ops = cache->c_ops->co_obj_ops; 797 798 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { 799 if (filter && !nl_object_match_filter(obj, filter)) 800 continue; 801 802 cb(obj, arg); 803 } 804} 805 806/** @} */ 807 808/** @} */ 809