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