1/* 2 * lib/cache.c Caching Module 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> 10 */ 11 12/** 13 * @ingroup cache_mngt 14 * @defgroup cache Cache 15 * 16 * @code 17 * Cache Management | | Type Specific Cache Operations 18 * 19 * | | +----------------+ +------------+ 20 * | request update | | msg_parser | 21 * | | +----------------+ +------------+ 22 * +- - - - -^- - - - - - - -^- -|- - - - 23 * nl_cache_update: | | | | 24 * 1) --------- co_request_update ------+ | | 25 * | | | 26 * 2) destroy old cache +----------- pp_cb ---------|---+ 27 * | | | 28 * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ 29 * +--------------+ | | | | 30 * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - 31 * +--------------+ | | +-------------+ 32 * | nl_recvmsgs | 33 * | | +-----|-^-----+ 34 * +---v-|---+ 35 * | | | nl_recv | 36 * +---------+ 37 * | | Core Netlink 38 * @endcode 39 * 40 * @{ 41 */ 42 43#include <netlink-local.h> 44#include <netlink/netlink.h> 45#include <netlink/cache.h> 46#include <netlink/object.h> 47#include <netlink/utils.h> 48 49/** 50 * @name Cache Creation/Deletion 51 * @{ 52 */ 53 54/** 55 * Allocate an empty cache 56 * @arg ops cache operations to base the cache on 57 * 58 * @return A newly allocated and initialized cache. 59 */ 60struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) 61{ 62 struct nl_cache *cache; 63 64 cache = calloc(1, sizeof(*cache)); 65 if (!cache) 66 return NULL; 67 68 nl_init_list_head(&cache->c_items); 69 cache->c_ops = ops; 70 71 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); 72 73 return cache; 74} 75 76int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, 77 struct nl_cache **result) 78{ 79 struct nl_cache *cache; 80 int err; 81 82 if (!(cache = nl_cache_alloc(ops))) 83 return -NLE_NOMEM; 84 85 if (sock && (err = nl_cache_refill(sock, cache)) < 0) { 86 nl_cache_free(cache); 87 return err; 88 } 89 90 *result = cache; 91 return 0; 92} 93 94/** 95 * Clear a cache. 96 * @arg cache cache to clear 97 * 98 * Removes all elements of a cache. 99 */ 100void nl_cache_clear(struct nl_cache *cache) 101{ 102 struct nl_object *obj, *tmp; 103 104 NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); 105 106 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) 107 nl_cache_remove(obj); 108} 109 110/** 111 * Free a cache. 112 * @arg cache Cache to free. 113 * 114 * Removes all elements of a cache and frees all memory. 115 * 116 * @note Use this function if you are working with allocated caches. 117 */ 118void nl_cache_free(struct nl_cache *cache) 119{ 120 if (!cache) 121 return; 122 123 nl_cache_clear(cache); 124 NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); 125 free(cache); 126} 127 128/** @} */ 129 130/** 131 * @name Cache Modifications 132 * @{ 133 */ 134 135static int __cache_add(struct nl_cache *cache, struct nl_object *obj) 136{ 137 obj->ce_cache = cache; 138 139 nl_list_add_tail(&obj->ce_list, &cache->c_items); 140 cache->c_nitems++; 141 142 NL_DBG(1, "Added %p to cache %p <%s>.\n", 143 obj, cache, nl_cache_name(cache)); 144 145 return 0; 146} 147 148/** 149 * Add object to a cache. 150 * @arg cache Cache to add object to 151 * @arg obj Object to be added to the cache 152 * 153 * Adds the given object to the specified cache. The object is cloned 154 * if it has been added to another cache already. 155 * 156 * @return 0 or a negative error code. 157 */ 158int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) 159{ 160 struct nl_object *new; 161 162 if (cache->c_ops->co_obj_ops != obj->ce_ops) 163 return -NLE_OBJ_MISMATCH; 164 165 if (!nl_list_empty(&obj->ce_list)) { 166 new = nl_object_clone(obj); 167 if (!new) 168 return -NLE_NOMEM; 169 } else { 170 nl_object_get(obj); 171 new = obj; 172 } 173 174 return __cache_add(cache, new); 175} 176 177/** 178 * Removes an object from a cache. 179 * @arg obj Object to remove from its cache 180 * 181 * Removes the object \c obj from the cache it is assigned to, since 182 * an object can only be assigned to one cache at a time, the cache 183 * must ne be passed along with it. 184 */ 185void nl_cache_remove(struct nl_object *obj) 186{ 187 struct nl_cache *cache = obj->ce_cache; 188 189 if (cache == NULL) 190 return; 191 192 nl_list_del(&obj->ce_list); 193 obj->ce_cache = NULL; 194 nl_object_put(obj); 195 cache->c_nitems--; 196 197 NL_DBG(1, "Deleted %p from cache %p <%s>.\n", 198 obj, cache, nl_cache_name(cache)); 199} 200 201/** @} */ 202 203/** 204 * @name Synchronization 205 * @{ 206 */ 207 208/** 209 * Request a full dump from the kernel to fill a cache 210 * @arg sk Netlink socket. 211 * @arg cache Cache subjected to be filled. 212 * 213 * Send a dumping request to the kernel causing it to dump all objects 214 * related to the specified cache to the netlink socket. 215 * 216 * Use nl_cache_pickup() to read the objects from the socket and fill them 217 * into a cache. 218 */ 219int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache) 220{ 221 NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", 222 cache, nl_cache_name(cache)); 223 224 if (cache->c_ops->co_request_update == NULL) 225 return -NLE_OPNOTSUPP; 226 227 return cache->c_ops->co_request_update(cache, sk); 228} 229 230/** @cond SKIP */ 231struct update_xdata { 232 struct nl_cache_ops *ops; 233 struct nl_parser_param *params; 234}; 235 236static int update_msg_parser(struct nl_msg *msg, void *arg) 237{ 238 struct update_xdata *x = arg; 239 240 return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); 241} 242/** @endcond */ 243 244int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, 245 struct nl_parser_param *param) 246{ 247 int err; 248 struct nl_cb *cb; 249 struct update_xdata x = { 250 .ops = cache->c_ops, 251 .params = param, 252 }; 253 254 NL_DBG(1, "Picking up answer for cache %p <%s>...\n", 255 cache, nl_cache_name(cache)); 256 257 cb = nl_cb_clone(sk->s_cb); 258 if (cb == NULL) 259 return -NLE_NOMEM; 260 261 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); 262 263 err = nl_recvmsgs(sk, cb); 264 if (err < 0) 265 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ 266 "%d: %s", cache, nl_cache_name(cache), 267 err, nl_geterror(err)); 268 269 nl_cb_put(cb); 270 271 return err; 272} 273 274static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) 275{ 276 return nl_cache_add((struct nl_cache *) p->pp_arg, c); 277} 278 279/** 280 * Pickup a netlink dump response and put it into a cache. 281 * @arg sk Netlink socket. 282 * @arg cache Cache to put items into. 283 * 284 * Waits for netlink messages to arrive, parses them and puts them into 285 * the specified cache. 286 * 287 * @return 0 on success or a negative error code. 288 */ 289int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) 290{ 291 struct nl_parser_param p = { 292 .pp_cb = pickup_cb, 293 .pp_arg = cache, 294 }; 295 296 return __cache_pickup(sk, cache, &p); 297} 298 299 300/** @} */ 301 302/** 303 * @name Parsing 304 * @{ 305 */ 306 307/** @cond SKIP */ 308int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, 309 struct nlmsghdr *nlh, struct nl_parser_param *params) 310{ 311 int i, err; 312 313 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) 314 return -NLE_MSG_TOOSHORT; 315 316 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { 317 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { 318 err = ops->co_msg_parser(ops, who, nlh, params); 319 if (err != -NLE_OPNOTSUPP) 320 goto errout; 321 } 322 } 323 324 325 err = -NLE_MSGTYPE_NOSUPPORT; 326errout: 327 return err; 328} 329/** @endcond */ 330 331/** 332 * Parse a netlink message and add it to the cache. 333 * @arg cache cache to add element to 334 * @arg msg netlink message 335 * 336 * Parses a netlink message by calling the cache specific message parser 337 * and adds the new element to the cache. 338 * 339 * @return 0 or a negative error code. 340 */ 341int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) 342{ 343 struct nl_parser_param p = { 344 .pp_cb = pickup_cb, 345 .pp_arg = cache, 346 }; 347 348 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); 349} 350 351/** 352 * (Re)fill a cache with the contents in the kernel. 353 * @arg sk Netlink socket. 354 * @arg cache cache to update 355 * 356 * Clears the specified cache and fills it with the current state in 357 * the kernel. 358 * 359 * @return 0 or a negative error code. 360 */ 361int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) 362{ 363 int err; 364 365 err = nl_cache_request_full_dump(sk, cache); 366 if (err < 0) 367 return err; 368 369 NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", 370 cache, nl_cache_name(cache)); 371 nl_cache_clear(cache); 372 373 return nl_cache_pickup(sk, cache); 374} 375 376/** @} */ 377