]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lib/atom.c
lib: stricly-prevent use of a connection used to watch events
[thirdparty/lldpd.git] / src / lib / atom.c
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*
28 lldpctl_atom_get_connection(lldpctl_atom_t *atom)
29 {
30 if (atom)
31 return atom->conn;
32 return NULL;
33 }
34
35 void
36 lldpctl_atom_inc_ref(lldpctl_atom_t *atom)
37 {
38 if (atom)
39 atom->count++;
40 }
41
42 void
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*
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*
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*
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*
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*
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*
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
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*
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*
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*
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*
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*
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
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)))) {
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)))) {
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
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
384 lldpctl_watch(lldpctl_conn_t *conn)
385 {
386 int rc = 0;
387
388 RESET_ERROR(conn);
389
390 if (conn->state != CONN_STATE_WATCHING)
391 return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
392
393 conn->watch_triggered = 0;
394 while (!conn->watch_triggered) {
395 rc = _lldpctl_needs(conn, 1);
396 if (rc < 0)
397 return SET_ERROR(conn, rc);
398 }
399
400 RESET_ERROR(conn);
401 return 0;
402 }
403
404 lldpctl_atom_t*
405 lldpctl_get_configuration(lldpctl_conn_t *conn)
406 {
407 int rc;
408 struct lldpd_config *config;
409 void *p;
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,
417 &p, &MARSHAL_INFO(lldpd_config));
418 if (rc == 0) {
419 config = p;
420 return _lldpctl_new_atom(conn, atom_config, config);
421 }
422 return NULL;
423 }
424
425 lldpctl_atom_t*
426 lldpctl_get_interfaces(lldpctl_conn_t *conn)
427 {
428 struct lldpd_interface_list *ifs;
429 void *p;
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,
438 &p, &MARSHAL_INFO(lldpd_interface_list));
439 if (rc == 0) {
440 ifs = p;
441 return _lldpctl_new_atom(conn, atom_interfaces_list, ifs);
442 }
443 return NULL;
444 }
445
446 lldpctl_atom_t*
447 lldpctl_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;
462 return _lldpctl_new_atom(conn, atom_chassis, chassis, NULL, 0);
463 }
464 return NULL;
465 }
466
467 lldpctl_atom_t*
468 lldpctl_get_port(lldpctl_atom_t *atom)
469 {
470 int rc;
471 lldpctl_conn_t *conn = atom->conn;
472 struct lldpd_hardware *hardware;
473 void *p;
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),
486 &p, &MARSHAL_INFO(lldpd_hardware));
487 if (rc == 0) {
488 hardware = p;
489 return _lldpctl_new_atom(conn, atom_port, 1,
490 hardware, &hardware->h_lport, NULL);
491 }
492 return NULL;
493 }
494
495 lldpctl_atom_t*
496 lldpctl_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
515 static lldpctl_map_t empty_map[] = {{ 0, NULL }};
516
517 static struct atom_map atom_map_list = {
518 .next = NULL
519 };
520
521 lldpctl_map_t*
522 lldpctl_key_get_map(lldpctl_key_t key)
523 {
524 init_atom_map();
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
533 void atom_map_register(struct atom_map *map, int prio)
534 {
535 (void)prio;
536 struct atom_map* iter = &atom_map_list;
537
538 while (iter->next)
539 iter = iter->next;
540
541 iter->next = map;
542 }
543
544 static struct atom_builder atom_builder_list = {
545 .nextb = NULL
546 };
547
548 void atom_builder_register(struct atom_builder *builder, int prio)
549 {
550 (void)prio;
551 struct atom_builder* iter = &atom_builder_list;
552
553 while (iter->nextb)
554 iter = iter->nextb;
555
556 iter->nextb = builder;
557 }
558
559 lldpctl_atom_t*
560 _lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...)
561 {
562 init_atom_builder();
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 */
619 void*
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 */
644 const 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 }