cache_mngr.c

00001 /*
00002  * lib/cache_mngr.c     Cache Manager
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup cache_mngt
00014  * @defgroup cache_mngr Manager
00015  * @brief Helps keeping caches up to date.
00016  *
00017  * The purpose of a cache manager is to keep track of caches and
00018  * automatically receive event notifications to keep the caches
00019  * up to date with the kernel state. Each manager has exactly one
00020  * netlink socket assigned which limits the scope of each manager
00021  * to exactly one netlink family. Therefore all caches committed
00022  * to a manager must be part of the same netlink family. Due to the
00023  * nature of a manager, it is not possible to have a cache maintain
00024  * two instances of the same cache type. The socket is subscribed
00025  * to the event notification group of each cache and also put into
00026  * non-blocking mode. Functions exist to poll() on the socket to
00027  * wait for new events to be received.
00028  *
00029  * @code
00030  * App       libnl                        Kernel
00031  *        |                            |
00032  *            +-----------------+        [ notification, link change ]
00033  *        |   |  Cache Manager  |      | [   (IFF_UP | IFF_RUNNING)  ]
00034  *            |                 |                |
00035  *        |   |   +------------+|      |         |  [ notification, new addr ]
00036  *    <-------|---| route/link |<-------(async)--+  [  10.0.1.1/32 dev eth1  ]
00037  *        |   |   +------------+|      |                      |
00038  *            |   +------------+|                             |
00039  *    <---|---|---| route/addr |<------|-(async)--------------+
00040  *            |   +------------+|
00041  *        |   |   +------------+|      |
00042  *    <-------|---| ...        ||
00043  *        |   |   +------------+|      |
00044  *            +-----------------+
00045  *        |                            |
00046  * @endcode
00047  *
00048  * @par 1) Creating a new cache manager
00049  * @code
00050  * struct nl_cache_mngr *mngr;
00051  *
00052  * // Allocate a new cache manager for RTNETLINK and automatically
00053  * // provide the caches added to the manager.
00054  * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE);
00055  * @endcode
00056  *
00057  * @par 2) Keep track of a cache
00058  * @code
00059  * struct nl_cache *cache;
00060  *
00061  * // Create a new cache for links/interfaces and ask the manager to
00062  * // keep it up to date for us. This will trigger a full dump request
00063  * // to initially fill the cache.
00064  * cache = nl_cache_mngr_add(mngr, "route/link");
00065  * @endcode
00066  *
00067  * @par 3) Make the manager receive updates
00068  * @code
00069  * // Give the manager the ability to receive updates, will call poll()
00070  * // with a timeout of 5 seconds.
00071  * if (nl_cache_mngr_poll(mngr, 5000) > 0) {
00072  *         // Manager received at least one update, dump cache?
00073  *         nl_cache_dump(cache, ...);
00074  * }
00075  * @endcode
00076  *
00077  * @par 4) Release cache manager
00078  * @code
00079  * nl_cache_mngr_free(mngr);
00080  * @endcode
00081  * @{
00082  */
00083 
00084 #include <netlink-local.h>
00085 #include <netlink/netlink.h>
00086 #include <netlink/cache.h>
00087 #include <netlink/utils.h>
00088 
00089 static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
00090 {
00091         struct nl_cache_assoc *ca = p->pp_arg;
00092 
00093         NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
00094 #ifdef NL_DEBUG
00095         if (nl_debug >= 4)
00096                 nl_object_dump(obj, &nl_debug_dp);
00097 #endif
00098         return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
00099 }
00100 
00101 static int event_input(struct nl_msg *msg, void *arg)
00102 {
00103         struct nl_cache_mngr *mngr = arg;
00104         int protocol = nlmsg_get_proto(msg);
00105         int type = nlmsg_hdr(msg)->nlmsg_type;
00106         struct nl_cache_ops *ops;
00107         int i, n;
00108         struct nl_parser_param p = {
00109                 .pp_cb = include_cb,
00110         };
00111 
00112         NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
00113                mngr, msg);
00114 #ifdef NL_DEBUG
00115         if (nl_debug >= 4)
00116                 nl_msg_dump(msg, stderr);
00117 #endif
00118 
00119         if (mngr->cm_protocol != protocol)
00120                 BUG();
00121 
00122         for (i = 0; i < mngr->cm_nassocs; i++) {
00123                 if (mngr->cm_assocs[i].ca_cache) {
00124                         ops = mngr->cm_assocs[i].ca_cache->c_ops;
00125                         for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
00126                                 if (ops->co_msgtypes[n].mt_id == type)
00127                                         goto found;
00128                 }
00129         }
00130 
00131         return NL_SKIP;
00132 
00133 found:
00134         NL_DBG(2, "Associated message %p to cache %p\n",
00135                msg, mngr->cm_assocs[i].ca_cache);
00136         p.pp_arg = &mngr->cm_assocs[i];
00137 
00138         return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
00139 }
00140 
00141 /**
00142  * Allocate new cache manager
00143  * @arg sk              Netlink socket.
00144  * @arg protocol        Netlink Protocol this manager is used for
00145  * @arg flags           Flags
00146  *
00147  * @return Newly allocated cache manager or NULL on failure.
00148  */
00149 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
00150                         struct nl_cache_mngr **result)
00151 {
00152         struct nl_cache_mngr *mngr;
00153         int err = -NLE_NOMEM;
00154 
00155         if (sk == NULL)
00156                 BUG();
00157 
00158         mngr = calloc(1, sizeof(*mngr));
00159         if (!mngr)
00160                 goto errout;
00161 
00162         mngr->cm_handle = sk;
00163         mngr->cm_nassocs = 32;
00164         mngr->cm_protocol = protocol;
00165         mngr->cm_flags = flags;
00166         mngr->cm_assocs = calloc(mngr->cm_nassocs,
00167                                  sizeof(struct nl_cache_assoc));
00168         if (!mngr->cm_assocs)
00169                 goto errout;
00170 
00171         nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
00172                             event_input, mngr);
00173 
00174         /* Required to receive async event notifications */
00175         nl_socket_disable_seq_check(mngr->cm_handle);
00176 
00177         if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
00178                 goto errout;
00179 
00180         if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
00181                 goto errout;
00182 
00183         NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
00184                mngr, protocol, mngr->cm_nassocs);
00185 
00186         *result = mngr;
00187         return 0;
00188 
00189 errout:
00190         nl_cache_mngr_free(mngr);
00191         return err;
00192 }
00193 
00194 /**
00195  * Add cache responsibility to cache manager
00196  * @arg mngr            Cache manager.
00197  * @arg name            Name of cache to keep track of
00198  * @arg cb              Function to be called upon changes.
00199  * @arg result          Pointer to store added cache.
00200  *
00201  * Allocates a new cache of the specified type and adds it to the manager.
00202  * The operation will trigger a full dump request from the kernel to
00203  * initially fill the contents of the cache. The manager will subscribe
00204  * to the notification group of the cache to keep track of any further
00205  * changes.
00206  *
00207  * @return 0 on success or a negative error code.
00208  */
00209 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
00210                       change_func_t cb, void *data, struct nl_cache **result)
00211 {
00212         struct nl_cache_ops *ops;
00213         struct nl_cache *cache;
00214         struct nl_af_group *grp;
00215         int err, i;
00216 
00217         ops = nl_cache_ops_lookup(name);
00218         if (!ops)
00219                 return -NLE_NOCACHE;
00220 
00221         if (ops->co_protocol != mngr->cm_protocol)
00222                 return -NLE_PROTO_MISMATCH;
00223 
00224         if (ops->co_groups == NULL)
00225                 return -NLE_OPNOTSUPP;
00226 
00227         for (i = 0; i < mngr->cm_nassocs; i++)
00228                 if (mngr->cm_assocs[i].ca_cache &&
00229                     mngr->cm_assocs[i].ca_cache->c_ops == ops)
00230                         return -NLE_EXIST;
00231 
00232 retry:
00233         for (i = 0; i < mngr->cm_nassocs; i++)
00234                 if (!mngr->cm_assocs[i].ca_cache)
00235                         break;
00236 
00237         if (i >= mngr->cm_nassocs) {
00238                 mngr->cm_nassocs += 16;
00239                 mngr->cm_assocs = realloc(mngr->cm_assocs,
00240                                           mngr->cm_nassocs *
00241                                           sizeof(struct nl_cache_assoc));
00242                 if (mngr->cm_assocs == NULL)
00243                         return -NLE_NOMEM;
00244                 else {
00245                         NL_DBG(1, "Increased capacity of cache manager %p " \
00246                                   "to %d\n", mngr, mngr->cm_nassocs);
00247                         goto retry;
00248                 }
00249         }
00250 
00251         cache = nl_cache_alloc(ops);
00252         if (!cache)
00253                 return -NLE_NOMEM;
00254 
00255         for (grp = ops->co_groups; grp->ag_group; grp++) {
00256                 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
00257                 if (err < 0)
00258                         goto errout_free_cache;
00259         }
00260 
00261         err = nl_cache_refill(mngr->cm_handle, cache);
00262         if (err < 0)
00263                 goto errout_drop_membership;
00264 
00265         mngr->cm_assocs[i].ca_cache = cache;
00266         mngr->cm_assocs[i].ca_change = cb;
00267         mngr->cm_assocs[i].ca_change_data = data;
00268 
00269         if (mngr->cm_flags & NL_AUTO_PROVIDE)
00270                 nl_cache_mngt_provide(cache);
00271 
00272         NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
00273                cache, nl_cache_name(cache), mngr);
00274 
00275         *result = cache;
00276         return 0;
00277 
00278 errout_drop_membership:
00279         for (grp = ops->co_groups; grp->ag_group; grp++)
00280                 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
00281 errout_free_cache:
00282         nl_cache_free(cache);
00283 
00284         return err;
00285 }
00286 
00287 /**
00288  * Get file descriptor
00289  * @arg mngr            Cache Manager
00290  *
00291  * Get the file descriptor of the socket associated to the manager.
00292  * This can be used to change socket options or monitor activity
00293  * using poll()/select().
00294  */
00295 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
00296 {
00297         return nl_socket_get_fd(mngr->cm_handle);
00298 }
00299 
00300 /**
00301  * Check for event notifications
00302  * @arg mngr            Cache Manager
00303  * @arg timeout         Upper limit poll() will block, in milliseconds.
00304  *
00305  * Causes poll() to be called to check for new event notifications
00306  * being available. Automatically receives and handles available
00307  * notifications.
00308  *
00309  * This functionally is ideally called regularly during an idle
00310  * period.
00311  *
00312  * @return A positive value if at least one update was handled, 0
00313  *         for none, or a  negative error code.
00314  */
00315 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
00316 {
00317         int ret;
00318         struct pollfd fds = {
00319                 .fd = nl_socket_get_fd(mngr->cm_handle),
00320                 .events = POLLIN,
00321         };
00322 
00323         NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
00324         ret = poll(&fds, 1, timeout);
00325         NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
00326         if (ret < 0)
00327                 return -nl_syserr2nlerr(errno);
00328 
00329         if (ret == 0)
00330                 return 0;
00331 
00332         return nl_cache_mngr_data_ready(mngr);
00333 }
00334 
00335 /**
00336  * Receive available event notifications
00337  * @arg mngr            Cache manager
00338  *
00339  * This function can be called if the socket associated to the manager
00340  * contains updates to be received. This function should not be used
00341  * if nl_cache_mngr_poll() is used.
00342  *
00343  * @return A positive value if at least one update was handled, 0
00344  *         for none, or a  negative error code.
00345  */
00346 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
00347 {
00348         int err;
00349 
00350         err = nl_recvmsgs_default(mngr->cm_handle);
00351         if (err < 0)
00352                 return err;
00353 
00354         return 1;
00355 }
00356 
00357 /**
00358  * Free cache manager and all caches.
00359  * @arg mngr            Cache manager.
00360  *
00361  * Release all resources after usage of a cache manager.
00362  */
00363 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
00364 {
00365         int i;
00366 
00367         if (!mngr)
00368                 return;
00369 
00370         if (mngr->cm_handle)
00371                 nl_close(mngr->cm_handle);
00372 
00373         for (i = 0; i < mngr->cm_nassocs; i++)
00374                 if (mngr->cm_assocs[i].ca_cache)
00375                         nl_cache_free(mngr->cm_assocs[i].ca_cache);
00376 
00377         free(mngr->cm_assocs);
00378         free(mngr);
00379 
00380         NL_DBG(1, "Cache manager %p freed\n", mngr);
00381 }
00382 
00383 /** @} */