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"
31 lldpctl_get_default_transport(void)
33 return LLDPD_CTL_SOCKET
;
36 /* Connect to the remote end */
38 sync_connect(lldpctl_conn_t
*lldpctl
)
40 return ctl_connect(lldpctl
->ctlname
);
43 /* Synchronously send data to remote end. */
45 sync_send(lldpctl_conn_t
*lldpctl
,
46 const uint8_t *data
, size_t length
, void *user_data
)
48 struct lldpctl_conn_sync_t
*conn
= user_data
;
52 ((conn
->fd
= sync_connect(lldpctl
)) == -1)) {
53 return LLDPCTL_ERR_CANNOT_CONNECT
;
56 while ((nb
= write(conn
->fd
, data
, length
)) == -1) {
57 if (errno
== EAGAIN
|| errno
== EINTR
) continue;
58 return LLDPCTL_ERR_CALLBACK_FAILURE
;
63 /* Statically receive data from remote end. */
65 sync_recv(lldpctl_conn_t
*lldpctl
,
66 const uint8_t *data
, size_t length
, void *user_data
)
68 struct lldpctl_conn_sync_t
*conn
= user_data
;
70 size_t remain
, offset
= 0;
73 ((conn
->fd
= sync_connect(lldpctl
)) == -1)) {
74 lldpctl
->error
= LLDPCTL_ERR_CANNOT_CONNECT
;
75 return LLDPCTL_ERR_CANNOT_CONNECT
;
80 if ((nb
= read(conn
->fd
, (unsigned char*)data
+ offset
, remain
)) == -1) {
81 if (errno
== EAGAIN
|| errno
== EINTR
)
83 return LLDPCTL_ERR_CALLBACK_FAILURE
;
87 } while (remain
> 0 && nb
!= 0);
92 lldpctl_new(lldpctl_send_callback send
, lldpctl_recv_callback recv
, void *user_data
)
94 return lldpctl_new_name(lldpctl_get_default_transport(), send
, recv
, user_data
);
98 lldpctl_new_name(const char *ctlname
, lldpctl_send_callback send
, lldpctl_recv_callback recv
, void *user_data
)
100 lldpctl_conn_t
*conn
= NULL
;
101 struct lldpctl_conn_sync_t
*data
= NULL
;
103 /* Both callbacks are mandatory or should be NULL. */
104 if (send
&& !recv
) return NULL
;
105 if (recv
&& !send
) return NULL
;
107 if ((conn
= calloc(1, sizeof(lldpctl_conn_t
))) == NULL
)
110 conn
->ctlname
= strdup(ctlname
);
111 if (conn
->ctlname
== NULL
) {
115 if (!send
&& !recv
) {
116 if ((data
= malloc(sizeof(struct lldpctl_conn_sync_t
))) == NULL
) {
122 conn
->send
= sync_send
;
123 conn
->recv
= sync_recv
;
124 conn
->user_data
= data
;
128 conn
->user_data
= user_data
;
135 lldpctl_release(lldpctl_conn_t
*conn
)
137 if (conn
== NULL
) return 0;
139 if (conn
->send
== sync_send
) {
140 struct lldpctl_conn_sync_t
*data
= conn
->user_data
;
141 if (data
->fd
!= -1) close(data
->fd
);
142 free(conn
->user_data
);
144 free(conn
->input_buffer
);
145 free(conn
->output_buffer
);
151 * Request some bytes if they are not already here.
153 * @param conn The connection to lldpd.
154 * @param length The number of requested bytes.
155 * @return A negative integer if we can't have the bytes or the number of bytes we got.
158 _lldpctl_needs(lldpctl_conn_t
*conn
, size_t length
)
160 uint8_t *buffer
= NULL
;
163 if ((buffer
= malloc(length
)) == NULL
)
164 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
165 rc
= conn
->recv(conn
, buffer
, length
, conn
->user_data
);
168 return SET_ERROR(conn
, rc
);
172 return SET_ERROR(conn
, LLDPCTL_ERR_EOF
);
174 rc
= lldpctl_recv(conn
, buffer
, rc
);
177 return SET_ERROR(conn
, rc
);
183 check_for_notification(lldpctl_conn_t
*conn
)
185 struct lldpd_neighbor_change
*change
;
188 lldpctl_change_t type
;
189 lldpctl_atom_t
*interface
= NULL
, *neighbor
= NULL
;
190 rc
= ctl_msg_recv_unserialized(&conn
->input_buffer
,
191 &conn
->input_buffer_len
,
194 &MARSHAL_INFO(lldpd_neighbor_change
));
195 if (rc
!= 0) return rc
;
198 /* We have a notification, call the callback */
199 if (conn
->watch_cb
) {
200 switch (change
->state
) {
201 case NEIGHBOR_CHANGE_DELETED
: type
= lldpctl_c_deleted
; break;
202 case NEIGHBOR_CHANGE_ADDED
: type
= lldpctl_c_added
; break;
203 case NEIGHBOR_CHANGE_UPDATED
: type
= lldpctl_c_updated
; break;
205 log_warnx("control", "unknown notification type (%d)",
209 interface
= _lldpctl_new_atom(conn
, atom_interface
,
211 if (interface
== NULL
) goto end
;
212 neighbor
= _lldpctl_new_atom(conn
, atom_port
, 0,
213 NULL
, change
->neighbor
, NULL
);
214 if (neighbor
== NULL
) goto end
;
215 conn
->watch_cb(conn
, type
, interface
, neighbor
, conn
->watch_data
);
216 conn
->watch_triggered
= 1;
221 if (interface
) lldpctl_atom_dec_ref(interface
);
222 if (neighbor
) lldpctl_atom_dec_ref(neighbor
);
224 lldpd_chassis_cleanup(change
->neighbor
->p_chassis
, 1);
225 lldpd_port_cleanup(change
->neighbor
, 1);
226 free(change
->neighbor
);
228 free(change
->ifname
);
231 /* Indicate if more data remains in the buffer for processing */
236 lldpctl_recv(lldpctl_conn_t
*conn
, const uint8_t *data
, size_t length
)
241 if (length
== 0) return 0;
243 /* Received data should be appended to the input buffer. */
244 if (conn
->input_buffer
== NULL
) {
245 conn
->input_buffer_len
= 0;
246 if ((conn
->input_buffer
= malloc(length
)) == NULL
)
247 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
249 uint8_t *new = realloc(conn
->input_buffer
, conn
->input_buffer_len
+ length
);
251 return SET_ERROR(conn
, LLDPCTL_ERR_NOMEM
);
252 conn
->input_buffer
= new;
254 memcpy(conn
->input_buffer
+ conn
->input_buffer_len
, data
, length
);
255 conn
->input_buffer_len
+= length
;
257 /* Read all notifications */
258 while(!check_for_notification(conn
));
262 return conn
->input_buffer_len
;
265 int lldpctl_process_conn_buffer(lldpctl_conn_t
*conn
)
269 rc
= check_for_notification(conn
);
277 lldpctl_send(lldpctl_conn_t
*conn
)
279 /* Send waiting data. */
284 if (!conn
->output_buffer
) return 0;
285 rc
= conn
->send(conn
,
286 conn
->output_buffer
, conn
->output_buffer_len
,
288 if (rc
< 0) return SET_ERROR(conn
, rc
);
290 /* "Shrink" the output buffer. */
291 if (rc
== conn
->output_buffer_len
) {
292 free(conn
->output_buffer
);
293 conn
->output_buffer
= NULL
;
294 conn
->output_buffer_len
= 0;
298 conn
->output_buffer_len
-= rc
;
299 memmove(conn
->output_buffer
, conn
->output_buffer
+ rc
, conn
->output_buffer_len
);
300 /* We don't shrink the buffer. It will be either freed or shrinked later */