1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32 /*
33 * utilities that help link users (i.e. clients that use device drivers)
34 *
35 * $Id: link_user.c,v 1.32 2007-12-08 04:05:04 girod Exp $
36 */
37
38 char link_user_cvsid[] = "$Id: link_user.c,v 1.32 2007-12-08 04:05:04 girod Exp $";
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <time.h>
46
47 #include "liblink_i.h"
48
49 int lu_check_status(lu_context_t *lu);
50
51 /*
52 * This is called every time a packet arrives from the underlying
53 * packetdev. We check to make sure it's a valid link packet, then
54 * pass it up.
55 */
56 static int lu_input_handler(void *pkt, ssize_t len, pd_client_context_t *pdc)
57 {
58 lu_context_t *lu = (lu_context_t *) pd_client_opts(pdc)->data;
59 link_pkt_t *link_pkt = (link_pkt_t *) pkt;
60
61 /* Make sure there's a receive callback! */
62 if (!lu->opts.receive) {
63 elog(LOG_ERR, "receiving packet with no callback; shouldn't happen!");
64 free(pkt);
65 return EVENT_DONE;
66 }
67
68 /* All drivers provide packets with this common header */
69 if (len < sizeof(link_pkt_t)) {
70 elog(LOG_WARNING, "short packet (%d bytes) from %s", (int)len,
71 lu_name(lu, NULL));
72 free(pkt);
73 return EVENT_RENEW;
74 }
75
76 /* Make sure we aren't passing a packet type that the user didn't ask for */
77 if (lu->opts.opts.pkt_type && link_pkt->type != lu->opts.opts.pkt_type) {
78 pd_client_filter_failure(pdc);
79 free(pkt);
80 return EVENT_RENEW;
81 }
82
83 /* Call the user's callback and return their retval */
84 return (lu->opts.receive)(lu, link_pkt, len - sizeof(link_pkt_t));
85 }
86
87
88
89 /*
90 * This is called when there's space in the link device's outgoing
91 * queue. If you've provided a "writable" callback when opening the
92 * link, use link_writable_cb_set_enable to activate and deactivate
93 * it. When active, your callback will be called every time there is
94 * space available in the queue (i.e. link_send will succeed without
95 * EAGAIN). Disable the writable callback if you have nothing to
96 * send, or you'll get a continuous stream of "writeable"
97 * notifications.
98 */
99 static int lu_writable_handler(pd_client_context_t *pdc)
100 {
101 lu_context_t *lu = (lu_context_t *) pd_client_opts(pdc)->data;
102
103 /* Make sure there's a writable callback! */
104 if (!lu->opts.writable) {
105 elog(LOG_ERR, "writeable, but no callback; shouldn't happen!");
106 return EVENT_DONE;
107 }
108
109 return (lu->opts.writable)(lu);
110 }
111
112
113 static int lu_status_handler(void *new_buffer, size_t size, void *data)
114 {
115 lu_context_t *lu = (lu_context_t *) data;
116 link_status_t *stat = (link_status_t *)new_buffer;
117
118 /* check size */
119 if (size != sizeof(link_status_t)) {
120 elog(LOG_WARNING, "Got link status, wrong size: %d/%d",
121 (int) size, (int) sizeof(link_status_t));
122 goto out;
123 }
124
125 /* valid now */
126 lu->status_valid = 1;
127
128 /* copy status */
129 memmove(&(lu->last_status), stat, sizeof(link_status_t));
130
131 /* notify */
132 if (lu->opts.status_notify) lu->opts.status_notify(lu);
133
134 out:
135 if (new_buffer) free(new_buffer);
136 return EVENT_RENEW;
137 }
138
139
140 static
141 int lu_configure_delay(void *data, int interval, g_event_t *ev)
142 {
143 lu_context_t *lu = (lu_context_t *) data;
144 if (lu->pdc == NULL) {
145 elog(LOG_WARNING, "delayed configure callback failed???..");
146 }
147 else
148 lu->opts.configure(lu);
149 return TIMER_DONE;
150 }
151
152
153 /* enable the link user to reconfigure the link on (re)open */
154 int lu_configure_handler(int fd, pd_client_context_t *pdc)
155 {
156 /* note: this can be called before the lu is finished initializing.
157 * lu is valid but lu->pdc may not be */
158
159 lu_context_t *lu = (lu_context_t *) pd_client_opts(pdc)->data;
160
161 /* set promisc from opts */
162 if (ioctl(fd, LINK_SET_PROMISC, &lu->opts.promisc) < 0) {
163 elog(LOG_WARNING, "Error setting promiscuous mode: %m");
164 }
165
166 /* call optional configure handler */
167 if (lu && lu->opts.configure) {
168 /* configure is sometimes called before initialization has completed.. */
169 if (lu->pdc == NULL) {
170 elog(LOG_DEBUG(0), "delaying configure callback..");
171 g_timer_add(0, lu_configure_delay, lu, NULL, &(lu->configure_delay));
172 }
173 else
174 lu->opts.configure(lu);
175 }
176
177 return EVENT_RENEW;
178 }
179
180
181 /*
182 * link_open: API function called by users when they want to start
183 * using the link. link_opts is a struct containing options
184 * describing the desired link; see definition of link_opts for
185 * details.
186 *
187 * If the open is successful, link_open returns 0, and writes the link
188 * context to "ref" (a pointer provided by the user). If it fails,
189 * link_open returns -1 and sets errno appropriately.
190 *
191 * If a "receive" callback is set in the link_opts structure, it will
192 * be called when packets arrive.
193 */
194 gint lu_open(lu_opts_t *opts, lu_context_t **ref)
195 {
196 pd_filter_t *filter;
197 int retval;
198
199 /* link device state */
200 lu_context_t *lu = g_new0(lu_context_t, 1);
201
202 /* make sure we allocated this sucker */
203 if (lu == NULL) {
204 retval = -ENOMEM;
205 goto done;
206 }
207
208 /* make sure the options are valid */
209 if (opts == NULL) {
210 elog(LOG_ERR, "invalid link options (null)");
211 retval = -EINVAL;
212 goto done;
213 }
214
215 /* copy the options to be used */
216 lu->opts = *opts;
217
218 /* copy the name */
219 lu->opts.opts.name = g_strdup(opts->opts.name);
220
221 {
222 /* configure the packet client options */
223 pd_client_opts_t pd_opts = {
224 data: lu,
225 q_opts: lu->opts.opts.q_opts,
226 devname: lu_name(lu, LINK_DATA_SUBDEV),
227 configure: lu_configure_handler,
228 blocking_writes: lu->opts.blocking_writes
229 };
230
231 if (lu->opts.receive)
232 pd_opts.receive = lu_input_handler;
233
234 if (lu->opts.writable)
235 pd_opts.writable = lu_writable_handler;
236
237 /* convert the user's packettype into a pd filter */
238 filter = &(pd_opts.pd_filter);
239 memset(filter, 0, sizeof(pd_filter_t));
240 if (lu->opts.opts.pkt_type != PKT_TYPE_ALL) {
241 filter->len = sizeof(lu->opts.opts.pkt_type);
242 memcpy(&(filter->data), &(lu->opts.opts.pkt_type),
243 sizeof(lu->opts.opts.pkt_type));
244 }
245
246 /* try to open the selected file */
247 if (pd_client_open(&pd_opts, &lu->pdc) < 0) {
248 retval = -1;
249 goto done;
250 }
251 }
252
253 /* open the status device */
254 {
255 status_client_opts_t opts = {
256 private_data: lu,
257 devname: lu_name(lu, LINK_STATUS_SUBDEV),
258 handler: lu_status_handler
259 };
260
261 if (g_status_client_full(&opts, &(lu->scc)) < 0) {
262 elog(LOG_WARNING, "Can't open link status device: %m");
263 retval = -1;
264 goto done;
265 }
266 }
267
268 /* success! */
269 retval = 0;
270
271 done:
272
273 /* if we've had an error, close and dealloc */
274 if (retval < 0) {
275 lu_destroy(lu);
276 lu = NULL;
277 }
278
279 /* write the link context (or, a NULL link context pointer) to the
280 * user's provided reference, if any */
281 if (ref) {
282 if (*ref) {
283 elog(LOG_WARNING, "Destroying link user before reusing reference");
284 lu_destroy(*ref);
285 }
286 *ref = lu;
287 }
288
289 /* and return either success or failure with errno properly set */
290 if (retval < 0) {
291 errno = -retval;
292 retval = -1;
293 } else {
294 errno = 0;
295 retval = 0;
296 }
297
298 /* force read now, after the ref is populated */
299 if (retval == 0)
300 lu_check_status(lu);
301
302 return retval;
303 }
304
305
306 void lu_destroy(lu_context_t *lu)
307 {
308 if (lu) {
309 /* clear reference */
310 if (lu->ref)
311 *(lu->ref) = NULL;
312
313 /* destroy packet and status clients */
314 pd_client_destroy(lu->pdc);
315 g_status_client_destroy(lu->scc);
316
317 /* kill scheduling timer */
318 g_event_destroy(lu->configure_delay);
319
320 /* free memory */
321 g_free(lu->opts.opts.name);
322 g_free(lu);
323 }
324 }
325
326
327 int lu_poll(lu_context_t *lu)
328 {
329 if (lu) return pd_client_poll(lu->pdc);
330 return -1;
331 }
332
333
334 /* Returns -1 on failure, and 0 on success */
335 gint lu_send_simple(lu_context_t *lu, if_id_t dst, if_id_t src, int pkt_type,
336 void* pkt, ssize_t pkt_len)
337 {
338 link_pkt_t lhdr = {
339 dst: {
340 id: dst,
341 },
342 src: {
343 id: src,
344 },
345 type: pkt_type,
346 };
347
348 buf_t* buf = buf_new();
349 bufcpy(buf, &lhdr, sizeof(link_pkt_t));
350 if (pkt_len > 0) bufcpy(buf, pkt, pkt_len);
351 elog(LOG_DEBUG(1), "Sending pkt of size: %d\n",
352 (int)(buf->len - sizeof(link_pkt_t)));
353 if (lu_send(lu, (link_pkt_t *)buf->buf,
354 buf->len - sizeof(link_pkt_t)) < 0) {
355 elog(LOG_ERR, "ERROR Unable to send packet on link %m!\n");
356 return -1;
357 }
358 buf_free(buf);
359 return 0;
360 }
361
362 int lu_set_blocking_mode(lu_context_t *lu, int blocking)
363 {
364 if (lu) return pd_client_set_blocking_mode(lu->pdc, blocking);
365 return -1;
366 }
367
368 /*
369 * send a packet on the link. data_len is the size of the data, NOT
370 * including the link_pkt_t header.
371 */
372 gint lu_send(lu_context_t *lu, link_pkt_t *pkt, ssize_t data_len)
373 {
374 int status;
375
376 if (pkt->type == PKT_TYPE_ALL) {
377 elog(LOG_ERR, "invalid packet type %d (uninitialized pkt_type field?)",
378 pkt->type);
379 errno = EINVAL;
380 return -1;
381 }
382
383 if (lu==NULL) {
384 elog(LOG_ERR, "NULL link context pointer");
385 errno = EFAULT;
386 return -1;
387 }
388
389 status = pd_client_send(lu->pdc, pkt, data_len + sizeof(link_pkt_t));
390 if ((status < 0) && (errno == EMSGSIZE)) {
391 errno = EMSGSIZE;
392 }
393
394 return status;
395 }
396
397
398 /*
399 * enable or disable the delivery of callback notification
400 */
401 void lu_writable_cb_set_enable(lu_context_t *lu, int enable)
402 {
403 pd_client_writable_cb_set_enable(lu->pdc, enable);
404 }
405
406 void lu_readable_cb_set_enable(lu_context_t *lu, int enable)
407 {
408 pd_client_readable_cb_set_enable(lu->pdc, enable);
409 }
410
411 int lu_writable_cb_get_enable(lu_context_t *lu)
412 {
413 return pd_client_writable_cb_get_enable(lu->pdc);
414 }
415
416 int lu_readable_cb_get_enable(lu_context_t *lu)
417 {
418 return pd_client_readable_cb_get_enable(lu->pdc);
419 }
420
421
422 /*
423 * send an ioctl on a link
424 */
425 gint lu_ioctl(lu_context_t *lu, int cmd, void *arg)
426 {
427 return pd_client_ioctl(lu->pdc, cmd, arg);
428 }
429
430
431 int lu_set_promisc(lu_context_t *lu, int enable)
432 {
433 return lu_ioctl(lu, LINK_SET_PROMISC, &enable);
434 }
435
436
437 /*
438 * status accessors
439 */
440
441 int lu_check_status(lu_context_t *lu)
442 {
443 if (!lu->status_valid) {
444 if (g_status_client_force_read(lu->scc) >= 0) {
445 lu->status_valid = 1;
446 }
447 }
448 return lu->status_valid;
449 }
450
451 gint lu_get_mtu(lu_context_t *lu, uint16_t *mtu)
452 {
453 if (lu_check_status(lu)) {
454 *mtu = lu->last_status.MTU;
455 return 0;
456 }
457 return -1;
458 }
459
460
461 gint lu_get_if_id(lu_context_t *lu, if_id_t *if_id)
462 {
463 if (lu_check_status(lu)) {
464 *if_id = lu->last_status.if_id;
465 return 0;
466 }
467 return -1;
468 }
469
470
471 int lu_get_root_link_dev(lu_context_t *lu, char *device)
472 {
473 device[0] = 0;
474 if (lu) {
475 link_status_t status;
476 if (lu_status(lu, &status) == 0) {
477 char *first_comma = strchr(status.trace, ',');
478 if (first_comma) {
479 char *second_comma = strchr(first_comma+1, ',');
480 if (second_comma) {
481 strncpy(device, first_comma+1, second_comma - first_comma - 1);
482 device[second_comma - first_comma - 1] = 0;
483 }
484 else
485 strcpy(device, first_comma+1);
486 if (device[0])
487 return 0;
488 }
489 }
490 }
491 return -1;
492 }
493
494
495 gint lu_status(lu_context_t *lu, link_status_t *status_return)
496 {
497 if (lu_check_status(lu)) {
498 *status_return = lu->last_status;
499 return 0;
500 }
501 return -1;
502 }
503
504
505 gint lu_force_status(lu_context_t *lu, link_status_t *status_return)
506 {
507 /* $$$$ send forced refresh command $$$$ */
508 /* $$$$ wait for response? or just asynch? */
509 return lu_status(lu, status_return);
510 }
511
512
513 /* Accessor functions used to get info out of a link user context */
514 lu_opts_t *lu_opts(lu_context_t *lu)
515 {
516 return (lu) ? &lu->opts : NULL;
517 }
518
519 void *lu_data(lu_context_t *lu)
520 {
521 return (lu) ? lu->opts.opts.data : NULL;
522 }
523
524 /* get the name of the link */
525 char *lu_name(lu_context_t *lu, char *suffix)
526 {
527 return lu ? link_name(&(lu->opts.opts), suffix) : NULL;
528 }
529
530
531 int lu_printf_command(lu_context_t *lu, const char *fmt, ...)
532 {
533 va_list ap;
534 static char str[MAX_LINE];
535
536 va_start(ap, fmt);
537 vsnprintf(str, sizeof(str), fmt, ap);
538 va_end(ap);
539
540 return write_to_file(lu_name(lu, LINK_COMMAND_SUBDEV), str);
541 }
542
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.