00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef lint
00021 static const char rcsid[] = "$Id: throttle.c,v 1.5 2004-10-17 05:14:06 stdarg Exp $";
00022 #endif
00023
00024 #include <eggdrop/eggdrop.h>
00025 #include <unistd.h>
00026
00027 #define THROTTLE_LEVEL SOCKBUF_LEVEL_THROTTLE
00028
00029 typedef struct throttle {
00030 struct throttle *next, *prev;
00031 char *buf;
00032 int len;
00033 int idx;
00034 int bytes_in, bytes_out;
00035 int speed_in, speed_out;
00036 } throttle_t;
00037
00038 static throttle_t *throttle_head = NULL;
00039 static throttle_t *throttle_next = NULL;
00040 static int throttle_timer = -1;
00041
00042 static void throttle_remove(throttle_t *t);
00043
00044 static int throttle_on_read(void *client_data, int idx, char *data, int len)
00045 {
00046 throttle_t *t = client_data;
00047
00048 t->bytes_in += len;
00049 if (t->speed_in > 0 && t->bytes_in > t->speed_in) {
00050 sockbuf_noread(idx);
00051 }
00052 return sockbuf_on_read(idx, THROTTLE_LEVEL, data, len);
00053 }
00054
00055 static int throttle_on_write(void *client_data, int idx, const char *data, int len)
00056 {
00057 throttle_t *t = client_data;
00058
00059 if (t->speed_out <= 0) return sockbuf_on_write(idx, THROTTLE_LEVEL, data, len);
00060
00061 t->buf = realloc(t->buf, t->len + len);
00062 memcpy(t->buf + t->len, data, len);
00063 t->len += len;
00064
00065 return(0);
00066 }
00067
00068 static int throttle_on_written(void *client_data, int idx, int len, int remaining)
00069 {
00070 throttle_t *t = client_data;
00071
00072 return sockbuf_on_written(idx, THROTTLE_LEVEL, len, remaining + t->len);
00073 }
00074
00075 static int throttle_on_delete(void *client_data, int idx)
00076 {
00077 throttle_off(idx);
00078 return(0);
00079 }
00080
00081 static sockbuf_filter_t throttle_filter = {
00082 "throttle",
00083 THROTTLE_LEVEL,
00084 NULL, NULL, NULL,
00085 throttle_on_read, throttle_on_write, throttle_on_written,
00086 NULL, throttle_on_delete
00087 };
00088
00089 static throttle_t *throttle_lookup(int idx)
00090 {
00091 throttle_t *t;
00092
00093 for (t = throttle_head; t; t = t->next) {
00094 if (t->idx == idx) break;
00095 }
00096 return(t);
00097 }
00098
00099 static void throttle_add(throttle_t *t)
00100 {
00101 t->next = throttle_head;
00102 t->prev = NULL;
00103 if (throttle_head) throttle_head->prev = t;
00104 throttle_head = t;
00105 }
00106
00107 static void throttle_remove(throttle_t *t)
00108 {
00109
00110
00111 if (throttle_next == t) {
00112 throttle_next = t->next;
00113 }
00114
00115 if (t->prev) t->prev->next = t->next;
00116 else throttle_head = t->next;
00117
00118 if (t->next) t->next->prev = t->prev;
00119 }
00120
00121
00122 static int throttle_secondly(void *ignore)
00123 {
00124 throttle_t *t;
00125 int avail, r, out;
00126
00127 for (t = throttle_head; t; t = throttle_next) {
00128 throttle_next = t->next;
00129 t->bytes_in -= t->speed_in;
00130 if (t->bytes_in < 0) t->bytes_in = 0;
00131 else if (t->bytes_in < t->speed_in) sockbuf_read(t->idx);
00132
00133 out = 0;
00134 while (t->len && out < t->speed_out) {
00135
00136 avail = t->speed_out - out;
00137 if (avail > t->len) avail = t->len;
00138
00139 r = sockbuf_on_write(t->idx, THROTTLE_LEVEL, t->buf, avail);
00140 if (r < 0) break;
00141
00142 memmove(t->buf, t->buf+avail, t->len-avail);
00143 t->len -= avail;
00144 out += avail;
00145
00146 if (r > 0) sockbuf_on_written(t->idx, THROTTLE_LEVEL, r, t->len + avail - r);
00147
00148 }
00149 }
00150 return(0);
00151 }
00152
00153 int throttle_on(int idx)
00154 {
00155 throttle_t *t;
00156 egg_timeval_t howlong;
00157
00158 t = calloc(1, sizeof(*t));
00159 t->idx = idx;
00160 throttle_add(t);
00161 sockbuf_attach_filter(idx, &throttle_filter, t);
00162
00163 if (throttle_timer < 0) {
00164 howlong.sec = 1;
00165 howlong.usec = 0;
00166 throttle_timer = timer_create_repeater(&howlong, "bandwidth throttler", throttle_secondly);
00167 }
00168 return(0);
00169 }
00170
00171 int throttle_off(int idx)
00172 {
00173 throttle_t *t;
00174
00175 t = throttle_lookup(idx);
00176 if (!t) return(-1);
00177
00178 throttle_remove(t);
00179 if (sockbuf_isvalid(idx)) {
00180 sockbuf_detach_filter(idx, &throttle_filter, NULL);
00181 if (t->len) sockbuf_on_write(idx, THROTTLE_LEVEL, t->buf, t->len);
00182 }
00183
00184 if (t->buf) free(t->buf);
00185 free(t);
00186
00187 if (!throttle_head) {
00188 timer_destroy(throttle_timer);
00189 throttle_timer = -1;
00190 }
00191 return(0);
00192 }
00193
00194 int throttle_set(int idx, int dir, int speed)
00195 {
00196 throttle_t *t;
00197 int r;
00198
00199 t = throttle_lookup(idx);
00200 if (!t) return(-1);
00201 if (dir) {
00202 t->speed_out = speed;
00203 if (speed <= 0 && t->len) {
00204 r = sockbuf_on_write(idx, THROTTLE_LEVEL, t->buf, t->len);
00205 if (r > 0) sockbuf_on_written(idx, THROTTLE_LEVEL, r, t->len - r);
00206 t->len = 0;
00207 }
00208 }
00209 else t->speed_in = speed;
00210 return(0);
00211 }