1 /*
2 * lib/object.c Generic Cacheable Object
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@suug.ch>
10 */
11
12 /**
13 * @ingroup core_types
14 * @defgroup object Object (Cacheable)
15 *
16 * Generic object data type, for inheritance purposes to implement cacheable
17 * data types.
18 *
19 * Related sections in the development guide:
20 *
21 * @{
22 *
23 * Header
24 * ------
25 * ~~~~{.c}
26 * #include <netlink/object.h>
27 * ~~~~
28 */
29
30 #include <netlink-private/netlink.h>
31 #include <netlink/netlink.h>
32 #include <netlink/cache.h>
33 #include <netlink/object.h>
34 #include <netlink/utils.h>
35
obj_ops(struct nl_object * obj)36 static inline struct nl_object_ops *obj_ops(struct nl_object *obj)
37 {
38 if (!obj->ce_ops)
39 BUG();
40
41 return obj->ce_ops;
42 }
43
44 /**
45 * @name Object Creation/Deletion
46 * @{
47 */
48
49 /**
50 * Allocate a new object of kind specified by the operations handle
51 * @arg ops cache operations handle
52 * @return The new object or NULL
53 */
nl_object_alloc(struct nl_object_ops * ops)54 struct nl_object *nl_object_alloc(struct nl_object_ops *ops)
55 {
56 struct nl_object *new;
57
58 if (ops->oo_size < sizeof(*new))
59 BUG();
60
61 new = calloc(1, ops->oo_size);
62 if (!new)
63 return NULL;
64
65 new->ce_refcnt = 1;
66 nl_init_list_head(&new->ce_list);
67
68 new->ce_ops = ops;
69 if (ops->oo_constructor)
70 ops->oo_constructor(new);
71
72 NL_DBG(4, "Allocated new object %p\n", new);
73
74 return new;
75 }
76
77 /**
78 * Allocate new object of kind specified by the name
79 * @arg kind name of object type
80 * @arg result Result pointer
81 *
82 * @return 0 on success or a negative error code.
83 */
nl_object_alloc_name(const char * kind,struct nl_object ** result)84 int nl_object_alloc_name(const char *kind, struct nl_object **result)
85 {
86 struct nl_cache_ops *ops;
87
88 ops = nl_cache_ops_lookup_safe(kind);
89 if (!ops)
90 return -NLE_OPNOTSUPP;
91
92 *result = nl_object_alloc(ops->co_obj_ops);
93 nl_cache_ops_put(ops);
94 if (!*result)
95 return -NLE_NOMEM;
96
97 return 0;
98 }
99
100 struct nl_derived_object {
101 NLHDR_COMMON
102 char data;
103 };
104
105 /**
106 * Allocate a new object and copy all data from an existing object
107 * @arg obj object to inherite data from
108 * @return The new object or NULL.
109 */
nl_object_clone(struct nl_object * obj)110 struct nl_object *nl_object_clone(struct nl_object *obj)
111 {
112 struct nl_object *new;
113 struct nl_object_ops *ops;
114 int doff = offsetof(struct nl_derived_object, data);
115 int size;
116
117 if (!obj)
118 return NULL;
119
120 ops = obj_ops(obj);
121 new = nl_object_alloc(ops);
122 if (!new)
123 return NULL;
124
125 size = ops->oo_size - doff;
126 if (size < 0)
127 BUG();
128
129 new->ce_ops = obj->ce_ops;
130 new->ce_msgtype = obj->ce_msgtype;
131 new->ce_mask = obj->ce_mask;
132
133 if (size)
134 memcpy((void *)new + doff, (void *)obj + doff, size);
135
136 if (ops->oo_clone) {
137 if (ops->oo_clone(new, obj) < 0) {
138 nl_object_free(new);
139 return NULL;
140 }
141 } else if (size && ops->oo_free_data)
142 BUG();
143
144 return new;
145 }
146
147 /**
148 * Merge a cacheable object
149 * @arg dst object to be merged into
150 * @arg src new object to be merged into dst
151 *
152 * @return 0 or a negative error code.
153 */
nl_object_update(struct nl_object * dst,struct nl_object * src)154 int nl_object_update(struct nl_object *dst, struct nl_object *src)
155 {
156 struct nl_object_ops *ops = obj_ops(dst);
157
158 if (ops->oo_update)
159 return ops->oo_update(dst, src);
160
161 return -NLE_OPNOTSUPP;
162 }
163
164 /**
165 * Free a cacheable object
166 * @arg obj object to free
167 *
168 * @return 0 or a negative error code.
169 */
nl_object_free(struct nl_object * obj)170 void nl_object_free(struct nl_object *obj)
171 {
172 struct nl_object_ops *ops;
173
174 if (!obj)
175 return;
176
177 ops = obj_ops(obj);
178
179 if (obj->ce_refcnt > 0)
180 NL_DBG(1, "Warning: Freeing object in use...\n");
181
182 if (obj->ce_cache)
183 nl_cache_remove(obj);
184
185 if (ops->oo_free_data)
186 ops->oo_free_data(obj);
187
188 NL_DBG(4, "Freed object %p\n", obj);
189
190 free(obj);
191 }
192
193 /** @} */
194
195 /**
196 * @name Reference Management
197 * @{
198 */
199
200 /**
201 * Acquire a reference on a object
202 * @arg obj object to acquire reference from
203 */
nl_object_get(struct nl_object * obj)204 void nl_object_get(struct nl_object *obj)
205 {
206 obj->ce_refcnt++;
207 NL_DBG(4, "New reference to object %p, total %d\n",
208 obj, obj->ce_refcnt);
209 }
210
211 /**
212 * Release a reference from an object
213 * @arg obj object to release reference from
214 */
nl_object_put(struct nl_object * obj)215 void nl_object_put(struct nl_object *obj)
216 {
217 if (!obj)
218 return;
219
220 obj->ce_refcnt--;
221 NL_DBG(4, "Returned object reference %p, %d remaining\n",
222 obj, obj->ce_refcnt);
223
224 if (obj->ce_refcnt < 0)
225 BUG();
226
227 if (obj->ce_refcnt <= 0)
228 nl_object_free(obj);
229 }
230
231 /**
232 * Check whether this object is used by multiple users
233 * @arg obj object to check
234 * @return true or false
235 */
nl_object_shared(struct nl_object * obj)236 int nl_object_shared(struct nl_object *obj)
237 {
238 return obj->ce_refcnt > 1;
239 }
240
241 /** @} */
242
243 /**
244 * @name Marks
245 * @{
246 */
247
248 /**
249 * Add mark to object
250 * @arg obj Object to mark
251 */
nl_object_mark(struct nl_object * obj)252 void nl_object_mark(struct nl_object *obj)
253 {
254 obj->ce_flags |= NL_OBJ_MARK;
255 }
256
257 /**
258 * Remove mark from object
259 * @arg obj Object to unmark
260 */
nl_object_unmark(struct nl_object * obj)261 void nl_object_unmark(struct nl_object *obj)
262 {
263 obj->ce_flags &= ~NL_OBJ_MARK;
264 }
265
266 /**
267 * Return true if object is marked
268 * @arg obj Object to check
269 * @return true if object is marked, otherwise false
270 */
nl_object_is_marked(struct nl_object * obj)271 int nl_object_is_marked(struct nl_object *obj)
272 {
273 return (obj->ce_flags & NL_OBJ_MARK);
274 }
275
276 /** @} */
277
278 /**
279 * @name Utillities
280 * @{
281 */
282
283 /**
284 * Dump this object according to the specified parameters
285 * @arg obj object to dump
286 * @arg params dumping parameters
287 */
nl_object_dump(struct nl_object * obj,struct nl_dump_params * params)288 void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
289 {
290 if (params->dp_buf)
291 memset(params->dp_buf, 0, params->dp_buflen);
292
293 dump_from_ops(obj, params);
294 }
295
nl_object_dump_buf(struct nl_object * obj,char * buf,size_t len)296 void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len)
297 {
298 struct nl_dump_params dp = {
299 .dp_buf = buf,
300 .dp_buflen = len,
301 };
302
303 return nl_object_dump(obj, &dp);
304 }
305
306 /**
307 * Check if the identifiers of two objects are identical
308 * @arg a an object
309 * @arg b another object of same type
310 *
311 * @return true if both objects have equal identifiers, otherwise false.
312 */
nl_object_identical(struct nl_object * a,struct nl_object * b)313 int nl_object_identical(struct nl_object *a, struct nl_object *b)
314 {
315 struct nl_object_ops *ops = obj_ops(a);
316 uint32_t req_attrs;
317
318 /* Both objects must be of same type */
319 if (ops != obj_ops(b))
320 return 0;
321
322 if (ops->oo_id_attrs_get) {
323 int req_attrs_a = ops->oo_id_attrs_get(a);
324 int req_attrs_b = ops->oo_id_attrs_get(b);
325 if (req_attrs_a != req_attrs_b)
326 return 0;
327 req_attrs = req_attrs_a;
328 } else if (ops->oo_id_attrs) {
329 req_attrs = ops->oo_id_attrs;
330 } else {
331 req_attrs = 0xFFFFFFFF;
332 }
333 if (req_attrs == 0xFFFFFFFF)
334 req_attrs = a->ce_mask & b->ce_mask;
335
336 /* Both objects must provide all required attributes to uniquely
337 * identify an object */
338 if ((a->ce_mask & req_attrs) != req_attrs ||
339 (b->ce_mask & req_attrs) != req_attrs)
340 return 0;
341
342 /* Can't judge unless we can compare */
343 if (ops->oo_compare == NULL)
344 return 0;
345
346 return !(ops->oo_compare(a, b, req_attrs, 0));
347 }
348
349 /**
350 * Compute bitmask representing difference in attribute values
351 * @arg a an object
352 * @arg b another object of same type
353 *
354 * The bitmask returned is specific to an object type, each bit set represents
355 * an attribute which mismatches in either of the two objects. Unavailability
356 * of an attribute in one object and presence in the other is regarded a
357 * mismatch as well.
358 *
359 * @return Bitmask describing differences or 0 if they are completely identical.
360 */
nl_object_diff(struct nl_object * a,struct nl_object * b)361 uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
362 {
363 struct nl_object_ops *ops = obj_ops(a);
364
365 if (ops != obj_ops(b) || ops->oo_compare == NULL)
366 return UINT_MAX;
367
368 return ops->oo_compare(a, b, ~0, 0);
369 }
370
371 /**
372 * Match a filter against an object
373 * @arg obj object to check
374 * @arg filter object of same type acting as filter
375 *
376 * @return 1 if the object matches the filter or 0
377 * if no filter procedure is available or if the
378 * filter does not match.
379 */
nl_object_match_filter(struct nl_object * obj,struct nl_object * filter)380 int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter)
381 {
382 struct nl_object_ops *ops = obj_ops(obj);
383
384 if (ops != obj_ops(filter) || ops->oo_compare == NULL)
385 return 0;
386
387 return !(ops->oo_compare(obj, filter, filter->ce_mask,
388 LOOSE_COMPARISON));
389 }
390
391 /**
392 * Convert bitmask of attributes to a character string
393 * @arg obj object of same type as attribute bitmask
394 * @arg attrs bitmask of attribute types
395 * @arg buf destination buffer
396 * @arg len length of destination buffer
397 *
398 * Converts the bitmask of attribute types into a list of attribute
399 * names separated by comas.
400 *
401 * @return destination buffer.
402 */
nl_object_attrs2str(struct nl_object * obj,uint32_t attrs,char * buf,size_t len)403 char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs,
404 char *buf, size_t len)
405 {
406 struct nl_object_ops *ops = obj_ops(obj);
407
408 if (ops->oo_attrs2str != NULL)
409 return ops->oo_attrs2str(attrs, buf, len);
410 else {
411 memset(buf, 0, len);
412 return buf;
413 }
414 }
415
416 /**
417 * Return list of attributes present in an object
418 * @arg obj an object
419 * @arg buf destination buffer
420 * @arg len length of destination buffer
421 *
422 * @return destination buffer.
423 */
nl_object_attr_list(struct nl_object * obj,char * buf,size_t len)424 char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
425 {
426 return nl_object_attrs2str(obj, obj->ce_mask, buf, len);
427 }
428
429 /**
430 * Generate object hash key
431 * @arg obj the object
432 * @arg hashkey destination buffer to be used for key stream
433 * @arg hashtbl_sz hash table size
434 *
435 * @return hash key in destination buffer
436 */
nl_object_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t hashtbl_sz)437 void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey,
438 uint32_t hashtbl_sz)
439 {
440 struct nl_object_ops *ops = obj_ops(obj);
441
442 if (ops->oo_keygen)
443 ops->oo_keygen(obj, hashkey, hashtbl_sz);
444 else
445 *hashkey = 0;
446
447 return;
448 }
449
450 /** @} */
451
452 /**
453 * @name Attributes
454 * @{
455 */
456
457 /**
458 * Return number of references held
459 * @arg obj object
460 *
461 * @return The number of references held to this object
462 */
nl_object_get_refcnt(struct nl_object * obj)463 int nl_object_get_refcnt(struct nl_object *obj)
464 {
465 return obj->ce_refcnt;
466 }
467
468 /**
469 * Return cache the object is associated with
470 * @arg obj object
471 *
472 * @note The returned pointer is not protected with a reference counter,
473 * it is your responsibility.
474 *
475 * @return Pointer to cache or NULL if not associated with a cache.
476 */
nl_object_get_cache(struct nl_object * obj)477 struct nl_cache *nl_object_get_cache(struct nl_object *obj)
478 {
479 return obj->ce_cache;
480 }
481
482 /**
483 * Return the object's type
484 * @arg obj object
485 *
486 * FIXME: link to list of object types
487 *
488 * @return Name of the object type
489 */
nl_object_get_type(const struct nl_object * obj)490 const char *nl_object_get_type(const struct nl_object *obj)
491 {
492 if (!obj->ce_ops)
493 BUG();
494
495 return obj->ce_ops->oo_name;
496 }
497
498 /**
499 * Return the netlink message type the object was derived from
500 * @arg obj object
501 *
502 * @return Netlink message type or 0.
503 */
nl_object_get_msgtype(const struct nl_object * obj)504 int nl_object_get_msgtype(const struct nl_object *obj)
505 {
506 return obj->ce_msgtype;
507 }
508
509 /**
510 * Return object operations structure
511 * @arg obj object
512 *
513 * @return Pointer to the object operations structure
514 */
nl_object_get_ops(const struct nl_object * obj)515 struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj)
516 {
517 return obj->ce_ops;
518 }
519
520 /**
521 * Return object id attribute mask
522 * @arg obj object
523 *
524 * @return object id attribute mask
525 */
nl_object_get_id_attrs(struct nl_object * obj)526 uint32_t nl_object_get_id_attrs(struct nl_object *obj)
527 {
528 struct nl_object_ops *ops = obj_ops(obj);
529 uint32_t id_attrs;
530
531 if (!ops)
532 return 0;
533
534 if (ops->oo_id_attrs_get)
535 id_attrs = ops->oo_id_attrs_get(obj);
536 else
537 id_attrs = ops->oo_id_attrs;
538
539 return id_attrs;
540 }
541
542 /** @} */
543
544 /** @} */
545