]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lib/connection.c
Add call to process more messages from data already read.
[thirdparty/lldpd.git] / src / lib / connection.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 <unistd.h>
19#include <errno.h>
4e5f34c5 20#include <string.h>
4b292b55
VB
21#include <sys/socket.h>
22#include <sys/un.h>
23
24#include "lldpctl.h"
25#include "private.h"
26#include "../compat/compat.h"
27#include "../ctl.h"
4e90a9e0
VB
28#include "../log.h"
29#include "../lldpd-structs.h"
4b292b55
VB
30
31const char*
32lldpctl_get_default_transport(void)
33{
34 return LLDPD_CTL_SOCKET;
35}
36
37/* Connect to the remote end */
38static int
0262adbb 39sync_connect(lldpctl_conn_t *lldpctl)
4b292b55 40{
0262adbb 41 return ctl_connect(lldpctl->ctlname);
4b292b55
VB
42}
43
44/* Synchronously send data to remote end. */
45static ssize_t
46sync_send(lldpctl_conn_t *lldpctl,
47 const uint8_t *data, size_t length, void *user_data)
48{
49 struct lldpctl_conn_sync_t *conn = user_data;
50 size_t nb;
51
52 if (conn->fd == -1 &&
0262adbb 53 ((conn->fd = sync_connect(lldpctl)) == -1)) {
4b292b55
VB
54 return LLDPCTL_ERR_CANNOT_CONNECT;
55 }
56
57 while ((nb = write(conn->fd, data, length)) == -1) {
58 if (errno == EAGAIN || errno == EINTR) continue;
59 return LLDPCTL_ERR_CALLBACK_FAILURE;
60 }
61 return nb;
62}
63
bdc954b7 64/* Statically receive data from remote end. */
4b292b55
VB
65static ssize_t
66sync_recv(lldpctl_conn_t *lldpctl,
67 const uint8_t *data, size_t length, void *user_data)
68{
69 struct lldpctl_conn_sync_t *conn = user_data;
70 size_t nb;
e581ec30 71 size_t remain, offset = 0;
4b292b55
VB
72
73 if (conn->fd == -1 &&
0262adbb 74 ((conn->fd = sync_connect(lldpctl)) == -1)) {
4b292b55
VB
75 lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
76 return LLDPCTL_ERR_CANNOT_CONNECT;
77 }
78
e581ec30
VB
79 remain = length;
80 do {
81 if ((nb = read(conn->fd, (void*)data + offset, remain)) == -1 &&
82 (errno == EAGAIN || errno == EINTR))
83 continue;
84 remain -= nb;
85 offset += nb;
86 } while (remain > 0 && nb > 0);
87 return offset;
4b292b55
VB
88}
89
4b292b55
VB
90lldpctl_conn_t*
91lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
0262adbb
ZM
92{
93 return lldpctl_new_name(lldpctl_get_default_transport(), send, recv, user_data);
94}
95
96lldpctl_conn_t*
97lldpctl_new_name(const char *ctlname, lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
4b292b55
VB
98{
99 lldpctl_conn_t *conn = NULL;
100 struct lldpctl_conn_sync_t *data = NULL;
101
102 /* Both callbacks are mandatory or should be NULL. */
103 if (send && !recv) return NULL;
104 if (recv && !send) return NULL;
105
106 if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL)
107 return NULL;
108
0262adbb
ZM
109 conn->ctlname = strdup(ctlname);
110 if (conn->ctlname == NULL) {
111 free(conn);
112 return NULL;
113 }
4b292b55
VB
114 if (!send && !recv) {
115 if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
116 free(conn);
117 return NULL;
118 }
119 data->fd = -1;
120 conn->send = sync_send;
121 conn->recv = sync_recv;
122 conn->user_data = data;
123 } else {
124 conn->send = send;
125 conn->recv = recv;
126 conn->user_data = user_data;
127 }
128
129 return conn;
130}
131
132int
133lldpctl_release(lldpctl_conn_t *conn)
134{
135 if (conn == NULL) return 0;
0262adbb 136 free(conn->ctlname);
4b292b55
VB
137 if (conn->send == sync_send) {
138 struct lldpctl_conn_sync_t *data = conn->user_data;
139 if (data->fd != -1) close(data->fd);
140 free(conn->user_data);
141 }
142 free(conn->input_buffer);
143 free(conn->output_buffer);
144 free(conn);
145 return 0;
146}
147
148/**
149 * Request some bytes if they are not already here.
150 *
151 * @param conn The connection to lldpd.
152 * @param length The number of requested bytes.
153 * @return A negative integer if we can't have the bytes or the number of bytes we got.
154 */
155ssize_t
156_lldpctl_needs(lldpctl_conn_t *conn, size_t length)
157{
158 uint8_t *buffer = NULL;
159 ssize_t rc;
4b292b55
VB
160
161 if ((buffer = malloc(length)) == NULL)
162 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
163 rc = conn->recv(conn, buffer, length, conn->user_data);
164 if (rc < 0) {
165 free(buffer);
166 return SET_ERROR(conn, rc);
167 }
168 if (rc == 0) {
169 free(buffer);
170 return SET_ERROR(conn, LLDPCTL_ERR_EOF);
171 }
172 rc = lldpctl_recv(conn, buffer, rc);
173 free(buffer);
174 if (rc < 0)
175 return SET_ERROR(conn, rc);
176 RESET_ERROR(conn);
177 return rc;
178}
179
1fa64c11 180static int
4e90a9e0
VB
181check_for_notification(lldpctl_conn_t *conn)
182{
183 struct lldpd_neighbor_change *change;
0e940d8d 184 void *p;
4e90a9e0
VB
185 int rc;
186 lldpctl_change_t type;
187 lldpctl_atom_t *interface = NULL, *neighbor = NULL;
188 rc = ctl_msg_recv_unserialized(&conn->input_buffer,
189 &conn->input_buffer_len,
190 NOTIFICATION,
0e940d8d 191 &p,
4e90a9e0 192 &MARSHAL_INFO(lldpd_neighbor_change));
1fa64c11 193 if (rc != 0) return rc;
0e940d8d 194 change = p;
4e90a9e0
VB
195
196 /* We have a notification, call the callback */
197 if (conn->watch_cb) {
198 switch (change->state) {
199 case NEIGHBOR_CHANGE_DELETED: type = lldpctl_c_deleted; break;
200 case NEIGHBOR_CHANGE_ADDED: type = lldpctl_c_added; break;
201 case NEIGHBOR_CHANGE_UPDATED: type = lldpctl_c_updated; break;
202 default:
6f8925be 203 log_warnx("control", "unknown notification type (%d)",
4e90a9e0
VB
204 change->state);
205 goto end;
206 }
207 interface = _lldpctl_new_atom(conn, atom_interface,
208 change->ifname);
209 if (interface == NULL) goto end;
210 neighbor = _lldpctl_new_atom(conn, atom_port,
211 NULL, change->neighbor, NULL);
212 if (neighbor == NULL) goto end;
213 conn->watch_cb(conn, type, interface, neighbor, conn->watch_data);
214 conn->watch_triggered = 1;
215 goto end;
216 }
217
218end:
219 if (interface) lldpctl_atom_dec_ref(interface);
220 if (neighbor) lldpctl_atom_dec_ref(neighbor);
221 else {
222 lldpd_chassis_cleanup(change->neighbor->p_chassis, 1);
223 lldpd_port_cleanup(change->neighbor, 1);
224 free(change->neighbor);
225 }
226 free(change->ifname);
227 free(change);
1fa64c11
ST
228
229 /* Indicate if more data remains in the buffer for processing */
230 return (rc);
4e90a9e0
VB
231}
232
4b292b55
VB
233ssize_t
234lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
235{
236
237 RESET_ERROR(conn);
238
239 if (length == 0) return 0;
240
241 /* Received data should be appended to the input buffer. */
242 if (conn->input_buffer == NULL) {
243 conn->input_buffer_len = 0;
244 if ((conn->input_buffer = malloc(length)) == NULL)
245 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
246 } else {
247 uint8_t *new = realloc(conn->input_buffer, conn->input_buffer_len + length);
248 if (new == NULL)
249 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
250 conn->input_buffer = new;
251 }
252 memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
253 conn->input_buffer_len += length;
4e90a9e0
VB
254
255 /* Is it a notification? */
256 check_for_notification(conn);
257
4b292b55 258 RESET_ERROR(conn);
4e90a9e0
VB
259
260 return conn->input_buffer_len;
4b292b55
VB
261}
262
1fa64c11
ST
263int lldpctl_process_conn_buffer(lldpctl_conn_t *conn)
264{
265 int rc;
266
267 rc = check_for_notification(conn);
268
269 RESET_ERROR(conn);
270
271 return rc;
272}
273
4b292b55
VB
274ssize_t
275lldpctl_send(lldpctl_conn_t *conn)
276{
277 /* Send waiting data. */
278 ssize_t rc;
279
280 RESET_ERROR(conn);
281
282 if (!conn->output_buffer) return 0;
283 rc = conn->send(conn,
284 conn->output_buffer, conn->output_buffer_len,
285 conn->user_data);
286 if (rc < 0) return SET_ERROR(conn, rc);
287
288 /* "Shrink" the output buffer. */
289 if (rc == conn->output_buffer_len) {
290 free(conn->output_buffer);
291 conn->output_buffer = NULL;
292 conn->output_buffer_len = 0;
293 RESET_ERROR(conn);
294 return rc;
295 }
296 conn->output_buffer_len -= rc;
297 memmove(conn->output_buffer, conn->output_buffer + rc, conn->output_buffer_len);
298 /* We don't shrink the buffer. It will be either freed or shrinked later */
299 RESET_ERROR(conn);
300 return rc;
301}