addition of silc.css
[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         g_free(rec);
46 }
47
48 static void sig_disconnect(NET_DISCONNECT_REC *rec)
49 {
50         char buf[512];
51         int count, ret;
52
53         /* check if there's any data waiting in socket. read max. 5kB so
54            if server just keeps sending us stuff we won't get stuck */
55         count = 0;
56         do {
57                 ret = net_receive(rec->handle, buf, sizeof(buf));
58                 if (ret == -1) {
59                         /* socket was closed */
60                         net_disconnect_remove(rec);
61                 }
62                 count++;
63         } while (ret == sizeof(buf) && count < 10);
64 }
65
66 static int sig_timeout_disconnect(void)
67 {
68         NET_DISCONNECT_REC *rec;
69         GSList *tmp, *next;
70         time_t now;
71
72         /* check if we've waited enough for sockets to close themselves */
73         now = time(NULL);
74         for (tmp = disconnects; tmp != NULL; tmp = next) {
75                 rec = tmp->data;
76                 next = tmp->next;
77
78                 if (rec->created+MAX_CLOSE_WAIT <= now)
79                         net_disconnect_remove(rec);
80         }
81
82         if (disconnects == NULL) {
83                 /* no more sockets in disconnect queue, stop calling this
84                    function */
85                 timeout_tag = -1;
86         }
87         return disconnects != NULL;
88 }
89
90 /* Try to let the other side close the connection, if it still isn't
91    disconnected after certain amount of time, close it ourself */
92 void net_disconnect_later(GIOChannel *handle)
93 {
94         NET_DISCONNECT_REC *rec;
95
96         rec = g_new(NET_DISCONNECT_REC, 1);
97         rec->created = time(NULL);
98         rec->handle = handle;
99         rec->tag = g_input_add(handle, G_INPUT_READ,
100                                (GInputFunction) sig_disconnect, rec);
101
102         if (timeout_tag == -1) {
103                 timeout_tag = g_timeout_add(10000, (GSourceFunc)
104                                             sig_timeout_disconnect, NULL);
105         }
106
107         disconnects = g_slist_append(disconnects, rec);
108 }
109
110 void net_disconnect_init(void)
111 {
112         disconnects = NULL;
113         timeout_tag = -1;
114 }
115
116 void net_disconnect_deinit(void)
117 {
118 #ifndef WIN32
119         NET_DISCONNECT_REC *rec;
120         time_t now, max;
121         int first, fd;
122         struct timeval tv;
123         fd_set set;
124
125         /* give the sockets a chance to disconnect themselves.. */
126         max = time(NULL)+MAX_QUIT_CLOSE_WAIT;
127         first = 1;
128         while (disconnects != NULL) {
129                 rec = disconnects->data;
130
131                 now = time(NULL);
132                 if (rec->created+MAX_QUIT_CLOSE_WAIT <= now || max <= now) {
133                         /* this one has waited enough */
134                         net_disconnect_remove(rec);
135                         continue;
136                 }
137
138                 fd = g_io_channel_unix_get_fd(rec->handle);
139                 FD_ZERO(&set);
140                 FD_SET(fd, &set);
141                 tv.tv_sec = first ? 0 : max-now;
142                 tv.tv_usec = first ? 100000 : 0;
143                 if (select(fd+1, &set, NULL, NULL, &tv) > 0 &&
144                     FD_ISSET(fd, &set)) {
145                         /* data coming .. check if we can close the handle */
146                         sig_disconnect(rec);
147                 } else if (first) {
148                         /* Display the text when we have already waited
149                            for a while */
150                         printf("Please wait, waiting for servers to close "
151                                "connections..\n");
152                         fflush(stdout);
153
154                         first = 0;
155                 }
156         }
157 #endif
158 }