nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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-2008 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 | */ |
||
58 | int 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 | */ |
||
68 | int 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 | */ |
||
94 | int 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 | */ |
||
103 | struct 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 | */ |
||
112 | struct 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 | */ |
||
125 | struct 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 | */ |
||
138 | struct 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 | */ |
||
151 | struct 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 | */ |
||
173 | struct 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 | return NULL; |
||
180 | |||
181 | nl_init_list_head(&cache->c_items); |
||
182 | cache->c_ops = ops; |
||
183 | |||
184 | NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); |
||
185 | |||
186 | return cache; |
||
187 | } |
||
188 | |||
189 | int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, |
||
190 | struct nl_cache **result) |
||
191 | { |
||
192 | struct nl_cache *cache; |
||
193 | int err; |
||
194 | |||
195 | if (!(cache = nl_cache_alloc(ops))) |
||
196 | return -NLE_NOMEM; |
||
197 | |||
198 | if (sock && (err = nl_cache_refill(sock, cache)) < 0) { |
||
199 | nl_cache_free(cache); |
||
200 | return err; |
||
201 | } |
||
202 | |||
203 | *result = cache; |
||
204 | return 0; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Allocate an empty cache based on type name |
||
209 | * @arg kind Name of cache type |
||
210 | * @return A newly allocated and initialized cache. |
||
211 | */ |
||
212 | int nl_cache_alloc_name(const char *kind, struct nl_cache **result) |
||
213 | { |
||
214 | struct nl_cache_ops *ops; |
||
215 | struct nl_cache *cache; |
||
216 | |||
217 | ops = nl_cache_ops_lookup(kind); |
||
218 | if (!ops) |
||
219 | return -NLE_NOCACHE; |
||
220 | |||
221 | if (!(cache = nl_cache_alloc(ops))) |
||
222 | return -NLE_NOMEM; |
||
223 | |||
224 | *result = cache; |
||
225 | return 0; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Allocate a new cache containing a subset of a cache |
||
230 | * @arg orig Original cache to be based on |
||
231 | * @arg filter Filter defining the subset to be filled into new cache |
||
232 | * @return A newly allocated cache or NULL. |
||
233 | */ |
||
234 | struct nl_cache *nl_cache_subset(struct nl_cache *orig, |
||
235 | struct nl_object *filter) |
||
236 | { |
||
237 | struct nl_cache *cache; |
||
238 | struct nl_object_ops *ops; |
||
239 | struct nl_object *obj; |
||
240 | |||
241 | if (!filter) |
||
242 | BUG(); |
||
243 | |||
244 | cache = nl_cache_alloc(orig->c_ops); |
||
245 | if (!cache) |
||
246 | return NULL; |
||
247 | |||
248 | ops = orig->c_ops->co_obj_ops; |
||
249 | |||
250 | nl_list_for_each_entry(obj, &orig->c_items, ce_list) { |
||
251 | if (!nl_object_match_filter(obj, filter)) |
||
252 | continue; |
||
253 | |||
254 | nl_cache_add(cache, obj); |
||
255 | } |
||
256 | |||
257 | return cache; |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Clear a cache. |
||
262 | * @arg cache cache to clear |
||
263 | * |
||
264 | * Removes all elements of a cache. |
||
265 | */ |
||
266 | void nl_cache_clear(struct nl_cache *cache) |
||
267 | { |
||
268 | struct nl_object *obj, *tmp; |
||
269 | |||
270 | NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); |
||
271 | |||
272 | nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) |
||
273 | nl_cache_remove(obj); |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Free a cache. |
||
278 | * @arg cache Cache to free. |
||
279 | * |
||
280 | * Removes all elements of a cache and frees all memory. |
||
281 | * |
||
282 | * @note Use this function if you are working with allocated caches. |
||
283 | */ |
||
284 | void nl_cache_free(struct nl_cache *cache) |
||
285 | { |
||
286 | if (!cache) |
||
287 | return; |
||
288 | |||
289 | nl_cache_clear(cache); |
||
290 | NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); |
||
291 | free(cache); |
||
292 | } |
||
293 | |||
294 | /** @} */ |
||
295 | |||
296 | /** |
||
297 | * @name Cache Modifications |
||
298 | * @{ |
||
299 | */ |
||
300 | |||
301 | static int __cache_add(struct nl_cache *cache, struct nl_object *obj) |
||
302 | { |
||
303 | obj->ce_cache = cache; |
||
304 | |||
305 | nl_list_add_tail(&obj->ce_list, &cache->c_items); |
||
306 | cache->c_nitems++; |
||
307 | |||
308 | NL_DBG(1, "Added %p to cache %p <%s>.\n", |
||
309 | obj, cache, nl_cache_name(cache)); |
||
310 | |||
311 | return 0; |
||
312 | } |
||
313 | |||
314 | /** |
||
315 | * Add object to a cache. |
||
316 | * @arg cache Cache to add object to |
||
317 | * @arg obj Object to be added to the cache |
||
318 | * |
||
319 | * Adds the given object to the specified cache. The object is cloned |
||
320 | * if it has been added to another cache already. |
||
321 | * |
||
322 | * @return 0 or a negative error code. |
||
323 | */ |
||
324 | int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) |
||
325 | { |
||
326 | struct nl_object *new; |
||
327 | |||
328 | if (cache->c_ops->co_obj_ops != obj->ce_ops) |
||
329 | return -NLE_OBJ_MISMATCH; |
||
330 | |||
331 | if (!nl_list_empty(&obj->ce_list)) { |
||
332 | new = nl_object_clone(obj); |
||
333 | if (!new) |
||
334 | return -NLE_NOMEM; |
||
335 | } else { |
||
336 | nl_object_get(obj); |
||
337 | new = obj; |
||
338 | } |
||
339 | |||
340 | return __cache_add(cache, new); |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Move object from one cache to another |
||
345 | * @arg cache Cache to move object to. |
||
346 | * @arg obj Object subject to be moved |
||
347 | * |
||
348 | * Removes the given object from its associated cache if needed |
||
349 | * and adds it to the new cache. |
||
350 | * |
||
351 | * @return 0 on success or a negative error code. |
||
352 | */ |
||
353 | int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) |
||
354 | { |
||
355 | if (cache->c_ops->co_obj_ops != obj->ce_ops) |
||
356 | return -NLE_OBJ_MISMATCH; |
||
357 | |||
358 | NL_DBG(3, "Moving object %p to cache %p\n", obj, cache); |
||
359 | |||
360 | /* Acquire reference, if already in a cache this will be |
||
361 | * reverted during removal */ |
||
362 | nl_object_get(obj); |
||
363 | |||
364 | if (!nl_list_empty(&obj->ce_list)) |
||
365 | nl_cache_remove(obj); |
||
366 | |||
367 | return __cache_add(cache, obj); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Removes an object from a cache. |
||
372 | * @arg obj Object to remove from its cache |
||
373 | * |
||
374 | * Removes the object \c obj from the cache it is assigned to, since |
||
375 | * an object can only be assigned to one cache at a time, the cache |
||
376 | * must ne be passed along with it. |
||
377 | */ |
||
378 | void nl_cache_remove(struct nl_object *obj) |
||
379 | { |
||
380 | struct nl_cache *cache = obj->ce_cache; |
||
381 | |||
382 | if (cache == NULL) |
||
383 | return; |
||
384 | |||
385 | nl_list_del(&obj->ce_list); |
||
386 | obj->ce_cache = NULL; |
||
387 | nl_object_put(obj); |
||
388 | cache->c_nitems--; |
||
389 | |||
390 | NL_DBG(1, "Deleted %p from cache %p <%s>.\n", |
||
391 | obj, cache, nl_cache_name(cache)); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * Search for an object in a cache |
||
396 | * @arg cache Cache to search in. |
||
397 | * @arg needle Object to look for. |
||
398 | * |
||
399 | * Iterates over the cache and looks for an object with identical |
||
400 | * identifiers as the needle. |
||
401 | * |
||
402 | * @return Reference to object or NULL if not found. |
||
403 | * @note The returned object must be returned via nl_object_put(). |
||
404 | */ |
||
405 | struct nl_object *nl_cache_search(struct nl_cache *cache, |
||
406 | struct nl_object *needle) |
||
407 | { |
||
408 | struct nl_object *obj; |
||
409 | |||
410 | nl_list_for_each_entry(obj, &cache->c_items, ce_list) { |
||
411 | if (nl_object_identical(obj, needle)) { |
||
412 | nl_object_get(obj); |
||
413 | return obj; |
||
414 | } |
||
415 | } |
||
416 | |||
417 | return NULL; |
||
418 | } |
||
419 | |||
420 | |||
421 | /** @} */ |
||
422 | |||
423 | /** |
||
424 | * @name Synchronization |
||
425 | * @{ |
||
426 | */ |
||
427 | |||
428 | /** |
||
429 | * Request a full dump from the kernel to fill a cache |
||
430 | * @arg sk Netlink socket. |
||
431 | * @arg cache Cache subjected to be filled. |
||
432 | * |
||
433 | * Send a dumping request to the kernel causing it to dump all objects |
||
434 | * related to the specified cache to the netlink socket. |
||
435 | * |
||
436 | * Use nl_cache_pickup() to read the objects from the socket and fill them |
||
437 | * into a cache. |
||
438 | */ |
||
439 | int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache) |
||
440 | { |
||
441 | NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", |
||
442 | cache, nl_cache_name(cache)); |
||
443 | |||
444 | if (cache->c_ops->co_request_update == NULL) |
||
445 | return -NLE_OPNOTSUPP; |
||
446 | |||
447 | return cache->c_ops->co_request_update(cache, sk); |
||
448 | } |
||
449 | |||
450 | /** @cond SKIP */ |
||
451 | struct update_xdata { |
||
452 | struct nl_cache_ops *ops; |
||
453 | struct nl_parser_param *params; |
||
454 | }; |
||
455 | |||
456 | static int update_msg_parser(struct nl_msg *msg, void *arg) |
||
457 | { |
||
458 | struct update_xdata *x = arg; |
||
459 | |||
460 | return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); |
||
461 | } |
||
462 | /** @endcond */ |
||
463 | |||
464 | int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, |
||
465 | struct nl_parser_param *param) |
||
466 | { |
||
467 | int err; |
||
468 | struct nl_cb *cb; |
||
469 | struct update_xdata x = { |
||
470 | .ops = cache->c_ops, |
||
471 | .params = param, |
||
472 | }; |
||
473 | |||
474 | NL_DBG(1, "Picking up answer for cache %p <%s>...\n", |
||
475 | cache, nl_cache_name(cache)); |
||
476 | |||
477 | cb = nl_cb_clone(sk->s_cb); |
||
478 | if (cb == NULL) |
||
479 | return -NLE_NOMEM; |
||
480 | |||
481 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); |
||
482 | |||
483 | err = nl_recvmsgs(sk, cb); |
||
484 | if (err < 0) |
||
485 | NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ |
||
486 | "%d: %s", cache, nl_cache_name(cache), |
||
487 | err, nl_geterror(err)); |
||
488 | |||
489 | nl_cb_put(cb); |
||
490 | |||
491 | return err; |
||
492 | } |
||
493 | |||
494 | static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) |
||
495 | { |
||
496 | return nl_cache_add((struct nl_cache *) p->pp_arg, c); |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * Pickup a netlink dump response and put it into a cache. |
||
501 | * @arg sk Netlink socket. |
||
502 | * @arg cache Cache to put items into. |
||
503 | * |
||
504 | * Waits for netlink messages to arrive, parses them and puts them into |
||
505 | * the specified cache. |
||
506 | * |
||
507 | * @return 0 on success or a negative error code. |
||
508 | */ |
||
509 | int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) |
||
510 | { |
||
511 | struct nl_parser_param p = { |
||
512 | .pp_cb = pickup_cb, |
||
513 | .pp_arg = cache, |
||
514 | }; |
||
515 | |||
516 | return __cache_pickup(sk, cache, &p); |
||
517 | } |
||
518 | |||
519 | static int cache_include(struct nl_cache *cache, struct nl_object *obj, |
||
520 | struct nl_msgtype *type, change_func_t cb, void *data) |
||
521 | { |
||
522 | struct nl_object *old; |
||
523 | |||
524 | switch (type->mt_act) { |
||
525 | case NL_ACT_NEW: |
||
526 | case NL_ACT_DEL: |
||
527 | old = nl_cache_search(cache, obj); |
||
528 | if (old) { |
||
529 | nl_cache_remove(old); |
||
530 | if (type->mt_act == NL_ACT_DEL) { |
||
531 | if (cb) |
||
532 | cb(cache, old, NL_ACT_DEL, data); |
||
533 | nl_object_put(old); |
||
534 | } |
||
535 | } |
||
536 | |||
537 | if (type->mt_act == NL_ACT_NEW) { |
||
538 | nl_cache_move(cache, obj); |
||
539 | if (old == NULL && cb) |
||
540 | cb(cache, obj, NL_ACT_NEW, data); |
||
541 | else if (old) { |
||
542 | if (nl_object_diff(old, obj) && cb) |
||
543 | cb(cache, obj, NL_ACT_CHANGE, data); |
||
544 | |||
545 | nl_object_put(old); |
||
546 | } |
||
547 | } |
||
548 | break; |
||
549 | default: |
||
550 | NL_DBG(2, "Unknown action associated to object %p\n", obj); |
||
551 | return 0; |
||
552 | } |
||
553 | |||
554 | return 0; |
||
555 | } |
||
556 | |||
557 | int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, |
||
558 | change_func_t change_cb, void *data) |
||
559 | { |
||
560 | struct nl_cache_ops *ops = cache->c_ops; |
||
561 | int i; |
||
562 | |||
563 | if (ops->co_obj_ops != obj->ce_ops) |
||
564 | return -NLE_OBJ_MISMATCH; |
||
565 | |||
566 | for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) |
||
567 | if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) |
||
568 | return cache_include(cache, obj, &ops->co_msgtypes[i], |
||
569 | change_cb, data); |
||
570 | |||
571 | return -NLE_MSGTYPE_NOSUPPORT; |
||
572 | } |
||
573 | |||
574 | static int resync_cb(struct nl_object *c, struct nl_parser_param *p) |
||
575 | { |
||
576 | struct nl_cache_assoc *ca = p->pp_arg; |
||
577 | |||
578 | return nl_cache_include(ca->ca_cache, c, ca->ca_change, ca->ca_change_data); |
||
579 | } |
||
580 | |||
581 | int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, |
||
582 | change_func_t change_cb, void *data) |
||
583 | { |
||
584 | struct nl_object *obj, *next; |
||
585 | struct nl_cache_assoc ca = { |
||
586 | .ca_cache = cache, |
||
587 | .ca_change = change_cb, |
||
588 | .ca_change_data = data, |
||
589 | }; |
||
590 | struct nl_parser_param p = { |
||
591 | .pp_cb = resync_cb, |
||
592 | .pp_arg = &ca, |
||
593 | }; |
||
594 | int err; |
||
595 | |||
596 | NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); |
||
597 | |||
598 | /* Mark all objects so we can see if some of them are obsolete */ |
||
599 | nl_cache_mark_all(cache); |
||
600 | |||
601 | err = nl_cache_request_full_dump(sk, cache); |
||
602 | if (err < 0) |
||
603 | goto errout; |
||
604 | |||
605 | err = __cache_pickup(sk, cache, &p); |
||
606 | if (err < 0) |
||
607 | goto errout; |
||
608 | |||
609 | nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) { |
||
610 | if (nl_object_is_marked(obj)) { |
||
611 | nl_object_get(obj); |
||
612 | nl_cache_remove(obj); |
||
613 | if (change_cb) |
||
614 | change_cb(cache, obj, NL_ACT_DEL, data); |
||
615 | nl_object_put(obj); |
||
616 | } |
||
617 | } |
||
618 | |||
619 | NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); |
||
620 | |||
621 | err = 0; |
||
622 | errout: |
||
623 | return err; |
||
624 | } |
||
625 | |||
626 | /** @} */ |
||
627 | |||
628 | /** |
||
629 | * @name Parsing |
||
630 | * @{ |
||
631 | */ |
||
632 | |||
633 | /** @cond SKIP */ |
||
634 | int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, |
||
635 | struct nlmsghdr *nlh, struct nl_parser_param *params) |
||
636 | { |
||
637 | int i, err; |
||
638 | |||
639 | if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) |
||
640 | return -NLE_MSG_TOOSHORT; |
||
641 | |||
642 | for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { |
||
643 | if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { |
||
644 | err = ops->co_msg_parser(ops, who, nlh, params); |
||
645 | if (err != -NLE_OPNOTSUPP) |
||
646 | goto errout; |
||
647 | } |
||
648 | } |
||
649 | |||
650 | |||
651 | err = -NLE_MSGTYPE_NOSUPPORT; |
||
652 | errout: |
||
653 | return err; |
||
654 | } |
||
655 | /** @endcond */ |
||
656 | |||
657 | /** |
||
658 | * Parse a netlink message and add it to the cache. |
||
659 | * @arg cache cache to add element to |
||
660 | * @arg msg netlink message |
||
661 | * |
||
662 | * Parses a netlink message by calling the cache specific message parser |
||
663 | * and adds the new element to the cache. |
||
664 | * |
||
665 | * @return 0 or a negative error code. |
||
666 | */ |
||
667 | int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) |
||
668 | { |
||
669 | struct nl_parser_param p = { |
||
670 | .pp_cb = pickup_cb, |
||
671 | .pp_arg = cache, |
||
672 | }; |
||
673 | |||
674 | return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); |
||
675 | } |
||
676 | |||
677 | /** |
||
678 | * (Re)fill a cache with the contents in the kernel. |
||
679 | * @arg sk Netlink socket. |
||
680 | * @arg cache cache to update |
||
681 | * |
||
682 | * Clears the specified cache and fills it with the current state in |
||
683 | * the kernel. |
||
684 | * |
||
685 | * @return 0 or a negative error code. |
||
686 | */ |
||
687 | int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) |
||
688 | { |
||
689 | int err; |
||
690 | |||
691 | err = nl_cache_request_full_dump(sk, cache); |
||
692 | if (err < 0) |
||
693 | return err; |
||
694 | |||
695 | NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", |
||
696 | cache, nl_cache_name(cache)); |
||
697 | nl_cache_clear(cache); |
||
698 | |||
699 | return nl_cache_pickup(sk, cache); |
||
700 | } |
||
701 | |||
702 | /** @} */ |
||
703 | |||
704 | /** |
||
705 | * @name Utillities |
||
706 | * @{ |
||
707 | */ |
||
708 | |||
709 | /** |
||
710 | * Mark all objects in a cache |
||
711 | * @arg cache Cache to mark all objects in |
||
712 | */ |
||
713 | void nl_cache_mark_all(struct nl_cache *cache) |
||
714 | { |
||
715 | struct nl_object *obj; |
||
716 | |||
717 | NL_DBG(2, "Marking all objects in cache %p <%s>...\n", |
||
718 | cache, nl_cache_name(cache)); |
||
719 | |||
720 | nl_list_for_each_entry(obj, &cache->c_items, ce_list) |
||
721 | nl_object_mark(obj); |
||
722 | } |
||
723 | |||
724 | /** @} */ |
||
725 | |||
726 | /** |
||
727 | * @name Dumping |
||
728 | * @{ |
||
729 | */ |
||
730 | |||
731 | /** |
||
732 | * Dump all elements of a cache. |
||
733 | * @arg cache cache to dump |
||
734 | * @arg params dumping parameters |
||
735 | * |
||
736 | * Dumps all elements of the \a cache to the file descriptor \a fd. |
||
737 | */ |
||
738 | void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) |
||
739 | { |
||
740 | nl_cache_dump_filter(cache, params, NULL); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Dump all elements of a cache (filtered). |
||
745 | * @arg cache cache to dump |
||
746 | * @arg params dumping parameters (optional) |
||
747 | * @arg filter filter object |
||
748 | * |
||
749 | * Dumps all elements of the \a cache to the file descriptor \a fd |
||
750 | * given they match the given filter \a filter. |
||
751 | */ |
||
752 | void nl_cache_dump_filter(struct nl_cache *cache, |
||
753 | struct nl_dump_params *params, |
||
754 | struct nl_object *filter) |
||
755 | { |
||
756 | int type = params ? params->dp_type : NL_DUMP_DETAILS; |
||
757 | struct nl_object_ops *ops; |
||
758 | struct nl_object *obj; |
||
759 | |||
760 | NL_DBG(2, "Dumping cache %p <%s> filter %p\n", |
||
761 | cache, nl_cache_name(cache), filter); |
||
762 | |||
763 | if (type > NL_DUMP_MAX || type < 0) |
||
764 | BUG(); |
||
765 | |||
766 | if (cache->c_ops == NULL) |
||
767 | BUG(); |
||
768 | |||
769 | ops = cache->c_ops->co_obj_ops; |
||
770 | if (!ops->oo_dump[type]) |
||
771 | return; |
||
772 | |||
773 | nl_list_for_each_entry(obj, &cache->c_items, ce_list) { |
||
774 | if (filter && !nl_object_match_filter(obj, filter)) |
||
775 | continue; |
||
776 | |||
777 | NL_DBG(4, "Dumping object %p...\n", obj); |
||
778 | dump_from_ops(obj, params); |
||
779 | } |
||
780 | } |
||
781 | |||
782 | /** @} */ |
||
783 | |||
784 | /** |
||
785 | * @name Iterators |
||
786 | * @{ |
||
787 | */ |
||
788 | |||
789 | /** |
||
790 | * Call a callback on each element of the cache. |
||
791 | * @arg cache cache to iterate on |
||
792 | * @arg cb callback function |
||
793 | * @arg arg argument passed to callback function |
||
794 | * |
||
795 | * Calls a callback function \a cb on each element of the \a cache. |
||
796 | * The argument \a arg is passed on the callback function. |
||
797 | */ |
||
798 | void nl_cache_foreach(struct nl_cache *cache, |
||
799 | void (*cb)(struct nl_object *, void *), void *arg) |
||
800 | { |
||
801 | nl_cache_foreach_filter(cache, NULL, cb, arg); |
||
802 | } |
||
803 | |||
804 | /** |
||
805 | * Call a callback on each element of the cache (filtered). |
||
806 | * @arg cache cache to iterate on |
||
807 | * @arg filter filter object |
||
808 | * @arg cb callback function |
||
809 | * @arg arg argument passed to callback function |
||
810 | * |
||
811 | * Calls a callback function \a cb on each element of the \a cache |
||
812 | * that matches the \a filter. The argument \a arg is passed on |
||
813 | * to the callback function. |
||
814 | */ |
||
815 | void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, |
||
816 | void (*cb)(struct nl_object *, void *), void *arg) |
||
817 | { |
||
818 | struct nl_object *obj, *tmp; |
||
819 | struct nl_object_ops *ops; |
||
820 | |||
821 | if (cache->c_ops == NULL) |
||
822 | BUG(); |
||
823 | |||
824 | ops = cache->c_ops->co_obj_ops; |
||
825 | |||
826 | nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { |
||
827 | if (filter && !nl_object_match_filter(obj, filter)) |
||
828 | continue; |
||
829 | |||
830 | cb(obj, arg); |
||
831 | } |
||
832 | } |
||
833 | |||
834 | /** @} */ |
||
835 | |||
836 | /** @} */ |