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