libnl 1.1
|
00001 /* 00002 * lib/cache.c Caching Module 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup cache_mngt 00014 * @defgroup cache Cache 00015 * 00016 * @code 00017 * Cache Management | | Type Specific Cache Operations 00018 * 00019 * | | +----------------+ +------------+ 00020 * | request update | | msg_parser | 00021 * | | +----------------+ +------------+ 00022 * +- - - - -^- - - - - - - -^- -|- - - - 00023 * nl_cache_update: | | | | 00024 * 1) --------- co_request_update ------+ | | 00025 * | | | 00026 * 2) destroy old cache +----------- pp_cb ---------|---+ 00027 * | | | 00028 * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ 00029 * +--------------+ | | | | 00030 * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - 00031 * +--------------+ | | +-------------+ 00032 * | nl_recvmsgs | 00033 * | | +-----|-^-----+ 00034 * +---v-|---+ 00035 * | | | nl_recv | 00036 * +---------+ 00037 * | | Core Netlink 00038 * @endcode 00039 * 00040 * @{ 00041 */ 00042 00043 #include <netlink-local.h> 00044 #include <netlink/netlink.h> 00045 #include <netlink/cache.h> 00046 #include <netlink/object.h> 00047 #include <netlink/utils.h> 00048 00049 /** 00050 * @name Access Functions 00051 * @{ 00052 */ 00053 00054 /** 00055 * Return the number of items in the cache 00056 * @arg cache cache handle 00057 */ 00058 int nl_cache_nitems(struct nl_cache *cache) 00059 { 00060 return cache->c_nitems; 00061 } 00062 00063 /** 00064 * Return the number of items matching a filter in the cache 00065 * @arg cache Cache object. 00066 * @arg filter Filter object. 00067 */ 00068 int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) 00069 { 00070 struct nl_object_ops *ops; 00071 struct nl_object *obj; 00072 int nitems = 0; 00073 00074 if (cache->c_ops == NULL) 00075 BUG(); 00076 00077 ops = cache->c_ops->co_obj_ops; 00078 00079 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 00080 if (filter && !nl_object_match_filter(obj, filter)) 00081 continue; 00082 00083 nitems++; 00084 } 00085 00086 return nitems; 00087 } 00088 00089 /** 00090 * Returns \b true if the cache is empty. 00091 * @arg cache Cache to check 00092 * @return \a true if the cache is empty, otherwise \b false is returned. 00093 */ 00094 int nl_cache_is_empty(struct nl_cache *cache) 00095 { 00096 return nl_list_empty(&cache->c_items); 00097 } 00098 00099 /** 00100 * Return the operations set of the cache 00101 * @arg cache cache handle 00102 */ 00103 struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache) 00104 { 00105 return cache->c_ops; 00106 } 00107 00108 /** 00109 * Return the first element in the cache 00110 * @arg cache cache handle 00111 */ 00112 struct nl_object *nl_cache_get_first(struct nl_cache *cache) 00113 { 00114 if (nl_list_empty(&cache->c_items)) 00115 return NULL; 00116 00117 return nl_list_entry(cache->c_items.next, 00118 struct nl_object, ce_list); 00119 } 00120 00121 /** 00122 * Return the last element in the cache 00123 * @arg cache cache handle 00124 */ 00125 struct nl_object *nl_cache_get_last(struct nl_cache *cache) 00126 { 00127 if (nl_list_empty(&cache->c_items)) 00128 return NULL; 00129 00130 return nl_list_entry(cache->c_items.prev, 00131 struct nl_object, ce_list); 00132 } 00133 00134 /** 00135 * Return the next element in the cache 00136 * @arg obj current object 00137 */ 00138 struct nl_object *nl_cache_get_next(struct nl_object *obj) 00139 { 00140 if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list)) 00141 return NULL; 00142 else 00143 return nl_list_entry(obj->ce_list.next, 00144 struct nl_object, ce_list); 00145 } 00146 00147 /** 00148 * Return the previous element in the cache 00149 * @arg obj current object 00150 */ 00151 struct nl_object *nl_cache_get_prev(struct nl_object *obj) 00152 { 00153 if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list)) 00154 return NULL; 00155 else 00156 return nl_list_entry(obj->ce_list.prev, 00157 struct nl_object, ce_list); 00158 } 00159 00160 /** @} */ 00161 00162 /** 00163 * @name Cache Creation/Deletion 00164 * @{ 00165 */ 00166 00167 /** 00168 * Allocate an empty cache 00169 * @arg ops cache operations to base the cache on 00170 * 00171 * @return A newly allocated and initialized cache. 00172 */ 00173 struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) 00174 { 00175 struct nl_cache *cache; 00176 00177 cache = calloc(1, sizeof(*cache)); 00178 if (!cache) { 00179 nl_errno(ENOMEM); 00180 return NULL; 00181 } 00182 00183 nl_init_list_head(&cache->c_items); 00184 cache->c_ops = ops; 00185 00186 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); 00187 00188 return cache; 00189 } 00190 00191 /** 00192 * Allocate an empty cache based on type name 00193 * @arg kind Name of cache type 00194 * @return A newly allocated and initialized cache. 00195 */ 00196 struct nl_cache *nl_cache_alloc_name(const char *kind) 00197 { 00198 struct nl_cache_ops *ops; 00199 00200 ops = nl_cache_ops_lookup(kind); 00201 if (!ops) { 00202 nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind); 00203 return NULL; 00204 } 00205 00206 return nl_cache_alloc(ops); 00207 } 00208 00209 /** 00210 * Allocate a new cache containing a subset of a cache 00211 * @arg orig Original cache to be based on 00212 * @arg filter Filter defining the subset to be filled into new cache 00213 * @return A newly allocated cache or NULL. 00214 */ 00215 struct nl_cache *nl_cache_subset(struct nl_cache *orig, 00216 struct nl_object *filter) 00217 { 00218 struct nl_cache *cache; 00219 struct nl_object_ops *ops; 00220 struct nl_object *obj; 00221 00222 if (!filter) 00223 BUG(); 00224 00225 cache = nl_cache_alloc(orig->c_ops); 00226 if (!cache) 00227 return NULL; 00228 00229 ops = orig->c_ops->co_obj_ops; 00230 00231 nl_list_for_each_entry(obj, &orig->c_items, ce_list) { 00232 if (!nl_object_match_filter(obj, filter)) 00233 continue; 00234 00235 nl_cache_add(cache, obj); 00236 } 00237 00238 return cache; 00239 } 00240 00241 /** 00242 * Clear a cache. 00243 * @arg cache cache to clear 00244 * 00245 * Removes all elements of a cache. 00246 */ 00247 void nl_cache_clear(struct nl_cache *cache) 00248 { 00249 struct nl_object *obj, *tmp; 00250 00251 NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); 00252 00253 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) 00254 nl_cache_remove(obj); 00255 } 00256 00257 /** 00258 * Free a cache. 00259 * @arg cache Cache to free. 00260 * 00261 * Removes all elements of a cache and frees all memory. 00262 * 00263 * @note Use this function if you are working with allocated caches. 00264 */ 00265 void nl_cache_free(struct nl_cache *cache) 00266 { 00267 nl_cache_clear(cache); 00268 NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); 00269 free(cache); 00270 } 00271 00272 /** @} */ 00273 00274 /** 00275 * @name Cache Modifications 00276 * @{ 00277 */ 00278 00279 static int __cache_add(struct nl_cache *cache, struct nl_object *obj) 00280 { 00281 obj->ce_cache = cache; 00282 00283 nl_list_add_tail(&obj->ce_list, &cache->c_items); 00284 cache->c_nitems++; 00285 00286 NL_DBG(1, "Added %p to cache %p <%s>.\n", 00287 obj, cache, nl_cache_name(cache)); 00288 00289 return 0; 00290 } 00291 00292 /** 00293 * Add object to a cache. 00294 * @arg cache Cache to add object to 00295 * @arg obj Object to be added to the cache 00296 * 00297 * Adds the given object to the specified cache. The object is cloned 00298 * if it has been added to another cache already. 00299 * 00300 * @return 0 or a negative error code. 00301 */ 00302 int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) 00303 { 00304 struct nl_object *new; 00305 00306 if (cache->c_ops->co_obj_ops != obj->ce_ops) 00307 return nl_error(EINVAL, "Object mismatches cache type"); 00308 00309 if (!nl_list_empty(&obj->ce_list)) { 00310 new = nl_object_clone(obj); 00311 if (!new) 00312 return nl_errno(ENOMEM); 00313 } else { 00314 nl_object_get(obj); 00315 new = obj; 00316 } 00317 00318 return __cache_add(cache, new); 00319 } 00320 00321 /** 00322 * Move object from one cache to another 00323 * @arg cache Cache to move object to. 00324 * @arg obj Object subject to be moved 00325 * 00326 * Removes the given object from its associated cache if needed 00327 * and adds it to the new cache. 00328 * 00329 * @return 0 on success or a negative error code. 00330 */ 00331 int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) 00332 { 00333 if (cache->c_ops->co_obj_ops != obj->ce_ops) 00334 return nl_error(EINVAL, "Object mismatches cache type"); 00335 00336 NL_DBG(3, "Moving object %p to cache %p\n", obj, cache); 00337 00338 /* Acquire reference, if already in a cache this will be 00339 * reverted during removal */ 00340 nl_object_get(obj); 00341 00342 if (!nl_list_empty(&obj->ce_list)) 00343 nl_cache_remove(obj); 00344 00345 return __cache_add(cache, obj); 00346 } 00347 00348 /** 00349 * Removes an object from a cache. 00350 * @arg obj Object to remove from its cache 00351 * 00352 * Removes the object \c obj from the cache it is assigned to, since 00353 * an object can only be assigned to one cache at a time, the cache 00354 * must ne be passed along with it. 00355 */ 00356 void nl_cache_remove(struct nl_object *obj) 00357 { 00358 struct nl_cache *cache = obj->ce_cache; 00359 00360 if (cache == NULL) 00361 return; 00362 00363 nl_list_del(&obj->ce_list); 00364 obj->ce_cache = NULL; 00365 nl_object_put(obj); 00366 cache->c_nitems--; 00367 00368 NL_DBG(1, "Deleted %p from cache %p <%s>.\n", 00369 obj, cache, nl_cache_name(cache)); 00370 } 00371 00372 /** 00373 * Search for an object in a cache 00374 * @arg cache Cache to search in. 00375 * @arg needle Object to look for. 00376 * 00377 * Iterates over the cache and looks for an object with identical 00378 * identifiers as the needle. 00379 * 00380 * @return Reference to object or NULL if not found. 00381 * @note The returned object must be returned via nl_object_put(). 00382 */ 00383 struct nl_object *nl_cache_search(struct nl_cache *cache, 00384 struct nl_object *needle) 00385 { 00386 struct nl_object *obj; 00387 00388 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 00389 if (nl_object_identical(obj, needle)) { 00390 nl_object_get(obj); 00391 return obj; 00392 } 00393 } 00394 00395 return NULL; 00396 } 00397 00398 00399 /** @} */ 00400 00401 /** 00402 * @name Synchronization 00403 * @{ 00404 */ 00405 00406 /** 00407 * Request a full dump from the kernel to fill a cache 00408 * @arg handle Netlink handle 00409 * @arg cache Cache subjected to be filled. 00410 * 00411 * Send a dumping request to the kernel causing it to dump all objects 00412 * related to the specified cache to the netlink socket. 00413 * 00414 * Use nl_cache_pickup() to read the objects from the socket and fill them 00415 * into a cache. 00416 */ 00417 int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache) 00418 { 00419 NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", 00420 cache, nl_cache_name(cache)); 00421 00422 if (cache->c_ops->co_request_update == NULL) 00423 return nl_error(EOPNOTSUPP, "Operation not supported"); 00424 00425 return cache->c_ops->co_request_update(cache, handle); 00426 } 00427 00428 /** @cond SKIP */ 00429 struct update_xdata { 00430 struct nl_cache_ops *ops; 00431 struct nl_parser_param *params; 00432 }; 00433 00434 static int update_msg_parser(struct nl_msg *msg, void *arg) 00435 { 00436 struct update_xdata *x = arg; 00437 00438 return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); 00439 } 00440 /** @endcond */ 00441 00442 int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache, 00443 struct nl_parser_param *param) 00444 { 00445 int err; 00446 struct nl_cb *cb; 00447 struct update_xdata x = { 00448 .ops = cache->c_ops, 00449 .params = param, 00450 }; 00451 00452 NL_DBG(1, "Picking up answer for cache %p <%s>...\n", 00453 cache, nl_cache_name(cache)); 00454 00455 cb = nl_cb_clone(handle->h_cb); 00456 if (cb == NULL) 00457 return nl_get_errno(); 00458 00459 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); 00460 00461 err = nl_recvmsgs(handle, cb); 00462 if (err < 0) 00463 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ 00464 "%d: %s", cache, nl_cache_name(cache), 00465 err, nl_geterror()); 00466 00467 nl_cb_put(cb); 00468 00469 return err; 00470 } 00471 00472 static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) 00473 { 00474 return nl_cache_add((struct nl_cache *) p->pp_arg, c); 00475 } 00476 00477 /** 00478 * Pickup a netlink dump response and put it into a cache. 00479 * @arg handle Netlink handle. 00480 * @arg cache Cache to put items into. 00481 * 00482 * Waits for netlink messages to arrive, parses them and puts them into 00483 * the specified cache. 00484 * 00485 * @return 0 on success or a negative error code. 00486 */ 00487 int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache) 00488 { 00489 struct nl_parser_param p = { 00490 .pp_cb = pickup_cb, 00491 .pp_arg = cache, 00492 }; 00493 00494 return __cache_pickup(handle, cache, &p); 00495 } 00496 00497 static int cache_include(struct nl_cache *cache, struct nl_object *obj, 00498 struct nl_msgtype *type, change_func_t cb) 00499 { 00500 struct nl_object *old; 00501 00502 switch (type->mt_act) { 00503 case NL_ACT_NEW: 00504 case NL_ACT_DEL: 00505 old = nl_cache_search(cache, obj); 00506 if (old) { 00507 nl_cache_remove(old); 00508 if (type->mt_act == NL_ACT_DEL) { 00509 if (cb) 00510 cb(cache, old, NL_ACT_DEL); 00511 nl_object_put(old); 00512 } 00513 } 00514 00515 if (type->mt_act == NL_ACT_NEW) { 00516 nl_cache_move(cache, obj); 00517 if (old == NULL && cb) 00518 cb(cache, obj, NL_ACT_NEW); 00519 else if (old) { 00520 if (nl_object_diff(old, obj) && cb) 00521 cb(cache, obj, NL_ACT_CHANGE); 00522 00523 nl_object_put(old); 00524 } 00525 } 00526 break; 00527 default: 00528 NL_DBG(2, "Unknown action associated to object %p\n", obj); 00529 return 0; 00530 } 00531 00532 return 0; 00533 } 00534 00535 int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, 00536 change_func_t change_cb) 00537 { 00538 struct nl_cache_ops *ops = cache->c_ops; 00539 int i; 00540 00541 if (ops->co_obj_ops != obj->ce_ops) 00542 return nl_error(EINVAL, "Object mismatches cache type"); 00543 00544 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) 00545 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) 00546 return cache_include(cache, obj, &ops->co_msgtypes[i], 00547 change_cb); 00548 00549 return nl_errno(EINVAL); 00550 } 00551 00552 static int resync_cb(struct nl_object *c, struct nl_parser_param *p) 00553 { 00554 struct nl_cache_assoc *ca = p->pp_arg; 00555 00556 return nl_cache_include(ca->ca_cache, c, ca->ca_change); 00557 } 00558 00559 int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache, 00560 change_func_t change_cb) 00561 { 00562 struct nl_object *obj, *next; 00563 struct nl_cache_assoc ca = { 00564 .ca_cache = cache, 00565 .ca_change = change_cb, 00566 }; 00567 struct nl_parser_param p = { 00568 .pp_cb = resync_cb, 00569 .pp_arg = &ca, 00570 }; 00571 int err; 00572 00573 NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); 00574 00575 /* Mark all objects so we can see if some of them are obsolete */ 00576 nl_cache_mark_all(cache); 00577 00578 err = nl_cache_request_full_dump(handle, cache); 00579 if (err < 0) 00580 goto errout; 00581 00582 err = __cache_pickup(handle, cache, &p); 00583 if (err < 0) 00584 goto errout; 00585 00586 nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) 00587 if (nl_object_is_marked(obj)) 00588 nl_cache_remove(obj); 00589 00590 NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); 00591 00592 err = 0; 00593 errout: 00594 return err; 00595 } 00596 00597 /** @} */ 00598 00599 /** 00600 * @name Parsing 00601 * @{ 00602 */ 00603 00604 /** @cond SKIP */ 00605 int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, 00606 struct nlmsghdr *nlh, struct nl_parser_param *params) 00607 { 00608 int i, err; 00609 00610 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) { 00611 err = nl_error(EINVAL, "netlink message too short to be " 00612 "of kind %s", ops->co_name); 00613 goto errout; 00614 } 00615 00616 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { 00617 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { 00618 err = ops->co_msg_parser(ops, who, nlh, params); 00619 if (err != -ENOENT) 00620 goto errout; 00621 } 00622 } 00623 00624 00625 err = nl_error(EINVAL, "Unsupported netlink message type %d", 00626 nlh->nlmsg_type); 00627 errout: 00628 return err; 00629 } 00630 /** @endcond */ 00631 00632 /** 00633 * Parse a netlink message and add it to the cache. 00634 * @arg cache cache to add element to 00635 * @arg msg netlink message 00636 * 00637 * Parses a netlink message by calling the cache specific message parser 00638 * and adds the new element to the cache. 00639 * 00640 * @return 0 or a negative error code. 00641 */ 00642 int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) 00643 { 00644 struct nl_parser_param p = { 00645 .pp_cb = pickup_cb, 00646 .pp_arg = cache, 00647 }; 00648 00649 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); 00650 } 00651 00652 /** 00653 * (Re)fill a cache with the contents in the kernel. 00654 * @arg handle netlink handle 00655 * @arg cache cache to update 00656 * 00657 * Clears the specified cache and fills it with the current state in 00658 * the kernel. 00659 * 00660 * @return 0 or a negative error code. 00661 */ 00662 int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache) 00663 { 00664 int err; 00665 00666 err = nl_cache_request_full_dump(handle, cache); 00667 if (err < 0) 00668 return err; 00669 00670 NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", 00671 cache, nl_cache_name(cache)); 00672 nl_cache_clear(cache); 00673 00674 return nl_cache_pickup(handle, cache); 00675 } 00676 00677 /** @} */ 00678 00679 /** 00680 * @name Utillities 00681 * @{ 00682 */ 00683 00684 /** 00685 * Mark all objects in a cache 00686 * @arg cache Cache to mark all objects in 00687 */ 00688 void nl_cache_mark_all(struct nl_cache *cache) 00689 { 00690 struct nl_object *obj; 00691 00692 NL_DBG(2, "Marking all objects in cache %p <%s>...\n", 00693 cache, nl_cache_name(cache)); 00694 00695 nl_list_for_each_entry(obj, &cache->c_items, ce_list) 00696 nl_object_mark(obj); 00697 } 00698 00699 /** @} */ 00700 00701 /** 00702 * @name Dumping 00703 * @{ 00704 */ 00705 00706 /** 00707 * Dump all elements of a cache. 00708 * @arg cache cache to dump 00709 * @arg params dumping parameters 00710 * 00711 * Dumps all elements of the \a cache to the file descriptor \a fd. 00712 */ 00713 void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) 00714 { 00715 nl_cache_dump_filter(cache, params, NULL); 00716 } 00717 00718 /** 00719 * Dump all elements of a cache (filtered). 00720 * @arg cache cache to dump 00721 * @arg params dumping parameters (optional) 00722 * @arg filter filter object 00723 * 00724 * Dumps all elements of the \a cache to the file descriptor \a fd 00725 * given they match the given filter \a filter. 00726 */ 00727 void nl_cache_dump_filter(struct nl_cache *cache, 00728 struct nl_dump_params *params, 00729 struct nl_object *filter) 00730 { 00731 int type = params ? params->dp_type : NL_DUMP_FULL; 00732 struct nl_object_ops *ops; 00733 struct nl_object *obj; 00734 00735 NL_DBG(2, "Dumping cache %p <%s> filter %p\n", 00736 cache, nl_cache_name(cache), filter); 00737 00738 if (type > NL_DUMP_MAX || type < 0) 00739 BUG(); 00740 00741 if (cache->c_ops == NULL) 00742 BUG(); 00743 00744 ops = cache->c_ops->co_obj_ops; 00745 if (!ops->oo_dump[type]) 00746 return; 00747 00748 nl_list_for_each_entry(obj, &cache->c_items, ce_list) { 00749 if (filter && !nl_object_match_filter(obj, filter)) 00750 continue; 00751 00752 NL_DBG(4, "Dumping object %p...\n", obj); 00753 dump_from_ops(obj, params); 00754 } 00755 } 00756 00757 /** @} */ 00758 00759 /** 00760 * @name Iterators 00761 * @{ 00762 */ 00763 00764 /** 00765 * Call a callback on each element of the cache. 00766 * @arg cache cache to iterate on 00767 * @arg cb callback function 00768 * @arg arg argument passed to callback function 00769 * 00770 * Calls a callback function \a cb on each element of the \a cache. 00771 * The argument \a arg is passed on the callback function. 00772 */ 00773 void nl_cache_foreach(struct nl_cache *cache, 00774 void (*cb)(struct nl_object *, void *), void *arg) 00775 { 00776 nl_cache_foreach_filter(cache, NULL, cb, arg); 00777 } 00778 00779 /** 00780 * Call a callback on each element of the cache (filtered). 00781 * @arg cache cache to iterate on 00782 * @arg filter filter object 00783 * @arg cb callback function 00784 * @arg arg argument passed to callback function 00785 * 00786 * Calls a callback function \a cb on each element of the \a cache 00787 * that matches the \a filter. The argument \a arg is passed on 00788 * to the callback function. 00789 */ 00790 void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, 00791 void (*cb)(struct nl_object *, void *), void *arg) 00792 { 00793 struct nl_object *obj, *tmp; 00794 struct nl_object_ops *ops; 00795 00796 if (cache->c_ops == NULL) 00797 BUG(); 00798 00799 ops = cache->c_ops->co_obj_ops; 00800 00801 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { 00802 if (filter && !nl_object_match_filter(obj, filter)) 00803 continue; 00804 00805 cb(obj, arg); 00806 } 00807 } 00808 00809 /** @} */ 00810 00811 /** @} */