info: minor layout improvements
[olsrd.git] / lib / info / olsrd_info.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2015
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <assert.h>
45 #include <ctype.h>
46 #include <stdbool.h>
47
48 #include "olsrd_info.h"
49 #include "olsr.h"
50 #include "scheduler.h"
51 #include "ipcalc.h"
52 #include "http_headers.h"
53
54 #ifdef _WIN32
55 #define close(x) closesocket(x)
56 #endif /* _WIN32 */
57
58 #define MAX_CLIENTS 3
59
60 /*
61  * There is the problem that writing to a network socket can block,
62  * and the olsrd scheduler does not care about write events.
63  *
64  * There was a case that olsrd just froze for minutes when people used
65  * jsoninfo/txtinfo with large topologies over bad WiFi.
66  *
67  * This is the solution that was chosen at that time, unwilling to
68  * rewrite the whole scheduler:
69  * A timer was added and each time it expires each non-empty buffer
70  * in this structure will try to write data into a "non-blocking"
71  * socket until all data is sent, so that no blocking occurs.
72  */
73 typedef struct {
74   int socket[MAX_CLIENTS];
75   char *buffer[MAX_CLIENTS];
76   size_t size[MAX_CLIENTS];
77   size_t written[MAX_CLIENTS];
78   int count;
79 } info_plugin_outbuffer_t;
80
81 static char sink_buffer[4096];
82
83 static const char * name = NULL;
84
85 static info_plugin_functions_t *functions = NULL;
86
87 static info_plugin_config_t *config = NULL;
88
89 static int ipc_socket = -1;
90
91 static info_plugin_outbuffer_t outbuffer;
92
93 static struct timer_entry *writetimer_entry = NULL;
94
95 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
96
97 static char * skipMultipleSlashes(char * requ) {
98   char * r = requ;
99
100   if ((r[0] == '\0') // zero length
101       || (r[0] != '/') // does not start with a slash
102       || (r[1] != '/')) // does not have another slash
103   {
104     return r;
105   }
106
107   while (r[1] == '/') {
108     r++;
109   }
110   return r;
111 }
112
113 static unsigned int determine_single_action(char *requ) {
114   static unsigned long long SIW_ENTRIES[] = {
115   //
116       SIW_OLSRD_CONF,//
117       SIW_ALL, //
118       //
119       // these are the two overarching categories
120       SIW_RUNTIME_ALL,//
121       SIW_STARTUP_ALL, //
122       //
123       // these are the individual sections
124       SIW_NEIGHBORS,//
125       SIW_LINKS, //
126       SIW_ROUTES, //
127       SIW_HNA, //
128       SIW_MID, //
129       SIW_TOPOLOGY, //
130       SIW_GATEWAYS, //
131       SIW_INTERFACES, //
132       SIW_2HOP, //
133       SIW_SGW, //
134       //
135       // specials
136       SIW_VERSION,//
137       SIW_CONFIG, //
138       SIW_PLUGINS, //
139       //
140       // Freifunk special
141       SIW_NEIGHBORS_FREIFUNK //
142       };
143
144   unsigned int i;
145
146   if (!functions->is_command)
147     return 0;
148
149   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES); ++i) {
150     unsigned long long siw = SIW_ENTRIES[i];
151     if (functions->is_command(requ, siw))
152       return siw;
153   }
154
155   return 0;
156 }
157
158 static unsigned int determine_action(char *requ) {
159   if (!functions->is_command) {
160     return 0;
161   }
162
163   if (!requ || (requ[0] == '\0')) {
164     /* no more text */
165     return 0;
166   }
167
168   /* requ is guaranteed to be at least 1 character long */
169
170   if (!functions->supportsCompositeCommands) {
171     /* no support for composite commands */
172     return determine_single_action(requ);
173   }
174
175   /* composite commands */
176
177   {
178     unsigned int action = 0;
179
180     char * requestSegment = requ;
181     while (requestSegment) {
182       requestSegment = skipMultipleSlashes(requestSegment);
183       if (requestSegment[0] == '\0') {
184         /* there is no more text */
185         requestSegment = NULL;
186         continue;
187       }
188
189       /* there is more text */
190
191       {
192         unsigned int r = 0;
193         char * requestSegmentTail = strchr(&requestSegment[1], '/');
194
195         if (!requestSegmentTail) {
196           /* there isn't another slash, process everything that is left */
197           r = determine_single_action(requestSegment);
198         } else {
199           /* there is another slash, process everything before the slash */
200           char savedCharacter = *requestSegmentTail;
201           *requestSegmentTail = '\0';
202           r = determine_single_action(requestSegment);
203           *requestSegmentTail = savedCharacter;
204         }
205
206         if (!r) {
207           /* not found */
208           return 0;
209         }
210
211         action |= r;
212
213         /* process everything that is left in the next iteration */
214         requestSegment = requestSegmentTail;
215       }
216     }
217
218     return action;
219   }
220 }
221
222 static void write_data(void *fullyWritten) {
223   fd_set set;
224   int result, i, max;
225   struct timeval tv;
226
227   if (outbuffer.count <= 0) {
228     /* exit early if there is nothing to send */
229     return;
230   }
231
232   FD_ZERO(&set);
233   max = 0;
234   for (i = 0; i < outbuffer.count; i++) {
235     if (outbuffer.socket[i] < 0) {
236       continue;
237     }
238
239     /* And we cast here since we get a warning on Win32 */
240     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
241
242     if (outbuffer.socket[i] > max) {
243       max = outbuffer.socket[i];
244     }
245   }
246
247   tv.tv_sec = 0;
248   tv.tv_usec = 0;
249
250   result = select(max + 1, NULL, &set, NULL, &tv);
251   if (result <= 0) {
252     /* exit early if any of the sockets is not ready for writing */
253     return;
254   }
255
256   for (i = 0; i < MAX_CLIENTS; i++) {
257     if (outbuffer.socket[i] < 0) {
258       continue;
259     }
260
261     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
262     if (result > 0) {
263       outbuffer.written[i] += result;
264     }
265
266     if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
267       continue;
268     }
269
270     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
271       /* close this socket and cleanup*/
272       close(outbuffer.socket[i]);
273       outbuffer.socket[i] = -1;
274       free(outbuffer.buffer[i]);
275       outbuffer.buffer[i] = NULL;
276       outbuffer.size[i] = 0;
277       outbuffer.written[i] = 0;
278
279       outbuffer.count--;
280       if (fullyWritten) {
281         bool * p = fullyWritten;
282         *p = true;
283       }
284     }
285   }
286
287   if (!outbuffer.count) {
288     olsr_stop_timer(writetimer_entry);
289   }
290 }
291
292 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
293   struct autobuf abuf;
294   unsigned int outputLength = 0;
295
296   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
297   int contentLengthIndex = 0;
298   int headerLength = 0;
299
300   abuf_init(&abuf, 2 * 4096);
301
302   if (config->http_headers) {
303     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
304     headerLength = abuf.len;
305   }
306
307   if (status == INFO_HTTP_OK) {
308     /* OK */
309
310     // only add if normal format
311     if (send_what & SIW_ALL) {
312       typedef struct {
313         unsigned int siw;
314         printer_generic func;
315       } SiwLookupTableEntry;
316
317       SiwLookupTableEntry funcs[] = {
318         { SIW_NEIGHBORS , functions->neighbors  }, //
319         { SIW_LINKS     , functions->links      }, //
320         { SIW_ROUTES    , functions->routes     }, //
321         { SIW_HNA       , functions->hna        }, //
322         { SIW_MID       , functions->mid        }, //
323         { SIW_TOPOLOGY  , functions->topology   }, //
324         { SIW_GATEWAYS  , functions->gateways   }, //
325         { SIW_INTERFACES, functions->interfaces }, //
326         { SIW_2HOP      , functions->twohop     }, //
327         { SIW_SGW       , functions->sgw        }, //
328         //
329         { SIW_VERSION   , functions->version    }, //
330         { SIW_CONFIG    , functions->config     }, //
331         { SIW_PLUGINS   , functions->plugins    } //
332       };
333
334       unsigned int i;
335       unsigned int preLength;
336
337       if (functions->output_start) {
338         functions->output_start(&abuf);
339       }
340
341       preLength = abuf.len;
342
343       for (i = 0; i < ARRAY_SIZE(funcs); i++) {
344         if (send_what & funcs[i].siw) {
345           printer_generic func = funcs[i].func;
346           if (func) {
347             func(&abuf);
348           }
349         }
350       }
351
352       outputLength = abuf.len - preLength;
353
354       if (functions->output_end) {
355         functions->output_end(&abuf);
356       }
357     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
358       /* this outputs the olsrd.conf text directly, not normal format */
359       unsigned int preLength = abuf.len;
360       functions->olsrd_conf(&abuf);
361       outputLength = abuf.len - preLength;
362     }
363
364     if (!abuf.len || !outputLength) {
365       status = INFO_HTTP_NOCONTENT;
366       abuf.buf[0] = '\0';
367       abuf.len = 0;
368       if (config->http_headers) {
369         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
370         headerLength = abuf.len;
371       }
372     }
373   }
374
375   if (status != INFO_HTTP_OK) {
376     if (functions->output_error) {
377       functions->output_error(&abuf, status, req, config->http_headers);
378     } else if (status == INFO_HTTP_NOCONTENT) {
379       /* wget can't handle output of zero length */
380       abuf_puts(&abuf, "\n");
381     }
382   }
383
384   if (config->http_headers) {
385     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
386   }
387
388   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
389   outbuffer.buffer[outbuffer.count] = abuf.buf;
390   outbuffer.size[outbuffer.count] = abuf.len;
391   outbuffer.written[outbuffer.count] = 0;
392   outbuffer.socket[outbuffer.count] = the_socket;
393   abuf.buf = NULL;
394   abuf.len = 0;
395   abuf.size = 0;
396
397   outbuffer.count++;
398
399   {
400     bool fullyWritten = false;
401     write_data(&fullyWritten);
402
403     if (!fullyWritten && (outbuffer.count == 1)) {
404       writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
405     }
406   }
407
408   abuf_free(&abuf);
409 }
410
411 static char * skipLeadingWhitespace(char * requ, size_t *len) {
412   while (isspace(*requ) && (*requ != '\0')) {
413     *len = *len - 1;
414     requ++;
415   }
416   return requ;
417 }
418
419 static char * stripEOLs(char * requ, size_t *len) {
420   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
421     *len = *len - 1;
422     requ[*len] = '\0';
423   }
424   return requ;
425 }
426
427 static char * stripTrailingSlashes(char * requ, size_t *len) {
428   while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
429     *len = *len - 1;
430     requ[*len] = '\0';
431   }
432   return requ;
433 }
434
435 static char * cutAtFirstEOL(char * requ, size_t *len) {
436   char * s = requ;
437   size_t l = 0;
438   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
439     s++;
440     l++;
441   }
442   if ((*s == '\n') || (*s == '\r')) {
443     *s = '\0';
444   }
445   *len = l;
446   return requ;
447 }
448
449 static char * parseRequest(char * requ, size_t *len) {
450   char * req = requ;
451
452   if (!req || !*len) {
453     return requ;
454   }
455
456   req = skipLeadingWhitespace(req, len);
457   req = stripEOLs(req, len);
458
459   /* HTTP request: GET whitespace URI whitespace HTTP/1.1 */
460   if (*len < (3 + 1 + 1 + 1 + 8)) {
461     return req;
462   }
463
464   if (strncasecmp(req, "GET", 3) || !isspace(req[3])) {
465     /* does not start with 'GET ' */
466     return req;
467   }
468
469   /* skip 'GET ' and further leading whitespace */
470   req = skipLeadingWhitespace(&req[4], len);
471   if (!*len) return req;
472
473   /* cut req at the first '\n' */
474   req = cutAtFirstEOL(req, len);
475   if (!*len) return req;
476
477   /* strip req of trailing EOL and whitespace */
478   req = stripEOLs(req, len);
479   if (*len < 9) return req;
480
481   if (!isspace(req[*len - 9]) //
482       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
483       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
484     return req;
485   }
486   *len = *len - 8;
487   req[*len] = '\0';
488   if (!*len) return req;
489
490   /* strip req of trailing EOL and whitespace */
491   req = stripEOLs(req, len);
492   req = stripTrailingSlashes(req, len);
493
494   return req;
495 }
496
497 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
498 #ifndef NODEBUG
499   char addr[INET6_ADDRSTRLEN];
500 #endif /* NODEBUG */
501
502   char * req = NULL;
503   unsigned int http_status = INFO_HTTP_OK;
504   union olsr_sockaddr sock_addr;
505   socklen_t sock_addr_len = sizeof(sock_addr);
506   fd_set rfds;
507   struct timeval tv;
508   unsigned int send_what = 0;
509   int ipc_connection = -1;
510
511   if (outbuffer.count >= MAX_CLIENTS) {
512     return;
513   }
514
515   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
516 #ifndef NODEBUG
517     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
518 #endif /* NODEBUG */
519     return;
520   }
521
522 #ifndef NODEBUG
523   if (!inet_ntop( //
524       olsr_cnf->ip_version, //
525       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
526       addr, //
527       sizeof(addr))) {
528     addr[0] = '\0';
529   }
530 #endif /* NODEBUG */
531
532   tv.tv_sec = tv.tv_usec = 0;
533   if (olsr_cnf->ip_version == AF_INET) {
534     if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
535         && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
536 #ifndef NODEBUG
537       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
538 #endif /* NODEBUG */
539       close(ipc_connection);
540       return;
541     }
542   } else {
543     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
544     if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
545 #ifndef NODEBUG
546       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
547 #endif /* NODEBUG */
548       close(ipc_connection);
549       return;
550     }
551   }
552
553 #ifndef NODEBUG
554   olsr_printf(2, "(%s) Connect from %s\n", name, addr);
555 #endif /* NODEBUG */
556
557   /* purge read buffer to prevent blocking on linux */
558   FD_ZERO(&rfds);
559   FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
560   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
561     char requ[1024];
562     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
563
564     if (s >= (ssize_t) (sizeof(requ) - 1)) {
565       /* input was much too long, just skip the rest */
566       while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
567         ;
568       s = -1;
569     }
570
571     if (0 <= s) {
572       req = requ;
573       req[s] = '\0';
574       req = parseRequest(req, (size_t*)&s);
575       req = skipMultipleSlashes(req);
576       if ((req[0] == '\0') || ((req[0] == '/') && (req[1] == '\0'))) {
577         /* empty or '/' */
578         send_what = SIW_ALL;
579       } else {
580         send_what = determine_action(req);
581       }
582     }
583
584     if (!send_what) {
585       http_status = INFO_HTTP_NOTFOUND;
586     }
587   }
588
589   send_info(req ? req : "", send_what, ipc_connection, http_status);
590 }
591
592 static int plugin_ipc_init(void) {
593   union olsr_sockaddr sock_addr;
594   uint32_t yes = 1;
595   socklen_t sock_addr_len;
596
597   /* Init ipc socket */
598   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
599 #ifndef NODEBUG
600     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
601 #endif /* NODEBUG */
602     goto error_out;
603   }
604
605   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
606 #ifndef NODEBUG
607     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
608 #endif /* NODEBUG */
609     goto error_out;
610   }
611
612 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
613   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
614     perror("SO_NOSIGPIPE failed");
615     goto error_out;
616   }
617 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
618
619 #if defined __linux__ && defined IPV6_V6ONLY
620   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
621       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
622     perror("IPV6_V6ONLY failed");
623     goto error_out;
624   }
625 #endif /* defined __linux__ && defined IPV6_V6ONLY */
626
627   /* complete the socket structure */
628   memset(&sock_addr, 0, sizeof(sock_addr));
629   if (olsr_cnf->ip_version == AF_INET) {
630     sock_addr.in4.sin_family = AF_INET;
631     sock_addr_len = sizeof(struct sockaddr_in);
632 #ifdef SIN6_LEN
633     sock_addr.in4.sin_len = sock_addr_len;
634 #endif /* SIN6_LEN */
635     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
636     sock_addr.in4.sin_port = htons(config->ipc_port);
637   } else {
638     sock_addr.in6.sin6_family = AF_INET6;
639     sock_addr_len = sizeof(struct sockaddr_in6);
640 #ifdef SIN6_LEN
641     sock_addr.in6.sin6_len = sock_addr_len;
642 #endif /* SIN6_LEN */
643     sock_addr.in6.sin6_addr = config->listen_ip.v6;
644     sock_addr.in6.sin6_port = htons(config->ipc_port);
645   }
646
647   /* bind the socket to the port number */
648   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
649 #ifndef NODEBUG
650     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
651 #endif /* NODEBUG */
652     goto error_out;
653   }
654
655   /* show that we are willing to listen */
656   if (listen(ipc_socket, 1) == -1) {
657 #ifndef NODEBUG
658     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
659 #endif /* NODEBUG */
660     goto error_out;
661   }
662
663   /* Register with olsrd */
664   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
665
666 #ifndef NODEBUG
667   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
668 #endif /* NODEBUG */
669
670   return 1;
671
672   error_out: //
673   if (ipc_socket >= 0) {
674     close(ipc_socket);
675     ipc_socket = -1;
676   }
677   return 0;
678 }
679
680 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
681   int i;
682
683   assert(plugin_name);
684   assert(plugin_functions);
685   assert(plugin_config);
686
687   name = plugin_name;
688   functions = plugin_functions;
689   config = plugin_config;
690
691   memset(&outbuffer, 0, sizeof(outbuffer));
692   for (i = 0; i < MAX_CLIENTS; ++i) {
693     outbuffer.socket[i] = -1;
694   }
695
696   ipc_socket = -1;
697
698   if (functions->init) {
699     functions->init(name);
700   }
701
702   plugin_ipc_init();
703   return 1;
704 }
705
706 void info_plugin_exit(void) {
707   int i;
708
709   if (ipc_socket != -1) {
710     close(ipc_socket);
711     ipc_socket = -1;
712   }
713   for (i = 0; i < MAX_CLIENTS; ++i) {
714     if (outbuffer.buffer[i]) {
715       free(outbuffer.buffer[i]);
716       outbuffer.buffer[i] = NULL;
717     }
718     outbuffer.size[i] = 0;
719     outbuffer.written[i] = 0;
720     if (outbuffer.socket[i]) {
721       close(outbuffer.socket[i]);
722       outbuffer.socket[i] = -1;
723     }
724   }
725   outbuffer.count = 0;
726 }