Home | History | Annotate | Download | only in lib
      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-2008 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup cache_mngt
     14  * @defgroup cache_mngr Manager
     15  * @brief Helps keeping caches up to date.
     16  *
     17  * The purpose of a cache manager is to keep track of caches and
     18  * automatically receive event notifications to keep the caches
     19  * up to date with the kernel state. Each manager has exactly one
     20  * netlink socket assigned which limits the scope of each manager
     21  * to exactly one netlink family. Therefore all caches committed
     22  * to a manager must be part of the same netlink family. Due to the
     23  * nature of a manager, it is not possible to have a cache maintain
     24  * two instances of the same cache type. The socket is subscribed
     25  * to the event notification group of each cache and also put into
     26  * non-blocking mode. Functions exist to poll() on the socket to
     27  * wait for new events to be received.
     28  *
     29  * @code
     30  * App       libnl                        Kernel
     31  *        |                            |
     32  *            +-----------------+        [ notification, link change ]
     33  *        |   |  Cache Manager  |      | [   (IFF_UP | IFF_RUNNING)  ]
     34  *            |                 |                |
     35  *        |   |   +------------+|      |         |  [ notification, new addr ]
     36  *    <-------|---| route/link |<-------(async)--+  [  10.0.1.1/32 dev eth1  ]
     37  *        |   |   +------------+|      |                      |
     38  *            |   +------------+|                             |
     39  *    <---|---|---| route/addr |<------|-(async)--------------+
     40  *            |   +------------+|
     41  *        |   |   +------------+|      |
     42  *    <-------|---| ...        ||
     43  *        |   |   +------------+|      |
     44  *            +-----------------+
     45  *        |                            |
     46  * @endcode
     47  *
     48  * @par 1) Creating a new cache manager
     49  * @code
     50  * struct nl_cache_mngr *mngr;
     51  *
     52  * // Allocate a new cache manager for RTNETLINK and automatically
     53  * // provide the caches added to the manager.
     54  * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE);
     55  * @endcode
     56  *
     57  * @par 2) Keep track of a cache
     58  * @code
     59  * struct nl_cache *cache;
     60  *
     61  * // Create a new cache for links/interfaces and ask the manager to
     62  * // keep it up to date for us. This will trigger a full dump request
     63  * // to initially fill the cache.
     64  * cache = nl_cache_mngr_add(mngr, "route/link");
     65  * @endcode
     66  *
     67  * @par 3) Make the manager receive updates
     68  * @code
     69  * // Give the manager the ability to receive updates, will call poll()
     70  * // with a timeout of 5 seconds.
     71  * if (nl_cache_mngr_poll(mngr, 5000) > 0) {
     72  *         // Manager received at least one update, dump cache?
     73  *         nl_cache_dump(cache, ...);
     74  * }
     75  * @endcode
     76  *
     77  * @par 4) Release cache manager
     78  * @code
     79  * nl_cache_mngr_free(mngr);
     80  * @endcode
     81  * @{
     82  */
     83 
     84 #include <netlink-local.h>
     85 #include <netlink/netlink.h>
     86 #include <netlink/cache.h>
     87 #include <netlink/utils.h>
     88 
     89 static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
     90 {
     91 	struct nl_cache_assoc *ca = p->pp_arg;
     92 
     93 	NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
     94 #ifdef NL_DEBUG
     95 	if (nl_debug >= 4)
     96 		nl_object_dump(obj, &nl_debug_dp);
     97 #endif
     98 	return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
     99 }
    100 
    101 static int event_input(struct nl_msg *msg, void *arg)
    102 {
    103 	struct nl_cache_mngr *mngr = arg;
    104 	int protocol = nlmsg_get_proto(msg);
    105 	int type = nlmsg_hdr(msg)->nlmsg_type;
    106 	struct nl_cache_ops *ops;
    107 	int i, n;
    108 	struct nl_parser_param p = {
    109 		.pp_cb = include_cb,
    110 	};
    111 
    112 	NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
    113 	       mngr, msg);
    114 #ifdef NL_DEBUG
    115 	if (nl_debug >= 4)
    116 		nl_msg_dump(msg, stderr);
    117 #endif
    118 
    119 	if (mngr->cm_protocol != protocol)
    120 		BUG();
    121 
    122 	for (i = 0; i < mngr->cm_nassocs; i++) {
    123 		if (mngr->cm_assocs[i].ca_cache) {
    124 			ops = mngr->cm_assocs[i].ca_cache->c_ops;
    125 			for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
    126 				if (ops->co_msgtypes[n].mt_id == type)
    127 					goto found;
    128 		}
    129 	}
    130 
    131 	return NL_SKIP;
    132 
    133 found:
    134 	NL_DBG(2, "Associated message %p to cache %p\n",
    135 	       msg, mngr->cm_assocs[i].ca_cache);
    136 	p.pp_arg = &mngr->cm_assocs[i];
    137 
    138 	return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
    139 }
    140 
    141 /**
    142  * Allocate new cache manager
    143  * @arg sk		Netlink socket.
    144  * @arg protocol	Netlink Protocol this manager is used for
    145  * @arg flags		Flags
    146  *
    147  * @return Newly allocated cache manager or NULL on failure.
    148  */
    149 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
    150 			struct nl_cache_mngr **result)
    151 {
    152 	struct nl_cache_mngr *mngr;
    153 	int err = -NLE_NOMEM;
    154 
    155 	if (sk == NULL)
    156 		BUG();
    157 
    158 	mngr = calloc(1, sizeof(*mngr));
    159 	if (!mngr)
    160 		goto errout;
    161 
    162 	mngr->cm_handle = sk;
    163 	mngr->cm_nassocs = 32;
    164 	mngr->cm_protocol = protocol;
    165 	mngr->cm_flags = flags;
    166 	mngr->cm_assocs = calloc(mngr->cm_nassocs,
    167 				 sizeof(struct nl_cache_assoc));
    168 	if (!mngr->cm_assocs)
    169 		goto errout;
    170 
    171 	nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
    172 			    event_input, mngr);
    173 
    174 	/* Required to receive async event notifications */
    175 	nl_socket_disable_seq_check(mngr->cm_handle);
    176 
    177 	if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
    178 		goto errout;
    179 
    180 	if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
    181 		goto errout;
    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:
    190 	nl_cache_mngr_free(mngr);
    191 	return err;
    192 }
    193 
    194 /**
    195  * Add cache responsibility to cache manager
    196  * @arg mngr		Cache manager.
    197  * @arg name		Name of cache to keep track of
    198  * @arg cb		Function to be called upon changes.
    199  * @arg result		Pointer to store added cache.
    200  *
    201  * Allocates a new cache of the specified type and adds it to the manager.
    202  * The operation will trigger a full dump request from the kernel to
    203  * initially fill the contents of the cache. The manager will subscribe
    204  * to the notification group of the cache to keep track of any further
    205  * changes.
    206  *
    207  * @return 0 on success or a negative error code.
    208  */
    209 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
    210 		      change_func_t cb, void *data, struct nl_cache **result)
    211 {
    212 	struct nl_cache_ops *ops;
    213 	struct nl_cache *cache;
    214 	struct nl_af_group *grp;
    215 	int err, i;
    216 
    217 	ops = nl_cache_ops_lookup(name);
    218 	if (!ops)
    219 		return -NLE_NOCACHE;
    220 
    221 	if (ops->co_protocol != mngr->cm_protocol)
    222 		return -NLE_PROTO_MISMATCH;
    223 
    224 	if (ops->co_groups == NULL)
    225 		return -NLE_OPNOTSUPP;
    226 
    227 	for (i = 0; i < mngr->cm_nassocs; i++)
    228 		if (mngr->cm_assocs[i].ca_cache &&
    229 		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
    230 			return -NLE_EXIST;
    231 
    232 retry:
    233 	for (i = 0; i < mngr->cm_nassocs; i++)
    234 		if (!mngr->cm_assocs[i].ca_cache)
    235 			break;
    236 
    237 	if (i >= mngr->cm_nassocs) {
    238 		mngr->cm_nassocs += 16;
    239 		mngr->cm_assocs = realloc(mngr->cm_assocs,
    240 					  mngr->cm_nassocs *
    241 					  sizeof(struct nl_cache_assoc));
    242 		if (mngr->cm_assocs == NULL)
    243 			return -NLE_NOMEM;
    244 		else {
    245 			NL_DBG(1, "Increased capacity of cache manager %p " \
    246 				  "to %d\n", mngr, mngr->cm_nassocs);
    247 			goto retry;
    248 		}
    249 	}
    250 
    251 	cache = nl_cache_alloc(ops);
    252 	if (!cache)
    253 		return -NLE_NOMEM;
    254 
    255 	for (grp = ops->co_groups; grp->ag_group; grp++) {
    256 		err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
    257 		if (err < 0)
    258 			goto errout_free_cache;
    259 	}
    260 
    261 	err = nl_cache_refill(mngr->cm_handle, cache);
    262 	if (err < 0)
    263 		goto errout_drop_membership;
    264 
    265 	mngr->cm_assocs[i].ca_cache = cache;
    266 	mngr->cm_assocs[i].ca_change = cb;
    267 	mngr->cm_assocs[i].ca_change_data = data;
    268 
    269 	if (mngr->cm_flags & NL_AUTO_PROVIDE)
    270 		nl_cache_mngt_provide(cache);
    271 
    272 	NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
    273 	       cache, nl_cache_name(cache), mngr);
    274 
    275 	*result = cache;
    276 	return 0;
    277 
    278 errout_drop_membership:
    279 	for (grp = ops->co_groups; grp->ag_group; grp++)
    280 		nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
    281 errout_free_cache:
    282 	nl_cache_free(cache);
    283 
    284 	return err;
    285 }
    286 
    287 /**
    288  * Get file descriptor
    289  * @arg mngr		Cache Manager
    290  *
    291  * Get the file descriptor of the socket associated to the manager.
    292  * This can be used to change socket options or monitor activity
    293  * using poll()/select().
    294  */
    295 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
    296 {
    297 	return nl_socket_get_fd(mngr->cm_handle);
    298 }
    299 
    300 /**
    301  * Check for event notifications
    302  * @arg mngr		Cache Manager
    303  * @arg timeout		Upper limit poll() will block, in milliseconds.
    304  *
    305  * Causes poll() to be called to check for new event notifications
    306  * being available. Automatically receives and handles available
    307  * notifications.
    308  *
    309  * This functionally is ideally called regularly during an idle
    310  * period.
    311  *
    312  * @return A positive value if at least one update was handled, 0
    313  *         for none, or a  negative error code.
    314  */
    315 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
    316 {
    317 	int ret;
    318 	struct pollfd fds = {
    319 		.fd = nl_socket_get_fd(mngr->cm_handle),
    320 		.events = POLLIN,
    321 	};
    322 
    323 	NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
    324 	ret = poll(&fds, 1, timeout);
    325 	NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
    326 	if (ret < 0)
    327 		return -nl_syserr2nlerr(errno);
    328 
    329 	if (ret == 0)
    330 		return 0;
    331 
    332 	return nl_cache_mngr_data_ready(mngr);
    333 }
    334 
    335 /**
    336  * Receive available event notifications
    337  * @arg mngr		Cache manager
    338  *
    339  * This function can be called if the socket associated to the manager
    340  * contains updates to be received. This function should not be used
    341  * if nl_cache_mngr_poll() is used.
    342  *
    343  * @return A positive value if at least one update was handled, 0
    344  *         for none, or a  negative error code.
    345  */
    346 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
    347 {
    348 	int err;
    349 
    350 	err = nl_recvmsgs_default(mngr->cm_handle);
    351 	if (err < 0)
    352 		return err;
    353 
    354 	return 1;
    355 }
    356 
    357 /**
    358  * Free cache manager and all caches.
    359  * @arg mngr		Cache manager.
    360  *
    361  * Release all resources after usage of a cache manager.
    362  */
    363 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
    364 {
    365 	int i;
    366 
    367 	if (!mngr)
    368 		return;
    369 
    370 	if (mngr->cm_handle)
    371 		nl_close(mngr->cm_handle);
    372 
    373 	for (i = 0; i < mngr->cm_nassocs; i++)
    374 		if (mngr->cm_assocs[i].ca_cache)
    375 			nl_cache_free(mngr->cm_assocs[i].ca_cache);
    376 
    377 	free(mngr->cm_assocs);
    378 	free(mngr);
    379 
    380 	NL_DBG(1, "Cache manager %p freed\n", mngr);
    381 }
    382 
    383 /** @} */
    384