00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include <netlink-local.h>
00044 #include <netlink/netlink.h>
00045 #include <netlink/cache.h>
00046 #include <netlink/object.h>
00047 #include <netlink/utils.h>
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 int nl_cache_nitems(struct nl_cache *cache)
00059 {
00060 return cache->c_nitems;
00061 }
00062
00063
00064
00065
00066
00067
00068 int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter)
00069 {
00070 struct nl_object_ops *ops;
00071 struct nl_object *obj;
00072 int nitems = 0;
00073
00074 if (cache->c_ops == NULL)
00075 BUG();
00076
00077 ops = cache->c_ops->co_obj_ops;
00078
00079 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00080 if (filter && !nl_object_match_filter(obj, filter))
00081 continue;
00082
00083 nitems++;
00084 }
00085
00086 return nitems;
00087 }
00088
00089
00090
00091
00092
00093
00094 int nl_cache_is_empty(struct nl_cache *cache)
00095 {
00096 return nl_list_empty(&cache->c_items);
00097 }
00098
00099
00100
00101
00102
00103 struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache)
00104 {
00105 return cache->c_ops;
00106 }
00107
00108
00109
00110
00111
00112 struct nl_object *nl_cache_get_first(struct nl_cache *cache)
00113 {
00114 if (nl_list_empty(&cache->c_items))
00115 return NULL;
00116
00117 return nl_list_entry(cache->c_items.next,
00118 struct nl_object, ce_list);
00119 }
00120
00121
00122
00123
00124
00125 struct nl_object *nl_cache_get_last(struct nl_cache *cache)
00126 {
00127 if (nl_list_empty(&cache->c_items))
00128 return NULL;
00129
00130 return nl_list_entry(cache->c_items.prev,
00131 struct nl_object, ce_list);
00132 }
00133
00134
00135
00136
00137
00138 struct nl_object *nl_cache_get_next(struct nl_object *obj)
00139 {
00140 if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list))
00141 return NULL;
00142 else
00143 return nl_list_entry(obj->ce_list.next,
00144 struct nl_object, ce_list);
00145 }
00146
00147
00148
00149
00150
00151 struct nl_object *nl_cache_get_prev(struct nl_object *obj)
00152 {
00153 if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list))
00154 return NULL;
00155 else
00156 return nl_list_entry(obj->ce_list.prev,
00157 struct nl_object, ce_list);
00158 }
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
00174 {
00175 struct nl_cache *cache;
00176
00177 cache = calloc(1, sizeof(*cache));
00178 if (!cache) {
00179 nl_errno(ENOMEM);
00180 return NULL;
00181 }
00182
00183 nl_init_list_head(&cache->c_items);
00184 cache->c_ops = ops;
00185
00186 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
00187
00188 return cache;
00189 }
00190
00191
00192
00193
00194
00195
00196 struct nl_cache *nl_cache_alloc_name(const char *kind)
00197 {
00198 struct nl_cache_ops *ops;
00199
00200 ops = nl_cache_ops_lookup(kind);
00201 if (!ops) {
00202 nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind);
00203 return NULL;
00204 }
00205
00206 return nl_cache_alloc(ops);
00207 }
00208
00209
00210
00211
00212
00213
00214
00215 struct nl_cache *nl_cache_subset(struct nl_cache *orig,
00216 struct nl_object *filter)
00217 {
00218 struct nl_cache *cache;
00219 struct nl_object_ops *ops;
00220 struct nl_object *obj;
00221
00222 if (!filter)
00223 BUG();
00224
00225 cache = nl_cache_alloc(orig->c_ops);
00226 if (!cache)
00227 return NULL;
00228
00229 ops = orig->c_ops->co_obj_ops;
00230
00231 nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
00232 if (!nl_object_match_filter(obj, filter))
00233 continue;
00234
00235 nl_cache_add(cache, obj);
00236 }
00237
00238 return cache;
00239 }
00240
00241
00242
00243
00244
00245
00246
00247 void nl_cache_clear(struct nl_cache *cache)
00248 {
00249 struct nl_object *obj, *tmp;
00250
00251 NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
00252
00253 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
00254 nl_cache_remove(obj);
00255 }
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 void nl_cache_free(struct nl_cache *cache)
00266 {
00267 nl_cache_clear(cache);
00268 NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
00269 free(cache);
00270 }
00271
00272
00273
00274
00275
00276
00277
00278
00279 static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
00280 {
00281 obj->ce_cache = cache;
00282
00283 nl_list_add_tail(&obj->ce_list, &cache->c_items);
00284 cache->c_nitems++;
00285
00286 NL_DBG(1, "Added %p to cache %p <%s>.\n",
00287 obj, cache, nl_cache_name(cache));
00288
00289 return 0;
00290 }
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
00303 {
00304 struct nl_object *new;
00305
00306 if (cache->c_ops->co_obj_ops != obj->ce_ops)
00307 return nl_error(EINVAL, "Object mismatches cache type");
00308
00309 if (!nl_list_empty(&obj->ce_list)) {
00310 new = nl_object_clone(obj);
00311 if (!new)
00312 return nl_errno(ENOMEM);
00313 } else {
00314 nl_object_get(obj);
00315 new = obj;
00316 }
00317
00318 return __cache_add(cache, new);
00319 }
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331 int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
00332 {
00333 if (cache->c_ops->co_obj_ops != obj->ce_ops)
00334 return nl_error(EINVAL, "Object mismatches cache type");
00335
00336 NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
00337
00338
00339
00340 nl_object_get(obj);
00341
00342 if (!nl_list_empty(&obj->ce_list))
00343 nl_cache_remove(obj);
00344
00345 return __cache_add(cache, obj);
00346 }
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 void nl_cache_remove(struct nl_object *obj)
00357 {
00358 struct nl_cache *cache = obj->ce_cache;
00359
00360 if (cache == NULL)
00361 return;
00362
00363 nl_list_del(&obj->ce_list);
00364 obj->ce_cache = NULL;
00365 nl_object_put(obj);
00366 cache->c_nitems--;
00367
00368 NL_DBG(1, "Deleted %p from cache %p <%s>.\n",
00369 obj, cache, nl_cache_name(cache));
00370 }
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 struct nl_object *nl_cache_search(struct nl_cache *cache,
00384 struct nl_object *needle)
00385 {
00386 struct nl_object *obj;
00387
00388 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00389 if (nl_object_identical(obj, needle)) {
00390 nl_object_get(obj);
00391 return obj;
00392 }
00393 }
00394
00395 return NULL;
00396 }
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache)
00418 {
00419 NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
00420 cache, nl_cache_name(cache));
00421
00422 if (cache->c_ops->co_request_update == NULL)
00423 return nl_error(EOPNOTSUPP, "Operation not supported");
00424
00425 return cache->c_ops->co_request_update(cache, handle);
00426 }
00427
00428
00429 struct update_xdata {
00430 struct nl_cache_ops *ops;
00431 struct nl_parser_param *params;
00432 };
00433
00434 static int update_msg_parser(struct nl_msg *msg, void *arg)
00435 {
00436 struct update_xdata *x = arg;
00437
00438 return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
00439 }
00440
00441
00442 int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache,
00443 struct nl_parser_param *param)
00444 {
00445 int err;
00446 struct nl_cb *cb;
00447 struct update_xdata x = {
00448 .ops = cache->c_ops,
00449 .params = param,
00450 };
00451
00452 NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
00453 cache, nl_cache_name(cache));
00454
00455 cb = nl_cb_clone(handle->h_cb);
00456 if (cb == NULL)
00457 return nl_get_errno();
00458
00459 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
00460
00461 err = nl_recvmsgs(handle, cb);
00462 if (err < 0)
00463 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \
00464 "%d: %s", cache, nl_cache_name(cache),
00465 err, nl_geterror());
00466
00467 nl_cb_put(cb);
00468
00469 return err;
00470 }
00471
00472 static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
00473 {
00474 return nl_cache_add((struct nl_cache *) p->pp_arg, c);
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487 int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache)
00488 {
00489 struct nl_parser_param p = {
00490 .pp_cb = pickup_cb,
00491 .pp_arg = cache,
00492 };
00493
00494 return __cache_pickup(handle, cache, &p);
00495 }
00496
00497 static int cache_include(struct nl_cache *cache, struct nl_object *obj,
00498 struct nl_msgtype *type, change_func_t cb)
00499 {
00500 struct nl_object *old;
00501
00502 switch (type->mt_act) {
00503 case NL_ACT_NEW:
00504 case NL_ACT_DEL:
00505 old = nl_cache_search(cache, obj);
00506 if (old) {
00507 nl_cache_remove(old);
00508 if (type->mt_act == NL_ACT_DEL) {
00509 if (cb)
00510 cb(cache, old, NL_ACT_DEL);
00511 nl_object_put(old);
00512 }
00513 }
00514
00515 if (type->mt_act == NL_ACT_NEW) {
00516 nl_cache_move(cache, obj);
00517 if (old == NULL && cb)
00518 cb(cache, obj, NL_ACT_NEW);
00519 else if (old) {
00520 if (nl_object_diff(old, obj) && cb)
00521 cb(cache, obj, NL_ACT_CHANGE);
00522
00523 nl_object_put(old);
00524 }
00525 }
00526 break;
00527 default:
00528 NL_DBG(2, "Unknown action associated to object %p\n", obj);
00529 return 0;
00530 }
00531
00532 return 0;
00533 }
00534
00535 int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
00536 change_func_t change_cb)
00537 {
00538 struct nl_cache_ops *ops = cache->c_ops;
00539 int i;
00540
00541 if (ops->co_obj_ops != obj->ce_ops)
00542 return nl_error(EINVAL, "Object mismatches cache type");
00543
00544 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
00545 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
00546 return cache_include(cache, obj, &ops->co_msgtypes[i],
00547 change_cb);
00548
00549 return nl_errno(EINVAL);
00550 }
00551
00552 static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
00553 {
00554 struct nl_cache_assoc *ca = p->pp_arg;
00555
00556 return nl_cache_include(ca->ca_cache, c, ca->ca_change);
00557 }
00558
00559 int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache,
00560 change_func_t change_cb)
00561 {
00562 struct nl_object *obj, *next;
00563 struct nl_cache_assoc ca = {
00564 .ca_cache = cache,
00565 .ca_change = change_cb,
00566 };
00567 struct nl_parser_param p = {
00568 .pp_cb = resync_cb,
00569 .pp_arg = &ca,
00570 };
00571 int err;
00572
00573 NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache));
00574
00575
00576 nl_cache_mark_all(cache);
00577
00578 err = nl_cache_request_full_dump(handle, cache);
00579 if (err < 0)
00580 goto errout;
00581
00582 err = __cache_pickup(handle, cache, &p);
00583 if (err < 0)
00584 goto errout;
00585
00586 nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list)
00587 if (nl_object_is_marked(obj))
00588 nl_cache_remove(obj);
00589
00590 NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
00591
00592 err = 0;
00593 errout:
00594 return err;
00595 }
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605 int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00606 struct nlmsghdr *nlh, struct nl_parser_param *params)
00607 {
00608 int i, err;
00609
00610 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) {
00611 err = nl_error(EINVAL, "netlink message too short to be "
00612 "of kind %s", ops->co_name);
00613 goto errout;
00614 }
00615
00616 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
00617 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
00618 err = ops->co_msg_parser(ops, who, nlh, params);
00619 if (err != -ENOENT)
00620 goto errout;
00621 }
00622 }
00623
00624
00625 err = nl_error(EINVAL, "Unsupported netlink message type %d",
00626 nlh->nlmsg_type);
00627 errout:
00628 return err;
00629 }
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
00643 {
00644 struct nl_parser_param p = {
00645 .pp_cb = pickup_cb,
00646 .pp_arg = cache,
00647 };
00648
00649 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p);
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662 int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
00663 {
00664 int err;
00665
00666 err = nl_cache_request_full_dump(handle, cache);
00667 if (err < 0)
00668 return err;
00669
00670 NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
00671 cache, nl_cache_name(cache));
00672 nl_cache_clear(cache);
00673
00674 return nl_cache_pickup(handle, cache);
00675 }
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688 void nl_cache_mark_all(struct nl_cache *cache)
00689 {
00690 struct nl_object *obj;
00691
00692 NL_DBG(2, "Marking all objects in cache %p <%s>...\n",
00693 cache, nl_cache_name(cache));
00694
00695 nl_list_for_each_entry(obj, &cache->c_items, ce_list)
00696 nl_object_mark(obj);
00697 }
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params)
00714 {
00715 nl_cache_dump_filter(cache, params, NULL);
00716 }
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727 void nl_cache_dump_filter(struct nl_cache *cache,
00728 struct nl_dump_params *params,
00729 struct nl_object *filter)
00730 {
00731 int type = params ? params->dp_type : NL_DUMP_FULL;
00732 struct nl_object_ops *ops;
00733 struct nl_object *obj;
00734
00735 NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
00736 cache, nl_cache_name(cache), filter);
00737
00738 if (type > NL_DUMP_MAX || type < 0)
00739 BUG();
00740
00741 if (cache->c_ops == NULL)
00742 BUG();
00743
00744 ops = cache->c_ops->co_obj_ops;
00745 if (!ops->oo_dump[type])
00746 return;
00747
00748 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00749 if (filter && !nl_object_match_filter(obj, filter))
00750 continue;
00751
00752 NL_DBG(4, "Dumping object %p...\n", obj);
00753 dump_from_ops(obj, params);
00754 }
00755 }
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773 void nl_cache_foreach(struct nl_cache *cache,
00774 void (*cb)(struct nl_object *, void *), void *arg)
00775 {
00776 nl_cache_foreach_filter(cache, NULL, cb, arg);
00777 }
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790 void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter,
00791 void (*cb)(struct nl_object *, void *), void *arg)
00792 {
00793 struct nl_object *obj, *tmp;
00794 struct nl_object_ops *ops;
00795
00796 if (cache->c_ops == NULL)
00797 BUG();
00798
00799 ops = cache->c_ops->co_obj_ops;
00800
00801 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) {
00802 if (filter && !nl_object_match_filter(obj, filter))
00803 continue;
00804
00805 cb(obj, arg);
00806 }
00807 }
00808
00809
00810
00811