Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / core / net-disconnect.c
1 /*
2  net-disconnect.c : 
3
4     Copyright (C) 1999-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "network.h"
23
24 /* when quitting, wait for max. 5 seconds before forcing to close the socket */
25 #define MAX_QUIT_CLOSE_WAIT 5
26
27 /* wait for max. 2 minutes for other side to close the socket */
28 #define MAX_CLOSE_WAIT (60*2)
29
30 typedef struct {
31         time_t created;
32         GIOChannel *handle;
33         int tag;
34 } NET_DISCONNECT_REC;
35
36 static GSList *disconnects;
37
38 static int timeout_tag;
39
40 static void net_disconnect_remove(NET_DISCONNECT_REC *rec)
41 {
42         disconnects = g_slist_remove(disconnects, rec);
43
44         g_source_remove(rec->tag);
45         net_disconnect(rec->handle);
46         g_free(rec);
47 }
48
49 static void sig_disconnect(NET_DISCONNECT_REC *rec)
50 {
51         char buf[512];
52         int count, ret;
53
54         /* check if there's any data waiting in socket. read max. 5kB so
55            if server just keeps sending us stuff we won't get stuck */
56         count = 0;
57         do {
58                 ret = net_receive(rec->handle, buf, sizeof(buf));
59                 if (ret == -1) {
60                         /* socket was closed */
61                         net_disconnect_remove(rec);
62                 }
63                 count++;
64         } while (ret == sizeof(buf) && count < 10);
65 }
66
67 static int sig_timeout_disconnect(void)
68 {
69         NET_DISCONNECT_REC *rec;
70         GSList *tmp, *next;
71         time_t now;
72
73         /* check if we've waited enough for sockets to close themselves */
74         now = time(NULL);
75         for (tmp = disconnects; tmp != NULL; tmp = next) {
76                 rec = tmp->data;
77                 next = tmp->next;
78
79                 if (rec->created+MAX_CLOSE_WAIT <= now)
80                         net_disconnect_remove(rec);
81         }
82
83         if (disconnects == NULL) {
84                 /* no more sockets in disconnect queue, stop calling this
85                    function */
86                 timeout_tag = -1;
87         }
88         return disconnects != NULL;
89 }
90
91 /* Try to let the other side close the connection, if it still isn't
92    disconnected after certain amount of time, close it ourself */
93 void net_disconnect_later(GIOChannel *handle)
94 {
95         NET_DISCONNECT_REC *rec;
96
97         rec = g_new(NET_DISCONNECT_REC, 1);
98         rec->created = time(NULL);
99         rec->handle = handle;
100         rec->tag = g_input_add(handle, G_INPUT_READ,
101                                (GInputFunction) sig_disconnect, rec);
102
103         if (timeout_tag == -1) {
104                 timeout_tag = g_timeout_add(10000, (GSourceFunc)
105                                             sig_timeout_disconnect, NULL);
106         }
107
108         disconnects = g_slist_append(disconnects, rec);
109 }
110
111 void net_disconnect_init(void)
112 {
113         disconnects = NULL;
114         timeout_tag = -1;
115 }
116
117 void net_disconnect_deinit(void)
118 {
119 #ifndef WIN32
120         NET_DISCONNECT_REC *rec;
121         time_t now, max;
122         int first, fd;
123         struct timeval tv;
124         fd_set set;
125
126         /* give the sockets a chance to disconnect themselves.. */
127         max = time(NULL)+MAX_QUIT_CLOSE_WAIT;
128         first = 1;
129         while (disconnects != NULL) {
130                 rec = disconnects->data;
131
132                 now = time(NULL);
133                 if (rec->created+MAX_QUIT_CLOSE_WAIT <= now || max <= now) {
134                         /* this one has waited enough */
135                         net_disconnect_remove(rec);
136                         continue;
137                 }
138
139                 fd = g_io_channel_unix_get_fd(rec->handle);
140                 FD_ZERO(&set);
141                 FD_SET(fd, &set);
142                 tv.tv_sec = first ? 0 : max-now;
143                 tv.tv_usec = first ? 100000 : 0;
144                 if (select(fd+1, &set, NULL, NULL, &tv) > 0 &&
145                     FD_ISSET(fd, &set)) {
146                         /* data coming .. check if we can close the handle */
147                         sig_disconnect(rec);
148                 } else if (first) {
149                         /* Display the text when we have already waited
150                            for a while */
151                         printf("Please wait, waiting for servers to close "
152                                "connections..\n");
153                         fflush(stdout);
154
155                         first = 0;
156                 }
157         }
158 #endif
159 }