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
45static 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
67static 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
99found:
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 */
135int 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
189errout_free_sync_sock:
190	nl_socket_free(mngr->cm_sync_sock);
191errout:
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 */
221int 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
243retry:
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
286errout_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 */
321int 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
344errout_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 */
359int 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 */
383int 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 */
419int 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 */
455void 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 */
494void 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