1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
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.
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.
24 #include "../marshal.h"
28 lldpctl_atom_get_connection(lldpctl_atom_t
*atom
)
36 lldpctl_atom_inc_ref(lldpctl_atom_t
*atom
)
43 lldpctl_atom_dec_ref(lldpctl_atom_t
*atom
)
45 struct atom_buffer
*buffer
, *buffer_next
;
46 if (atom
&& (--atom
->count
== 0)) {
50 /* Remove special allocated buffers */
51 for (buffer
= TAILQ_FIRST(&atom
->buffers
);
53 buffer
= buffer_next
) {
54 buffer_next
= TAILQ_NEXT(buffer
, next
);
63 lldpctl_atom_get(lldpctl_atom_t
*atom
, lldpctl_key_t key
)
65 if (atom
== NULL
) return NULL
;
66 RESET_ERROR(atom
->conn
);
68 if (atom
->get
== NULL
) {
69 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
72 return atom
->get(atom
, key
);
76 lldpctl_atom_set(lldpctl_atom_t
*atom
, lldpctl_key_t key
,
77 lldpctl_atom_t
*value
)
79 if (atom
== NULL
) return NULL
;
80 RESET_ERROR(atom
->conn
);
82 if (atom
->set
== NULL
) {
83 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
86 return atom
->set(atom
, key
, value
);
90 lldpctl_atom_get_str(lldpctl_atom_t
*atom
, lldpctl_key_t key
)
92 char *strresult
= NULL
;
93 const uint8_t *bufresult
= NULL
;
94 long int intresult
= -1;
98 if (atom
== NULL
) return NULL
;
99 RESET_ERROR(atom
->conn
);
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
)
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)
117 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOMEM
); /* Not really true... */
122 RESET_ERROR(atom
->conn
);
123 if (atom
->get_buffer
!= NULL
) {
124 bufresult
= atom
->get_buffer(atom
, key
, &n2
);
126 return _lldpctl_dump_in_atom(atom
, bufresult
, n2
, ' ', 0);
127 if (lldpctl_last_error(atom
->conn
) != LLDPCTL_ERR_NOT_EXIST
)
131 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
136 lldpctl_atom_set_str(lldpctl_atom_t
*atom
, lldpctl_key_t key
,
139 lldpctl_atom_t
*result
= NULL
;
141 long long converted
= 0;
145 if (atom
== NULL
) return NULL
;
146 RESET_ERROR(atom
->conn
);
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
)
154 bad
= bad
|| (lldpctl_last_error(atom
->conn
) == LLDPCTL_ERR_BAD_VALUE
);
158 converted
= strtonum(value
, LLONG_MIN
, LLONG_MAX
, &errstr
);
159 isint
= (errstr
== NULL
);
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
)
169 bad
= bad
|| (lldpctl_last_error(atom
->conn
) == LLDPCTL_ERR_BAD_VALUE
);
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
)
179 bad
= bad
|| (lldpctl_last_error(atom
->conn
) == LLDPCTL_ERR_BAD_VALUE
);
182 SET_ERROR(atom
->conn
, bad
?LLDPCTL_ERR_BAD_VALUE
:LLDPCTL_ERR_NOT_EXIST
);
187 lldpctl_atom_get_buffer(lldpctl_atom_t
*atom
, lldpctl_key_t key
,
190 if (atom
== NULL
) return NULL
;
191 RESET_ERROR(atom
->conn
);
193 if (atom
->get_buffer
== NULL
) {
194 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
197 return atom
->get_buffer(atom
, key
, length
);
201 lldpctl_atom_set_buffer(lldpctl_atom_t
*atom
, lldpctl_key_t key
,
202 const u_int8_t
* value
, size_t length
)
204 if (atom
== NULL
) return NULL
;
205 RESET_ERROR(atom
->conn
);
207 if (atom
->set_buffer
== NULL
) {
208 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
211 return atom
->set_buffer(atom
, key
, value
, length
);
215 lldpctl_atom_get_int(lldpctl_atom_t
*atom
, lldpctl_key_t key
)
217 if (atom
== NULL
) return LLDPCTL_ERR_NOT_EXIST
;
218 RESET_ERROR(atom
->conn
);
220 if (atom
->get_int
== NULL
)
221 return SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
222 return atom
->get_int(atom
, key
);
226 lldpctl_atom_set_int(lldpctl_atom_t
*atom
, lldpctl_key_t key
,
229 if (atom
== NULL
) return NULL
;
230 RESET_ERROR(atom
->conn
);
232 if (atom
->set_int
== NULL
) {
233 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOT_EXIST
);
236 return atom
->set_int(atom
, key
, value
);
240 lldpctl_atom_iter(lldpctl_atom_t
*atom
)
242 if (atom
== NULL
) return NULL
;
243 RESET_ERROR(atom
->conn
);
246 SET_ERROR(atom
->conn
, LLDPCTL_ERR_CANNOT_ITERATE
);
249 return atom
->iter(atom
);
253 lldpctl_atom_iter_next(lldpctl_atom_t
*atom
, lldpctl_atom_iter_t
*iter
)
255 if (atom
== NULL
) return NULL
;
256 RESET_ERROR(atom
->conn
);
259 SET_ERROR(atom
->conn
, LLDPCTL_ERR_CANNOT_ITERATE
);
262 return atom
->next(atom
, iter
);
266 lldpctl_atom_iter_value(lldpctl_atom_t
*atom
, lldpctl_atom_iter_t
*iter
)
268 if (atom
== NULL
) return NULL
;
269 RESET_ERROR(atom
->conn
);
272 SET_ERROR(atom
->conn
, LLDPCTL_ERR_CANNOT_ITERATE
);
275 return atom
->value(atom
, iter
);
279 lldpctl_atom_create(lldpctl_atom_t
*atom
)
281 if (atom
== NULL
) return NULL
;
282 RESET_ERROR(atom
->conn
);
285 SET_ERROR(atom
->conn
, LLDPCTL_ERR_CANNOT_CREATE
);
288 return atom
->create(atom
);
292 * Get somethin with IO.
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.
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.
309 _lldpctl_do_something(lldpctl_conn_t
*conn
,
310 int state_send
, int state_recv
, const char *state_data
,
312 void *to_send
, struct marshal_info
*mi_send
,
313 void **to_recv
, struct marshal_info
*mi_recv
)
317 if (conn
->state
== CONN_STATE_WATCHING
)
318 /* The connection cannot be used anymore. */
319 return SET_ERROR(conn
, LLDPCTL_ERR_INVALID_STATE
);
321 if (conn
->state
== CONN_STATE_IDLE
) {
322 /* We need to build the message to send, then send
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
;
329 strlcpy(conn
->state_data
, state_data
, sizeof(conn
->state_data
));
331 conn
->state_data
[0] = 0;
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
);
338 return SET_ERROR(conn
, rc
);
339 conn
->state
= state_recv
;
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
);
350 return SET_ERROR(conn
, rc
);
353 return SET_ERROR(conn
, LLDPCTL_ERR_SERIALIZATION
);
355 conn
->state
= CONN_STATE_IDLE
;
356 conn
->state_data
[0] = 0;
359 return SET_ERROR(conn
, LLDPCTL_ERR_INVALID_STATE
);
364 lldpctl_watch_callback(lldpctl_conn_t
*conn
,
365 lldpctl_change_callback cb
,
372 rc
= _lldpctl_do_something(conn
,
373 CONN_STATE_SET_WATCH_SEND
, CONN_STATE_SET_WATCH_RECV
, NULL
,
374 SUBSCRIBE
, NULL
, NULL
, NULL
, NULL
);
377 conn
->watch_data
= data
;
378 conn
->state
= CONN_STATE_WATCHING
;
384 lldpctl_watch(lldpctl_conn_t
*conn
)
390 if (conn
->state
!= CONN_STATE_WATCHING
)
391 return SET_ERROR(conn
, LLDPCTL_ERR_INVALID_STATE
);
393 conn
->watch_triggered
= 0;
394 while (!conn
->watch_triggered
) {
395 rc
= _lldpctl_needs(conn
, 1);
397 return SET_ERROR(conn
, rc
);
405 lldpctl_get_configuration(lldpctl_conn_t
*conn
)
408 struct lldpd_config
*config
;
413 rc
= _lldpctl_do_something(conn
,
414 CONN_STATE_GET_CONFIG_SEND
, CONN_STATE_GET_CONFIG_RECV
, NULL
,
417 &p
, &MARSHAL_INFO(lldpd_config
));
420 return _lldpctl_new_atom(conn
, atom_config
, config
);
426 lldpctl_get_interfaces(lldpctl_conn_t
*conn
)
428 struct lldpd_interface_list
*ifs
;
434 rc
= _lldpctl_do_something(conn
,
435 CONN_STATE_GET_INTERFACES_SEND
, CONN_STATE_GET_INTERFACES_RECV
, NULL
,
438 &p
, &MARSHAL_INFO(lldpd_interface_list
));
441 return _lldpctl_new_atom(conn
, atom_interfaces_list
, ifs
);
447 lldpctl_get_local_chassis(lldpctl_conn_t
*conn
)
449 struct lldpd_chassis
*chassis
;
455 rc
= _lldpctl_do_something(conn
,
456 CONN_STATE_GET_CHASSIS_SEND
, CONN_STATE_GET_CHASSIS_RECV
, NULL
,
459 &p
, &MARSHAL_INFO(lldpd_chassis
));
462 return _lldpctl_new_atom(conn
, atom_chassis
, chassis
, NULL
, 0);
468 lldpctl_get_port(lldpctl_atom_t
*atom
)
471 lldpctl_conn_t
*conn
= atom
->conn
;
472 struct lldpd_hardware
*hardware
;
474 struct _lldpctl_atom_interface_t
*iface
=
475 (struct _lldpctl_atom_interface_t
*)atom
;
479 if (atom
->type
!= atom_interface
) {
480 SET_ERROR(conn
, LLDPCTL_ERR_INCORRECT_ATOM_TYPE
);
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
));
489 return _lldpctl_new_atom(conn
, atom_port
, 1,
490 hardware
, &hardware
->h_lport
, NULL
);
496 lldpctl_get_default_port(lldpctl_conn_t
*conn
)
498 struct lldpd_port
*port
;
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
));
510 return _lldpctl_new_atom(conn
, atom_port
, 1, NULL
, port
, NULL
);
515 static lldpctl_map_t empty_map
[] = {{ 0, NULL
}};
517 static struct atom_map atom_map_list
= {
522 lldpctl_key_get_map(lldpctl_key_t key
)
525 struct atom_map
*map
;
526 for (map
= atom_map_list
.next
; map
; map
= map
->next
) {
533 void atom_map_register(struct atom_map
*map
, int prio
)
536 struct atom_map
* iter
= &atom_map_list
;
544 static struct atom_builder atom_builder_list
= {
548 void atom_builder_register(struct atom_builder
*builder
, int prio
)
551 struct atom_builder
* iter
= &atom_builder_list
;
556 iter
->nextb
= builder
;
560 _lldpctl_new_atom(lldpctl_conn_t
*conn
, atom_t type
, ...)
563 struct atom_builder
*builder
;
564 struct lldpctl_atom_t
*atom
;
566 for (builder
= atom_builder_list
.nextb
; builder
; builder
= builder
->nextb
) {
567 if (builder
->type
!= type
) continue;
568 atom
= calloc(1, builder
->size
);
570 SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
576 TAILQ_INIT(&atom
->buffers
);
577 atom
->free
= builder
->free
;
579 atom
->iter
= builder
->iter
;
580 atom
->next
= builder
->next
;
581 atom
->value
= builder
->value
;
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
;
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
;
595 if (builder
->init
&& builder
->init(atom
, ap
) == 0) {
598 /* Error to be set in init() */
604 log_warnx("rpc", "unknown atom type: %d", type
);
605 SET_ERROR(conn
, LLDPCTL_ERR_FATAL
);
610 * Allocate a buffer inside an atom.
612 * It will be freed automatically when the atom is released. This buffer cannot
613 * be reallocated and should not be freed!
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.
620 _lldpctl_alloc_in_atom(lldpctl_atom_t
*atom
, size_t size
)
622 struct atom_buffer
*buffer
;
624 if ((buffer
= calloc(1, size
+ sizeof(struct atom_buffer
))) == NULL
) {
625 SET_ERROR(atom
->conn
, LLDPCTL_ERR_NOMEM
);
628 TAILQ_INSERT_TAIL(&atom
->buffers
, buffer
, next
);
629 return &buffer
->data
[0];
633 * Allocate a buffer inside an atom and dump another buffer in it.
635 * The dump is done in hexadecimal with the provided separator.
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.
645 _lldpctl_dump_in_atom(lldpctl_atom_t
*atom
,
646 const uint8_t *input
, size_t size
,
647 char sep
, size_t max
)
649 static const char truncation
[] = "[...]";
653 if (max
> 0 && size
> max
)
654 len
= max
* 3 + sizeof(truncation
) + 1;
658 if ((buffer
= _lldpctl_alloc_in_atom(atom
, len
)) == NULL
)
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
);
666 *(buffer
+ i
*3 - 1) = 0;