libnl 1.1

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-2007 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);
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 handle          Netlink socket/handle to be used
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 struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle,
00150                                           int protocol, int flags)
00151 {
00152         struct nl_cache_mngr *mngr;
00153 
00154         if (handle == NULL)
00155                 BUG();
00156 
00157         mngr = calloc(1, sizeof(*mngr));
00158         if (!mngr)
00159                 goto enomem;
00160 
00161         mngr->cm_handle = handle;
00162         mngr->cm_nassocs = 32;
00163         mngr->cm_protocol = protocol;
00164         mngr->cm_flags = flags;
00165         mngr->cm_assocs = calloc(mngr->cm_nassocs,
00166                                  sizeof(struct nl_cache_assoc));
00167         if (!mngr->cm_assocs)
00168                 goto enomem;
00169 
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_disable_sequence_check(mngr->cm_handle);
00176 
00177         if (nl_connect(mngr->cm_handle, protocol) < 0)
00178                 goto errout;
00179 
00180         if (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         return mngr;
00187 
00188 enomem:
00189         nl_errno(ENOMEM);
00190 errout:
00191         nl_cache_mngr_free(mngr);
00192         return NULL;
00193 }
00194 
00195 /**
00196  * Add cache responsibility to cache manager
00197  * @arg mngr            Cache manager.
00198  * @arg name            Name of cache to keep track of
00199  * @arg cb              Function to be called upon changes.
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 The newly allocated cache or NULL on failure.
00208  */
00209 struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
00210                                    change_func_t cb)
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                 nl_error(ENOENT, "Unknown cache type");
00220                 return NULL;
00221         }
00222 
00223         if (ops->co_protocol != mngr->cm_protocol) {
00224                 nl_error(EINVAL, "Netlink protocol mismatch");
00225                 return NULL;
00226         }
00227 
00228         if (ops->co_groups == NULL) {
00229                 nl_error(EOPNOTSUPP, NULL);
00230                 return NULL;
00231         }
00232 
00233         for (i = 0; i < mngr->cm_nassocs; i++) {
00234                 if (mngr->cm_assocs[i].ca_cache &&
00235                     mngr->cm_assocs[i].ca_cache->c_ops == ops) {
00236                         nl_error(EEXIST, "Cache of this type already managed");
00237                         return NULL;
00238                 }
00239         }
00240 
00241 retry:
00242         for (i = 0; i < mngr->cm_nassocs; i++)
00243                 if (!mngr->cm_assocs[i].ca_cache)
00244                         break;
00245 
00246         if (i >= mngr->cm_nassocs) {
00247                 mngr->cm_nassocs += 16;
00248                 mngr->cm_assocs = realloc(mngr->cm_assocs,
00249                                           mngr->cm_nassocs *
00250                                           sizeof(struct nl_cache_assoc));
00251                 if (mngr->cm_assocs == NULL) {
00252                         nl_errno(ENOMEM);
00253                         return NULL;
00254                 } else {
00255                         NL_DBG(1, "Increased capacity of cache manager %p " \
00256                                   "to %d\n", mngr, mngr->cm_nassocs);
00257                         goto retry;
00258                 }
00259         }
00260 
00261         cache = nl_cache_alloc(ops);
00262         if (!cache) {
00263                 nl_errno(ENOMEM);
00264                 return NULL;
00265         }
00266 
00267         for (grp = ops->co_groups; grp->ag_group; grp++) {
00268                 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
00269                 if (err < 0)
00270                         goto errout_free_cache;
00271         }
00272 
00273         err = nl_cache_refill(mngr->cm_handle, cache);
00274         if (err < 0)
00275                 goto errout_drop_membership;
00276 
00277         mngr->cm_assocs[i].ca_cache = cache;
00278         mngr->cm_assocs[i].ca_change = cb;
00279 
00280         if (mngr->cm_flags & NL_AUTO_PROVIDE)
00281                 nl_cache_mngt_provide(cache);
00282 
00283         NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
00284                cache, nl_cache_name(cache), mngr);
00285 
00286         return cache;
00287 
00288 errout_drop_membership:
00289         for (grp = ops->co_groups; grp->ag_group; grp++)
00290                 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
00291 errout_free_cache:
00292         nl_cache_free(cache);
00293 
00294         return NULL;
00295 }
00296 
00297 /**
00298  * Get file descriptor
00299  * @arg mngr            Cache Manager
00300  *
00301  * Get the file descriptor of the socket associated to the manager.
00302  * This can be used to change socket options or monitor activity
00303  * using poll()/select().
00304  */
00305 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
00306 {
00307         return nl_socket_get_fd(mngr->cm_handle);
00308 }
00309 
00310 /**
00311  * Check for event notifications
00312  * @arg mngr            Cache Manager
00313  * @arg timeout         Upper limit poll() will block, in milliseconds.
00314  *
00315  * Causes poll() to be called to check for new event notifications
00316  * being available. Automatically receives and handles available
00317  * notifications.
00318  *
00319  * This functionally is ideally called regularly during an idle
00320  * period.
00321  *
00322  * @return A positive value if at least one update was handled, 0
00323  *         for none, or a  negative error code.
00324  */
00325 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
00326 {
00327         int ret;
00328         struct pollfd fds = {
00329                 .fd = nl_socket_get_fd(mngr->cm_handle),
00330                 .events = POLLIN,
00331         };
00332 
00333         NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
00334         ret = poll(&fds, 1, timeout);
00335         NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
00336         if (ret < 0)
00337                 return nl_errno(errno);
00338 
00339         if (ret == 0)
00340                 return 0;
00341 
00342         return nl_cache_mngr_data_ready(mngr);
00343 }
00344 
00345 /**
00346  * Receive available event notifications
00347  * @arg mngr            Cache manager
00348  *
00349  * This function can be called if the socket associated to the manager
00350  * contains updates to be received. This function should not be used
00351  * if nl_cache_mngr_poll() is used.
00352  *
00353  * @return A positive value if at least one update was handled, 0
00354  *         for none, or a  negative error code.
00355  */
00356 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
00357 {
00358         int err;
00359 
00360         err = nl_recvmsgs_default(mngr->cm_handle);
00361         if (err < 0)
00362                 return err;
00363 
00364         return 1;
00365 }
00366 
00367 /**
00368  * Free cache manager
00369  * @arg mngr            Cache manager
00370  *
00371  * Release all resources after usage of a cache manager.
00372  */
00373 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
00374 {
00375         if (!mngr)
00376                 return;
00377 
00378         if (mngr->cm_handle) {
00379                 nl_close(mngr->cm_handle);
00380                 nl_handle_destroy(mngr->cm_handle);
00381         }
00382 
00383         free(mngr->cm_assocs);
00384         free(mngr);
00385 
00386         NL_DBG(1, "Cache manager %p freed\n", mngr);
00387 }
00388 
00389 /** @} */