]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lib/connection.c
729ee092b339fa6dcf48728b1fa05b17369e6422
[thirdparty/lldpd.git] / src / lib / connection.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 <unistd.h>
19 #include <errno.h>
20 #include <string.h>
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"
28 #include "../log.h"
29 #include "../lldpd-structs.h"
30
31 const char*
32 lldpctl_get_default_transport(void)
33 {
34 return LLDPD_CTL_SOCKET;
35 }
36
37 /* Connect to the remote end */
38 static int
39 sync_connect()
40 {
41 return ctl_connect(LLDPD_CTL_SOCKET);
42 }
43
44 /* Synchronously send data to remote end. */
45 static ssize_t
46 sync_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 &&
53 ((conn->fd = sync_connect()) == -1)) {
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
64 /* Statiscally receive data from remote end. */
65 static ssize_t
66 sync_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;
71
72 if (conn->fd == -1 &&
73 ((conn->fd = sync_connect()) == -1)) {
74 lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
75 return LLDPCTL_ERR_CANNOT_CONNECT;
76 }
77
78 while ((nb = read(conn->fd, (void*)data, length)) == -1) {
79 if (errno == EAGAIN || errno == EINTR) continue;
80 return LLDPCTL_ERR_CALLBACK_FAILURE;
81 }
82 return nb;
83 }
84
85
86
87 lldpctl_conn_t*
88 lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
89 {
90 lldpctl_conn_t *conn = NULL;
91 struct lldpctl_conn_sync_t *data = NULL;
92
93 /* Both callbacks are mandatory or should be NULL. */
94 if (send && !recv) return NULL;
95 if (recv && !send) return NULL;
96
97 if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL)
98 return NULL;
99
100 if (!send && !recv) {
101 if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
102 free(conn);
103 return NULL;
104 }
105 data->fd = -1;
106 conn->send = sync_send;
107 conn->recv = sync_recv;
108 conn->user_data = data;
109 } else {
110 conn->send = send;
111 conn->recv = recv;
112 conn->user_data = user_data;
113 }
114
115 return conn;
116 }
117
118 int
119 lldpctl_release(lldpctl_conn_t *conn)
120 {
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);
126 }
127 free(conn->input_buffer);
128 free(conn->output_buffer);
129 free(conn);
130 return 0;
131 }
132
133 /**
134 * Request some bytes if they are not already here.
135 *
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.
139 */
140 ssize_t
141 _lldpctl_needs(lldpctl_conn_t *conn, size_t length)
142 {
143 uint8_t *buffer = NULL;
144 ssize_t rc;
145 if (conn->input_buffer_len >= length) return 0;
146
147 if ((buffer = malloc(length)) == NULL)
148 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
149 rc = conn->recv(conn, buffer, length, conn->user_data);
150 if (rc < 0) {
151 free(buffer);
152 return SET_ERROR(conn, rc);
153 }
154 if (rc == 0) {
155 free(buffer);
156 return SET_ERROR(conn, LLDPCTL_ERR_EOF);
157 }
158 rc = lldpctl_recv(conn, buffer, rc);
159 free(buffer);
160 if (rc < 0)
161 return SET_ERROR(conn, rc);
162 RESET_ERROR(conn);
163 return rc;
164 }
165
166 static void
167 check_for_notification(lldpctl_conn_t *conn)
168 {
169 struct lldpd_neighbor_change *change;
170 void *p;
171 int rc;
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,
176 NOTIFICATION,
177 &p,
178 &MARSHAL_INFO(lldpd_neighbor_change));
179 if (rc != 0) return;
180 change = p;
181
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;
188 default:
189 log_warnx("control", "unknown notification type (%d)",
190 change->state);
191 goto end;
192 }
193 interface = _lldpctl_new_atom(conn, atom_interface,
194 change->ifname);
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;
201 goto end;
202 }
203
204 end:
205 if (interface) lldpctl_atom_dec_ref(interface);
206 if (neighbor) lldpctl_atom_dec_ref(neighbor);
207 else {
208 lldpd_chassis_cleanup(change->neighbor->p_chassis, 1);
209 lldpd_port_cleanup(change->neighbor, 1);
210 free(change->neighbor);
211 }
212 free(change->ifname);
213 free(change);
214 }
215
216 ssize_t
217 lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
218 {
219
220 RESET_ERROR(conn);
221
222 if (length == 0) return 0;
223
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);
229 } else {
230 uint8_t *new = realloc(conn->input_buffer, conn->input_buffer_len + length);
231 if (new == NULL)
232 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
233 conn->input_buffer = new;
234 }
235 memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
236 conn->input_buffer_len += length;
237
238 /* Is it a notification? */
239 check_for_notification(conn);
240
241 RESET_ERROR(conn);
242
243 return conn->input_buffer_len;
244 }
245
246 ssize_t
247 lldpctl_send(lldpctl_conn_t *conn)
248 {
249 /* Send waiting data. */
250 ssize_t rc;
251
252 RESET_ERROR(conn);
253
254 if (!conn->output_buffer) return 0;
255 rc = conn->send(conn,
256 conn->output_buffer, conn->output_buffer_len,
257 conn->user_data);
258 if (rc < 0) return SET_ERROR(conn, rc);
259
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;
265 RESET_ERROR(conn);
266 return rc;
267 }
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 */
271 RESET_ERROR(conn);
272 return rc;
273 }