2 * The olsr.org Optimized Link-State Routing daemon (olsrd)
4 * (c) by the OLSR project
6 * See our Git repository to find out who worked on this file
7 * and thus is a copyright holder on it.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in
19 * the documentation and/or other materials provided with the
21 * * Neither the name of olsr.org, olsrd nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
38 * Visit http://www.olsr.org for more information.
40 * If you find this software useful feel free to make a donation
41 * to the project. For more information see the website or contact
42 * the copyright holders.
46 #include <arpa/inet.h>
52 #include "olsrd_info.h"
54 #include "scheduler.h"
56 #include "http_headers.h"
59 #define close(x) closesocket(x)
65 * There is the problem that writing to a network socket can block,
66 * and the olsrd scheduler does not care about write events.
68 * There was a case that olsrd just froze for minutes when people used
69 * jsoninfo/txtinfo with large topologies over bad WiFi.
71 * This is the solution that was chosen at that time, unwilling to
72 * rewrite the whole scheduler:
73 * A timer was added and each time it expires each non-empty buffer
74 * in this structure will try to write data into a "non-blocking"
75 * socket until all data is sent, so that no blocking occurs.
78 int socket[MAX_CLIENTS];
79 char *buffer[MAX_CLIENTS];
80 size_t size[MAX_CLIENTS];
81 size_t written[MAX_CLIENTS];
83 } info_plugin_outbuffer_t;
85 static const char * name;
87 static info_plugin_functions_t *functions = NULL;
89 static info_plugin_config_t *config = NULL;
91 static int ipc_socket = -1;
93 static info_plugin_outbuffer_t outbuffer;
95 static struct timer_entry *writetimer_entry = NULL;
97 static struct info_cache_t info_cache;
99 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
101 static char * skipMultipleSlashes(char * requ, size_t* len) {
104 if ((r[0] == '\0') // zero length
105 || (r[0] != '/') // does not start with a slash
106 || (r[1] != '/')) // does not have another slash
111 while (r[1] == '/') {
120 static unsigned long long SIW_ENTRIES_ALL[] = {
134 SIW_NEIGHBORS_FREIFUNK, //
145 SIW_NETJSON_NETWORK_ROUTES, //
146 SIW_NETJSON_NETWORK_GRAPH, //
147 SIW_NETJSON_DEVICE_CONFIGURATION, //
148 SIW_NETJSON_DEVICE_MONITORING, //
149 SIW_NETJSON_NETWORK_COLLECTION //
152 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
153 long timeout = !plugin_config ? 0 : plugin_config->cache_timeout;
169 case SIW_PUD_POSITION:
171 case SIW_NETJSON_NETWORK_ROUTES:
172 case SIW_NETJSON_NETWORK_GRAPH:
173 case SIW_NETJSON_DEVICE_CONFIGURATION:
174 case SIW_NETJSON_DEVICE_MONITORING:
175 case SIW_NETJSON_NETWORK_COLLECTION:
189 static void info_plugin_cache_init(bool init) {
192 if (!functions->cache_timeout) {
196 for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
197 unsigned long long siw = SIW_ENTRIES_ALL[i];
198 struct info_cache_entry_t * entry = info_cache_get_entry(&info_cache, siw);
204 entry->timestamp = 0;
205 abuf_init(&entry->buf, 0);
207 abuf_free(&entry->buf);
208 entry->timestamp = 0;
213 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
214 if (!entry->buf.buf) {
215 entry->timestamp = 0;
216 abuf_init(&entry->buf, AUTOBUFCHUNK);
217 assert(!entry->timestamp);
218 assert(!entry->buf.len);
219 assert(entry->buf.size == AUTOBUFCHUNK);
220 assert(entry->buf.buf);
222 assert(entry->timestamp >= 0);
223 assert(entry->buf.len >= 0);
224 assert(entry->buf.size >= AUTOBUFCHUNK);
225 assert(entry->buf.buf);
229 static unsigned int determine_single_action(char *requ) {
231 unsigned long long siw_mask = !functions->supported_commands_mask ? SIW_EVERYTHING : functions->supported_commands_mask();
233 if (!functions->is_command || !siw_mask)
236 for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
237 unsigned long long siw = SIW_ENTRIES_ALL[i];
238 if ((siw & siw_mask) && functions->is_command(requ, siw))
245 static unsigned int determine_action(char *requ) {
246 if (!functions->is_command) {
250 if (!requ || (requ[0] == '\0')) {
255 /* requ is guaranteed to be at least 1 character long */
257 if (!functions->supportsCompositeCommands) {
258 /* no support for composite commands */
259 return determine_single_action(requ);
262 /* composite commands */
265 unsigned int action = 0;
267 char * requestSegment = requ;
268 while (requestSegment) {
269 requestSegment = skipMultipleSlashes(requestSegment, NULL);
270 if (requestSegment[0] == '\0') {
271 /* there is no more text */
272 requestSegment = NULL;
276 /* there is more text */
280 char * requestSegmentTail = strchr(&requestSegment[1], '/');
282 if (!requestSegmentTail) {
283 /* there isn't another slash, process everything that is left */
284 r = determine_single_action(requestSegment);
286 /* there is another slash, process everything before the slash */
287 char savedCharacter = *requestSegmentTail;
288 *requestSegmentTail = '\0';
289 r = determine_single_action(requestSegment);
290 *requestSegmentTail = savedCharacter;
300 /* process everything that is left in the next iteration */
301 requestSegment = requestSegmentTail;
309 static void send_status_no_retries(const char * req, int the_socket, unsigned int status) {
312 abuf_init(&abuf, AUTOBUFCHUNK);
314 if (config->http_headers) {
315 http_header_build_result(status, &abuf);
316 } else if (status != INFO_HTTP_OK) {
317 if (functions->output_error) {
318 functions->output_error(&abuf, status, req, config->http_headers);
319 } else if (status == INFO_HTTP_NOCONTENT) {
320 /* wget can't handle output of zero length */
321 abuf_puts(&abuf, "\n");
325 (void) send(the_socket, abuf.buf, abuf.len, 0);
330 static void write_data(void *unused __attribute__((unused))) {
335 if (outbuffer.count <= 0) {
336 /* exit early if there is nothing to send */
342 for (i = 0; i < MAX_CLIENTS; i++) {
343 if (outbuffer.socket[i] < 0) {
347 /* And we cast here since we get a warning on Win32 */
348 FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
350 if (outbuffer.socket[i] > max) {
351 max = outbuffer.socket[i];
358 result = select(max + 1, NULL, &set, NULL, &tv);
360 /* exit early if any of the sockets is not ready for writing */
364 for (i = 0; i < MAX_CLIENTS; i++) {
365 if (outbuffer.socket[i] < 0) {
369 result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
371 outbuffer.written[i] += result;
374 #if EWOULDBLOCK == EAGAIN
375 if ((result < 0) && (errno == EAGAIN)) {
377 if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
382 if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
383 /* close this socket and cleanup*/
384 close(outbuffer.socket[i]);
385 outbuffer.socket[i] = -1;
386 free(outbuffer.buffer[i]);
387 outbuffer.buffer[i] = NULL;
388 outbuffer.size[i] = 0;
389 outbuffer.written[i] = 0;
395 if (!outbuffer.count) {
396 olsr_stop_timer(writetimer_entry);
401 unsigned long long siw;
402 printer_generic func;
403 } SiwLookupTableEntry;
405 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
407 unsigned int preLength;
408 unsigned int what = send_what;
409 cache_timeout_func cache_timeout_f = functions->cache_timeout;
411 if (functions->output_start) {
412 functions->output_start(abuf);
415 preLength = abuf->len;
417 for (i = 0; (i < funcsSize) && what; i++) {
418 unsigned long long siw = funcs[i].siw;
420 printer_generic func = funcs[i].func;
422 long cache_timeout = 0;
423 struct info_cache_entry_t *cache_entry = NULL;
425 if (cache_timeout_f) {
426 cache_timeout = cache_timeout_f(config, siw);
427 cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
436 info_plugin_cache_init_entry(cache_entry);
439 age = llabs(now - cache_entry->timestamp);
440 if (!cache_entry->timestamp || (age >= cache_timeout)) {
441 /* cache is never used before or cache is too old */
442 cache_entry->buf.buf[0] = '\0';
443 cache_entry->buf.len = 0;
444 cache_entry->timestamp = now;
445 func(&cache_entry->buf);
448 abuf_concat(abuf, &cache_entry->buf);
455 *outputLength = abuf->len - preLength;
457 if (functions->output_end) {
458 functions->output_end(abuf);
462 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
464 unsigned int outputLength = 0;
465 unsigned int send_index = 0;
466 bool first_reply = false;
468 const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
469 int contentLengthIndex = 0;
470 int headerLength = 0;
472 assert(outbuffer.count <= MAX_CLIENTS);
474 abuf_init(&abuf, AUTOBUFCHUNK);
476 if (config->http_headers) {
477 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
478 headerLength = abuf.len;
481 if (status == INFO_HTTP_OK) {
484 // only add if normal format
485 if (send_what & SIW_ALL) {
486 SiwLookupTableEntry funcs[] = {
487 { SIW_NEIGHBORS , functions->neighbors }, //
488 { SIW_LINKS , functions->links }, //
489 { SIW_ROUTES , functions->routes }, //
490 { SIW_HNA , functions->hna }, //
491 { SIW_MID , functions->mid }, //
492 { SIW_TOPOLOGY , functions->topology }, //
493 { SIW_GATEWAYS , functions->gateways }, //
494 { SIW_INTERFACES , functions->interfaces }, //
495 { SIW_2HOP , functions->twohop }, //
496 { SIW_SGW , functions->sgw }, //
497 { SIW_PUD_POSITION, functions->pudPosition }, //
499 { SIW_VERSION , functions->version }, //
500 { SIW_CONFIG , functions->config }, //
501 { SIW_PLUGINS , functions->plugins } //
504 send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
505 } else if (send_what & SIW_NETJSON) {
506 SiwLookupTableEntry funcs[] = {
507 { SIW_NETJSON_NETWORK_ROUTES , functions->networkRoutes }, //
508 { SIW_NETJSON_NETWORK_GRAPH , functions->networkGraph }, //
509 { SIW_NETJSON_DEVICE_CONFIGURATION, functions->deviceConfiguration}, //
510 { SIW_NETJSON_DEVICE_MONITORING , functions->deviceMonitoring }, //
511 { SIW_NETJSON_NETWORK_COLLECTION , functions->networkCollection } //
514 send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
515 } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
516 /* this outputs the olsrd.conf text directly, not normal format */
517 unsigned int preLength = abuf.len;
518 functions->olsrd_conf(&abuf);
519 outputLength = abuf.len - preLength;
522 if (!abuf.len || !outputLength) {
523 status = INFO_HTTP_NOCONTENT;
526 if (config->http_headers) {
527 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
528 headerLength = abuf.len;
533 if (status != INFO_HTTP_OK) {
534 if (functions->output_error) {
535 functions->output_error(&abuf, status, req, config->http_headers);
536 } else if (status == INFO_HTTP_NOCONTENT) {
537 /* wget can't handle output of zero length */
538 abuf_puts(&abuf, "\n");
542 if (config->http_headers) {
543 http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
547 * Determine the last available outbuffer slot.
548 * Search from the end towards the start to avoid starvation of
549 * older replies that are still in-flight (since the send function
550 * iterates from the start towards the end).
552 send_index = MAX_CLIENTS;
555 if (!outbuffer.buffer[send_index]) {
559 assert(send_index < MAX_CLIENTS);
560 assert(!outbuffer.buffer[send_index]);
562 /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
563 outbuffer.buffer[send_index] = abuf.buf;
564 outbuffer.size[send_index] = abuf.len;
565 outbuffer.written[send_index] = 0;
566 outbuffer.socket[send_index] = the_socket;
571 first_reply = !outbuffer.count;
577 if (first_reply && outbuffer.buffer[send_index]) {
578 writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
582 static char * skipLeadingWhitespace(char * requ, size_t *len) {
583 while (isspace(*requ) && (*requ != '\0')) {
590 static char * stripEOLs(char * requ, size_t *len) {
591 while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
598 static char * stripTrailingSlashes(char * requ, size_t *len) {
599 while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
606 static char * cutAtFirstEOL(char * requ, size_t *len) {
609 while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
613 if ((*s == '\n') || (*s == '\r')) {
620 static char * parseRequest(char * req, size_t *len) {
621 if (!req || !len || !*len) {
625 /* HTTP request: GET whitespace URI whitespace HTTP/1.[01] */
626 if (*len < (3 + 1 + 1 + 1 + 8) //
627 || strncasecmp(req, "GET", 3) //
628 || !isspace(req[3]) //
629 || !isspace(req[*len - 9]) //
630 || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
631 || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
632 /* too short or does not start with 'GET ' nor ends with ' HTTP/1.[01]'*/
640 /* strip ' HTTP/1.[01]' */
647 static void drain_request(int ipc_connection) {
648 static char drain_buffer[AUTOBUFCHUNK];
650 /* input was much too long: read until the end for graceful connection termination
651 * because wget can't handle the premature connection termination that is allowed
652 * by the INFO_HTTP_REQUEST_ENTITY_TOO_LARGE HTTP status code
654 while (recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), 0) == sizeof(drain_buffer)) {}
657 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
659 char addr[INET6_ADDRSTRLEN];
662 int ipc_connection = -1;
663 union olsr_sockaddr sock_addr;
664 socklen_t sock_addr_len = sizeof(sock_addr);
665 bool hostDenied = false;
666 struct timeval timeout;
668 char req_buffer[1024];
669 char * req = req_buffer;
670 ssize_t rx_count = 0;
671 unsigned int send_what = 0;
672 unsigned int http_status = INFO_HTTP_OK;
676 if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) < 0) {
678 olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
680 /* the caller will retry later */
684 if (outbuffer.count >= MAX_CLIENTS) {
685 /* limit the number of replies that are in-flight */
686 drain_request(ipc_connection);
687 send_status_no_retries(req, ipc_connection, INFO_HTTP_SERVICE_UNAVAILABLE);
691 if (olsr_cnf->ip_version == AF_INET) {
693 (ntohl(config->accept_ip.v4.s_addr) != INADDR_ANY) //
694 && !ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) //
695 && (!config->allow_localhost //
696 || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK));
699 !ip6equal(&config->accept_ip.v6, &in6addr_any) //
700 && !ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) //
701 && (!config->allow_localhost //
702 || !ip6equal(&config->accept_ip.v6, &in6addr_loopback));
707 olsr_cnf->ip_version, //
708 (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
717 olsr_printf(1, "(%s) Connect from host %s is not allowed!\n", name, addr);
719 drain_request(ipc_connection);
720 send_info(req, send_what, ipc_connection, INFO_HTTP_FORBIDDEN);
725 olsr_printf(2, "(%s) Connect from host %s is allowed\n", name, addr);
728 timeout.tv_sec = timeout.tv_usec = 0;
732 FD_SET(ipc_connection, &read_fds);
734 FD_SET((unsigned int ) ipc_connection, &read_fds);
737 /* On success, select() and pselect() return the number of file descriptors
738 * contained in the three returned descriptor sets (that is, the total number
739 * of bits that are set in readfds, writefds, exceptfds) which may be zero if
740 * the timeout expires before anything interesting happens. On error, -1 is
741 * returned, and errno is set to indicate the error; the file descriptor sets
742 * are unmodified, and timeout becomes undefined.
745 if (select(ipc_connection + 1, &read_fds, NULL, NULL, &timeout) < 0) {
747 olsr_printf(1, "(%s) select()=%s\n", name, strerror(errno));
749 drain_request(ipc_connection);
750 send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
754 rx_count = recv(ipc_connection, req, sizeof(req_buffer), 0); /* Win32 needs the cast here */
756 /* Upon successful completion, recv() shall return the length of the message
757 * in bytes. If no messages are available to be received and the peer has
758 * performed an orderly shutdown, recv() shall return 0. Otherwise, −1 shall
759 * be returned and errno set to indicate the error.
764 olsr_printf(1, "(%s) rx_count < 0\n", name);
767 drain_request(ipc_connection);
768 send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
776 olsr_printf(1, "(%s) rx_count == 0\n", name);
779 drain_request(ipc_connection);
780 send_info(req, SIW_EVERYTHING, ipc_connection, INFO_HTTP_OK);
786 if (rx_count >= (ssize_t) sizeof(req_buffer)) {
788 olsr_printf(1, "(%s) rx_count > %ld\n", name, (long int) sizeof(req_buffer));
790 req[sizeof(req_buffer) - 1] = '\0';
791 drain_request(ipc_connection);
792 send_info(req, send_what, ipc_connection, INFO_HTTP_REQUEST_ENTITY_TOO_LARGE);
796 /* 0 < rx_count < sizeof(requ) */
798 req[rx_count] = '\0';
800 req = cutAtFirstEOL(req, (size_t*) &rx_count);
802 req = stripEOLs(req, (size_t*) &rx_count);
803 req = skipLeadingWhitespace(req, (size_t*) &rx_count);
805 req = parseRequest(req, (size_t*) &rx_count);
807 req = stripEOLs(req, (size_t*) &rx_count);
808 req = stripTrailingSlashes(req, (size_t*) &rx_count);
809 req = skipLeadingWhitespace(req, (size_t*) &rx_count);
810 req = skipMultipleSlashes(req, (size_t*) &rx_count);
812 if ((req[0] == '\0') //
813 || ((req[0] == '/') && (req[1] == '\0'))) {
815 send_what = SIW_EVERYTHING;
817 send_what = determine_action(req);
821 http_status = INFO_HTTP_NOTFOUND;
824 send_info(req, send_what, ipc_connection, http_status);
827 static int plugin_ipc_init(void) {
828 union olsr_sockaddr sock_addr;
830 socklen_t sock_addr_len;
832 /* Init ipc socket */
833 if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
835 olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
840 if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
842 olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
847 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
848 if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
849 perror("SO_NOSIGPIPE failed");
852 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
854 #if defined __linux__ && defined IPV6_V6ONLY
855 if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
856 && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
857 perror("IPV6_V6ONLY failed");
860 #endif /* defined __linux__ && defined IPV6_V6ONLY */
862 /* complete the socket structure */
863 memset(&sock_addr, 0, sizeof(sock_addr));
864 if (olsr_cnf->ip_version == AF_INET) {
865 sock_addr.in4.sin_family = AF_INET;
866 sock_addr_len = sizeof(struct sockaddr_in);
868 sock_addr.in4.sin_len = sock_addr_len;
869 #endif /* SIN6_LEN */
870 sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
871 sock_addr.in4.sin_port = htons(config->ipc_port);
873 sock_addr.in6.sin6_family = AF_INET6;
874 sock_addr_len = sizeof(struct sockaddr_in6);
876 sock_addr.in6.sin6_len = sock_addr_len;
877 #endif /* SIN6_LEN */
878 sock_addr.in6.sin6_addr = config->listen_ip.v6;
879 sock_addr.in6.sin6_port = htons(config->ipc_port);
882 /* bind the socket to the port number */
883 if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
885 olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
890 /* show that we are willing to listen */
891 if (listen(ipc_socket, 1) == -1) {
893 olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
898 /* Register with olsrd */
899 add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
902 olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
908 if (ipc_socket >= 0) {
915 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
919 assert(plugin_functions);
920 assert(plugin_config);
923 functions = plugin_functions;
924 config = plugin_config;
926 memset(&outbuffer, 0, sizeof(outbuffer));
927 for (i = 0; i < MAX_CLIENTS; ++i) {
928 outbuffer.socket[i] = -1;
933 if (functions->init) {
934 functions->init(name);
937 info_plugin_cache_init(true);
939 return plugin_ipc_init();
942 void info_plugin_exit(void) {
945 if (ipc_socket != -1) {
949 for (i = 0; i < MAX_CLIENTS; ++i) {
950 if (outbuffer.buffer[i]) {
951 free(outbuffer.buffer[i]);
952 outbuffer.buffer[i] = NULL;
954 outbuffer.size[i] = 0;
955 outbuffer.written[i] = 0;
956 if (outbuffer.socket[i]) {
957 close(outbuffer.socket[i]);
958 outbuffer.socket[i] = -1;
963 info_plugin_cache_init(false);