libnl  3.2.3
/build/buildd/libnl3-3.2.3/lib/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         struct nl_cache_ops *ops = ca->ca_cache->c_ops;
00093 
00094         NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
00095 #ifdef NL_DEBUG
00096         if (nl_debug >= 4)
00097                 nl_object_dump(obj, &nl_debug_dp);
00098 #endif
00099 
00100         if (ops->co_event_filter)
00101                 if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK)
00102                         return 0;
00103 
00104         return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
00105 }
00106 
00107 static int event_input(struct nl_msg *msg, void *arg)
00108 {
00109         struct nl_cache_mngr *mngr = arg;
00110         int protocol = nlmsg_get_proto(msg);
00111         int type = nlmsg_hdr(msg)->nlmsg_type;
00112         struct nl_cache_ops *ops;
00113         int i, n;
00114         struct nl_parser_param p = {
00115                 .pp_cb = include_cb,
00116         };
00117 
00118         NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
00119                mngr, msg);
00120 #ifdef NL_DEBUG
00121         if (nl_debug >= 4)
00122                 nl_msg_dump(msg, stderr);
00123 #endif
00124 
00125         if (mngr->cm_protocol != protocol)
00126                 BUG();
00127 
00128         for (i = 0; i < mngr->cm_nassocs; i++) {
00129                 if (mngr->cm_assocs[i].ca_cache) {
00130                         ops = mngr->cm_assocs[i].ca_cache->c_ops;
00131                         for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
00132                                 if (ops->co_msgtypes[n].mt_id == type)
00133                                         goto found;
00134                 }
00135         }
00136 
00137         return NL_SKIP;
00138 
00139 found:
00140         NL_DBG(2, "Associated message %p to cache %p\n",
00141                msg, mngr->cm_assocs[i].ca_cache);
00142         p.pp_arg = &mngr->cm_assocs[i];
00143 
00144         return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
00145 }
00146 
00147 /**
00148  * Allocate new cache manager
00149  * @arg sk              Netlink socket.
00150  * @arg protocol        Netlink Protocol this manager is used for
00151  * @arg flags           Flags
00152  * @arg result          Result pointer
00153  *
00154  * @return 0 on success or a negative error code.
00155  */
00156 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
00157                         struct nl_cache_mngr **result)
00158 {
00159         struct nl_cache_mngr *mngr;
00160         int err = -NLE_NOMEM;
00161 
00162         if (sk == NULL)
00163                 BUG();
00164 
00165         mngr = calloc(1, sizeof(*mngr));
00166         if (!mngr)
00167                 goto errout;
00168 
00169         mngr->cm_handle = sk;
00170         mngr->cm_nassocs = 32;
00171         mngr->cm_protocol = protocol;
00172         mngr->cm_flags = flags;
00173         mngr->cm_assocs = calloc(mngr->cm_nassocs,
00174                                  sizeof(struct nl_cache_assoc));
00175         if (!mngr->cm_assocs)
00176                 goto errout;
00177 
00178         nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
00179                             event_input, mngr);
00180 
00181         /* Required to receive async event notifications */
00182         nl_socket_disable_seq_check(mngr->cm_handle);
00183 
00184         if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
00185                 goto errout;
00186 
00187         if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
00188                 goto errout;
00189 
00190         NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
00191                mngr, protocol, mngr->cm_nassocs);
00192 
00193         *result = mngr;
00194         return 0;
00195 
00196 errout:
00197         nl_cache_mngr_free(mngr);
00198         return err;
00199 }
00200 
00201 /**
00202  * Add cache responsibility to cache manager
00203  * @arg mngr            Cache manager.
00204  * @arg name            Name of cache to keep track of
00205  * @arg cb              Function to be called upon changes.
00206  * @arg data            Argument passed on to change callback
00207  * @arg result          Pointer to store added cache.
00208  *
00209  * Allocates a new cache of the specified type and adds it to the manager.
00210  * The operation will trigger a full dump request from the kernel to
00211  * initially fill the contents of the cache. The manager will subscribe
00212  * to the notification group of the cache to keep track of any further
00213  * changes.
00214  *
00215  * @return 0 on success or a negative error code.
00216  */
00217 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
00218                       change_func_t cb, void *data, struct nl_cache **result)
00219 {
00220         struct nl_cache_ops *ops;
00221         struct nl_cache *cache;
00222         struct nl_af_group *grp;
00223         int err, i;
00224 
00225         ops = nl_cache_ops_lookup(name);
00226         if (!ops)
00227                 return -NLE_NOCACHE;
00228 
00229         if (ops->co_protocol != mngr->cm_protocol)
00230                 return -NLE_PROTO_MISMATCH;
00231 
00232         if (ops->co_groups == NULL)
00233                 return -NLE_OPNOTSUPP;
00234 
00235         for (i = 0; i < mngr->cm_nassocs; i++)
00236                 if (mngr->cm_assocs[i].ca_cache &&
00237                     mngr->cm_assocs[i].ca_cache->c_ops == ops)
00238                         return -NLE_EXIST;
00239 
00240 retry:
00241         for (i = 0; i < mngr->cm_nassocs; i++)
00242                 if (!mngr->cm_assocs[i].ca_cache)
00243                         break;
00244 
00245         if (i >= mngr->cm_nassocs) {
00246                 mngr->cm_nassocs += 16;
00247                 mngr->cm_assocs = realloc(mngr->cm_assocs,
00248                                           mngr->cm_nassocs *
00249                                           sizeof(struct nl_cache_assoc));
00250                 if (mngr->cm_assocs == NULL)
00251                         return -NLE_NOMEM;
00252                 else {
00253                         NL_DBG(1, "Increased capacity of cache manager %p " \
00254                                   "to %d\n", mngr, mngr->cm_nassocs);
00255                         goto retry;
00256                 }
00257         }
00258 
00259         cache = nl_cache_alloc(ops);
00260         if (!cache)
00261                 return -NLE_NOMEM;
00262 
00263         for (grp = ops->co_groups; grp->ag_group; grp++) {
00264                 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
00265                 if (err < 0)
00266                         goto errout_free_cache;
00267         }
00268 
00269         err = nl_cache_refill(mngr->cm_handle, cache);
00270         if (err < 0)
00271                 goto errout_drop_membership;
00272 
00273         mngr->cm_assocs[i].ca_cache = cache;
00274         mngr->cm_assocs[i].ca_change = cb;
00275         mngr->cm_assocs[i].ca_change_data = data;
00276 
00277         if (mngr->cm_flags & NL_AUTO_PROVIDE)
00278                 nl_cache_mngt_provide(cache);
00279 
00280         NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
00281                cache, nl_cache_name(cache), mngr);
00282 
00283         *result = cache;
00284         return 0;
00285 
00286 errout_drop_membership:
00287         for (grp = ops->co_groups; grp->ag_group; grp++)
00288                 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
00289 errout_free_cache:
00290         nl_cache_free(cache);
00291 
00292         return err;
00293 }
00294 
00295 /**
00296  * Get file descriptor
00297  * @arg mngr            Cache Manager
00298  *
00299  * Get the file descriptor of the socket associated to the manager.
00300  * This can be used to change socket options or monitor activity
00301  * using poll()/select().
00302  */
00303 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
00304 {
00305         return nl_socket_get_fd(mngr->cm_handle);
00306 }
00307 
00308 /**
00309  * Check for event notifications
00310  * @arg mngr            Cache Manager
00311  * @arg timeout         Upper limit poll() will block, in milliseconds.
00312  *
00313  * Causes poll() to be called to check for new event notifications
00314  * being available. Automatically receives and handles available
00315  * notifications.
00316  *
00317  * This functionally is ideally called regularly during an idle
00318  * period.
00319  *
00320  * @return A positive value if at least one update was handled, 0
00321  *         for none, or a  negative error code.
00322  */
00323 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
00324 {
00325         int ret;
00326         struct pollfd fds = {
00327                 .fd = nl_socket_get_fd(mngr->cm_handle),
00328                 .events = POLLIN,
00329         };
00330 
00331         NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
00332         ret = poll(&fds, 1, timeout);
00333         NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
00334         if (ret < 0)
00335                 return -nl_syserr2nlerr(errno);
00336 
00337         if (ret == 0)
00338                 return 0;
00339 
00340         return nl_cache_mngr_data_ready(mngr);
00341 }
00342 
00343 /**
00344  * Receive available event notifications
00345  * @arg mngr            Cache manager
00346  *
00347  * This function can be called if the socket associated to the manager
00348  * contains updates to be received. This function should not be used
00349  * if nl_cache_mngr_poll() is used.
00350  *
00351  * @return A positive value if at least one update was handled, 0
00352  *         for none, or a  negative error code.
00353  */
00354 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
00355 {
00356         int err;
00357 
00358         err = nl_recvmsgs_default(mngr->cm_handle);
00359         if (err < 0)
00360                 return err;
00361 
00362         return 1;
00363 }
00364 
00365 /**
00366  * Free cache manager and all caches.
00367  * @arg mngr            Cache manager.
00368  *
00369  * Release all resources after usage of a cache manager.
00370  */
00371 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
00372 {
00373         int i;
00374 
00375         if (!mngr)
00376                 return;
00377 
00378         if (mngr->cm_handle)
00379                 nl_close(mngr->cm_handle);
00380 
00381         for (i = 0; i < mngr->cm_nassocs; i++) {
00382                 if (mngr->cm_assocs[i].ca_cache) {
00383                         nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache);
00384                         nl_cache_free(mngr->cm_assocs[i].ca_cache);
00385                 }
00386         }
00387 
00388         free(mngr->cm_assocs);
00389         free(mngr);
00390 
00391         NL_DBG(1, "Cache manager %p freed\n", mngr);
00392 }
00393 
00394 /** @} */