1 /* 2 * lib/cache_mngr.c Cache Manager 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-2012 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup cache_mngt 14 * @defgroup cache_mngr Manager 15 * @brief Manager keeping caches up to date automatically. 16 * 17 * The cache manager keeps caches up to date automatically by listening to 18 * netlink notifications and integrating the received information into the 19 * existing cache. 20 * 21 * @note This functionality is still considered experimental. 22 * 23 * Related sections in the development guide: 24 * - @core_doc{_cache_manager,Cache Manager} 25 * 26 * @{ 27 * 28 * Header 29 * ------ 30 * ~~~~{.c} 31 * #include <netlink/cache.h> 32 * ~~~~ 33 */ 34 35 #include <netlink-private/netlink.h> 36 #include <netlink/netlink.h> 37 #include <netlink/cache.h> 38 #include <netlink/utils.h> 39 40 /** @cond SKIP */ 41 #define NASSOC_INIT 16 42 #define NASSOC_EXPAND 8 43 /** @endcond */ 44 45 static int include_cb(struct nl_object *obj, struct nl_parser_param *p) 46 { 47 struct nl_cache_assoc *ca = p->pp_arg; 48 struct nl_cache_ops *ops = ca->ca_cache->c_ops; 49 50 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); 51 #ifdef NL_DEBUG 52 if (nl_debug >= 4) 53 nl_object_dump(obj, &nl_debug_dp); 54 #endif 55 56 if (ops->co_event_filter) 57 if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) 58 return 0; 59 60 if (ops->co_include_event) 61 return ops->co_include_event(ca->ca_cache, obj, ca->ca_change, 62 ca->ca_change_data); 63 else 64 return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); 65 } 66 67 static int event_input(struct nl_msg *msg, void *arg) 68 { 69 struct nl_cache_mngr *mngr = arg; 70 int protocol = nlmsg_get_proto(msg); 71 int type = nlmsg_hdr(msg)->nlmsg_type; 72 struct nl_cache_ops *ops; 73 int i, n; 74 struct nl_parser_param p = { 75 .pp_cb = include_cb, 76 }; 77 78 NL_DBG(2, "Cache manager %p, handling new message %p as event\n", 79 mngr, msg); 80 #ifdef NL_DEBUG 81 if (nl_debug >= 4) 82 nl_msg_dump(msg, stderr); 83 #endif 84 85 if (mngr->cm_protocol != protocol) 86 BUG(); 87 88 for (i = 0; i < mngr->cm_nassocs; i++) { 89 if (mngr->cm_assocs[i].ca_cache) { 90 ops = mngr->cm_assocs[i].ca_cache->c_ops; 91 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) 92 if (ops->co_msgtypes[n].mt_id == type) 93 goto found; 94 } 95 } 96 97 return NL_SKIP; 98 99 found: 100 NL_DBG(2, "Associated message %p to cache %p\n", 101 msg, mngr->cm_assocs[i].ca_cache); 102 p.pp_arg = &mngr->cm_assocs[i]; 103 104 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); 105 } 106 107 /** 108 * Allocate new cache manager 109 * @arg sk Netlink socket or NULL to auto allocate 110 * @arg protocol Netlink protocol this manager is used for 111 * @arg flags Flags (\c NL_AUTO_PROVIDE) 112 * @arg result Result pointer 113 * 114 * Allocates a new cache manager for the specified netlink protocol. 115 * 116 * 1. If sk is not specified (\c NULL) a netlink socket matching the 117 * specified protocol will be automatically allocated. 118 * 119 * 2. The socket will be put in non-blocking mode and sequence checking 120 * will be disabled regardless of whether the socket was provided by 121 * the caller or automatically allocated. 122 * 123 * 3. The socket will be connected. 124 * 125 * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the 126 * manager will automatically be made available to other users using 127 * nl_cache_mngt_provide(). 128 * 129 * @note If the socket is provided by the caller, it is NOT recommended 130 * to use the socket for anything else besides receiving netlink 131 * notifications. 132 * 133 * @return 0 on success or a negative error code. 134 */ 135 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, 136 struct nl_cache_mngr **result) 137 { 138 struct nl_cache_mngr *mngr; 139 int err = -NLE_NOMEM; 140 141 /* Catch abuse of flags */ 142 if (flags & NL_ALLOCATED_SOCK) 143 BUG(); 144 145 mngr = calloc(1, sizeof(*mngr)); 146 if (!mngr) 147 return -NLE_NOMEM; 148 149 if (!sk) { 150 if (!(sk = nl_socket_alloc())) 151 goto errout; 152 153 flags |= NL_ALLOCATED_SOCK; 154 } 155 156 mngr->cm_sock = sk; 157 mngr->cm_nassocs = NASSOC_INIT; 158 mngr->cm_protocol = protocol; 159 mngr->cm_flags = flags; 160 mngr->cm_assocs = calloc(mngr->cm_nassocs, 161 sizeof(struct nl_cache_assoc)); 162 if (!mngr->cm_assocs) 163 goto errout; 164 165 /* Required to receive async event notifications */ 166 nl_socket_disable_seq_check(mngr->cm_sock); 167 168 if ((err = nl_connect(mngr->cm_sock, protocol)) < 0) 169 goto errout; 170 171 if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0) 172 goto errout; 173 174 /* Create and allocate socket for sync cache fills */ 175 mngr->cm_sync_sock = nl_socket_alloc(); 176 if (!mngr->cm_sync_sock) { 177 err = -NLE_NOMEM; 178 goto errout; 179 } 180 if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0) 181 goto errout_free_sync_sock; 182 183 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", 184 mngr, protocol, mngr->cm_nassocs); 185 186 *result = mngr; 187 return 0; 188 189 errout_free_sync_sock: 190 nl_socket_free(mngr->cm_sync_sock); 191 errout: 192 nl_cache_mngr_free(mngr); 193 return err; 194 } 195 196 /** 197 * Add cache to cache manager 198 * @arg mngr Cache manager. 199 * @arg cache Cache to be added to cache manager 200 * @arg cb Function to be called upon changes. 201 * @arg data Argument passed on to change callback 202 * 203 * Adds cache to the manager. The operation will trigger a full 204 * dump request from the kernel to initially fill the contents 205 * of the cache. The manager will subscribe to the notification group 206 * of the cache and keep track of any further changes. 207 * 208 * The user is responsible for calling nl_cache_mngr_poll() or monitor 209 * the socket and call nl_cache_mngr_data_ready() to allow the library 210 * to process netlink notification events. 211 * 212 * @see nl_cache_mngr_poll() 213 * @see nl_cache_mngr_data_ready() 214 * 215 * @return 0 on success or a negative error code. 216 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 217 * cache type 218 * @return -NLE_OPNOTSUPP Cache type does not support updates 219 * @return -NLE_EXIST Cache of this type already being managed 220 */ 221 int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, 222 change_func_t cb, void *data) 223 { 224 struct nl_cache_ops *ops; 225 struct nl_af_group *grp; 226 int err, i; 227 228 ops = cache->c_ops; 229 if (!ops) 230 return -NLE_INVAL; 231 232 if (ops->co_protocol != mngr->cm_protocol) 233 return -NLE_PROTO_MISMATCH; 234 235 if (ops->co_groups == NULL) 236 return -NLE_OPNOTSUPP; 237 238 for (i = 0; i < mngr->cm_nassocs; i++) 239 if (mngr->cm_assocs[i].ca_cache && 240 mngr->cm_assocs[i].ca_cache->c_ops == ops) 241 return -NLE_EXIST; 242 243 retry: 244 for (i = 0; i < mngr->cm_nassocs; i++) 245 if (!mngr->cm_assocs[i].ca_cache) 246 break; 247 248 if (i >= mngr->cm_nassocs) { 249 mngr->cm_nassocs += NASSOC_EXPAND; 250 mngr->cm_assocs = realloc(mngr->cm_assocs, 251 mngr->cm_nassocs * 252 sizeof(struct nl_cache_assoc)); 253 if (mngr->cm_assocs == NULL) 254 return -NLE_NOMEM; 255 256 memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0, 257 NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); 258 259 NL_DBG(1, "Increased capacity of cache manager %p " \ 260 "to %d\n", mngr, mngr->cm_nassocs); 261 goto retry; 262 } 263 264 for (grp = ops->co_groups; grp->ag_group; grp++) { 265 err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); 266 if (err < 0) 267 return err; 268 } 269 270 err = nl_cache_refill(mngr->cm_sync_sock, cache); 271 if (err < 0) 272 goto errout_drop_membership; 273 274 mngr->cm_assocs[i].ca_cache = cache; 275 mngr->cm_assocs[i].ca_change = cb; 276 mngr->cm_assocs[i].ca_change_data = data; 277 278 if (mngr->cm_flags & NL_AUTO_PROVIDE) 279 nl_cache_mngt_provide(cache); 280 281 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", 282 cache, nl_cache_name(cache), mngr); 283 284 return 0; 285 286 errout_drop_membership: 287 for (grp = ops->co_groups; grp->ag_group; grp++) 288 nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); 289 290 return err; 291 } 292 293 /** 294 * Add cache to cache manager 295 * @arg mngr Cache manager. 296 * @arg name Name of cache to keep track of 297 * @arg cb Function to be called upon changes. 298 * @arg data Argument passed on to change callback 299 * @arg result Pointer to store added cache (optional) 300 * 301 * Allocates a new cache of the specified type and adds it to the manager. 302 * The operation will trigger a full dump request from the kernel to 303 * initially fill the contents of the cache. The manager will subscribe 304 * to the notification group of the cache and keep track of any further 305 * changes. 306 * 307 * The user is responsible for calling nl_cache_mngr_poll() or monitor 308 * the socket and call nl_cache_mngr_data_ready() to allow the library 309 * to process netlink notification events. 310 * 311 * @see nl_cache_mngr_poll() 312 * @see nl_cache_mngr_data_ready() 313 * 314 * @return 0 on success or a negative error code. 315 * @return -NLE_NOCACHE Unknown cache type 316 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 317 * cache type 318 * @return -NLE_OPNOTSUPP Cache type does not support updates 319 * @return -NLE_EXIST Cache of this type already being managed 320 */ 321 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, 322 change_func_t cb, void *data, struct nl_cache **result) 323 { 324 struct nl_cache_ops *ops; 325 struct nl_cache *cache; 326 int err; 327 328 ops = nl_cache_ops_lookup_safe(name); 329 if (!ops) 330 return -NLE_NOCACHE; 331 332 cache = nl_cache_alloc(ops); 333 nl_cache_ops_put(ops); 334 if (!cache) 335 return -NLE_NOMEM; 336 337 err = nl_cache_mngr_add_cache(mngr, cache, cb, data); 338 if (err < 0) 339 goto errout_free_cache; 340 341 *result = cache; 342 return 0; 343 344 errout_free_cache: 345 nl_cache_free(cache); 346 347 return err; 348 } 349 350 /** 351 * Get socket file descriptor 352 * @arg mngr Cache Manager 353 * 354 * Get the file descriptor of the socket associated with the manager. 355 * 356 * @note Do not use the socket for anything besides receiving 357 * notifications. 358 */ 359 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) 360 { 361 return nl_socket_get_fd(mngr->cm_sock); 362 } 363 364 /** 365 * Check for event notifications 366 * @arg mngr Cache Manager 367 * @arg timeout Upper limit poll() will block, in milliseconds. 368 * 369 * Causes poll() to be called to check for new event notifications 370 * being available. Calls nl_cache_mngr_data_ready() to process 371 * available data. 372 * 373 * This functionally is ideally called regularly during an idle 374 * period. 375 * 376 * A timeout can be specified in milliseconds to limit the time the 377 * function will wait for updates. 378 * 379 * @see nl_cache_mngr_data_ready() 380 * 381 * @return The number of messages processed or a negative error code. 382 */ 383 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) 384 { 385 int ret; 386 struct pollfd fds = { 387 .fd = nl_socket_get_fd(mngr->cm_sock), 388 .events = POLLIN, 389 }; 390 391 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); 392 ret = poll(&fds, 1, timeout); 393 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); 394 if (ret < 0) 395 return -nl_syserr2nlerr(errno); 396 397 /* No events, return */ 398 if (ret == 0) 399 return 0; 400 401 return nl_cache_mngr_data_ready(mngr); 402 } 403 404 /** 405 * Receive available event notifications 406 * @arg mngr Cache manager 407 * 408 * This function can be called if the socket associated to the manager 409 * contains updates to be received. This function should only be used 410 * if nl_cache_mngr_poll() is not used. 411 * 412 * The function will process messages until there is no more data to 413 * be read from the socket. 414 * 415 * @see nl_cache_mngr_poll() 416 * 417 * @return The number of messages processed or a negative error code. 418 */ 419 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) 420 { 421 int err, nread = 0; 422 struct nl_cb *cb; 423 424 NL_DBG(2, "Cache manager %p, reading new data from fd %d\n", 425 mngr, nl_socket_get_fd(mngr->cm_sock)); 426 427 cb = nl_cb_clone(mngr->cm_sock->s_cb); 428 if (cb == NULL) 429 return -NLE_NOMEM; 430 431 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); 432 433 while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) { 434 NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n", 435 mngr, err); 436 nread += err; 437 } 438 439 nl_cb_put(cb); 440 if (err < 0 && err != -NLE_AGAIN) 441 return err; 442 443 return nread; 444 } 445 446 /** 447 * Print information about cache manager 448 * @arg mngr Cache manager 449 * @arg p Dumping parameters 450 * 451 * Prints information about the cache manager including all managed caches. 452 * 453 * @note This is a debugging function. 454 */ 455 void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p) 456 { 457 char buf[128]; 458 int i; 459 460 nl_dump_line(p, "cache-manager <%p>\n", mngr); 461 nl_dump_line(p, " .protocol = %s\n", 462 nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf))); 463 nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags); 464 nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs); 465 nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock); 466 467 for (i = 0; i < mngr->cm_nassocs; i++) { 468 struct nl_cache_assoc *assoc = &mngr->cm_assocs[i]; 469 470 if (assoc->ca_cache) { 471 nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache); 472 nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name); 473 nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change); 474 nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data); 475 nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache)); 476 nl_dump_line(p, " .objects = {\n"); 477 478 p->dp_prefix += 6; 479 nl_cache_dump(assoc->ca_cache, p); 480 p->dp_prefix -= 6; 481 482 nl_dump_line(p, " }\n"); 483 nl_dump_line(p, " }\n"); 484 } 485 } 486 } 487 488 /** 489 * Free cache manager and all caches. 490 * @arg mngr Cache manager. 491 * 492 * Release all resources held by a cache manager. 493 */ 494 void nl_cache_mngr_free(struct nl_cache_mngr *mngr) 495 { 496 int i; 497 498 if (!mngr) 499 return; 500 501 if (mngr->cm_sock) 502 nl_close(mngr->cm_sock); 503 504 if (mngr->cm_sync_sock) { 505 nl_close(mngr->cm_sync_sock); 506 nl_socket_free(mngr->cm_sync_sock); 507 } 508 509 if (mngr->cm_flags & NL_ALLOCATED_SOCK) 510 nl_socket_free(mngr->cm_sock); 511 512 for (i = 0; i < mngr->cm_nassocs; i++) { 513 if (mngr->cm_assocs[i].ca_cache) { 514 nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); 515 nl_cache_free(mngr->cm_assocs[i].ca_cache); 516 } 517 } 518 519 free(mngr->cm_assocs); 520 521 NL_DBG(1, "Cache manager %p freed\n", mngr); 522 523 free(mngr); 524 } 525 526 /** @} */ 527