comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:697df372a170
1 /*
2 * $Id: gt_node_cache.c,v 1.11 2004/03/05 17:58:39 hipnod Exp $
3 *
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17 #include "gt_gnutella.h"
18
19 #include "gt_node.h"
20 #include "gt_node_cache.h"
21
22 #include "file_cache.h"
23
24 /*****************************************************************************/
25
26 #define MAX_RECENT (150)
27 #define MAX_STABLE (30)
28
29 #define MAX_STICKY_RECENT (150)
30 #define MAX_STICKY_STABLE (30)
31
32 /*****************************************************************************/
33
34 static List *recent;
35 static List *stable;
36
37 static List *sticky_recent; /* synced to disk */
38 static List *sticky_stable; /* like stable list, but nodes stay */
39
40 #if 0
41 static List *compressible;
42 #endif
43
44 /*****************************************************************************/
45
46 static void ipv4_addr_init (struct ipv4_addr *addr, in_addr_t ip,
47 in_port_t port)
48 {
49 memset (addr, 0, sizeof (struct ipv4_addr));
50
51 addr->ip = ip;
52 addr->port = port;
53 }
54
55 static void cached_node_init (struct cached_node *node, in_addr_t ipv4,
56 in_port_t port, gt_node_class_t klass,
57 time_t timestamp, time_t uptime,
58 in_addr_t src_ip)
59 {
60 memset (node, 0, sizeof (*node));
61
62 ipv4_addr_init (&node->addr, ipv4, port);
63
64 node->klass = klass;
65 node->timestamp = timestamp;
66 node->uptime = uptime;
67 node->src_ip = src_ip;
68 }
69
70 static int cmp_ipv4 (struct cached_node *a, struct cached_node *b)
71 {
72 return memcmp (&a->addr, &b->addr, sizeof (a->addr));
73 }
74
75 static int cmp_recent (struct cached_node *a, struct cached_node *b)
76 {
77 return INTCMP (b->timestamp, a->timestamp);
78 }
79
80 static int cmp_stable (struct cached_node *a, struct cached_node *b)
81 {
82 time_t a_time, b_time;
83
84 /*
85 * Assume the node will be up for as much time as it was up
86 * already, and convert this to a timestamp.
87 *
88 * This makes a difference than just comparing the uptime
89 * because the cache gets synced to disk.
90 */
91 a_time = a->uptime * 2 + a->timestamp;
92 b_time = b->uptime * 2 + b->timestamp;
93
94 return INTCMP (b_time, a_time);
95 }
96
97 static List *add_list (List *list, size_t max_elements, CompareFunc func,
98 struct cached_node *node)
99 {
100 struct cached_node *new_node;
101 struct cached_node *rm;
102 List *dup;
103 List *link;
104
105 if ((dup = list_find_custom (list, node, (CompareFunc)cmp_ipv4)))
106 {
107 free (dup->data);
108 list = list_remove_link (list, dup);
109 }
110
111 if (!(new_node = gift_memdup (node, sizeof (struct cached_node))))
112 return list;
113
114 list = list_insert_sorted (list, func, new_node);
115
116 /*
117 * Truncate list at max_elements.
118 */
119 link = list_nth (list, max_elements);
120 rm = list_nth_data (link, 0);
121
122 list = list_remove_link (list, link);
123 free (rm);
124
125 return list;
126 }
127
128 static void add_to_cache (struct cached_node *node)
129 {
130 recent = add_list (recent, MAX_RECENT,
131 (CompareFunc)cmp_recent, node);
132 sticky_recent = add_list (sticky_recent, MAX_STICKY_RECENT,
133 (CompareFunc)cmp_recent, node);
134
135 if (node->uptime > 0)
136 {
137 stable = add_list (stable, MAX_STABLE,
138 (CompareFunc)cmp_stable, node);
139 sticky_stable = add_list (sticky_stable, MAX_STICKY_STABLE,
140 (CompareFunc)cmp_stable, node);
141 }
142 }
143
144 void gt_node_cache_add_ipv4 (in_addr_t ipv4, in_port_t port,
145 gt_node_class_t klass, time_t timestamp,
146 time_t uptime, in_addr_t src_ip)
147 {
148 struct cached_node node;
149
150 /* make sure we don't add nodes with class GT_NODE_NONE */
151 if (klass == GT_NODE_NONE)
152 klass = GT_NODE_LEAF;
153
154 cached_node_init (&node, ipv4, port, klass, timestamp, uptime, src_ip);
155 add_to_cache (&node);
156
157 /* don't put nodes that are already in the node list in the cache */
158 if (gt_node_lookup (ipv4, port))
159 gt_node_cache_del_ipv4 (ipv4, port);
160 }
161
162 static List *del_list (List *list, struct cached_node *node,
163 in_addr_t ipv4, in_port_t port)
164 {
165 List *link;
166
167 if (!(link = list_find_custom (list, node, (CompareFunc)cmp_ipv4)))
168 return list;
169
170 free (link->data);
171 return list_remove_link (list, link);
172 }
173
174 static void del_from_cache (in_addr_t ipv4, in_port_t port)
175 {
176 struct cached_node node;
177
178 cached_node_init (&node, ipv4, port, GT_NODE_NONE, 0, 0, 0);
179
180 recent = del_list (recent, &node, ipv4, port);
181 stable = del_list (stable, &node, ipv4, port);
182 }
183
184 void gt_node_cache_del_ipv4 (in_addr_t ipv4, in_port_t port)
185 {
186 del_from_cache (ipv4, port);
187 }
188
189 /*****************************************************************************/
190
191 #if 0
192 static int print_node (struct cached_node *node, String *s)
193 {
194 char *src;
195
196 src = STRDUP (net_ip_str (node->src_ip));
197
198 string_appendf (s, "[%s:%hu {%s}] ", net_ip_str (node->addr.ip),
199 node->addr.port, src);
200 free (src);
201
202 return FALSE;
203 }
204
205 static void print_list (char *prefix, List *list)
206 {
207 String *s;
208
209 if (!(s = string_new (NULL, 0, 0, TRUE)))
210 return;
211
212 string_append (s, prefix);
213
214 list_foreach (list, (ListForeachFunc)print_node, s);
215 GT->dbg (GT, "%s", s->str);
216
217 string_free (s);
218 }
219 #endif
220
221 void gt_node_cache_trace (void)
222 {
223 #if 0
224 print_list ("recent: ", recent);
225 print_list ("stable: ", stable);
226 print_list ("sticky_recent: ", sticky_recent);
227 print_list ("sticky_stable: ", sticky_stable);
228 #endif
229 }
230
231 /*****************************************************************************/
232
233 /* return some nodes from the cache, and remove them */
234 size_t get_first (List **src_list, List **dst_list, size_t nr)
235 {
236 struct cached_node *node;
237 struct cached_node *dup;
238
239 node = list_nth_data (*src_list, 0);
240
241 if (!node || !(dup = gift_memdup (node, sizeof (*node))))
242 return nr;
243
244 *dst_list = list_prepend (*dst_list, dup);
245 nr--;
246
247 gt_node_cache_del_ipv4 (node->addr.ip, node->addr.port);
248 return nr;
249 }
250
251 /*
252 * Remove some elements from the cache and return them in a list .
253 *
254 * The nodes and the list returned SHOULD be freed.
255 */
256 List *gt_node_cache_get_remove (size_t nr)
257 {
258 List *list = NULL;
259
260 /*
261 * We check the recent list first, and then if that's empty check the
262 * stable list, so we don't end up checking the stable list all the time.
263 */
264 while (nr > 0 && recent != NULL)
265 nr = get_first (&recent, &list, nr);
266
267 while (nr > 0 && stable != NULL)
268 nr = get_first (&stable, &list, nr);
269
270 return list;
271 }
272
273 /*
274 * Return some elements from the cache in a list
275 *
276 * NOTE: the data in the list returned SHOULD NOT be freed
277 */
278 List *gt_node_cache_get (size_t nr)
279 {
280 List *list = NULL;
281 size_t len;
282 int index;
283
284 len = list_length (sticky_recent);
285
286 /*
287 * If we don't have more than twice the number of nodes, just return an
288 * offset in the list of recent nodes.
289 *
290 * This is done so we can use the simple (stupid) selection algorithm
291 * below that would be inefficient otherwise.
292 */
293 if (len / 2 < nr)
294 return list_copy (list_nth (sticky_recent, MAX (0, len - nr)));
295
296 while (nr > 0)
297 {
298 struct cached_node *node;
299
300 index = (float)len * rand() / (RAND_MAX + 1.0);
301
302 node = list_nth_data (sticky_recent, index);
303 assert (node != NULL);
304
305 if (list_find (list, node))
306 continue;
307
308 list = list_append (list, node);
309 nr--;
310 }
311
312 return list;
313 }
314
315 /*****************************************************************************/
316
317 static char *node_cache_file (const char *name)
318 {
319 return gift_conf_path ("Gnutella/%s", name);
320 }
321
322 /*
323 * Store the cache data in the dataset. The ip:port as a string
324 * is used as a key.
325 */
326 static BOOL write_line (struct cached_node *node, FileCache *cache)
327 {
328 char *ip_port;
329 char *line;
330
331 ip_port = stringf_dup ("%s:%hu", net_ip_str (node->addr.ip),
332 node->addr.port);
333
334 if (!ip_port)
335 return FALSE;
336
337 line = stringf_dup ("%s %lu %lu %s", gt_node_class_str (node->klass),
338 (long)node->timestamp, (long)node->uptime,
339 net_ip_str (node->src_ip));
340
341 if (!line)
342 {
343 free (ip_port);
344 return FALSE;
345 }
346
347 file_cache_insert (cache, ip_port, line);
348
349 free (ip_port);
350 free (line);
351
352 return FALSE;
353 }
354
355 /*
356 * Read in a line from a cache file.
357 *
358 * The ip:port is the key in the Dataset. The value
359 * is everything else that dsecribe an entry in the node
360 * cache, as a string.
361 */
362 static void parse_line (ds_data_t *key, ds_data_t *value, void *udata)
363 {
364 char *ip_port = key->data;
365 char *str = value->data;
366 in_addr_t ipv4;
367 in_port_t port;
368 time_t timestamp;
369 time_t uptime;
370 in_addr_t src_ip;
371 char *klass;
372
373 ipv4 = net_ip (string_sep (&ip_port, ":"));
374 port = ATOUL (ip_port);
375
376 if (ipv4 == 0 || ipv4 == INADDR_NONE || port == 0)
377 return;
378
379 /* NOTE: we ignore the class string for now */
380 klass = string_sep (&str, " ");
381 timestamp = ATOUL (string_sep (&str, " "));
382 uptime = ATOUL (string_sep (&str, " "));
383 src_ip = net_ip (string_sep (&str, " "));
384
385 if (!klass || timestamp == 0)
386 return;
387
388 /* add it to the cache */
389 gt_node_cache_add_ipv4 (ipv4, port, GT_NODE_ULTRA, timestamp, uptime,
390 src_ip);
391 }
392
393 static BOOL load_cache (char *name)
394 {
395 FileCache *cache;
396 char *file;
397
398 file = node_cache_file (name);
399 cache = file_cache_new (file);
400
401 if (!cache)
402 return FALSE;
403
404 dataset_foreach (cache->d, DS_FOREACH(parse_line), NULL);
405
406 file_cache_free (cache);
407 return TRUE;
408 }
409
410 static BOOL save_cache (char *name, List *list)
411 {
412 FileCache *cache;
413 char *file;
414
415 file = node_cache_file (name);
416 cache = file_cache_new (file);
417
418 /* flush the existing data (in memory, only) */
419 file_cache_flush (cache);
420
421 /* save each entry in the node cache to the file cache */
422 list_foreach (list, (ListForeachFunc)write_line, cache);
423
424 if (!file_cache_sync (cache))
425 {
426 GT->DBGFN (GT, "error saving cache \"%s\": %s", name, GIFT_STRERROR());
427 return FALSE;
428 }
429
430 file_cache_free (cache);
431
432 return TRUE;
433 }
434
435 void gt_node_cache_load (void)
436 {
437 load_cache ("stable_nodes");
438 load_cache ("recent_nodes");
439 }
440
441 void gt_node_cache_save (void)
442 {
443 save_cache ("stable_nodes", sticky_stable);
444 save_cache ("recent_nodes", sticky_recent);
445 }
446
447 /*****************************************************************************/
448
449 void gt_node_cache_init (void)
450 {
451 gt_node_cache_load ();
452 }
453
454 void gt_node_cache_cleanup (void)
455 {
456 gt_node_cache_save ();
457
458 /* TODO: free node cache lists */
459 }