libnl 2.0
|
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 /** @} */