Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff src/gt_node_cache.c @ 0:d39e1d0d75b6
initial add
author | paulo@hit-nxdomain.opendns.com |
---|---|
date | Sat, 20 Feb 2010 21:18:28 -0800 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_node_cache.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,459 @@ 1.4 +/* 1.5 + * $Id: gt_node_cache.c,v 1.11 2004/03/05 17:58:39 hipnod Exp $ 1.6 + * 1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 + 1.22 +#include "gt_node.h" 1.23 +#include "gt_node_cache.h" 1.24 + 1.25 +#include "file_cache.h" 1.26 + 1.27 +/*****************************************************************************/ 1.28 + 1.29 +#define MAX_RECENT (150) 1.30 +#define MAX_STABLE (30) 1.31 + 1.32 +#define MAX_STICKY_RECENT (150) 1.33 +#define MAX_STICKY_STABLE (30) 1.34 + 1.35 +/*****************************************************************************/ 1.36 + 1.37 +static List *recent; 1.38 +static List *stable; 1.39 + 1.40 +static List *sticky_recent; /* synced to disk */ 1.41 +static List *sticky_stable; /* like stable list, but nodes stay */ 1.42 + 1.43 +#if 0 1.44 +static List *compressible; 1.45 +#endif 1.46 + 1.47 +/*****************************************************************************/ 1.48 + 1.49 +static void ipv4_addr_init (struct ipv4_addr *addr, in_addr_t ip, 1.50 + in_port_t port) 1.51 +{ 1.52 + memset (addr, 0, sizeof (struct ipv4_addr)); 1.53 + 1.54 + addr->ip = ip; 1.55 + addr->port = port; 1.56 +} 1.57 + 1.58 +static void cached_node_init (struct cached_node *node, in_addr_t ipv4, 1.59 + in_port_t port, gt_node_class_t klass, 1.60 + time_t timestamp, time_t uptime, 1.61 + in_addr_t src_ip) 1.62 +{ 1.63 + memset (node, 0, sizeof (*node)); 1.64 + 1.65 + ipv4_addr_init (&node->addr, ipv4, port); 1.66 + 1.67 + node->klass = klass; 1.68 + node->timestamp = timestamp; 1.69 + node->uptime = uptime; 1.70 + node->src_ip = src_ip; 1.71 +} 1.72 + 1.73 +static int cmp_ipv4 (struct cached_node *a, struct cached_node *b) 1.74 +{ 1.75 + return memcmp (&a->addr, &b->addr, sizeof (a->addr)); 1.76 +} 1.77 + 1.78 +static int cmp_recent (struct cached_node *a, struct cached_node *b) 1.79 +{ 1.80 + return INTCMP (b->timestamp, a->timestamp); 1.81 +} 1.82 + 1.83 +static int cmp_stable (struct cached_node *a, struct cached_node *b) 1.84 +{ 1.85 + time_t a_time, b_time; 1.86 + 1.87 + /* 1.88 + * Assume the node will be up for as much time as it was up 1.89 + * already, and convert this to a timestamp. 1.90 + * 1.91 + * This makes a difference than just comparing the uptime 1.92 + * because the cache gets synced to disk. 1.93 + */ 1.94 + a_time = a->uptime * 2 + a->timestamp; 1.95 + b_time = b->uptime * 2 + b->timestamp; 1.96 + 1.97 + return INTCMP (b_time, a_time); 1.98 +} 1.99 + 1.100 +static List *add_list (List *list, size_t max_elements, CompareFunc func, 1.101 + struct cached_node *node) 1.102 +{ 1.103 + struct cached_node *new_node; 1.104 + struct cached_node *rm; 1.105 + List *dup; 1.106 + List *link; 1.107 + 1.108 + if ((dup = list_find_custom (list, node, (CompareFunc)cmp_ipv4))) 1.109 + { 1.110 + free (dup->data); 1.111 + list = list_remove_link (list, dup); 1.112 + } 1.113 + 1.114 + if (!(new_node = gift_memdup (node, sizeof (struct cached_node)))) 1.115 + return list; 1.116 + 1.117 + list = list_insert_sorted (list, func, new_node); 1.118 + 1.119 + /* 1.120 + * Truncate list at max_elements. 1.121 + */ 1.122 + link = list_nth (list, max_elements); 1.123 + rm = list_nth_data (link, 0); 1.124 + 1.125 + list = list_remove_link (list, link); 1.126 + free (rm); 1.127 + 1.128 + return list; 1.129 +} 1.130 + 1.131 +static void add_to_cache (struct cached_node *node) 1.132 +{ 1.133 + recent = add_list (recent, MAX_RECENT, 1.134 + (CompareFunc)cmp_recent, node); 1.135 + sticky_recent = add_list (sticky_recent, MAX_STICKY_RECENT, 1.136 + (CompareFunc)cmp_recent, node); 1.137 + 1.138 + if (node->uptime > 0) 1.139 + { 1.140 + stable = add_list (stable, MAX_STABLE, 1.141 + (CompareFunc)cmp_stable, node); 1.142 + sticky_stable = add_list (sticky_stable, MAX_STICKY_STABLE, 1.143 + (CompareFunc)cmp_stable, node); 1.144 + } 1.145 +} 1.146 + 1.147 +void gt_node_cache_add_ipv4 (in_addr_t ipv4, in_port_t port, 1.148 + gt_node_class_t klass, time_t timestamp, 1.149 + time_t uptime, in_addr_t src_ip) 1.150 +{ 1.151 + struct cached_node node; 1.152 + 1.153 + /* make sure we don't add nodes with class GT_NODE_NONE */ 1.154 + if (klass == GT_NODE_NONE) 1.155 + klass = GT_NODE_LEAF; 1.156 + 1.157 + cached_node_init (&node, ipv4, port, klass, timestamp, uptime, src_ip); 1.158 + add_to_cache (&node); 1.159 + 1.160 + /* don't put nodes that are already in the node list in the cache */ 1.161 + if (gt_node_lookup (ipv4, port)) 1.162 + gt_node_cache_del_ipv4 (ipv4, port); 1.163 +} 1.164 + 1.165 +static List *del_list (List *list, struct cached_node *node, 1.166 + in_addr_t ipv4, in_port_t port) 1.167 +{ 1.168 + List *link; 1.169 + 1.170 + if (!(link = list_find_custom (list, node, (CompareFunc)cmp_ipv4))) 1.171 + return list; 1.172 + 1.173 + free (link->data); 1.174 + return list_remove_link (list, link); 1.175 +} 1.176 + 1.177 +static void del_from_cache (in_addr_t ipv4, in_port_t port) 1.178 +{ 1.179 + struct cached_node node; 1.180 + 1.181 + cached_node_init (&node, ipv4, port, GT_NODE_NONE, 0, 0, 0); 1.182 + 1.183 + recent = del_list (recent, &node, ipv4, port); 1.184 + stable = del_list (stable, &node, ipv4, port); 1.185 +} 1.186 + 1.187 +void gt_node_cache_del_ipv4 (in_addr_t ipv4, in_port_t port) 1.188 +{ 1.189 + del_from_cache (ipv4, port); 1.190 +} 1.191 + 1.192 +/*****************************************************************************/ 1.193 + 1.194 +#if 0 1.195 +static int print_node (struct cached_node *node, String *s) 1.196 +{ 1.197 + char *src; 1.198 + 1.199 + src = STRDUP (net_ip_str (node->src_ip)); 1.200 + 1.201 + string_appendf (s, "[%s:%hu {%s}] ", net_ip_str (node->addr.ip), 1.202 + node->addr.port, src); 1.203 + free (src); 1.204 + 1.205 + return FALSE; 1.206 +} 1.207 + 1.208 +static void print_list (char *prefix, List *list) 1.209 +{ 1.210 + String *s; 1.211 + 1.212 + if (!(s = string_new (NULL, 0, 0, TRUE))) 1.213 + return; 1.214 + 1.215 + string_append (s, prefix); 1.216 + 1.217 + list_foreach (list, (ListForeachFunc)print_node, s); 1.218 + GT->dbg (GT, "%s", s->str); 1.219 + 1.220 + string_free (s); 1.221 +} 1.222 +#endif 1.223 + 1.224 +void gt_node_cache_trace (void) 1.225 +{ 1.226 +#if 0 1.227 + print_list ("recent: ", recent); 1.228 + print_list ("stable: ", stable); 1.229 + print_list ("sticky_recent: ", sticky_recent); 1.230 + print_list ("sticky_stable: ", sticky_stable); 1.231 +#endif 1.232 +} 1.233 + 1.234 +/*****************************************************************************/ 1.235 + 1.236 +/* return some nodes from the cache, and remove them */ 1.237 +size_t get_first (List **src_list, List **dst_list, size_t nr) 1.238 +{ 1.239 + struct cached_node *node; 1.240 + struct cached_node *dup; 1.241 + 1.242 + node = list_nth_data (*src_list, 0); 1.243 + 1.244 + if (!node || !(dup = gift_memdup (node, sizeof (*node)))) 1.245 + return nr; 1.246 + 1.247 + *dst_list = list_prepend (*dst_list, dup); 1.248 + nr--; 1.249 + 1.250 + gt_node_cache_del_ipv4 (node->addr.ip, node->addr.port); 1.251 + return nr; 1.252 +} 1.253 + 1.254 +/* 1.255 + * Remove some elements from the cache and return them in a list . 1.256 + * 1.257 + * The nodes and the list returned SHOULD be freed. 1.258 + */ 1.259 +List *gt_node_cache_get_remove (size_t nr) 1.260 +{ 1.261 + List *list = NULL; 1.262 + 1.263 + /* 1.264 + * We check the recent list first, and then if that's empty check the 1.265 + * stable list, so we don't end up checking the stable list all the time. 1.266 + */ 1.267 + while (nr > 0 && recent != NULL) 1.268 + nr = get_first (&recent, &list, nr); 1.269 + 1.270 + while (nr > 0 && stable != NULL) 1.271 + nr = get_first (&stable, &list, nr); 1.272 + 1.273 + return list; 1.274 +} 1.275 + 1.276 +/* 1.277 + * Return some elements from the cache in a list 1.278 + * 1.279 + * NOTE: the data in the list returned SHOULD NOT be freed 1.280 + */ 1.281 +List *gt_node_cache_get (size_t nr) 1.282 +{ 1.283 + List *list = NULL; 1.284 + size_t len; 1.285 + int index; 1.286 + 1.287 + len = list_length (sticky_recent); 1.288 + 1.289 + /* 1.290 + * If we don't have more than twice the number of nodes, just return an 1.291 + * offset in the list of recent nodes. 1.292 + * 1.293 + * This is done so we can use the simple (stupid) selection algorithm 1.294 + * below that would be inefficient otherwise. 1.295 + */ 1.296 + if (len / 2 < nr) 1.297 + return list_copy (list_nth (sticky_recent, MAX (0, len - nr))); 1.298 + 1.299 + while (nr > 0) 1.300 + { 1.301 + struct cached_node *node; 1.302 + 1.303 + index = (float)len * rand() / (RAND_MAX + 1.0); 1.304 + 1.305 + node = list_nth_data (sticky_recent, index); 1.306 + assert (node != NULL); 1.307 + 1.308 + if (list_find (list, node)) 1.309 + continue; 1.310 + 1.311 + list = list_append (list, node); 1.312 + nr--; 1.313 + } 1.314 + 1.315 + return list; 1.316 +} 1.317 + 1.318 +/*****************************************************************************/ 1.319 + 1.320 +static char *node_cache_file (const char *name) 1.321 +{ 1.322 + return gift_conf_path ("Gnutella/%s", name); 1.323 +} 1.324 + 1.325 +/* 1.326 + * Store the cache data in the dataset. The ip:port as a string 1.327 + * is used as a key. 1.328 + */ 1.329 +static BOOL write_line (struct cached_node *node, FileCache *cache) 1.330 +{ 1.331 + char *ip_port; 1.332 + char *line; 1.333 + 1.334 + ip_port = stringf_dup ("%s:%hu", net_ip_str (node->addr.ip), 1.335 + node->addr.port); 1.336 + 1.337 + if (!ip_port) 1.338 + return FALSE; 1.339 + 1.340 + line = stringf_dup ("%s %lu %lu %s", gt_node_class_str (node->klass), 1.341 + (long)node->timestamp, (long)node->uptime, 1.342 + net_ip_str (node->src_ip)); 1.343 + 1.344 + if (!line) 1.345 + { 1.346 + free (ip_port); 1.347 + return FALSE; 1.348 + } 1.349 + 1.350 + file_cache_insert (cache, ip_port, line); 1.351 + 1.352 + free (ip_port); 1.353 + free (line); 1.354 + 1.355 + return FALSE; 1.356 +} 1.357 + 1.358 +/* 1.359 + * Read in a line from a cache file. 1.360 + * 1.361 + * The ip:port is the key in the Dataset. The value 1.362 + * is everything else that dsecribe an entry in the node 1.363 + * cache, as a string. 1.364 + */ 1.365 +static void parse_line (ds_data_t *key, ds_data_t *value, void *udata) 1.366 +{ 1.367 + char *ip_port = key->data; 1.368 + char *str = value->data; 1.369 + in_addr_t ipv4; 1.370 + in_port_t port; 1.371 + time_t timestamp; 1.372 + time_t uptime; 1.373 + in_addr_t src_ip; 1.374 + char *klass; 1.375 + 1.376 + ipv4 = net_ip (string_sep (&ip_port, ":")); 1.377 + port = ATOUL (ip_port); 1.378 + 1.379 + if (ipv4 == 0 || ipv4 == INADDR_NONE || port == 0) 1.380 + return; 1.381 + 1.382 + /* NOTE: we ignore the class string for now */ 1.383 + klass = string_sep (&str, " "); 1.384 + timestamp = ATOUL (string_sep (&str, " ")); 1.385 + uptime = ATOUL (string_sep (&str, " ")); 1.386 + src_ip = net_ip (string_sep (&str, " ")); 1.387 + 1.388 + if (!klass || timestamp == 0) 1.389 + return; 1.390 + 1.391 + /* add it to the cache */ 1.392 + gt_node_cache_add_ipv4 (ipv4, port, GT_NODE_ULTRA, timestamp, uptime, 1.393 + src_ip); 1.394 +} 1.395 + 1.396 +static BOOL load_cache (char *name) 1.397 +{ 1.398 + FileCache *cache; 1.399 + char *file; 1.400 + 1.401 + file = node_cache_file (name); 1.402 + cache = file_cache_new (file); 1.403 + 1.404 + if (!cache) 1.405 + return FALSE; 1.406 + 1.407 + dataset_foreach (cache->d, DS_FOREACH(parse_line), NULL); 1.408 + 1.409 + file_cache_free (cache); 1.410 + return TRUE; 1.411 +} 1.412 + 1.413 +static BOOL save_cache (char *name, List *list) 1.414 +{ 1.415 + FileCache *cache; 1.416 + char *file; 1.417 + 1.418 + file = node_cache_file (name); 1.419 + cache = file_cache_new (file); 1.420 + 1.421 + /* flush the existing data (in memory, only) */ 1.422 + file_cache_flush (cache); 1.423 + 1.424 + /* save each entry in the node cache to the file cache */ 1.425 + list_foreach (list, (ListForeachFunc)write_line, cache); 1.426 + 1.427 + if (!file_cache_sync (cache)) 1.428 + { 1.429 + GT->DBGFN (GT, "error saving cache \"%s\": %s", name, GIFT_STRERROR()); 1.430 + return FALSE; 1.431 + } 1.432 + 1.433 + file_cache_free (cache); 1.434 + 1.435 + return TRUE; 1.436 +} 1.437 + 1.438 +void gt_node_cache_load (void) 1.439 +{ 1.440 + load_cache ("stable_nodes"); 1.441 + load_cache ("recent_nodes"); 1.442 +} 1.443 + 1.444 +void gt_node_cache_save (void) 1.445 +{ 1.446 + save_cache ("stable_nodes", sticky_stable); 1.447 + save_cache ("recent_nodes", sticky_recent); 1.448 +} 1.449 + 1.450 +/*****************************************************************************/ 1.451 + 1.452 +void gt_node_cache_init (void) 1.453 +{ 1.454 + gt_node_cache_load (); 1.455 +} 1.456 + 1.457 +void gt_node_cache_cleanup (void) 1.458 +{ 1.459 + gt_node_cache_save (); 1.460 + 1.461 + /* TODO: free node cache lists */ 1.462 +}