/src/irssi/src/core/net-disconnect.c
Line | Count | Source |
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 along |
17 | | with this program; if not, write to the Free Software Foundation, Inc., |
18 | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | | */ |
20 | | |
21 | | #include "module.h" |
22 | | #include <irssi/src/core/network.h> |
23 | | |
24 | | #include <sys/select.h> |
25 | | |
26 | | /* when quitting, wait for max. 5 seconds before forcing to close the socket */ |
27 | 0 | #define MAX_QUIT_CLOSE_WAIT 5 |
28 | | |
29 | | /* wait for max. 2 minutes for other side to close the socket */ |
30 | 0 | #define MAX_CLOSE_WAIT (60*2) |
31 | | |
32 | | typedef struct { |
33 | | time_t created; |
34 | | GIOChannel *handle; |
35 | | int tag; |
36 | | } NET_DISCONNECT_REC; |
37 | | |
38 | | static GSList *disconnects; |
39 | | |
40 | | static int timeout_tag; |
41 | | |
42 | | static void net_disconnect_remove(NET_DISCONNECT_REC *rec) |
43 | 0 | { |
44 | 0 | disconnects = g_slist_remove(disconnects, rec); |
45 | |
|
46 | 0 | g_source_remove(rec->tag); |
47 | 0 | net_disconnect(rec->handle); |
48 | 0 | g_free(rec); |
49 | 0 | } |
50 | | |
51 | | static void sig_disconnect(NET_DISCONNECT_REC *rec) |
52 | 0 | { |
53 | 0 | char buf[512]; |
54 | 0 | int count, ret; |
55 | | |
56 | | /* check if there's any data waiting in socket. read max. 9kB so |
57 | | if server just keeps sending us stuff we won't get stuck */ |
58 | 0 | count = 0; |
59 | 0 | do { |
60 | 0 | ret = net_receive(rec->handle, buf, sizeof(buf)); |
61 | 0 | if (ret == -1) { |
62 | | /* socket was closed */ |
63 | 0 | net_disconnect_remove(rec); |
64 | 0 | } |
65 | 0 | count++; |
66 | 0 | } while (ret == sizeof(buf) && count < 18); |
67 | 0 | } |
68 | | |
69 | | static int sig_timeout_disconnect(void) |
70 | 0 | { |
71 | 0 | NET_DISCONNECT_REC *rec; |
72 | 0 | GSList *tmp, *next; |
73 | 0 | time_t now; |
74 | | |
75 | | /* check if we've waited enough for sockets to close themselves */ |
76 | 0 | now = time(NULL); |
77 | 0 | for (tmp = disconnects; tmp != NULL; tmp = next) { |
78 | 0 | rec = tmp->data; |
79 | 0 | next = tmp->next; |
80 | |
|
81 | 0 | if (rec->created+MAX_CLOSE_WAIT <= now) |
82 | 0 | net_disconnect_remove(rec); |
83 | 0 | } |
84 | |
|
85 | 0 | if (disconnects == NULL) { |
86 | | /* no more sockets in disconnect queue, stop calling this |
87 | | function */ |
88 | 0 | timeout_tag = -1; |
89 | 0 | } |
90 | 0 | return disconnects != NULL; |
91 | 0 | } |
92 | | |
93 | | /* Try to let the other side close the connection, if it still isn't |
94 | | disconnected after certain amount of time, close it ourself */ |
95 | | void net_disconnect_later(GIOChannel *handle) |
96 | 192 | { |
97 | 192 | NET_DISCONNECT_REC *rec; |
98 | | |
99 | 192 | rec = g_new(NET_DISCONNECT_REC, 1); |
100 | 192 | rec->created = time(NULL); |
101 | 192 | rec->handle = handle; |
102 | 192 | rec->tag = i_input_add(handle, I_INPUT_READ, (GInputFunction) sig_disconnect, rec); |
103 | | |
104 | 192 | if (timeout_tag == -1) { |
105 | 1 | timeout_tag = g_timeout_add(10000, (GSourceFunc) |
106 | 1 | sig_timeout_disconnect, NULL); |
107 | 1 | } |
108 | | |
109 | 192 | disconnects = g_slist_append(disconnects, rec); |
110 | 192 | } |
111 | | |
112 | | void net_disconnect_init(void) |
113 | 8 | { |
114 | 8 | disconnects = NULL; |
115 | 8 | timeout_tag = -1; |
116 | 8 | } |
117 | | |
118 | | void net_disconnect_deinit(void) |
119 | 0 | { |
120 | 0 | NET_DISCONNECT_REC *rec; |
121 | 0 | time_t now, max; |
122 | 0 | int first, fd; |
123 | 0 | struct timeval tv; |
124 | 0 | fd_set set; |
125 | | |
126 | | /* give the sockets a chance to disconnect themselves.. */ |
127 | 0 | max = time(NULL)+MAX_QUIT_CLOSE_WAIT; |
128 | 0 | first = 1; |
129 | 0 | while (disconnects != NULL) { |
130 | 0 | rec = disconnects->data; |
131 | |
|
132 | 0 | now = time(NULL); |
133 | 0 | if (rec->created+MAX_QUIT_CLOSE_WAIT <= now || max <= now) { |
134 | | /* this one has waited enough */ |
135 | 0 | net_disconnect_remove(rec); |
136 | 0 | continue; |
137 | 0 | } |
138 | | |
139 | 0 | fd = g_io_channel_unix_get_fd(rec->handle); |
140 | 0 | FD_ZERO(&set); |
141 | 0 | FD_SET(fd, &set); |
142 | 0 | tv.tv_sec = first ? 0 : max-now; |
143 | 0 | tv.tv_usec = first ? 100000 : 0; |
144 | 0 | if (select(fd+1, &set, NULL, NULL, &tv) > 0 && |
145 | 0 | FD_ISSET(fd, &set)) { |
146 | | /* data coming .. check if we can close the handle */ |
147 | 0 | sig_disconnect(rec); |
148 | 0 | } else if (first) { |
149 | | /* Display the text when we have already waited |
150 | | for a while */ |
151 | 0 | printf("Please wait, waiting for servers to close " |
152 | 0 | "connections..\n"); |
153 | 0 | fflush(stdout); |
154 | |
|
155 | 0 | first = 0; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | } |