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.
21 #include <sys/socket.h>
26 #include "../compat/compat.h"
29 #include "../lldpd-structs.h"
32 lldpctl_get_default_transport(void)
34 return LLDPD_CTL_SOCKET
;
37 /* Connect to the remote end */
41 return ctl_connect(LLDPD_CTL_SOCKET
);
44 /* Synchronously send data to remote end. */
46 sync_send(lldpctl_conn_t
*lldpctl
,
47 const uint8_t *data
, size_t length
, void *user_data
)
49 struct lldpctl_conn_sync_t
*conn
= user_data
;
53 ((conn
->fd
= sync_connect()) == -1)) {
54 return LLDPCTL_ERR_CANNOT_CONNECT
;
57 while ((nb
= write(conn
->fd
, data
, length
)) == -1) {
58 if (errno
== EAGAIN
|| errno
== EINTR
) continue;
59 return LLDPCTL_ERR_CALLBACK_FAILURE
;
64 /* Statiscally receive data from remote end. */
66 sync_recv(lldpctl_conn_t
*lldpctl
,
67 const uint8_t *data
, size_t length
, void *user_data
)
69 struct lldpctl_conn_sync_t
*conn
= user_data
;
73 ((conn
->fd
= sync_connect()) == -1)) {
74 lldpctl
->error
= LLDPCTL_ERR_CANNOT_CONNECT
;
75 return LLDPCTL_ERR_CANNOT_CONNECT
;
78 while ((nb
= read(conn
->fd
, (void*)data
, length
)) == -1) {
79 if (errno
== EAGAIN
|| errno
== EINTR
) continue;
80 return LLDPCTL_ERR_CALLBACK_FAILURE
;
88 lldpctl_new(lldpctl_send_callback send
, lldpctl_recv_callback recv
, void *user_data
)
90 lldpctl_conn_t
*conn
= NULL
;
91 struct lldpctl_conn_sync_t
*data
= NULL
;
93 /* Both callbacks are mandatory or should be NULL. */
94 if (send
&& !recv
) return NULL
;
95 if (recv
&& !send
) return NULL
;
97 if ((conn
= calloc(1, sizeof(lldpctl_conn_t
))) == NULL
)
100 if (!send
&& !recv
) {
101 if ((data
= malloc(sizeof(struct lldpctl_conn_sync_t
))) == NULL
) {
106 conn
->send
= sync_send
;
107 conn
->recv
= sync_recv
;
108 conn
->user_data
= data
;
112 conn
->user_data
= user_data
;
119 lldpctl_release(lldpctl_conn_t
*conn
)
121 if (conn
== NULL
) return 0;
122 if (conn
->send
== sync_send
) {
123 struct lldpctl_conn_sync_t
*data
= conn
->user_data
;
124 if (data
->fd
!= -1) close(data
->fd
);
125 free(conn
->user_data
);
127 free(conn
->input_buffer
);
128 free(conn
->output_buffer
);
134 * Request some bytes if they are not already here.
136 * @param conn The connection to lldpd.
137 * @param length The number of requested bytes.
138 * @return A negative integer if we can't have the bytes or the number of bytes we got.
141 _lldpctl_needs(lldpctl_conn_t
*conn
, size_t length
)
143 uint8_t *buffer
= NULL
;
145 if (conn
->input_buffer_len
>= length
) return 0;
147 if ((buffer
= malloc(length
)) == NULL
)
148 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
149 rc
= conn
->recv(conn
, buffer
, length
, conn
->user_data
);
152 return SET_ERROR(conn
, rc
);
156 return SET_ERROR(conn
, LLDPCTL_ERR_EOF
);
158 rc
= lldpctl_recv(conn
, buffer
, rc
);
161 return SET_ERROR(conn
, rc
);
167 check_for_notification(lldpctl_conn_t
*conn
)
169 struct lldpd_neighbor_change
*change
;
172 lldpctl_change_t type
;
173 lldpctl_atom_t
*interface
= NULL
, *neighbor
= NULL
;
174 rc
= ctl_msg_recv_unserialized(&conn
->input_buffer
,
175 &conn
->input_buffer_len
,
178 &MARSHAL_INFO(lldpd_neighbor_change
));
182 /* We have a notification, call the callback */
183 if (conn
->watch_cb
) {
184 switch (change
->state
) {
185 case NEIGHBOR_CHANGE_DELETED
: type
= lldpctl_c_deleted
; break;
186 case NEIGHBOR_CHANGE_ADDED
: type
= lldpctl_c_added
; break;
187 case NEIGHBOR_CHANGE_UPDATED
: type
= lldpctl_c_updated
; break;
189 log_warnx("control", "unknown notification type (%d)",
193 interface
= _lldpctl_new_atom(conn
, atom_interface
,
195 if (interface
== NULL
) goto end
;
196 neighbor
= _lldpctl_new_atom(conn
, atom_port
,
197 NULL
, change
->neighbor
, NULL
);
198 if (neighbor
== NULL
) goto end
;
199 conn
->watch_cb(conn
, type
, interface
, neighbor
, conn
->watch_data
);
200 conn
->watch_triggered
= 1;
205 if (interface
) lldpctl_atom_dec_ref(interface
);
206 if (neighbor
) lldpctl_atom_dec_ref(neighbor
);
208 lldpd_chassis_cleanup(change
->neighbor
->p_chassis
, 1);
209 lldpd_port_cleanup(change
->neighbor
, 1);
210 free(change
->neighbor
);
212 free(change
->ifname
);
217 lldpctl_recv(lldpctl_conn_t
*conn
, const uint8_t *data
, size_t length
)
222 if (length
== 0) return 0;
224 /* Received data should be appended to the input buffer. */
225 if (conn
->input_buffer
== NULL
) {
226 conn
->input_buffer_len
= 0;
227 if ((conn
->input_buffer
= malloc(length
)) == NULL
)
228 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
230 uint8_t *new = realloc(conn
->input_buffer
, conn
->input_buffer_len
+ length
);
232 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
233 conn
->input_buffer
= new;
235 memcpy(conn
->input_buffer
+ conn
->input_buffer_len
, data
, length
);
236 conn
->input_buffer_len
+= length
;
238 /* Is it a notification? */
239 check_for_notification(conn
);
243 return conn
->input_buffer_len
;
247 lldpctl_send(lldpctl_conn_t
*conn
)
249 /* Send waiting data. */
254 if (!conn
->output_buffer
) return 0;
255 rc
= conn
->send(conn
,
256 conn
->output_buffer
, conn
->output_buffer_len
,
258 if (rc
< 0) return SET_ERROR(conn
, rc
);
260 /* "Shrink" the output buffer. */
261 if (rc
== conn
->output_buffer_len
) {
262 free(conn
->output_buffer
);
263 conn
->output_buffer
= NULL
;
264 conn
->output_buffer_len
= 0;
268 conn
->output_buffer_len
-= rc
;
269 memmove(conn
->output_buffer
, conn
->output_buffer
+ rc
, conn
->output_buffer_len
);
270 /* We don't shrink the buffer. It will be either freed or shrinked later */