1 /*
2 * lib/cache_mngr.c Cache Manager
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-2012 Thomas Graf <tgraf@suug.ch>
10 */
11
12 /**
13 * @ingroup cache_mngt
14 * @defgroup cache_mngr Manager
15 * @brief Manager keeping caches up to date automatically.
16 *
17 * The cache manager keeps caches up to date automatically by listening to
18 * netlink notifications and integrating the received information into the
19 * existing cache.
20 *
21 * @note This functionality is still considered experimental.
22 *
23 * Related sections in the development guide:
24 * - @core_doc{_cache_manager,Cache Manager}
25 *
26 * @{
27 *
28 * Header
29 * ------
30 * ~~~~{.c}
31 * #include <netlink/cache.h>
32 * ~~~~
33 */
34
35 #include <netlink-private/netlink.h>
36 #include <netlink/netlink.h>
37 #include <netlink/cache.h>
38 #include <netlink/utils.h>
39
40 /** @cond SKIP */
41 #define NASSOC_INIT 16
42 #define NASSOC_EXPAND 8
43 /** @endcond */
44
include_cb(struct nl_object * obj,struct nl_parser_param * p)45 static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
46 {
47 struct nl_cache_assoc *ca = p->pp_arg;
48 struct nl_cache_ops *ops = ca->ca_cache->c_ops;
49
50 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
51 #ifdef NL_DEBUG
52 if (nl_debug >= 4)
53 nl_object_dump(obj, &nl_debug_dp);
54 #endif
55
56 if (ops->co_event_filter)
57 if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK)
58 return 0;
59
60 if (ops->co_include_event)
61 return ops->co_include_event(ca->ca_cache, obj, ca->ca_change,
62 ca->ca_change_data);
63 else
64 return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
65 }
66
event_input(struct nl_msg * msg,void * arg)67 static int event_input(struct nl_msg *msg, void *arg)
68 {
69 struct nl_cache_mngr *mngr = arg;
70 int protocol = nlmsg_get_proto(msg);
71 int type = nlmsg_hdr(msg)->nlmsg_type;
72 struct nl_cache_ops *ops;
73 int i, n;
74 struct nl_parser_param p = {
75 .pp_cb = include_cb,
76 };
77
78 NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
79 mngr, msg);
80 #ifdef NL_DEBUG
81 if (nl_debug >= 4)
82 nl_msg_dump(msg, stderr);
83 #endif
84
85 if (mngr->cm_protocol != protocol)
86 BUG();
87
88 for (i = 0; i < mngr->cm_nassocs; i++) {
89 if (mngr->cm_assocs[i].ca_cache) {
90 ops = mngr->cm_assocs[i].ca_cache->c_ops;
91 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
92 if (ops->co_msgtypes[n].mt_id == type)
93 goto found;
94 }
95 }
96
97 return NL_SKIP;
98
99 found:
100 NL_DBG(2, "Associated message %p to cache %p\n",
101 msg, mngr->cm_assocs[i].ca_cache);
102 p.pp_arg = &mngr->cm_assocs[i];
103
104 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
105 }
106
107 /**
108 * Allocate new cache manager
109 * @arg sk Netlink socket or NULL to auto allocate
110 * @arg protocol Netlink protocol this manager is used for
111 * @arg flags Flags (\c NL_AUTO_PROVIDE)
112 * @arg result Result pointer
113 *
114 * Allocates a new cache manager for the specified netlink protocol.
115 *
116 * 1. If sk is not specified (\c NULL) a netlink socket matching the
117 * specified protocol will be automatically allocated.
118 *
119 * 2. The socket will be put in non-blocking mode and sequence checking
120 * will be disabled regardless of whether the socket was provided by
121 * the caller or automatically allocated.
122 *
123 * 3. The socket will be connected.
124 *
125 * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the
126 * manager will automatically be made available to other users using
127 * nl_cache_mngt_provide().
128 *
129 * @note If the socket is provided by the caller, it is NOT recommended
130 * to use the socket for anything else besides receiving netlink
131 * notifications.
132 *
133 * @return 0 on success or a negative error code.
134 */
nl_cache_mngr_alloc(struct nl_sock * sk,int protocol,int flags,struct nl_cache_mngr ** result)135 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
136 struct nl_cache_mngr **result)
137 {
138 struct nl_cache_mngr *mngr;
139 int err = -NLE_NOMEM;
140
141 /* Catch abuse of flags */
142 if (flags & NL_ALLOCATED_SOCK)
143 BUG();
144
145 mngr = calloc(1, sizeof(*mngr));
146 if (!mngr)
147 return -NLE_NOMEM;
148
149 if (!sk) {
150 if (!(sk = nl_socket_alloc()))
151 goto errout;
152
153 flags |= NL_ALLOCATED_SOCK;
154 }
155
156 mngr->cm_sock = sk;
157 mngr->cm_nassocs = NASSOC_INIT;
158 mngr->cm_protocol = protocol;
159 mngr->cm_flags = flags;
160 mngr->cm_assocs = calloc(mngr->cm_nassocs,
161 sizeof(struct nl_cache_assoc));
162 if (!mngr->cm_assocs)
163 goto errout;
164
165 /* Required to receive async event notifications */
166 nl_socket_disable_seq_check(mngr->cm_sock);
167
168 if ((err = nl_connect(mngr->cm_sock, protocol)) < 0)
169 goto errout;
170
171 if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0)
172 goto errout;
173
174 /* Create and allocate socket for sync cache fills */
175 mngr->cm_sync_sock = nl_socket_alloc();
176 if (!mngr->cm_sync_sock) {
177 err = -NLE_NOMEM;
178 goto errout;
179 }
180 if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0)
181 goto errout_free_sync_sock;
182
183 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
184 mngr, protocol, mngr->cm_nassocs);
185
186 *result = mngr;
187 return 0;
188
189 errout_free_sync_sock:
190 nl_socket_free(mngr->cm_sync_sock);
191 errout:
192 nl_cache_mngr_free(mngr);
193 return err;
194 }
195
196 /**
197 * Add cache to cache manager
198 * @arg mngr Cache manager.
199 * @arg cache Cache to be added to cache manager
200 * @arg cb Function to be called upon changes.
201 * @arg data Argument passed on to change callback
202 *
203 * Adds cache to the manager. The operation will trigger a full
204 * dump request from the kernel to initially fill the contents
205 * of the cache. The manager will subscribe to the notification group
206 * of the cache and keep track of any further changes.
207 *
208 * The user is responsible for calling nl_cache_mngr_poll() or monitor
209 * the socket and call nl_cache_mngr_data_ready() to allow the library
210 * to process netlink notification events.
211 *
212 * @see nl_cache_mngr_poll()
213 * @see nl_cache_mngr_data_ready()
214 *
215 * @return 0 on success or a negative error code.
216 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
217 * cache type
218 * @return -NLE_OPNOTSUPP Cache type does not support updates
219 * @return -NLE_EXIST Cache of this type already being managed
220 */
nl_cache_mngr_add_cache(struct nl_cache_mngr * mngr,struct nl_cache * cache,change_func_t cb,void * data)221 int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache,
222 change_func_t cb, void *data)
223 {
224 struct nl_cache_ops *ops;
225 struct nl_af_group *grp;
226 int err, i;
227
228 ops = cache->c_ops;
229 if (!ops)
230 return -NLE_INVAL;
231
232 if (ops->co_protocol != mngr->cm_protocol)
233 return -NLE_PROTO_MISMATCH;
234
235 if (ops->co_groups == NULL)
236 return -NLE_OPNOTSUPP;
237
238 for (i = 0; i < mngr->cm_nassocs; i++)
239 if (mngr->cm_assocs[i].ca_cache &&
240 mngr->cm_assocs[i].ca_cache->c_ops == ops)
241 return -NLE_EXIST;
242
243 retry:
244 for (i = 0; i < mngr->cm_nassocs; i++)
245 if (!mngr->cm_assocs[i].ca_cache)
246 break;
247
248 if (i >= mngr->cm_nassocs) {
249 mngr->cm_nassocs += NASSOC_EXPAND;
250 mngr->cm_assocs = realloc(mngr->cm_assocs,
251 mngr->cm_nassocs *
252 sizeof(struct nl_cache_assoc));
253 if (mngr->cm_assocs == NULL)
254 return -NLE_NOMEM;
255
256 memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0,
257 NASSOC_EXPAND * sizeof(struct nl_cache_assoc));
258
259 NL_DBG(1, "Increased capacity of cache manager %p " \
260 "to %d\n", mngr, mngr->cm_nassocs);
261 goto retry;
262 }
263
264 for (grp = ops->co_groups; grp->ag_group; grp++) {
265 err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group);
266 if (err < 0)
267 return err;
268 }
269
270 err = nl_cache_refill(mngr->cm_sync_sock, cache);
271 if (err < 0)
272 goto errout_drop_membership;
273
274 mngr->cm_assocs[i].ca_cache = cache;
275 mngr->cm_assocs[i].ca_change = cb;
276 mngr->cm_assocs[i].ca_change_data = data;
277
278 if (mngr->cm_flags & NL_AUTO_PROVIDE)
279 nl_cache_mngt_provide(cache);
280
281 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
282 cache, nl_cache_name(cache), mngr);
283
284 return 0;
285
286 errout_drop_membership:
287 for (grp = ops->co_groups; grp->ag_group; grp++)
288 nl_socket_drop_membership(mngr->cm_sock, grp->ag_group);
289
290 return err;
291 }
292
293 /**
294 * Add cache to cache manager
295 * @arg mngr Cache manager.
296 * @arg name Name of cache to keep track of
297 * @arg cb Function to be called upon changes.
298 * @arg data Argument passed on to change callback
299 * @arg result Pointer to store added cache (optional)
300 *
301 * Allocates a new cache of the specified type and adds it to the manager.
302 * The operation will trigger a full dump request from the kernel to
303 * initially fill the contents of the cache. The manager will subscribe
304 * to the notification group of the cache and keep track of any further
305 * changes.
306 *
307 * The user is responsible for calling nl_cache_mngr_poll() or monitor
308 * the socket and call nl_cache_mngr_data_ready() to allow the library
309 * to process netlink notification events.
310 *
311 * @see nl_cache_mngr_poll()
312 * @see nl_cache_mngr_data_ready()
313 *
314 * @return 0 on success or a negative error code.
315 * @return -NLE_NOCACHE Unknown cache type
316 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
317 * cache type
318 * @return -NLE_OPNOTSUPP Cache type does not support updates
319 * @return -NLE_EXIST Cache of this type already being managed
320 */
nl_cache_mngr_add(struct nl_cache_mngr * mngr,const char * name,change_func_t cb,void * data,struct nl_cache ** result)321 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
322 change_func_t cb, void *data, struct nl_cache **result)
323 {
324 struct nl_cache_ops *ops;
325 struct nl_cache *cache;
326 int err;
327
328 ops = nl_cache_ops_lookup_safe(name);
329 if (!ops)
330 return -NLE_NOCACHE;
331
332 cache = nl_cache_alloc(ops);
333 nl_cache_ops_put(ops);
334 if (!cache)
335 return -NLE_NOMEM;
336
337 err = nl_cache_mngr_add_cache(mngr, cache, cb, data);
338 if (err < 0)
339 goto errout_free_cache;
340
341 *result = cache;
342 return 0;
343
344 errout_free_cache:
345 nl_cache_free(cache);
346
347 return err;
348 }
349
350 /**
351 * Get socket file descriptor
352 * @arg mngr Cache Manager
353 *
354 * Get the file descriptor of the socket associated with the manager.
355 *
356 * @note Do not use the socket for anything besides receiving
357 * notifications.
358 */
nl_cache_mngr_get_fd(struct nl_cache_mngr * mngr)359 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
360 {
361 return nl_socket_get_fd(mngr->cm_sock);
362 }
363
364 /**
365 * Check for event notifications
366 * @arg mngr Cache Manager
367 * @arg timeout Upper limit poll() will block, in milliseconds.
368 *
369 * Causes poll() to be called to check for new event notifications
370 * being available. Calls nl_cache_mngr_data_ready() to process
371 * available data.
372 *
373 * This functionally is ideally called regularly during an idle
374 * period.
375 *
376 * A timeout can be specified in milliseconds to limit the time the
377 * function will wait for updates.
378 *
379 * @see nl_cache_mngr_data_ready()
380 *
381 * @return The number of messages processed or a negative error code.
382 */
nl_cache_mngr_poll(struct nl_cache_mngr * mngr,int timeout)383 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
384 {
385 int ret;
386 struct pollfd fds = {
387 .fd = nl_socket_get_fd(mngr->cm_sock),
388 .events = POLLIN,
389 };
390
391 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
392 ret = poll(&fds, 1, timeout);
393 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
394 if (ret < 0)
395 return -nl_syserr2nlerr(errno);
396
397 /* No events, return */
398 if (ret == 0)
399 return 0;
400
401 return nl_cache_mngr_data_ready(mngr);
402 }
403
404 /**
405 * Receive available event notifications
406 * @arg mngr Cache manager
407 *
408 * This function can be called if the socket associated to the manager
409 * contains updates to be received. This function should only be used
410 * if nl_cache_mngr_poll() is not used.
411 *
412 * The function will process messages until there is no more data to
413 * be read from the socket.
414 *
415 * @see nl_cache_mngr_poll()
416 *
417 * @return The number of messages processed or a negative error code.
418 */
nl_cache_mngr_data_ready(struct nl_cache_mngr * mngr)419 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
420 {
421 int err, nread = 0;
422 struct nl_cb *cb;
423
424 NL_DBG(2, "Cache manager %p, reading new data from fd %d\n",
425 mngr, nl_socket_get_fd(mngr->cm_sock));
426
427 cb = nl_cb_clone(mngr->cm_sock->s_cb);
428 if (cb == NULL)
429 return -NLE_NOMEM;
430
431 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr);
432
433 while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) {
434 NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n",
435 mngr, err);
436 nread += err;
437 }
438
439 nl_cb_put(cb);
440 if (err < 0 && err != -NLE_AGAIN)
441 return err;
442
443 return nread;
444 }
445
446 /**
447 * Print information about cache manager
448 * @arg mngr Cache manager
449 * @arg p Dumping parameters
450 *
451 * Prints information about the cache manager including all managed caches.
452 *
453 * @note This is a debugging function.
454 */
nl_cache_mngr_info(struct nl_cache_mngr * mngr,struct nl_dump_params * p)455 void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p)
456 {
457 char buf[128];
458 int i;
459
460 nl_dump_line(p, "cache-manager <%p>\n", mngr);
461 nl_dump_line(p, " .protocol = %s\n",
462 nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf)));
463 nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags);
464 nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs);
465 nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock);
466
467 for (i = 0; i < mngr->cm_nassocs; i++) {
468 struct nl_cache_assoc *assoc = &mngr->cm_assocs[i];
469
470 if (assoc->ca_cache) {
471 nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache);
472 nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name);
473 nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change);
474 nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data);
475 nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache));
476 nl_dump_line(p, " .objects = {\n");
477
478 p->dp_prefix += 6;
479 nl_cache_dump(assoc->ca_cache, p);
480 p->dp_prefix -= 6;
481
482 nl_dump_line(p, " }\n");
483 nl_dump_line(p, " }\n");
484 }
485 }
486 }
487
488 /**
489 * Free cache manager and all caches.
490 * @arg mngr Cache manager.
491 *
492 * Release all resources held by a cache manager.
493 */
nl_cache_mngr_free(struct nl_cache_mngr * mngr)494 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
495 {
496 int i;
497
498 if (!mngr)
499 return;
500
501 if (mngr->cm_sock)
502 nl_close(mngr->cm_sock);
503
504 if (mngr->cm_sync_sock) {
505 nl_close(mngr->cm_sync_sock);
506 nl_socket_free(mngr->cm_sync_sock);
507 }
508
509 if (mngr->cm_flags & NL_ALLOCATED_SOCK)
510 nl_socket_free(mngr->cm_sock);
511
512 for (i = 0; i < mngr->cm_nassocs; i++) {
513 if (mngr->cm_assocs[i].ca_cache) {
514 nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache);
515 nl_cache_free(mngr->cm_assocs[i].ca_cache);
516 }
517 }
518
519 free(mngr->cm_assocs);
520
521 NL_DBG(1, "Cache manager %p freed\n", mngr);
522
523 free(mngr);
524 }
525
526 /** @} */
527