1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <limits.h>
21 #include "lldpctl.h"
22 #include "atom.h"
23 #include "../log.h"
24 #include "../marshal.h"
25 #include "../ctl.h"
26 
27 lldpctl_conn_t*
lldpctl_atom_get_connection(lldpctl_atom_t * atom)28 lldpctl_atom_get_connection(lldpctl_atom_t *atom)
29 {
30 	if (atom)
31 		return atom->conn;
32 	return NULL;
33 }
34 
35 void
lldpctl_atom_inc_ref(lldpctl_atom_t * atom)36 lldpctl_atom_inc_ref(lldpctl_atom_t *atom)
37 {
38 	if (atom)
39 		atom->count++;
40 }
41 
42 void
lldpctl_atom_dec_ref(lldpctl_atom_t * atom)43 lldpctl_atom_dec_ref(lldpctl_atom_t *atom)
44 {
45 	struct atom_buffer *buffer, *buffer_next;
46 	if (atom && (--atom->count == 0)) {
47 		if (atom->free)
48 			atom->free(atom);
49 
50 		/* Remove special allocated buffers */
51 		for (buffer = TAILQ_FIRST(&atom->buffers);
52 		     buffer;
53 		     buffer = buffer_next) {
54 			buffer_next = TAILQ_NEXT(buffer, next);
55 			free(buffer);
56 		}
57 
58 		free(atom);
59 	}
60 }
61 
62 lldpctl_atom_t*
lldpctl_atom_get(lldpctl_atom_t * atom,lldpctl_key_t key)63 lldpctl_atom_get(lldpctl_atom_t *atom, lldpctl_key_t key)
64 {
65 	if (atom == NULL) return NULL;
66 	RESET_ERROR(atom->conn);
67 
68 	if (atom->get == NULL) {
69 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
70 		return NULL;
71 	}
72 	return atom->get(atom, key);
73 }
74 
75 lldpctl_atom_t*
lldpctl_atom_set(lldpctl_atom_t * atom,lldpctl_key_t key,lldpctl_atom_t * value)76 lldpctl_atom_set(lldpctl_atom_t *atom, lldpctl_key_t key,
77     lldpctl_atom_t *value)
78 {
79 	if (atom == NULL) return NULL;
80 	RESET_ERROR(atom->conn);
81 
82 	if (atom->set == NULL) {
83 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
84 		return NULL;
85 	}
86 	return atom->set(atom, key, value);
87 }
88 
89 const char*
lldpctl_atom_get_str(lldpctl_atom_t * atom,lldpctl_key_t key)90 lldpctl_atom_get_str(lldpctl_atom_t *atom, lldpctl_key_t key)
91 {
92 	char *strresult = NULL;
93 	const uint8_t *bufresult = NULL;
94 	long int intresult = -1;
95 	int n1;
96 	size_t n2;
97 
98 	if (atom == NULL) return NULL;
99 	RESET_ERROR(atom->conn);
100 
101 	if (atom->get_str != NULL) {
102 		strresult = (char *)atom->get_str(atom, key);
103 		if (strresult) return strresult;
104 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST)
105 			return NULL;
106 	}
107 
108 	RESET_ERROR(atom->conn);
109 	if (atom->get_int != NULL) {
110 		intresult = atom->get_int(atom, key);
111 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST) {
112 			strresult = _lldpctl_alloc_in_atom(atom, 21);
113 			if (!strresult) return NULL;
114 			n1 = snprintf(strresult, 21, "%ld", intresult);
115 			if (n1 > -1 && n1 < 21)
116 				return strresult;
117 			SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); /* Not really true... */
118 			return NULL;
119 		}
120 	}
121 
122 	RESET_ERROR(atom->conn);
123 	if (atom->get_buffer != NULL) {
124 		bufresult = atom->get_buffer(atom, key, &n2);
125 		if (bufresult)
126 			return _lldpctl_dump_in_atom(atom, bufresult, n2, ' ', 0);
127 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST)
128 			return NULL;
129 	}
130 
131 	SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
132 	return NULL;
133 }
134 
135 lldpctl_atom_t*
lldpctl_atom_set_str(lldpctl_atom_t * atom,lldpctl_key_t key,const char * value)136 lldpctl_atom_set_str(lldpctl_atom_t *atom, lldpctl_key_t key,
137 	const char *value)
138 {
139 	lldpctl_atom_t *result = NULL;
140 	const char *errstr;
141 	long long converted = 0;
142 	int isint = 0;
143 	int bad = 0;
144 
145 	if (atom == NULL) return NULL;
146 	RESET_ERROR(atom->conn);
147 
148 	if (atom->set_str != NULL) {
149 		result = atom->set_str(atom, key, value);
150 		if (result) return result;
151 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
152 		    lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
153 			return NULL;
154 		bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
155 	}
156 
157 	if (value) {
158 		converted = strtonum(value, LLONG_MIN, LLONG_MAX, &errstr);
159 		isint = (errstr == NULL);
160 	}
161 
162 	RESET_ERROR(atom->conn);
163 	if (atom->set_int != NULL && isint) {
164 		result = atom->set_int(atom, key, converted);
165 		if (result) return result;
166 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
167 		    lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
168 			return NULL;
169 		bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
170 	}
171 
172 	RESET_ERROR(atom->conn);
173 	if (atom->set_buffer != NULL) {
174 		result = atom->set_buffer(atom, key, (u_int8_t*)value, value?strlen(value):0);
175 		if (result) return result;
176 		if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
177 		    lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
178 			return NULL;
179 		bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
180 	}
181 
182 	SET_ERROR(atom->conn, bad?LLDPCTL_ERR_BAD_VALUE:LLDPCTL_ERR_NOT_EXIST);
183 	return NULL;
184 }
185 
186 const u_int8_t*
lldpctl_atom_get_buffer(lldpctl_atom_t * atom,lldpctl_key_t key,size_t * length)187 lldpctl_atom_get_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
188     size_t *length)
189 {
190 	if (atom == NULL) return NULL;
191 	RESET_ERROR(atom->conn);
192 
193 	if (atom->get_buffer == NULL) {
194 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
195 		return NULL;
196 	}
197 	return atom->get_buffer(atom, key, length);
198 }
199 
200 lldpctl_atom_t*
lldpctl_atom_set_buffer(lldpctl_atom_t * atom,lldpctl_key_t key,const u_int8_t * value,size_t length)201 lldpctl_atom_set_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
202     const u_int8_t* value, size_t length)
203 {
204 	if (atom == NULL) return NULL;
205 	RESET_ERROR(atom->conn);
206 
207 	if (atom->set_buffer == NULL) {
208 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
209 		return NULL;
210 	}
211 	return atom->set_buffer(atom, key, value, length);
212 }
213 
214 long int
lldpctl_atom_get_int(lldpctl_atom_t * atom,lldpctl_key_t key)215 lldpctl_atom_get_int(lldpctl_atom_t *atom, lldpctl_key_t key)
216 {
217 	if (atom == NULL) return LLDPCTL_ERR_NOT_EXIST;
218 	RESET_ERROR(atom->conn);
219 
220 	if (atom->get_int == NULL)
221 		return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
222 	return atom->get_int(atom, key);
223 }
224 
225 lldpctl_atom_t*
lldpctl_atom_set_int(lldpctl_atom_t * atom,lldpctl_key_t key,long int value)226 lldpctl_atom_set_int(lldpctl_atom_t *atom, lldpctl_key_t key,
227     long int value)
228 {
229 	if (atom == NULL) return NULL;
230 	RESET_ERROR(atom->conn);
231 
232 	if (atom->set_int == NULL) {
233 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
234 		return NULL;
235 	}
236 	return atom->set_int(atom, key, value);
237 }
238 
239 lldpctl_atom_iter_t*
lldpctl_atom_iter(lldpctl_atom_t * atom)240 lldpctl_atom_iter(lldpctl_atom_t *atom)
241 {
242 	if (atom == NULL) return NULL;
243 	RESET_ERROR(atom->conn);
244 
245 	if (!atom->iter) {
246 		SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
247 		return NULL;
248 	}
249 	return atom->iter(atom);
250 }
251 
252 lldpctl_atom_iter_t*
lldpctl_atom_iter_next(lldpctl_atom_t * atom,lldpctl_atom_iter_t * iter)253 lldpctl_atom_iter_next(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
254 {
255 	if (atom == NULL) return NULL;
256 	RESET_ERROR(atom->conn);
257 
258 	if (!atom->next) {
259 		SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
260 		return NULL;
261 	}
262 	return atom->next(atom, iter);
263 }
264 
265 lldpctl_atom_t*
lldpctl_atom_iter_value(lldpctl_atom_t * atom,lldpctl_atom_iter_t * iter)266 lldpctl_atom_iter_value(lldpctl_atom_t *atom,  lldpctl_atom_iter_t *iter)
267 {
268 	if (atom == NULL) return NULL;
269 	RESET_ERROR(atom->conn);
270 
271 	if (!atom->value) {
272 		SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
273 		return NULL;
274 	}
275 	return atom->value(atom, iter);
276 }
277 
278 lldpctl_atom_t*
lldpctl_atom_create(lldpctl_atom_t * atom)279 lldpctl_atom_create(lldpctl_atom_t *atom)
280 {
281 	if (atom == NULL) return NULL;
282 	RESET_ERROR(atom->conn);
283 
284 	if (!atom->create) {
285 		SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_CREATE);
286 		return NULL;
287 	}
288 	return atom->create(atom);
289 }
290 
291 /**
292  * Get somethin with IO.
293  *
294  * @param conn       The connection to lldpd.
295  * @param state_send State to be when "sending"
296  * @param state_recv State to be when "receiving"
297  * @param state_data Ancillary data for state handling
298  * @param type       Type of message to send (and receive)
299  * @param to_send    Data to send.
300  * @param mi_send    Marshalling info for data to send.
301  * @param to_recv    Data to receive.
302  * @param mi_recv    Marshalling info for data to recive.
303  * @return 0 in case of success, a negative integer in case of failure.
304  *
305  * The current state must match one of @c CONN_STATE_IDLE, @c state_send or @c
306  * state_recv and in the two later cases, the provided @c state_data must match.
307  */
308 int
_lldpctl_do_something(lldpctl_conn_t * conn,int state_send,int state_recv,const char * state_data,enum hmsg_type type,void * to_send,struct marshal_info * mi_send,void ** to_recv,struct marshal_info * mi_recv)309 _lldpctl_do_something(lldpctl_conn_t *conn,
310     int state_send, int state_recv, const char *state_data,
311     enum hmsg_type type,
312     void *to_send, struct marshal_info *mi_send,
313     void **to_recv, struct marshal_info *mi_recv)
314 {
315 	ssize_t rc;
316 
317 	if (conn->state == CONN_STATE_WATCHING)
318 		/* The connection cannot be used anymore. */
319 		return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
320 
321 	if (conn->state == CONN_STATE_IDLE) {
322 		/* We need to build the message to send, then send
323 		 * it. */
324 		if (ctl_msg_send_unserialized(&conn->output_buffer, &conn->output_buffer_len,
325 			type, to_send, mi_send) != 0)
326 			return SET_ERROR(conn, LLDPCTL_ERR_SERIALIZATION);
327 		conn->state = state_send;
328 		if (state_data)
329 			strlcpy(conn->state_data, state_data, sizeof(conn->state_data));
330 		else
331 			conn->state_data[0] = 0;
332 	}
333 	if (conn->state == state_send &&
334 	    (state_data == NULL || !strncmp(conn->state_data, state_data, sizeof(conn->state_data) - 1))) {
335 		/* We need to send the currently built message */
336 		rc = lldpctl_send(conn);
337 		if (rc < 0)
338 			return SET_ERROR(conn, rc);
339 		conn->state = state_recv;
340 	}
341 	if (conn->state == state_recv &&
342 	    (state_data == NULL || !strncmp(conn->state_data, state_data, sizeof(conn->state_data) - 1))) {
343 		/* We need to receive the answer */
344 		while ((rc = ctl_msg_recv_unserialized(&conn->input_buffer,
345 			    &conn->input_buffer_len,
346 			    type, to_recv, mi_recv)) > 0) {
347 			/* We need more bytes */
348 			rc = _lldpctl_needs(conn, rc);
349 			if (rc < 0)
350 				return SET_ERROR(conn, rc);
351 		}
352 		if (rc < 0)
353 			return SET_ERROR(conn, LLDPCTL_ERR_SERIALIZATION);
354 		/* rc == 0 */
355 		conn->state = CONN_STATE_IDLE;
356 		conn->state_data[0] = 0;
357 		return 0;
358 	} else
359 		return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
360 }
361 
362 
363 int
lldpctl_watch_callback(lldpctl_conn_t * conn,lldpctl_change_callback cb,void * data)364 lldpctl_watch_callback(lldpctl_conn_t *conn,
365     lldpctl_change_callback cb,
366     void *data)
367 {
368 	int rc;
369 
370 	RESET_ERROR(conn);
371 
372 	rc = _lldpctl_do_something(conn,
373 	    CONN_STATE_SET_WATCH_SEND, CONN_STATE_SET_WATCH_RECV, NULL,
374 	    SUBSCRIBE, NULL, NULL, NULL, NULL);
375 	if (rc == 0) {
376 		conn->watch_cb = cb;
377 		conn->watch_data = data;
378 		conn->state = CONN_STATE_WATCHING;
379 	}
380 	return rc;
381 }
382 
383 int
lldpctl_watch_callback2(lldpctl_conn_t * conn,lldpctl_change_callback2 cb,void * data)384 lldpctl_watch_callback2(lldpctl_conn_t *conn,
385     lldpctl_change_callback2 cb,
386     void *data)
387 {
388 	int rc;
389 
390 	RESET_ERROR(conn);
391 
392 	rc = _lldpctl_do_something(conn,
393 	    CONN_STATE_SET_WATCH_SEND, CONN_STATE_SET_WATCH_RECV, NULL,
394 	    SUBSCRIBE, NULL, NULL, NULL, NULL);
395 	if (rc == 0) {
396 		conn->watch_cb2 = cb;
397 		conn->watch_data = data;
398 		conn->state = CONN_STATE_WATCHING;
399 	}
400 	return rc;
401 }
402 
403 int
lldpctl_watch(lldpctl_conn_t * conn)404 lldpctl_watch(lldpctl_conn_t *conn)
405 {
406 	int rc = 0;
407 
408 	RESET_ERROR(conn);
409 
410 	if (conn->state != CONN_STATE_WATCHING)
411 		return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
412 
413 	conn->watch_triggered = 0;
414 	while (!conn->watch_triggered) {
415 		rc = _lldpctl_needs(conn, 1);
416 		if (rc < 0)
417 			return SET_ERROR(conn, rc);
418 	}
419 
420 	RESET_ERROR(conn);
421 	return 0;
422 }
423 
424 lldpctl_atom_t*
lldpctl_get_configuration(lldpctl_conn_t * conn)425 lldpctl_get_configuration(lldpctl_conn_t *conn)
426 {
427 	int rc;
428 	struct lldpd_config *config;
429 	void *p;
430 
431 	RESET_ERROR(conn);
432 
433 	rc = _lldpctl_do_something(conn,
434 	    CONN_STATE_GET_CONFIG_SEND, CONN_STATE_GET_CONFIG_RECV, NULL,
435 	    GET_CONFIG,
436 	    NULL, NULL,
437 	    &p, &MARSHAL_INFO(lldpd_config));
438 	if (rc == 0) {
439 		config = p;
440 		return _lldpctl_new_atom(conn, atom_config, config);
441 	}
442 	return NULL;
443 }
444 
445 lldpctl_atom_t*
lldpctl_get_interfaces(lldpctl_conn_t * conn)446 lldpctl_get_interfaces(lldpctl_conn_t *conn)
447 {
448 	struct lldpd_interface_list *ifs;
449 	void *p;
450 	int rc;
451 
452 	RESET_ERROR(conn);
453 
454 	rc = _lldpctl_do_something(conn,
455 	    CONN_STATE_GET_INTERFACES_SEND, CONN_STATE_GET_INTERFACES_RECV, NULL,
456 	    GET_INTERFACES,
457 	    NULL, NULL,
458 	    &p, &MARSHAL_INFO(lldpd_interface_list));
459 	if (rc == 0) {
460 		ifs = p;
461 		return _lldpctl_new_atom(conn, atom_interfaces_list, ifs);
462 	}
463 	return NULL;
464 }
465 
466 lldpctl_atom_t*
lldpctl_get_local_chassis(lldpctl_conn_t * conn)467 lldpctl_get_local_chassis(lldpctl_conn_t *conn)
468 {
469 	struct lldpd_chassis *chassis;
470 	void *p;
471 	int rc;
472 
473 	RESET_ERROR(conn);
474 
475 	rc = _lldpctl_do_something(conn,
476 	    CONN_STATE_GET_CHASSIS_SEND, CONN_STATE_GET_CHASSIS_RECV, NULL,
477 	    GET_CHASSIS,
478 	    NULL, NULL,
479 	    &p, &MARSHAL_INFO(lldpd_chassis));
480 	if (rc == 0) {
481 		chassis = p;
482 		return _lldpctl_new_atom(conn, atom_chassis, chassis, NULL, 0);
483 	}
484 	return NULL;
485 }
486 
487 lldpctl_atom_t*
lldpctl_get_port(lldpctl_atom_t * atom)488 lldpctl_get_port(lldpctl_atom_t *atom)
489 {
490 	int rc;
491 	lldpctl_conn_t *conn = atom->conn;
492 	struct lldpd_hardware *hardware;
493 	void *p;
494 	struct _lldpctl_atom_interface_t *iface =
495 	    (struct _lldpctl_atom_interface_t *)atom;
496 
497 	RESET_ERROR(conn);
498 
499 	if (atom->type != atom_interface) {
500 		SET_ERROR(conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
501 		return NULL;
502 	}
503 	rc = _lldpctl_do_something(conn,
504 	    CONN_STATE_GET_PORT_SEND, CONN_STATE_GET_PORT_RECV, iface->name,
505 	    GET_INTERFACE, (void*)iface->name, &MARSHAL_INFO(string),
506 	    &p, &MARSHAL_INFO(lldpd_hardware));
507 	if (rc == 0) {
508 		hardware = p;
509 		return _lldpctl_new_atom(conn, atom_port, 1,
510 		    hardware, &hardware->h_lport, NULL);
511 	}
512 	return NULL;
513 }
514 
515 lldpctl_atom_t*
lldpctl_get_default_port(lldpctl_conn_t * conn)516 lldpctl_get_default_port(lldpctl_conn_t *conn)
517 {
518 	struct lldpd_port *port;
519 	void *p;
520 	int rc;
521 
522 	RESET_ERROR(conn);
523 
524 	rc = _lldpctl_do_something(conn,
525 	    CONN_STATE_GET_DEFAULT_PORT_SEND, CONN_STATE_GET_DEFAULT_PORT_RECV, "",
526 	    GET_DEFAULT_PORT, NULL, NULL,
527 	    &p, &MARSHAL_INFO(lldpd_port));
528 	if (rc == 0) {
529 		port = p;
530 		return _lldpctl_new_atom(conn, atom_port, 1, NULL, port, NULL);
531 	}
532 	return NULL;
533 }
534 
535 static lldpctl_map_t empty_map[] = {{ 0, NULL }};
536 
537 static struct atom_map atom_map_list = {
538 	.next = NULL
539 };
540 
541 lldpctl_map_t*
lldpctl_key_get_map(lldpctl_key_t key)542 lldpctl_key_get_map(lldpctl_key_t key)
543 {
544 	init_atom_map();
545 	struct atom_map *map;
546 	for (map = atom_map_list.next; map ; map = map->next) {
547 		if (map->key == key)
548 			return map->map;
549 	}
550 	return empty_map;
551 }
552 
atom_map_register(struct atom_map * map,int prio)553 void atom_map_register(struct atom_map *map, int prio)
554 {
555 	(void)prio;
556 	struct atom_map* iter = &atom_map_list;
557 
558 	while (iter->next)
559 		iter = iter->next;
560 
561 	iter->next = map;
562 }
563 
564 static struct atom_builder atom_builder_list = {
565 	.nextb = NULL
566 };
567 
atom_builder_register(struct atom_builder * builder,int prio)568 void atom_builder_register(struct atom_builder *builder, int prio)
569 {
570 	(void)prio;
571 	struct atom_builder* iter = &atom_builder_list;
572 
573 	while (iter->nextb)
574 		iter = iter->nextb;
575 
576 	iter->nextb = builder;
577 }
578 
579 lldpctl_atom_t*
_lldpctl_new_atom(lldpctl_conn_t * conn,atom_t type,...)580 _lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...)
581 {
582 	init_atom_builder();
583 	struct atom_builder *builder;
584 	struct lldpctl_atom_t *atom;
585 	va_list(ap);
586 	for (builder = atom_builder_list.nextb; builder ; builder = builder->nextb) {
587 		if (builder->type != type) continue;
588 		atom = calloc(1, builder->size);
589 		if (atom == NULL) {
590 			SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
591 			return NULL;
592 		}
593 		atom->count = 1;
594 		atom->type  = type;
595 		atom->conn  = conn;
596 		TAILQ_INIT(&atom->buffers);
597 		atom->free  = builder->free;
598 
599 		atom->iter  = builder->iter;
600 		atom->next  = builder->next;
601 		atom->value = builder->value;
602 
603 		atom->get       = builder->get;
604 		atom->get_str   = builder->get_str;
605 		atom->get_buffer= builder->get_buffer;
606 		atom->get_int   = builder->get_int;
607 
608 		atom->set       = builder->set;
609 		atom->set_str   = builder->set_str;
610 		atom->set_buffer= builder->set_buffer;
611 		atom->set_int   = builder->set_int;
612 		atom->create    = builder->create;
613 
614 		va_start(ap, type);
615 		if (builder->init && builder->init(atom, ap) == 0) {
616 			free(atom);
617 			va_end(ap);
618 			/* Error to be set in init() */
619 			return NULL;
620 		}
621 		va_end(ap);
622 		return atom;
623 	}
624 	log_warnx("rpc", "unknown atom type: %d", type);
625 	SET_ERROR(conn, LLDPCTL_ERR_FATAL);
626 	return NULL;
627 }
628 
629 /**
630  * Allocate a buffer inside an atom.
631  *
632  * It will be freed automatically when the atom is released. This buffer cannot
633  * be reallocated and should not be freed!
634  *
635  * @param atom Atom which will be used as a container.
636  * @param size Size of the allocated area.
637  * @return Pointer to the buffer or @c NULL if allocation fails.
638  */
639 void*
_lldpctl_alloc_in_atom(lldpctl_atom_t * atom,size_t size)640 _lldpctl_alloc_in_atom(lldpctl_atom_t *atom, size_t size)
641 {
642 	struct atom_buffer *buffer;
643 
644 	if ((buffer = calloc(1, size + sizeof(struct atom_buffer))) == NULL) {
645 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
646 		return NULL;
647 	}
648 	TAILQ_INSERT_TAIL(&atom->buffers, buffer, next);
649 	return &buffer->data[0];
650 }
651 
652 /**
653  * Allocate a buffer inside an atom and dump another buffer in it.
654  *
655  * The dump is done in hexadecimal with the provided separator.
656  *
657  * @param atom   Atom which will be used as a container.
658  * @param input  Buffer we want to dump.
659  * @param size   Size of the buffer
660  * @param sep    Separator to use.
661  * @param max    Maximum number of bytes to dump. Can be 0 if no maximum.
662  * @return A string representing the dump of the buffer or @c NULL if error.
663  */
664 const char*
_lldpctl_dump_in_atom(lldpctl_atom_t * atom,const uint8_t * input,size_t size,char sep,size_t max)665 _lldpctl_dump_in_atom(lldpctl_atom_t *atom,
666     const uint8_t *input, size_t size,
667     char sep, size_t max)
668 {
669 	static const char truncation[] = "[...]";
670 	size_t i, len;
671 	char *buffer = NULL;
672 
673 	if (max > 0 && size > max)
674 		len = max * 3 + sizeof(truncation) + 1;
675 	else
676 		len = size * 3 + 1;
677 
678 	if ((buffer = _lldpctl_alloc_in_atom(atom, len)) == NULL)
679 		return NULL;
680 
681 	for (i = 0; (i < size) && (max == 0 || i < max); i++)
682 		snprintf(buffer + i * 3, 4, "%02x%c", *(u_int8_t*)(input + i), sep);
683 	if (max > 0 && size > max)
684 		snprintf(buffer + i * 3, sizeof(truncation) + 1, "%s", truncation);
685 	else
686 		*(buffer + i*3 - 1) = 0;
687 	return buffer;
688 }
689