Remove memory allocation in olsr_timer_add()
[oonf.git] / src / core / olsr_stream_socket.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2011, the olsr.org team - see HISTORY file
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 <ctype.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <errno.h>
49
50 #include "common/autobuf.h"
51 #include "common/avl.h"
52 #include "common/list.h"
53 #include "olsr_cfg.h"
54 #include "olsr_logging.h"
55 #include "olsr_memcookie.h"
56 #include "olsr_socket.h"
57 #include "olsr_timer.h"
58 #include "os_net.h"
59 #include "olsr.h"
60 #include "olsr_stream_socket.h"
61
62 static int _apply_managed_socket(struct olsr_stream_managed *managed,
63     struct olsr_stream_socket *stream, struct netaddr *bindto, uint16_t port);
64 static void _cb_parse_request(int fd, void *data, bool, bool);
65 static struct olsr_stream_session *_create_session(
66     struct olsr_stream_socket *stream_socket, int sock, struct netaddr *remote_addr);
67 static void _cb_parse_connection(int fd, void *data, bool r,bool w);
68
69 static void _cb_timeout_handler(void *);
70
71 /* list of olsr stream sockets */
72 struct list_entity olsr_stream_head;
73
74 /* server socket */
75 static struct olsr_memcookie_info *connection_cookie;
76 static struct olsr_timer_info connection_timeout = {
77   .name = "stream socket timout",
78   .callback = _cb_timeout_handler,
79 };
80
81 /* remember if initialized or not */
82 OLSR_SUBSYSTEM_STATE(_stream_state);
83
84 /**
85  * Initialize the stream socket handlers
86  * @return -1 if an error happened, 0 otherwise.
87  */
88 int
89 olsr_stream_init(void) {
90   if (olsr_subsystem_is_initialized(&_stream_state))
91     return 0;
92
93   connection_cookie = olsr_memcookie_add("stream socket connections",
94       sizeof(struct olsr_stream_session));
95   if (connection_cookie == NULL) {
96     OLSR_WARN_OOM(LOG_SOCKET_STREAM);
97     return -1;
98   }
99
100   olsr_timer_add(&connection_timeout);
101
102   list_init_head(&olsr_stream_head);
103   olsr_subsystem_init(&_stream_state);
104   return 0;
105 }
106
107 /**
108  * Cleanup all resources allocated be stream socket handlers
109  */
110 void
111 olsr_stream_cleanup(void) {
112   struct olsr_stream_socket *comport;
113
114   if (olsr_subsystem_cleanup(&_stream_state))
115     return;
116
117   while (!list_is_empty(&olsr_stream_head)) {
118     comport = list_first_element(&olsr_stream_head, comport, node);
119
120     olsr_stream_remove(comport, true);
121   }
122
123   olsr_memcookie_remove(connection_cookie);
124   olsr_timer_remove(&connection_timeout);
125 }
126
127 /**
128  * Flush all data in outgoing buffer of a stream socket
129  * @param con pointer to stream socket
130  */
131 void
132 olsr_stream_flush(struct olsr_stream_session *con) {
133   olsr_socket_set_write(&con->scheduler_entry, true);
134 }
135
136 /**
137  * Add a new stream socket to the scheduler
138  * @param stream_socket pointer to uninitialized stream socket struct
139  * @param local pointer to local ip/port of socket, port must be 0 if
140  *   this shall be an outgoing socket
141  * @return -1 if an error happened, 0 otherwise
142  */
143 int
144 olsr_stream_add(struct olsr_stream_socket *stream_socket,
145     union netaddr_socket *local) {
146   int s = -1;
147 #if !defined(REMOVE_LOG_WARN)
148   struct netaddr_str buf;
149 #endif
150
151   memset(stream_socket, 0, sizeof(*stream_socket));
152
153   /* server socket not necessary for outgoing connections */
154   if (netaddr_socket_get_port(local) != 0) {
155     /* Init socket */
156     s = os_net_getsocket(local, OS_SOCKET_TCP, 0, LOG_SOCKET_STREAM);
157     if (s < 0) {
158       goto add_stream_error;
159     }
160
161     /* show that we are willing to listen */
162     if (listen(s, 1) == -1) {
163       OLSR_WARN(LOG_SOCKET_STREAM, "tcp socket listen failed for %s: %s (%d)\n",
164           netaddr_socket_to_string(&buf, local), strerror(errno), errno);
165       goto add_stream_error;
166     }
167
168     stream_socket->scheduler_entry.fd = s;
169     stream_socket->scheduler_entry.process = _cb_parse_request;
170     stream_socket->scheduler_entry.data = stream_socket;
171     stream_socket->scheduler_entry.event_read = true;
172
173     olsr_socket_add(&stream_socket->scheduler_entry);
174   }
175   memcpy(&stream_socket->local_socket, local, sizeof(stream_socket->local_socket));
176
177   if (stream_socket->config.memcookie == NULL) {
178     stream_socket->config.memcookie = connection_cookie;
179   }
180   if (stream_socket->config.allowed_sessions == 0) {
181     stream_socket->config.allowed_sessions = 10;
182   }
183   if (stream_socket->config.maximum_input_buffer == 0) {
184     stream_socket->config.maximum_input_buffer = 65536;
185   }
186
187   list_init_head(&stream_socket->session);
188   list_add_tail(&olsr_stream_head, &stream_socket->node);
189
190   return 0;
191
192 add_stream_error:
193   if (stream_socket->scheduler_entry.fd) {
194     olsr_socket_remove(&stream_socket->scheduler_entry);
195   }
196   if (s != -1) {
197     os_close(s);
198   }
199   return -1;
200 }
201
202 /**
203  * Remove a stream socket from the scheduler
204  * @param stream_socket pointer to socket
205  * @param force true if socket will be closed immediately,
206  *   false if scheduler should wait until outgoing buffers are empty
207  */
208 void
209 olsr_stream_remove(struct olsr_stream_socket *stream_socket, bool force) {
210   struct olsr_stream_session *session, *ptr;
211
212   if (stream_socket->busy && !force) {
213     stream_socket->remove = true;
214     return;
215   }
216
217   if (!list_is_node_added(&stream_socket->node)) {
218     return;
219   }
220
221   list_for_each_element_safe(&stream_socket->session, session, node, ptr) {
222     if (force || (abuf_getlen(&session->out) == 0 && !session->busy)) {
223       /* close everything that doesn't need to send data anymore */
224       olsr_stream_close(session, force);
225     }
226   }
227
228   if (!list_is_empty(&stream_socket->session)) {
229     return;
230   }
231
232   list_remove(&stream_socket->node);
233
234   if (stream_socket->scheduler_entry.fd) {
235     /* only for server sockets */
236     os_close(stream_socket->scheduler_entry.fd);
237     olsr_socket_remove(&stream_socket->scheduler_entry);
238   }
239 }
240
241 /**
242  * Create an outgoing stream socket.
243  * @param stream socket pointer to stream socket
244  * @param remote pointer to address of remote TCP server
245  * @return pointer to stream session, NULL if an error happened.
246  */
247 struct olsr_stream_session *
248 olsr_stream_connect_to(struct olsr_stream_socket *stream_socket,
249     union netaddr_socket *remote) {
250   struct olsr_stream_session *session;
251   struct netaddr remote_addr;
252   bool wait_for_connect = false;
253   int s;
254 #if !defined REMOVE_LOG_WARN
255   struct netaddr_str buf;
256 #endif
257
258   s = os_net_getsocket(&stream_socket->local_socket,
259       OS_SOCKET_TCP, 0, LOG_SOCKET_STREAM);
260   if (s < 0) {
261     return NULL;
262   }
263
264   if (connect(s, &remote->std, sizeof(*remote))) {
265     if (errno != EINPROGRESS) {
266       OLSR_WARN(LOG_SOCKET_STREAM, "Cannot connect outgoing tcp connection to %s: %s (%d)",
267           netaddr_socket_to_string(&buf, remote), strerror(errno), errno);
268       goto connect_to_error;
269     }
270     wait_for_connect = true;
271   }
272
273   netaddr_from_socket(&remote_addr, remote);
274   session = _create_session(stream_socket, s, &remote_addr);
275   if (session) {
276     session->wait_for_connect = wait_for_connect;
277     return session;
278   }
279
280   /* fall through */
281 connect_to_error:
282   if (s) {
283     os_close(s);
284   }
285   return NULL;
286 }
287
288 /**
289  * Reset the session timeout of a TCP session
290  * @param con pointer to stream session
291  * @param timeout timeout in milliseconds
292  */
293 void
294 olsr_stream_set_timeout(struct olsr_stream_session *con, uint32_t timeout) {
295   olsr_timer_set(&con->timeout, timeout, 0, con, &connection_timeout);
296 }
297
298 /**
299  * Close a TCP stream session
300  * @param session pointer to stream session
301  */
302 void
303 olsr_stream_close(struct olsr_stream_session *session, bool force) {
304   if (session->busy && !force) {
305     /* remove the session later */
306     session->removed = true;
307     return;
308   }
309
310   if (!list_is_node_added(&session->node)) {
311     return;
312   }
313
314   if (session->comport->config.cleanup) {
315     session->comport->config.cleanup(session);
316   }
317
318   if (session->timeout) {
319     olsr_timer_stop(session->timeout);
320   }
321
322   session->comport->config.allowed_sessions++;
323   list_remove(&session->node);
324
325   os_close(session->scheduler_entry.fd);
326   olsr_socket_remove(&session->scheduler_entry);
327
328   abuf_free(&session->in);
329   abuf_free(&session->out);
330
331   olsr_memcookie_free(session->comport->config.memcookie, session);
332 }
333
334 /**
335  * Initialized a managed TCP stream
336  * @param managed pointer to uninitialized managed stream
337  */
338 void
339 olsr_stream_add_managed(struct olsr_stream_managed *managed) {
340   memset(managed, 0, sizeof(*managed));
341   managed->config.allowed_sessions = 10;
342   managed->config.maximum_input_buffer = 65536;
343   managed->config.session_timeout = 120000;
344 }
345
346 /**
347  * Apply a configuration to a stream. Will reset both ACLs
348  * and socket ports/bindings.
349  * @param managed pointer to managed stream
350  * @param config pointer to stream config
351  * @return -1 if an error happened, 0 otherwise.
352  */
353 int
354 olsr_stream_apply_managed(struct olsr_stream_managed *managed,
355     struct olsr_stream_managed_config *config) {
356   olsr_acl_copy(&managed->acl, &config->acl);
357
358   if (config_global.ipv4) {
359     if (_apply_managed_socket(managed,
360         &managed->socket_v4, &config->bindto_v4, config->port)) {
361       return -1;
362     }
363   }
364   else {
365     olsr_stream_remove(&managed->socket_v4, true);
366   }
367
368   if (config_global.ipv6) {
369     if (_apply_managed_socket(managed,
370         &managed->socket_v6, &config->bindto_v6, config->port)) {
371       return -1;
372     }
373   }
374   else {
375     olsr_stream_remove(&managed->socket_v6, true);
376   }
377   return 0;
378 }
379
380 /**
381  * Remove a managed TCP stream
382  * @param managed pointer to managed stream
383  * @param force true if socket will be closed immediately,
384  *   false if scheduler should wait until outgoing buffers are empty
385  */
386 void
387 olsr_stream_remove_managed(struct olsr_stream_managed *managed, bool forced) {
388   olsr_stream_remove(&managed->socket_v4, forced);
389   olsr_stream_remove(&managed->socket_v6, forced);
390
391   olsr_acl_remove(&managed->acl);
392 }
393
394 /**
395  * Apply new configuration to a managed stream socket
396  * @param managed pointer to managed stream
397  * @param stream pointer to TCP stream to configure
398  * @param bindto local address to bind socket to
399  * @param port local port number
400  * @return -1 if an error happened, 0 otherwise.
401  */
402 static int
403 _apply_managed_socket(struct olsr_stream_managed *managed,
404     struct olsr_stream_socket *stream,
405     struct netaddr *bindto, uint16_t port) {
406   union netaddr_socket sock;
407 #if !defined(REMOVE_LOG_WARN)
408   struct netaddr_str buf;
409 #endif
410
411   if (netaddr_socket_init(&sock, bindto, port)) {
412     OLSR_WARN(LOG_SOCKET_STREAM, "Cannot create managed socket address: %s/%u",
413         netaddr_to_string(&buf, bindto), port);
414     return -1;
415   }
416
417   if (memcmp(&sock, &stream->local_socket, sizeof(sock)) == 0) {
418     /* nothing changed */
419     return 0;
420   }
421
422   olsr_stream_remove(stream, true);
423   if (olsr_stream_add(stream, &sock)) {
424     return -1;
425   }
426
427   /* copy configuration */
428   memcpy(&stream->config, &managed->config, sizeof(stream->config));
429   if (stream->config.memcookie == NULL) {
430     stream->config.memcookie = connection_cookie;
431   }
432   return 0;
433 }
434
435 /**
436  * Handle incoming server socket event from socket scheduler.
437  * @param fd filedescriptor for event
438  * @param data custom user data
439  * @param event_read true if read-event is incoming
440  * @param event_write true if write-event is incoming
441  */
442 static void
443 _cb_parse_request(int fd, void *data, bool event_read,
444     bool event_write __attribute__((unused))) {
445   struct olsr_stream_socket *comport;
446   union netaddr_socket remote_socket;
447   struct netaddr remote_addr;
448   socklen_t addrlen;
449   int sock;
450 #if !defined(REMOVE_LOG_DEBUG)
451       struct netaddr_str buf1, buf2;
452 #endif
453
454   if (!event_read) {
455     return;
456   }
457
458   comport = data;
459
460   addrlen = sizeof(remote_socket);
461   sock = accept(fd, &remote_socket.std, &addrlen);
462   if (sock < 0) {
463     OLSR_WARN(LOG_SOCKET_STREAM, "accept() call returned error: %s (%d)", strerror(errno), errno);
464     return;
465   }
466
467   netaddr_from_socket(&remote_addr, &remote_socket);
468   if (comport->config.acl) {
469     if (!olsr_acl_check_accept(comport->config.acl, &remote_addr)) {
470       OLSR_DEBUG(LOG_SOCKET_STREAM, "Access from %s to socket %s blocked because of ACL",
471           netaddr_to_string(&buf1, &remote_addr),
472           netaddr_socket_to_string(&buf2, &comport->local_socket));
473       close(sock);
474       return;
475     }
476   }
477   _create_session(comport, sock, &remote_addr);
478 }
479
480 /**
481  * Configure a TCP session socket
482  * @param stream_socket pointer to stream socket
483  * @param sock pointer to socket filedescriptor
484  * @param remote_addr pointer to remote address
485  * @return pointer to new stream session, NULL if an error happened.
486  */
487 static struct olsr_stream_session *
488 _create_session(struct olsr_stream_socket *stream_socket,
489     int sock, struct netaddr *remote_addr) {
490   struct olsr_stream_session *session;
491 #if !defined REMOVE_LOG_DEBUG
492   struct netaddr_str buf;
493 #endif
494
495   /* put socket into non-blocking mode */
496   if (os_net_set_nonblocking(sock)) {
497     OLSR_WARN(LOG_SOCKET_STREAM, "Cannot read comport socket status: %s (%d)",
498         strerror(errno), errno);
499     return NULL;
500   }
501
502   session = olsr_memcookie_malloc(stream_socket->config.memcookie);
503   if (session == NULL) {
504     OLSR_WARN(LOG_SOCKET_STREAM, "Cannot allocate memory for comport session");
505     goto parse_request_error;
506   }
507
508   if (abuf_init(&session->in)) {
509     OLSR_WARN(LOG_SOCKET_STREAM, "Cannot allocate memory for comport session");
510     goto parse_request_error;
511   }
512   if (abuf_init(&session->out)) {
513     OLSR_WARN(LOG_SOCKET_STREAM, "Cannot allocate memory for comport session");
514     goto parse_request_error;
515   }
516
517   session->scheduler_entry.fd = sock;
518   session->scheduler_entry.process = _cb_parse_connection;
519   session->scheduler_entry.data = session;
520   session->scheduler_entry.event_read = true;
521   session->scheduler_entry.event_write = true;
522   olsr_socket_add(&session->scheduler_entry);
523
524   session->send_first = stream_socket->config.send_first;
525   session->comport = stream_socket;
526
527   session->remote_address = *remote_addr;
528
529   if (stream_socket->config.allowed_sessions-- > 0) {
530     /* create active session */
531     session->state = STREAM_SESSION_ACTIVE;
532   } else {
533     /* too many sessions */
534     if (stream_socket->config.create_error) {
535       stream_socket->config.create_error(session, STREAM_SERVICE_UNAVAILABLE);
536     }
537     session->state = STREAM_SESSION_SEND_AND_QUIT;
538   }
539
540   if (stream_socket->config.session_timeout) {
541     session->timeout = olsr_timer_start(
542         stream_socket->config.session_timeout, 0, session, &connection_timeout);
543   }
544
545   if (stream_socket->config.init) {
546     if (stream_socket->config.init(session)) {
547       goto parse_request_error;
548     }
549   }
550
551   OLSR_DEBUG(LOG_SOCKET_STREAM, "Got connection through socket %d with %s.\n",
552       sock, netaddr_to_string(&buf, remote_addr));
553
554   list_add_tail(&stream_socket->session, &session->node);
555   return session;
556
557 parse_request_error:
558   abuf_free(&session->in);
559   abuf_free(&session->out);
560   olsr_memcookie_free(stream_socket->config.memcookie, session);
561
562   return NULL;
563 }
564
565 /**
566  * Handle TCP session timeout
567  * @param data custom data
568  */
569 static void
570 _cb_timeout_handler(void *data) {
571   struct olsr_stream_session *session = data;
572   olsr_stream_close(session, false);
573 }
574
575 /**
576  * Handle events for TCP session from network scheduler
577  * @param fd filedescriptor of TCP session
578  * @param data custom data
579  * @param event_read true if read-event is incoming
580  * @param event_write true if write-event is incoming
581  */
582 static void
583 _cb_parse_connection(int fd, void *data, bool event_read, bool event_write) {
584   struct olsr_stream_session *session;
585   struct olsr_stream_socket *s_sock;
586   int len;
587   char buffer[1024];
588 #if !defined(REMOVE_LOG_WARN)
589   struct netaddr_str buf;
590 #endif
591
592   session = data;
593   s_sock = session->comport;
594
595   OLSR_DEBUG(LOG_SOCKET_STREAM, "Parsing connection of socket %d\n", fd);
596
597   /* mark session and s_sock as busy */
598   session->busy = true;
599   s_sock->busy = true;
600
601   if (session->wait_for_connect) {
602     if (event_write) {
603       int value;
604       socklen_t value_len;
605
606       value_len = sizeof(value);
607
608       if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &value_len)) {
609         OLSR_WARN(LOG_SOCKET_STREAM, "getsockopt failed: %s (%d)",
610             strerror(errno), errno);
611         session->state = STREAM_SESSION_CLEANUP;
612       }
613       else if (value != 0) {
614         OLSR_WARN(LOG_SOCKET_STREAM, "Connection to %s failed: %s (%d)",
615             netaddr_to_string(&buf, &session->remote_address), strerror(value), value);
616         session->state = STREAM_SESSION_CLEANUP;
617       }
618       else {
619         session->wait_for_connect = false;
620       }
621     }
622   }
623
624   if (session->wait_for_connect) {
625     session->busy = false;
626     s_sock->busy = false;
627     return;
628   }
629
630   /* read data if necessary */
631   if (session->state == STREAM_SESSION_ACTIVE && event_read) {
632     len = recv(fd, buffer, sizeof(buffer), 0);
633     if (len > 0) {
634       OLSR_DEBUG(LOG_SOCKET_STREAM, "  recv returned %d\n", len);
635       if (abuf_memcpy(&session->in, buffer, len)) {
636         /* out of memory */
637         OLSR_WARN(LOG_SOCKET_STREAM, "Out of memory for comport session input buffer");
638         session->state = STREAM_SESSION_CLEANUP;
639       } else if (abuf_getlen(&session->in) > s_sock->config.maximum_input_buffer) {
640         /* input buffer overflow */
641         if (s_sock->config.create_error) {
642           s_sock->config.create_error(session, STREAM_REQUEST_TOO_LARGE);
643         }
644         session->state = STREAM_SESSION_SEND_AND_QUIT;
645       } else {
646         /* got new input block, reset timeout */
647         olsr_stream_set_timeout(session, s_sock->config.session_timeout);
648       }
649     } else if (len < 0 && errno != EINTR && errno != EAGAIN && errno
650         != EWOULDBLOCK) {
651       /* error during read */
652       OLSR_WARN(LOG_SOCKET_STREAM, "Error while reading from communication stream with %s: %s (%d)\n",
653           netaddr_to_string(&buf, &session->remote_address), strerror(errno), errno);
654       session->state = STREAM_SESSION_CLEANUP;
655     } else if (len == 0) {
656       /* external s_sock closed */
657       session->state = STREAM_SESSION_SEND_AND_QUIT;
658     }
659   }
660
661   if (session->state == STREAM_SESSION_ACTIVE && s_sock->config.receive_data != NULL
662       && (abuf_getlen(&session->in) > 0 || session->send_first)) {
663     session->state = s_sock->config.receive_data(session);
664     session->send_first = false;
665   }
666
667   /* send data if necessary */
668   if (session->state != STREAM_SESSION_CLEANUP && abuf_getlen(&session->out) > 0) {
669     if (event_write) {
670       len = send(fd, abuf_getptr(&session->out), abuf_getlen(&session->out), 0);
671
672       if (len > 0) {
673         OLSR_DEBUG(LOG_SOCKET_STREAM, "  send returned %d\n", len);
674         abuf_pull(&session->out, len);
675         olsr_stream_set_timeout(session, s_sock->config.session_timeout);
676       } else if (len < 0 && errno != EINTR && errno != EAGAIN && errno
677           != EWOULDBLOCK) {
678         OLSR_WARN(LOG_SOCKET_STREAM, "Error while writing to communication stream with %s: %s (%d)\n",
679             netaddr_to_string(&buf, &session->remote_address), strerror(errno), errno);
680         session->state = STREAM_SESSION_CLEANUP;
681       }
682     } else {
683       OLSR_DEBUG(LOG_SOCKET_STREAM, "  activating output in scheduler\n");
684       olsr_socket_set_write(&session->scheduler_entry, true);
685     }
686   }
687
688   if (abuf_getlen(&session->out) == 0) {
689     /* nothing to send anymore */
690     OLSR_DEBUG(LOG_SOCKET_STREAM, "  deactivating output in scheduler\n");
691     olsr_socket_set_write(&session->scheduler_entry, false);
692     if (session->state == STREAM_SESSION_SEND_AND_QUIT) {
693       session->state = STREAM_SESSION_CLEANUP;
694     }
695   }
696
697   session->busy = false;
698   s_sock->busy = false;
699
700   /* end of connection ? */
701   if (session->state == STREAM_SESSION_CLEANUP || session->removed) {
702     OLSR_DEBUG(LOG_SOCKET_STREAM, "  cleanup\n");
703
704     /* clean up connection by calling cleanup directly */
705     olsr_stream_close(session, session->state == STREAM_SESSION_CLEANUP);
706   }
707
708   /* lazy socket removal */
709   if (s_sock->remove) {
710     olsr_stream_remove(s_sock, false);
711   }
712   return;
713 }