OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | 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 Cache Creation/Deletion |
||
51 | * @{ |
||
52 | */ |
||
53 | |||
54 | /** |
||
55 | * Allocate an empty cache |
||
56 | * @arg ops cache operations to base the cache on |
||
57 | * |
||
58 | * @return A newly allocated and initialized cache. |
||
59 | */ |
||
60 | struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) |
||
61 | { |
||
62 | struct nl_cache *cache; |
||
63 | |||
64 | cache = calloc(1, sizeof(*cache)); |
||
65 | if (!cache) |
||
66 | return NULL; |
||
67 | |||
68 | nl_init_list_head(&cache->c_items); |
||
69 | cache->c_ops = ops; |
||
70 | |||
71 | NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); |
||
72 | |||
73 | return cache; |
||
74 | } |
||
75 | |||
76 | int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, |
||
77 | struct nl_cache **result) |
||
78 | { |
||
79 | struct nl_cache *cache; |
||
80 | int err; |
||
81 | |||
82 | if (!(cache = nl_cache_alloc(ops))) |
||
83 | return -NLE_NOMEM; |
||
84 | |||
85 | if (sock && (err = nl_cache_refill(sock, cache)) < 0) { |
||
86 | nl_cache_free(cache); |
||
87 | return err; |
||
88 | } |
||
89 | |||
90 | *result = cache; |
||
91 | return 0; |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Clear a cache. |
||
96 | * @arg cache cache to clear |
||
97 | * |
||
98 | * Removes all elements of a cache. |
||
99 | */ |
||
100 | void nl_cache_clear(struct nl_cache *cache) |
||
101 | { |
||
102 | struct nl_object *obj, *tmp; |
||
103 | |||
104 | NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); |
||
105 | |||
106 | nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) |
||
107 | nl_cache_remove(obj); |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Free a cache. |
||
112 | * @arg cache Cache to free. |
||
113 | * |
||
114 | * Removes all elements of a cache and frees all memory. |
||
115 | * |
||
116 | * @note Use this function if you are working with allocated caches. |
||
117 | */ |
||
118 | void nl_cache_free(struct nl_cache *cache) |
||
119 | { |
||
120 | if (!cache) |
||
121 | return; |
||
122 | |||
123 | nl_cache_clear(cache); |
||
124 | NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); |
||
125 | free(cache); |
||
126 | } |
||
127 | |||
128 | /** @} */ |
||
129 | |||
130 | /** |
||
131 | * @name Cache Modifications |
||
132 | * @{ |
||
133 | */ |
||
134 | |||
135 | static int __cache_add(struct nl_cache *cache, struct nl_object *obj) |
||
136 | { |
||
137 | obj->ce_cache = cache; |
||
138 | |||
139 | nl_list_add_tail(&obj->ce_list, &cache->c_items); |
||
140 | cache->c_nitems++; |
||
141 | |||
142 | NL_DBG(1, "Added %p to cache %p <%s>.\n", |
||
143 | obj, cache, nl_cache_name(cache)); |
||
144 | |||
145 | return 0; |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Add object to a cache. |
||
150 | * @arg cache Cache to add object to |
||
151 | * @arg obj Object to be added to the cache |
||
152 | * |
||
153 | * Adds the given object to the specified cache. The object is cloned |
||
154 | * if it has been added to another cache already. |
||
155 | * |
||
156 | * @return 0 or a negative error code. |
||
157 | */ |
||
158 | int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) |
||
159 | { |
||
160 | struct nl_object *new; |
||
161 | |||
162 | if (cache->c_ops->co_obj_ops != obj->ce_ops) |
||
163 | return -NLE_OBJ_MISMATCH; |
||
164 | |||
165 | if (!nl_list_empty(&obj->ce_list)) { |
||
166 | new = nl_object_clone(obj); |
||
167 | if (!new) |
||
168 | return -NLE_NOMEM; |
||
169 | } else { |
||
170 | nl_object_get(obj); |
||
171 | new = obj; |
||
172 | } |
||
173 | |||
174 | return __cache_add(cache, new); |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * Removes an object from a cache. |
||
179 | * @arg obj Object to remove from its cache |
||
180 | * |
||
181 | * Removes the object \c obj from the cache it is assigned to, since |
||
182 | * an object can only be assigned to one cache at a time, the cache |
||
183 | * must ne be passed along with it. |
||
184 | */ |
||
185 | void nl_cache_remove(struct nl_object *obj) |
||
186 | { |
||
187 | struct nl_cache *cache = obj->ce_cache; |
||
188 | |||
189 | if (cache == NULL) |
||
190 | return; |
||
191 | |||
192 | nl_list_del(&obj->ce_list); |
||
193 | obj->ce_cache = NULL; |
||
194 | nl_object_put(obj); |
||
195 | cache->c_nitems--; |
||
196 | |||
197 | NL_DBG(1, "Deleted %p from cache %p <%s>.\n", |
||
198 | obj, cache, nl_cache_name(cache)); |
||
199 | } |
||
200 | |||
201 | /** @} */ |
||
202 | |||
203 | /** |
||
204 | * @name Synchronization |
||
205 | * @{ |
||
206 | */ |
||
207 | |||
208 | /** |
||
209 | * Request a full dump from the kernel to fill a cache |
||
210 | * @arg sk Netlink socket. |
||
211 | * @arg cache Cache subjected to be filled. |
||
212 | * |
||
213 | * Send a dumping request to the kernel causing it to dump all objects |
||
214 | * related to the specified cache to the netlink socket. |
||
215 | * |
||
216 | * Use nl_cache_pickup() to read the objects from the socket and fill them |
||
217 | * into a cache. |
||
218 | */ |
||
219 | int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache) |
||
220 | { |
||
221 | NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", |
||
222 | cache, nl_cache_name(cache)); |
||
223 | |||
224 | if (cache->c_ops->co_request_update == NULL) |
||
225 | return -NLE_OPNOTSUPP; |
||
226 | |||
227 | return cache->c_ops->co_request_update(cache, sk); |
||
228 | } |
||
229 | |||
230 | /** @cond SKIP */ |
||
231 | struct update_xdata { |
||
232 | struct nl_cache_ops *ops; |
||
233 | struct nl_parser_param *params; |
||
234 | }; |
||
235 | |||
236 | static int update_msg_parser(struct nl_msg *msg, void *arg) |
||
237 | { |
||
238 | struct update_xdata *x = arg; |
||
239 | |||
240 | return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); |
||
241 | } |
||
242 | /** @endcond */ |
||
243 | |||
244 | int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, |
||
245 | struct nl_parser_param *param) |
||
246 | { |
||
247 | int err; |
||
248 | struct nl_cb *cb; |
||
249 | struct update_xdata x = { |
||
250 | .ops = cache->c_ops, |
||
251 | .params = param, |
||
252 | }; |
||
253 | |||
254 | NL_DBG(1, "Picking up answer for cache %p <%s>...\n", |
||
255 | cache, nl_cache_name(cache)); |
||
256 | |||
257 | cb = nl_cb_clone(sk->s_cb); |
||
258 | if (cb == NULL) |
||
259 | return -NLE_NOMEM; |
||
260 | |||
261 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); |
||
262 | |||
263 | err = nl_recvmsgs(sk, cb); |
||
264 | if (err < 0) |
||
265 | NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ |
||
266 | "%d: %s", cache, nl_cache_name(cache), |
||
267 | err, nl_geterror(err)); |
||
268 | |||
269 | nl_cb_put(cb); |
||
270 | |||
271 | return err; |
||
272 | } |
||
273 | |||
274 | static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) |
||
275 | { |
||
276 | return nl_cache_add((struct nl_cache *) p->pp_arg, c); |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Pickup a netlink dump response and put it into a cache. |
||
281 | * @arg sk Netlink socket. |
||
282 | * @arg cache Cache to put items into. |
||
283 | * |
||
284 | * Waits for netlink messages to arrive, parses them and puts them into |
||
285 | * the specified cache. |
||
286 | * |
||
287 | * @return 0 on success or a negative error code. |
||
288 | */ |
||
289 | int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) |
||
290 | { |
||
291 | struct nl_parser_param p = { |
||
292 | .pp_cb = pickup_cb, |
||
293 | .pp_arg = cache, |
||
294 | }; |
||
295 | |||
296 | return __cache_pickup(sk, cache, &p); |
||
297 | } |
||
298 | |||
299 | |||
300 | /** @} */ |
||
301 | |||
302 | /** |
||
303 | * @name Parsing |
||
304 | * @{ |
||
305 | */ |
||
306 | |||
307 | /** @cond SKIP */ |
||
308 | int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, |
||
309 | struct nlmsghdr *nlh, struct nl_parser_param *params) |
||
310 | { |
||
311 | int i, err; |
||
312 | |||
313 | if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) |
||
314 | return -NLE_MSG_TOOSHORT; |
||
315 | |||
316 | for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { |
||
317 | if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { |
||
318 | err = ops->co_msg_parser(ops, who, nlh, params); |
||
319 | if (err != -NLE_OPNOTSUPP) |
||
320 | goto errout; |
||
321 | } |
||
322 | } |
||
323 | |||
324 | |||
325 | err = -NLE_MSGTYPE_NOSUPPORT; |
||
326 | errout: |
||
327 | return err; |
||
328 | } |
||
329 | /** @endcond */ |
||
330 | |||
331 | /** |
||
332 | * Parse a netlink message and add it to the cache. |
||
333 | * @arg cache cache to add element to |
||
334 | * @arg msg netlink message |
||
335 | * |
||
336 | * Parses a netlink message by calling the cache specific message parser |
||
337 | * and adds the new element to the cache. |
||
338 | * |
||
339 | * @return 0 or a negative error code. |
||
340 | */ |
||
341 | int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) |
||
342 | { |
||
343 | struct nl_parser_param p = { |
||
344 | .pp_cb = pickup_cb, |
||
345 | .pp_arg = cache, |
||
346 | }; |
||
347 | |||
348 | return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * (Re)fill a cache with the contents in the kernel. |
||
353 | * @arg sk Netlink socket. |
||
354 | * @arg cache cache to update |
||
355 | * |
||
356 | * Clears the specified cache and fills it with the current state in |
||
357 | * the kernel. |
||
358 | * |
||
359 | * @return 0 or a negative error code. |
||
360 | */ |
||
361 | int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) |
||
362 | { |
||
363 | int err; |
||
364 | |||
365 | err = nl_cache_request_full_dump(sk, cache); |
||
366 | if (err < 0) |
||
367 | return err; |
||
368 | |||
369 | NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", |
||
370 | cache, nl_cache_name(cache)); |
||
371 | nl_cache_clear(cache); |
||
372 | |||
373 | return nl_cache_pickup(sk, cache); |
||
374 | } |
||
375 | |||
376 | /** @} */ |