1 /*
2 dcc-send-limiter.c : Limit the transmit speed of DCC sends
3
4 For irssi 0.8+
5
6 compile:
7 export IRSSI=~/cvs/irssi
8 gcc dcc-send-limiter.c -o ~/.irssi/modules/libdcc_send_limiter.so -g -shared -I$IRSSI -I$IRSSI/src -I$IRSSI/src/core -I$IRSSI/src/irc/core -I$IRSSI/src/irc/dcc `glib-config --cflags` -O
9
10 usage:
11 /LOAD dcc_send_limiter
12
13 Copyright (C) 2001 Timo Sirainen
14
15 Modified 2002/12/31 by Piotr Krukowiecki (Happy New Year! ;))
16 * fixed unnecesary lag in sending data when send is resume
17 * sends that were started before the module was loaded
18 now are being limited as well
19
20 Modified 2001/07/04 by Martin Persson
21 * updated to only keep track of the last 30 sec
22
23 Modified 2001/07/01 by Martin Persson
24 * added speed send checks
25 * fixed crash when destroying dcc sends
26 that didn't contain any module data
27 * fixed crash when initiating dcc send
28
29
30 This program is free software; you can redistribute it and/or modify
31 it under the terms of the GNU General Public License as published by
32 the Free Software Foundation; either version 2 of the License, or
33 (at your option) any later version.
34
35 This program is distributed in the hope that it will be useful,
36 but WITHOUT ANY WARRANTY; without even the implied warranty of
37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 GNU General Public License for more details.
39
40 You should have received a copy of the GNU General Public License
41 along with this program; if not, write to the Free Software
42 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
43 */
44
45 #define MODULE_NAME "irc/dcc/limiter"
46 #define HAVE_CONFIG_H
47
48 #include "common.h"
49 #include "signals.h"
50 #include "network.h"
51 #include "settings.h"
52
53 #include "irc.h"
54 #include "dcc-send.h"
55
56 typedef struct {
57 int timeout_tag;
58
59 unsigned long skip_bytes;
60 unsigned long starttime;
61 unsigned long max_speed;
62 } MODULE_SEND_DCC_REC;
63
64 static void dcc_send_data(SEND_DCC_REC *dcc);
65
reset_dcc_send(SEND_DCC_REC * dcc)66 static void reset_dcc_send(SEND_DCC_REC *dcc)
67 {
68 MODULE_SEND_DCC_REC *mdcc;
69
70 if (g_slist_find(dcc_conns, dcc) == NULL) {
71 /* the DCC was closed during the wait */
72 return;
73 }
74
75 mdcc = MODULE_DATA(dcc);
76 g_source_remove(mdcc->timeout_tag);
77 mdcc->timeout_tag = -1;
78
79 dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE,
80 (GInputFunction) dcc_send_data, dcc);
81 }
82
sent_too_much(SEND_DCC_REC * dcc,MODULE_SEND_DCC_REC * mdcc)83 static int sent_too_much(SEND_DCC_REC *dcc, MODULE_SEND_DCC_REC *mdcc)
84 {
85 GTimeVal gtv;
86 unsigned long timediff, curtime;
87 unsigned long transfd, speed;
88
89 /* 0 == unlimited speed */
90 if (mdcc->max_speed == 0) return 0;
91
92 /* get time difference in milliseconds */
93 g_get_current_time(>v);
94 curtime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
95
96 transfd = (dcc->transfd - mdcc->skip_bytes);
97 timediff = curtime - mdcc->starttime + 1;
98 speed = ((transfd * 1000) / timediff);
99
100 /* reset speed counter every 30 seconds */
101 if (timediff >= 30000) {
102 mdcc->starttime = curtime;
103 mdcc->skip_bytes = dcc->transfd;
104 }
105
106 return (speed > (mdcc->max_speed * 1024));
107 }
108
109 /* input function: DCC SEND - we're ready to send more data */
dcc_send_data(SEND_DCC_REC * dcc)110 static void dcc_send_data(SEND_DCC_REC *dcc)
111 {
112 MODULE_SEND_DCC_REC *mdcc;
113 char buffer[512];
114 int ret, max_speed;
115 GTimeVal gtv;
116
117 mdcc = MODULE_DATA(dcc);
118
119 max_speed = settings_get_int("dcc_send_top_speed");
120 if (max_speed != mdcc->max_speed) {
121 /* speed setting has changed, calculate speed from current position
122 instead of from the start to eliminate speed boosts/slowdowns */
123
124 mdcc->max_speed = max_speed;
125 mdcc->skip_bytes = dcc->transfd;
126
127 g_get_current_time(>v);
128 mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
129 }
130
131 if (sent_too_much(dcc, mdcc)) {
132 /* disable calling this function for 1/10th of a second. */
133 g_source_remove(dcc->tagwrite);
134 dcc->tagwrite = -1;
135 mdcc->timeout_tag =
136 g_timeout_add(100, (GSourceFunc) reset_dcc_send, dcc);
137 return;
138 }
139
140 ret = read(dcc->fhandle, buffer, sizeof(buffer));
141 if (ret <= 0) {
142 /* no need to call this function anymore..
143 in fact it just eats all the cpu.. */
144 dcc->waitforend = TRUE;
145 g_source_remove(dcc->tagwrite);
146 dcc->tagwrite = -1;
147 return;
148 }
149
150 ret = net_transmit(dcc->handle, buffer, ret);
151 if (ret > 0) dcc->transfd += ret;
152 dcc->gotalldata = FALSE;
153
154 lseek(dcc->fhandle, dcc->transfd, SEEK_SET);
155
156 signal_emit("dcc transfer update", 1, dcc);
157 }
158
sig_dcc_connected(SEND_DCC_REC * dcc)159 static void sig_dcc_connected(SEND_DCC_REC *dcc)
160 {
161 MODULE_SEND_DCC_REC *mdcc;
162 GTimeVal gtv;
163
164 if (!IS_DCC_SEND(dcc))
165 return;
166
167 mdcc = g_new0(MODULE_SEND_DCC_REC, 1);
168 MODULE_DATA_SET(dcc, mdcc);
169 mdcc->timeout_tag = -1;
170 mdcc->skip_bytes = dcc->transfd; /* now it works correct with dcc resume - doesn't wait 30s with sending data */
171 mdcc->max_speed = settings_get_int("dcc_send_top_speed");
172
173 /* get starttime in milliseconds */
174 g_get_current_time(>v);
175 mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
176
177 g_source_remove(dcc->tagwrite);
178 dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE,
179 (GInputFunction) dcc_send_data, dcc);
180 }
181
sig_dcc_destroyed(SEND_DCC_REC * dcc)182 static void sig_dcc_destroyed(SEND_DCC_REC *dcc)
183 {
184 MODULE_SEND_DCC_REC *mdcc;
185
186 if (!IS_DCC_SEND(dcc))
187 return;
188
189 mdcc = MODULE_DATA(dcc);
190 if (mdcc != NULL) {
191 if (mdcc->timeout_tag != -1)
192 g_source_remove(mdcc->timeout_tag);
193
194 g_free(mdcc);
195 }
196 }
197
dcc_send_limiter_init(void)198 void dcc_send_limiter_init(void)
199 {
200 GSList *tmp;
201 settings_add_int("dcc", "dcc_send_top_speed", 30);
202
203 signal_add_last("dcc connected", (SIGNAL_FUNC) sig_dcc_connected);
204 signal_add_first("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
205
206 /* Limit already existing sends */
207 for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
208 SEND_DCC_REC *dcc = tmp->data;
209 if (!IS_DCC_SEND(dcc)) continue;
210 if (!dcc_is_connected(dcc)) continue;
211
212 sig_dcc_connected(dcc);
213 }
214
215 module_register("dcc_send_limiter", "core");
216 }
217
dcc_send_limiter_deinit(void)218 void dcc_send_limiter_deinit(void)
219 {
220 signal_remove("dcc connected", (SIGNAL_FUNC) sig_dcc_connected);
221 signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
222 }
223