]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lib/atom.c
lib: stricly-prevent use of a connection used to watch events
[thirdparty/lldpd.git] / src / lib / atom.c
CommitLineData
4b292b55
VB
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>
8a378b16 20#include <limits.h>
4b292b55 21#include "lldpctl.h"
a5f98717 22#include "atom.h"
4b2ee1a0 23#include "../log.h"
4b292b55
VB
24#include "../marshal.h"
25#include "../ctl.h"
4b292b55
VB
26
27lldpctl_conn_t*
28lldpctl_atom_get_connection(lldpctl_atom_t *atom)
29{
30 if (atom)
31 return atom->conn;
32 return NULL;
33}
34
35void
36lldpctl_atom_inc_ref(lldpctl_atom_t *atom)
37{
e7113d69
AA
38 if (atom)
39 atom->count++;
4b292b55
VB
40}
41
42void
43lldpctl_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
62lldpctl_atom_t*
63lldpctl_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
75lldpctl_atom_t*
76lldpctl_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
89const char*
90lldpctl_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
135lldpctl_atom_t*
136lldpctl_atom_set_str(lldpctl_atom_t *atom, lldpctl_key_t key,
137 const char *value)
138{
139 lldpctl_atom_t *result = NULL;
8a378b16
VB
140 const char *errstr;
141 long long converted = 0;
6dd83015 142 int isint = 0;
4b292b55
VB
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
6dd83015 157 if (value) {
8a378b16
VB
158 converted = strtonum(value, LLONG_MIN, LLONG_MAX, &errstr);
159 isint = (errstr == NULL);
6dd83015 160 }
4b292b55
VB
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) {
6dd83015 174 result = atom->set_buffer(atom, key, (u_int8_t*)value, value?strlen(value):0);
4b292b55
VB
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
186const u_int8_t*
187lldpctl_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
200lldpctl_atom_t*
201lldpctl_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
214long int
215lldpctl_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
225lldpctl_atom_t*
226lldpctl_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
239lldpctl_atom_iter_t*
240lldpctl_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
252lldpctl_atom_iter_t*
253lldpctl_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
265lldpctl_atom_t*
266lldpctl_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
278lldpctl_atom_t*
279lldpctl_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"
b1eceab6 297 * @param state_data Ancillary data for state handling
4b292b55
VB
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.
b1eceab6
VB
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.
4b292b55
VB
307 */
308int
309_lldpctl_do_something(lldpctl_conn_t *conn,
89a84190 310 int state_send, int state_recv, const char *state_data,
4b292b55
VB
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
cb38337c
VB
317 if (conn->state == CONN_STATE_WATCHING)
318 /* The connection cannot be used anymore. */
319 return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
320
4b292b55
VB
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;
70f6059b 328 if (state_data)
9c49cedf
VB
329 strlcpy(conn->state_data, state_data, sizeof(conn->state_data));
330 else
331 conn->state_data[0] = 0;
4b292b55 332 }
89a84190 333 if (conn->state == state_send &&
9c49cedf 334 (state_data == NULL || !strncmp(conn->state_data, state_data, sizeof(conn->state_data)))) {
4b292b55
VB
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 }
89a84190 341 if (conn->state == state_recv &&
9c49cedf 342 (state_data == NULL || !strncmp(conn->state_data, state_data, sizeof(conn->state_data)))) {
4b292b55 343 /* We need to receive the answer */
4e90a9e0
VB
344 while ((rc = ctl_msg_recv_unserialized(&conn->input_buffer,
345 &conn->input_buffer_len,
346 type, to_recv, mi_recv)) > 0) {
4b292b55
VB
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;
9c49cedf 356 conn->state_data[0] = 0;
4b292b55
VB
357 return 0;
358 } else
359 return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
360}
361
4e90a9e0
VB
362
363int
364lldpctl_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;
cb38337c 378 conn->state = CONN_STATE_WATCHING;
4e90a9e0
VB
379 }
380 return rc;
381}
382
383int
384lldpctl_watch(lldpctl_conn_t *conn)
385{
bdc954b7 386 int rc = 0;
4e90a9e0
VB
387
388 RESET_ERROR(conn);
389
cb38337c 390 if (conn->state != CONN_STATE_WATCHING)
4e90a9e0
VB
391 return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
392
393 conn->watch_triggered = 0;
4e90a9e0 394 while (!conn->watch_triggered) {
bdc954b7 395 rc = _lldpctl_needs(conn, 1);
4e90a9e0
VB
396 if (rc < 0)
397 return SET_ERROR(conn, rc);
398 }
399
400 RESET_ERROR(conn);
401 return 0;
402}
403
8729d69f
VB
404lldpctl_atom_t*
405lldpctl_get_configuration(lldpctl_conn_t *conn)
406{
407 int rc;
408 struct lldpd_config *config;
0e940d8d 409 void *p;
8729d69f
VB
410
411 RESET_ERROR(conn);
412
413 rc = _lldpctl_do_something(conn,
414 CONN_STATE_GET_CONFIG_SEND, CONN_STATE_GET_CONFIG_RECV, NULL,
415 GET_CONFIG,
416 NULL, NULL,
0e940d8d
VB
417 &p, &MARSHAL_INFO(lldpd_config));
418 if (rc == 0) {
419 config = p;
8729d69f 420 return _lldpctl_new_atom(conn, atom_config, config);
0e940d8d 421 }
8729d69f
VB
422 return NULL;
423}
424
4b292b55
VB
425lldpctl_atom_t*
426lldpctl_get_interfaces(lldpctl_conn_t *conn)
427{
428 struct lldpd_interface_list *ifs;
0e940d8d 429 void *p;
4b292b55
VB
430 int rc;
431
432 RESET_ERROR(conn);
433
434 rc = _lldpctl_do_something(conn,
435 CONN_STATE_GET_INTERFACES_SEND, CONN_STATE_GET_INTERFACES_RECV, NULL,
436 GET_INTERFACES,
437 NULL, NULL,
0e940d8d
VB
438 &p, &MARSHAL_INFO(lldpd_interface_list));
439 if (rc == 0) {
440 ifs = p;
4b292b55 441 return _lldpctl_new_atom(conn, atom_interfaces_list, ifs);
0e940d8d 442 }
4b292b55
VB
443 return NULL;
444}
445
99ef55d3
VB
446lldpctl_atom_t*
447lldpctl_get_local_chassis(lldpctl_conn_t *conn)
448{
449 struct lldpd_chassis *chassis;
450 void *p;
451 int rc;
452
453 RESET_ERROR(conn);
454
455 rc = _lldpctl_do_something(conn,
456 CONN_STATE_GET_CHASSIS_SEND, CONN_STATE_GET_CHASSIS_RECV, NULL,
457 GET_CHASSIS,
458 NULL, NULL,
459 &p, &MARSHAL_INFO(lldpd_chassis));
460 if (rc == 0) {
461 chassis = p;
e55e7492 462 return _lldpctl_new_atom(conn, atom_chassis, chassis, NULL, 0);
99ef55d3
VB
463 }
464 return NULL;
465}
466
4b292b55
VB
467lldpctl_atom_t*
468lldpctl_get_port(lldpctl_atom_t *atom)
469{
470 int rc;
471 lldpctl_conn_t *conn = atom->conn;
472 struct lldpd_hardware *hardware;
0e940d8d 473 void *p;
4b292b55
VB
474 struct _lldpctl_atom_interface_t *iface =
475 (struct _lldpctl_atom_interface_t *)atom;
476
477 RESET_ERROR(conn);
478
479 if (atom->type != atom_interface) {
480 SET_ERROR(conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
481 return NULL;
482 }
483 rc = _lldpctl_do_something(conn,
484 CONN_STATE_GET_PORT_SEND, CONN_STATE_GET_PORT_RECV, iface->name,
485 GET_INTERFACE, (void*)iface->name, &MARSHAL_INFO(string),
0e940d8d
VB
486 &p, &MARSHAL_INFO(lldpd_hardware));
487 if (rc == 0) {
488 hardware = p;
9da663f7 489 return _lldpctl_new_atom(conn, atom_port, 1,
4b292b55 490 hardware, &hardware->h_lport, NULL);
0e940d8d 491 }
4b292b55
VB
492 return NULL;
493}
4b2ee1a0 494
9da663f7
VB
495lldpctl_atom_t*
496lldpctl_get_default_port(lldpctl_conn_t *conn)
497{
498 struct lldpd_port *port;
499 void *p;
500 int rc;
501
502 RESET_ERROR(conn);
503
504 rc = _lldpctl_do_something(conn,
505 CONN_STATE_GET_DEFAULT_PORT_SEND, CONN_STATE_GET_DEFAULT_PORT_RECV, "",
506 GET_DEFAULT_PORT, NULL, NULL,
507 &p, &MARSHAL_INFO(lldpd_port));
508 if (rc == 0) {
509 port = p;
510 return _lldpctl_new_atom(conn, atom_port, 1, NULL, port, NULL);
511 }
512 return NULL;
513}
514
4b2ee1a0
AA
515static lldpctl_map_t empty_map[] = {{ 0, NULL }};
516
517static struct atom_map atom_map_list = {
518 .next = NULL
519};
520
521lldpctl_map_t*
522lldpctl_key_get_map(lldpctl_key_t key)
523{
d954509e 524 init_atom_map();
4b2ee1a0
AA
525 struct atom_map *map;
526 for (map = atom_map_list.next; map ; map = map->next) {
527 if (map->key == key)
528 return map->map;
529 }
530 return empty_map;
531}
532
e3783368 533void atom_map_register(struct atom_map *map, int prio)
4b2ee1a0 534{
e3783368 535 (void)prio;
4b2ee1a0
AA
536 struct atom_map* iter = &atom_map_list;
537
538 while (iter->next)
539 iter = iter->next;
540
541 iter->next = map;
542}
543
544static struct atom_builder atom_builder_list = {
545 .nextb = NULL
546};
547
e3783368 548void atom_builder_register(struct atom_builder *builder, int prio)
4b2ee1a0 549{
e3783368 550 (void)prio;
4b2ee1a0
AA
551 struct atom_builder* iter = &atom_builder_list;
552
553 while (iter->nextb)
554 iter = iter->nextb;
555
556 iter->nextb = builder;
557}
558
559lldpctl_atom_t*
560_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...)
561{
d954509e 562 init_atom_builder();
4b2ee1a0
AA
563 struct atom_builder *builder;
564 struct lldpctl_atom_t *atom;
565 va_list(ap);
566 for (builder = atom_builder_list.nextb; builder ; builder = builder->nextb) {
567 if (builder->type != type) continue;
568 atom = calloc(1, builder->size);
569 if (atom == NULL) {
570 SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
571 return NULL;
572 }
573 atom->count = 1;
574 atom->type = type;
575 atom->conn = conn;
576 TAILQ_INIT(&atom->buffers);
577 atom->free = builder->free;
578
579 atom->iter = builder->iter;
580 atom->next = builder->next;
581 atom->value = builder->value;
582
583 atom->get = builder->get;
584 atom->get_str = builder->get_str;
585 atom->get_buffer= builder->get_buffer;
586 atom->get_int = builder->get_int;
587
588 atom->set = builder->set;
589 atom->set_str = builder->set_str;
590 atom->set_buffer= builder->set_buffer;
591 atom->set_int = builder->set_int;
592 atom->create = builder->create;
593
594 va_start(ap, type);
595 if (builder->init && builder->init(atom, ap) == 0) {
596 free(atom);
597 va_end(ap);
598 /* Error to be set in init() */
599 return NULL;
600 }
601 va_end(ap);
602 return atom;
603 }
604 log_warnx("rpc", "unknown atom type: %d", type);
605 SET_ERROR(conn, LLDPCTL_ERR_FATAL);
606 return NULL;
607}
608
609/**
610 * Allocate a buffer inside an atom.
611 *
612 * It will be freed automatically when the atom is released. This buffer cannot
613 * be reallocated and should not be freed!
614 *
615 * @param atom Atom which will be used as a container.
616 * @param size Size of the allocated area.
617 * @return Pointer to the buffer or @c NULL if allocation fails.
618 */
619void*
620_lldpctl_alloc_in_atom(lldpctl_atom_t *atom, size_t size)
621{
622 struct atom_buffer *buffer;
623
624 if ((buffer = calloc(1, size + sizeof(struct atom_buffer))) == NULL) {
625 SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
626 return NULL;
627 }
628 TAILQ_INSERT_TAIL(&atom->buffers, buffer, next);
629 return &buffer->data[0];
630}
631
632/**
633 * Allocate a buffer inside an atom and dump another buffer in it.
634 *
635 * The dump is done in hexadecimal with the provided separator.
636 *
637 * @param atom Atom which will be used as a container.
638 * @param input Buffer we want to dump.
639 * @param size Size of the buffer
640 * @param sep Separator to use.
641 * @param max Maximum number of bytes to dump. Can be 0 if no maximum.
642 * @return A string representing the dump of the buffer or @c NULL if error.
643 */
644const char*
645_lldpctl_dump_in_atom(lldpctl_atom_t *atom,
646 const uint8_t *input, size_t size,
647 char sep, size_t max)
648{
649 static const char truncation[] = "[...]";
650 size_t i, len;
651 char *buffer = NULL;
652
653 if (max > 0 && size > max)
654 len = max * 3 + sizeof(truncation) + 1;
655 else
656 len = size * 3 + 1;
657
658 if ((buffer = _lldpctl_alloc_in_atom(atom, len)) == NULL)
659 return NULL;
660
661 for (i = 0; (i < size) && (max == 0 || i < max); i++)
662 snprintf(buffer + i * 3, 4, "%02x%c", *(u_int8_t*)(input + i), sep);
663 if (max > 0 && size > max)
664 snprintf(buffer + i * 3, sizeof(truncation) + 1, "%s", truncation);
665 else
666 *(buffer + i*3 - 1) = 0;
667 return buffer;
668}