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