• R/O
  • SSH
  • HTTPS

ogup: 提交


Commit MetaInfo

修訂37 (tree)
時間2021-12-07 03:01:35
作者mateuszviste

Log Message

scheduler for checking servers in an organized and efficient way + cache for recent checks + lots of cleanups

Change Summary

差異

--- trunk/gopherjoker/glist.c (revision 36)
+++ trunk/gopherjoker/glist.c (revision 37)
@@ -54,7 +54,7 @@
5454 }
5555
5656
57-struct gopherlist *glist_findhost(struct gopherlist *glist, const char *host, const unsigned short port) {
57+struct gopherlist *glist_findhostport(struct gopherlist *glist, const char *host, const unsigned short port) {
5858 struct gopherlist *node;
5959 for (node = glist; node != NULL; node = node->next) {
6060 if ((node->port == port) && (strcasecmp(node->fqdn, host) == 0)) return(node);
@@ -65,11 +65,11 @@
6565
6666 /* add new host to glist, unless said host:port pair already exists there.
6767 * returns pointer to the new (or already existing) struct. NULL on error. */
68-struct gopherlist *glist_addnewhost(struct gopherlist **glist, unsigned long *glistlen, const char *newhost, unsigned short newport, time_t failedsince) {
68+struct gopherlist *glist_addnewhostport(struct gopherlist **glist, const char *newhost, unsigned short newport, time_t failedsince) {
6969 struct gopherlist *node;
7070
7171 /* is entry in list already? */
72- node = glist_findhost(*glist, newhost, newport);
72+ node = glist_findhostport(*glist, newhost, newport);
7373 if (node != NULL) return(node);
7474
7575 /* add it */
@@ -83,7 +83,6 @@
8383 node->next = *glist;
8484 if (*glist != NULL) (*glist)->prev = node;
8585 *glist = node;
86- *glistlen += 1;
8786 return(*glist);
8887 }
8988
@@ -96,11 +95,10 @@
9695 }
9796
9897
99-struct gopherlist *glist_node_unchain(struct gopherlist *glist, struct gopherlist *node, unsigned long *glistlen) {
98+struct gopherlist *glist_node_unchain(struct gopherlist *glist, struct gopherlist *node) {
10099 if (node->prev == NULL) {
101100 glist = node->next;
102101 glist->prev = NULL;
103- *glistlen -= 1;
104102 return(glist);
105103 }
106104 node->prev->next = node->next;
@@ -107,7 +105,6 @@
107105 if (node->next != NULL) {
108106 node->next->prev = node->prev;
109107 }
110- *glistlen -= 1;
111108 return(glist);
112109 }
113110
--- trunk/gopherjoker/glist.h (revision 36)
+++ trunk/gopherjoker/glist.h (revision 37)
@@ -11,6 +11,7 @@
1111
1212 struct gopherlist {
1313 time_t failedsince;
14+ time_t nextcheck;
1415 struct gopherlist *next;
1516 struct gopherlist *prev;
1617 unsigned short port;
@@ -26,15 +27,15 @@
2627
2728 void glist_free(struct gopherlist *glist);
2829
29-struct gopherlist *glist_findhost(struct gopherlist *glist, const char *host, const unsigned short port);
30+struct gopherlist *glist_findhostport(struct gopherlist *glist, const char *host, const unsigned short port);
3031
3132 /* add new host to glist, unless said host:port pair already exists there.
3233 * returns pointer to the new (or already existing) struct. NULL on error. */
33-struct gopherlist *glist_addnewhost(struct gopherlist **glist, unsigned long *glistlen, const char *newhost, unsigned short newport, time_t failedsince);
34+struct gopherlist *glist_addnewhostport(struct gopherlist **glist, const char *newhost, unsigned short newport, time_t failedsince);
3435
3536 void glist_node_free(struct gopherlist **node);
3637
37-struct gopherlist *glist_node_unchain(struct gopherlist *glist, struct gopherlist *node, unsigned long *glistlen);
38+struct gopherlist *glist_node_unchain(struct gopherlist *glist, struct gopherlist *node);
3839
3940 struct gopherlist *glist_node_dup(struct gopherlist *node);
4041
--- trunk/gopherjoker/gopher.c (revision 36)
+++ trunk/gopherjoker/gopher.c (revision 37)
@@ -23,23 +23,22 @@
2323 int r;
2424 struct timeval t;
2525 fd_set selset;
26- for (;;) {
27- r = connect(s, addr, addrlen);
28- if (r == 0) return(0);
29- if (errno != EINPROGRESS) return(r);
30- FD_ZERO(&selset);
31- FD_SET(s, &selset);
32- t.tv_sec = timeout;
33- t.tv_usec = 0;
34- r = select(s + 1, NULL, &selset, NULL, &t);
35- if (r < 0) return(-3);
36- r = connect(s, addr, addrlen);
37- return(r);
38- }
26+
27+ r = connect(s, addr, addrlen);
28+ if (r == 0) return(0);
29+ if (errno != EINPROGRESS) return(r);
30+ FD_ZERO(&selset);
31+ FD_SET(s, &selset);
32+ t.tv_sec = timeout;
33+ t.tv_usec = 0;
34+ r = select(s + 1, NULL, &selset, NULL, &t);
35+ if (r < 0) return(-3);
36+ r = connect(s, addr, addrlen);
37+ return(r);
3938 }
4039
4140
42-long gopher_fetch(char *buff, size_t buffsz, const char *host, unsigned short port, const char *selector, char *ipstr) {
41+long gopher_fetch(char *buff, size_t buffsz, const char *host, unsigned short port, const char *selector, char *ipstr, size_t ipstrsz) {
4342 int sock = -1;
4443 size_t len;
4544 int flags;
@@ -46,6 +45,8 @@
4645 time_t timeout;
4746 struct addrinfo *addr, *addrptr;
4847
48+ ipstr[0] = 0;
49+
4950 /* resolve host & connect */
5051 if (getaddrinfo(host, NULL, NULL, &addr) != 0) return(-1);
5152 for (addrptr = addr; addrptr != NULL; addrptr = addrptr->ai_next) {
@@ -61,7 +62,7 @@
6162 /* try to connect */
6263 if (connect_nonblocking(sock, addrptr->ai_addr, addrptr->ai_addrlen, 10) == 0) {
6364 /* fill ipstr and continue */
64- inet_ntop(addrptr->ai_family, addrptr->ai_addr, ipstr, addrptr->ai_addrlen);
65+ getnameinfo(addrptr->ai_addr, addrptr->ai_addrlen, ipstr, (socklen_t)ipstrsz, NULL, 0, NI_NUMERICHOST);
6566 break;
6667 } else { /* close sock and try next option (if any) */
6768 close(sock);
--- trunk/gopherjoker/gopher.h (revision 36)
+++ trunk/gopherjoker/gopher.h (revision 37)
@@ -7,7 +7,7 @@
77 #ifndef GOPHER_H
88 #define GOPHER_H
99
10-long gopher_fetch(char *buff, size_t buffsz, const char *host, unsigned short port, const char *selector, char *ipstr);
10+long gopher_fetch(char *buff, size_t buffsz, const char *host, unsigned short port, const char *selector, char *ipstr, size_t ipstrsz);
1111
1212 /* parse a gopher menu line entry and fill host, port and selector accordingly.
1313 * returns 0 on success, non-zero otherwise. */
--- trunk/gopherjoker/gopherjoker.c (revision 36)
+++ trunk/gopherjoker/gopherjoker.c (revision 37)
@@ -2,6 +2,7 @@
22 * gopherjoker, part of the Observable Gopherspace Universe Project
33 * Copyright (C) 2019-2021 Mateusz Viste
44 *
5+ * 2021-12-06: scheduler for probing servers + internal cache of recent checks
56 * 2021-12-04: added --saveperiod and --waitperiod cmdline parameters
67 * 2021-12-03: new hosts can be fed to gopherjoker through /tmp/ogup/
78 * 2020-01-13: added recent history so joker won't revisit places too often
@@ -35,7 +36,6 @@
3536
3637 #include <dirent.h> /* opendir() */
3738 #include <errno.h>
38-#include <netdb.h> /* INET6_ADDRSTRLEN */
3939 #include <stdio.h> /* printf(), fopen(), ... */
4040 #include <stdlib.h> /* calloc(), rand() */
4141 #include <string.h> /* strcpy() */
@@ -52,36 +52,22 @@
5252 #define NEWHOSTSDIR "/tmp/ogup/"
5353 #define MAXFAILTIME (3600 * 24 * 60)
5454 #define TTLINIT 64
55+#define CHECKPERIOD (3600 * 24)
56+#define CHECKPERIOD_FAST (3600 * 2)
5557
5658 static int WAITPERIOD = WAITPERIOD_DEFAULT;
5759 static int SAVEPERIOD = SAVEPERIOD_DEFAULT;
5860
5961
60-/* used to avoid going back to same url too often */
61-#define RECENT_HISTORY_SZ 64
62-static uint32_t glob_recenthistory[RECENT_HISTORY_SZ];
63-static int glob_recenthistoryptr = 0;
64-
65-
6662 /**************** FUNCTIONS ****************/
6763
6864
69-/* returns 0 on success (glist added to recent history), -1 on error (entry
70- * already present) */
71-static int add_glist_to_recent_history_if_not_present(const struct gopherlist *node) {
72- uint32_t hash;
73- int i;
74- if (node == NULL) return(-1);
75- hash = glist_node_hash(node);
76- /* is it in history already? */
77- for (i = 0; i < RECENT_HISTORY_SZ; i++) {
78- if (glob_recenthistory[i] == hash) return(-1);
79- }
80- /* add it */
81- glob_recenthistory[glob_recenthistoryptr] = hash;
82- glob_recenthistoryptr++; /* advance ptr */
83- glob_recenthistoryptr %= RECENT_HISTORY_SZ;
84- return(0);
65+/* return a human date/time string based on time_t timestamp */
66+static char *epoch2human(time_t timestamp) {
67+ static char buff[64];
68+ struct tm *t = gmtime(&timestamp);
69+ strftime(buff, sizeof(buff), "%F %R", t);
70+ return(buff);
8571 }
8672
8773
@@ -121,13 +107,12 @@
121107 }
122108
123109
124-static struct gopherlist *loaddb(const char *fname, unsigned long *glistlen) {
110+static struct gopherlist *loaddb(const char *fname) {
125111 FILE *fd;
126112 int i;
127113 char *ptrs[4];
128114 char lbuf[256];
129115 struct gopherlist *res = NULL;
130- *glistlen = 0;
131116 fd = fopen(fname, "rb");
132117 if (fd == NULL) return(NULL);
133118 for (;;) {
@@ -138,10 +123,9 @@
138123 * 1 port
139124 * 2 failedsince (time_t) */
140125
141- if (glist_addnewhost(&res, glistlen, ptrs[0], (unsigned short)atoi(ptrs[1]), atol(ptrs[2])) == NULL) {
126+ if (glist_addnewhostport(&res, ptrs[0], (unsigned short)atoi(ptrs[1]), atol(ptrs[2])) == NULL) {
142127 /* on error, free list and quit */
143128 glist_free(res);
144- *glistlen = 0;
145129 return(NULL);
146130 }
147131 }
@@ -217,7 +201,7 @@
217201
218202
219203 /* insert any newly-subnmitted servers to memory database (and remove the new host file) */
220-static struct gopherlist *loadextrahosts(struct gopherlist *glist, const char *dir, unsigned long *glistlen) {
204+static struct gopherlist *loadextrahosts(struct gopherlist *glist, const char *dir) {
221205 char fname[128];
222206 char buff[128];
223207 DIR *d;
@@ -235,10 +219,10 @@
235219 printf("loading new host file: %s\n", buff);
236220 port = get_server_from_file(buff, sizeof(buff), fname);
237221 if (port > 0) {
238- if (glist_addnewhost(&glist, glistlen, buff, (unsigned short)port, 1) != NULL) {
222+ if (glist_addnewhostport(&glist, buff, (unsigned short)port, 1) != NULL) {
239223 printf("added host: %s:%d\r\n", buff, port);
240224 } else {
241- puts("glist_addnewhost() call failed");
225+ puts("glist_addnewhostport() call failed");
242226 }
243227 }
244228 /* remove file and go to next one */
@@ -250,29 +234,54 @@
250234 }
251235
252236
253-/* pick a random node from glist, but avoid recently picked entries */
254-static struct gopherlist *pickrandhostfromlist(struct gopherlist *glist, unsigned long glistlen) {
255- static long choice;
256- long i;
257- int try;
258- if (glistlen == 0) return(NULL);
259- for (try = 0; try < 5; try++) {
260- choice += rand();
261- choice %= glistlen;
262- for (i = 0; i < choice; i++) {
263- if (glist == NULL) return(NULL);
264- glist = glist->next;
265- }
266- if (add_glist_to_recent_history_if_not_present(glist) == 0) {
267- return(glist);
268- } else if (glist != NULL) {
269- printf("NOTICE: picked selector %s:%u'%s' at first, but rejected as it is found in recent history already\n", glist->fqdn, glist->port, glist->selector);
270- }
237+/* pick a glist node that needs to be probed */
238+static struct gopherlist *pickhostfromlist(struct gopherlist *glist) {
239+ struct gopherlist *gptr, *res;
240+ time_t now = time(NULL);
241+
242+ if (glist == NULL) return(NULL);
243+
244+ /* find the most late entry */
245+ res = glist;
246+ for (gptr = glist; gptr != NULL; gptr = gptr->next) {
247+ if (gptr->nextcheck < res->nextcheck) res = gptr;
271248 }
272- return(NULL);
249+
250+ /* is it late at all? */
251+ if (res->nextcheck > now) {
252+ printf("pickhostfromlist(): found no candidates to be checked (next candidate is %s:%u to be checked at %s\n", res->fqdn, res->port, epoch2human(res->nextcheck));
253+ return(NULL);
254+ }
255+
256+ if (res->nextcheck == 0) {
257+ printf("pickhostfromlist(): %s:%u needs to be checked (never checked yet)\n", res->fqdn, res->port);
258+ } else {
259+ printf("pickhostfromlist(): %s:%u needs to be checked (late by %ds)\n", res->fqdn, res->port, (int)(now - res->nextcheck));
260+ }
261+ return(res);
273262 }
274263
275264
265+static struct gopherlist *pickrandhostfromlist(struct gopherlist *glist) {
266+ struct gopherlist *gptr;
267+ unsigned long cnt;
268+ unsigned long i;
269+
270+ if (glist == NULL) return(NULL);
271+
272+ /* count how many entries I have */
273+ cnt = 0;
274+ for (gptr = glist; gptr != NULL; gptr = gptr->next) cnt++;
275+
276+ /* random entry id */
277+ i = (unsigned long)rand() % cnt;
278+
279+ /* fast-forward to the given entry and return it */
280+ while (i--) glist = glist->next;
281+ return(glist);
282+}
283+
284+
276285 static int ishostvalid(const char *host) {
277286 unsigned short i;
278287 for (i = 0; host[i] != 0; i++) {
@@ -336,6 +345,64 @@
336345 }
337346
338347
348+/* a cached wrapper around gopher_fetch() */
349+static long cached_gopher_fetch(char *buff, size_t buffsz, const char *host, unsigned short port, const char *selector, char *ipstr, size_t ipstrsz) {
350+ long res;
351+ int i;
352+ char id[512];
353+ static unsigned short nextentry;
354+
355+ static struct goph_cache {
356+ char *id;
357+ char *ipstr;
358+ char *data;
359+ long bytes;
360+ } CACHE[TTLINIT];
361+
362+ /* compute a string that contains the host/port/selector tuple */
363+ snprintf(id, sizeof(id), "%s|%u|%s", host, port, (selector == NULL)?"":selector);
364+
365+ /* scan my cache, perhaps I have the content already */
366+ for (i = 0; i < TTLINIT; i++) {
367+ if (CACHE[i].id == NULL) continue;
368+ if (strcasecmp(CACHE[i].id, id) != 0) continue;
369+ /* found match! */
370+ printf("found id='%s' in CACHE[%d] (%ld bytes)\n", id, i, CACHE[i].bytes);
371+ if (CACHE[i].bytes > 0) memcpy(buff, CACHE[i].data, (unsigned long)(CACHE[i].bytes));
372+ if (CACHE[i].ipstr) strcpy(ipstr, CACHE[i].ipstr);
373+ return(CACHE[i].bytes);
374+ }
375+
376+ /* not found - fetch it for real */
377+ res = gopher_fetch(buff, buffsz, host, port, selector, ipstr, ipstrsz);
378+
379+ /* free old fields of the cache entry */
380+ free(CACHE[nextentry].id);
381+ free(CACHE[nextentry].ipstr);
382+ free(CACHE[nextentry].data);
383+ memset(&(CACHE[nextentry]), 0, sizeof(struct goph_cache));
384+
385+ /* write result to cache */
386+ CACHE[nextentry].id = strdup(id);
387+ CACHE[nextentry].ipstr = strdup(ipstr);
388+ if (res > 0) {
389+ CACHE[nextentry].data = malloc((unsigned long)res);
390+ memcpy(CACHE[nextentry].data, buff, (unsigned long)res);
391+ } else {
392+ CACHE[nextentry].data = NULL;
393+ }
394+ CACHE[nextentry].bytes = res;
395+
396+ /* */
397+ nextentry++;
398+ nextentry %= TTLINIT;
399+
400+ /* return */
401+ return(res);
402+}
403+
404+
405+
339406 /**************** MAIN ****************/
340407
341408 int main(int argc, char **argv) {
@@ -343,13 +410,11 @@
343410 time_t nextdbsave = 0;
344411 struct gopherlist *glist, *mlist, *gnode;
345412 struct gopherlist *curhost = NULL;
346- char curhost_ipaddr[INET6_ADDRSTRLEN];
413+ char curhost_ipaddr[64];
347414 int ttl = 0;
348415 char buff[0xffff];
349416 long bufflen;
350417 char *dbfile, *dbfilecnt;
351- unsigned long glistlen;
352- unsigned long i;
353418
354419 if (parseargs(argc, argv, &dbfile, &dbfilecnt) != 0) {
355420 printf("usage: gopherjoker [options] dbfile.csv dbcount.csv\n"
@@ -365,7 +430,7 @@
365430 }
366431
367432 /* load db file */
368- glist = loaddb(dbfile, &glistlen);
433+ glist = loaddb(dbfile);
369434
370435 /* init random engine */
371436 srand((unsigned int)time(NULL));
@@ -379,7 +444,7 @@
379444 nextaction = time(NULL) + WAITPERIOD;
380445
381446 /* if extra manual hosts required to be added, do it now */
382- glist = loadextrahosts(glist, NEWHOSTSDIR, &glistlen);
447+ glist = loadextrahosts(glist, NEWHOSTSDIR);
383448
384449 /* save the db once every hour */
385450 if (time(NULL) > nextdbsave) {
@@ -386,21 +451,20 @@
386451 unsigned long savedbcount;
387452 printf("dumping hosts lists to %s\n", dbfile);
388453 savedbcount = savedb(dbfile, dbfilecnt, glist);
389- if (savedbcount != glistlen) {
390- printf("ERR: savedbcount != glistlen @ %d (%lu != %lu)\n", __LINE__, savedbcount, glistlen);
391- }
454+ if (savedbcount == 0) printf("ERR: savedbcount == 0 @ %d\n", __LINE__);
392455 nextdbsave = time(NULL) + SAVEPERIOD;
393456 }
394457
395458 /* if ttl expired, pick a new host to browse */
396459 if ((--ttl == 0) || (curhost == NULL)) {
397- printf("TTL expired -> picking new host\n");
398- curhost = pickrandhostfromlist(glist, glistlen);
460+ printf("picking new host\n");
461+ curhost = pickhostfromlist(glist);
462+ curhost->nextcheck = time(NULL) + CHECKPERIOD;
399463 curhost = glist_node_dup(curhost);
400464 ttl = TTLINIT;
401465 }
402466
403- printf("TTL=%d | glistlen=%ld\n", ttl, glistlen);
467+ printf("TTL=%d\n", ttl);
404468
405469 /* if no host, continue */
406470 if (curhost == NULL) {
@@ -407,24 +471,25 @@
407471 printf("hosts list empty. add some through " NEWHOSTSDIR ".\n");
408472 continue;
409473 } else {
410- printf("fetching %s:%u/1%s ...\n", curhost->fqdn, curhost->port, curhost->selector);
474+ printf("fetching %s:%u/1%s ...\n", curhost->fqdn, curhost->port, (curhost->selector == NULL)?"":curhost->selector);
411475 }
412476
413477 /* fetch selector */
414- bufflen = gopher_fetch(buff, sizeof(buff), curhost->fqdn, curhost->port, curhost->selector, curhost_ipaddr);
478+ bufflen = cached_gopher_fetch(buff, sizeof(buff), curhost->fqdn, curhost->port, curhost->selector, curhost_ipaddr, sizeof(curhost_ipaddr));
415479 if (bufflen < 1) { /* fail */
416480 printf("failed\n");
417481 /* if it was about root selector, see if it's time to drop the server */
418482 if (curhost->selector == NULL) {
419- gnode = glist_findhost(glist, curhost->fqdn, curhost->port);
483+ gnode = glist_findhostport(glist, curhost->fqdn, curhost->port);
420484 if (gnode->failedsince == 0) {
421485 /* server was working at some point in the past, but not anymore */
422486 gnode->failedsince = time(NULL);
423- printf("server %s:%u went down -> flaged as failed since now (%s)\n", gnode->fqdn, gnode->port, ctime(&(gnode->failedsince)));
487+ gnode->nextcheck = time(NULL) + CHECKPERIOD_FAST;
488+ printf("server %s:%u went down -> flaged as failed since now (%s)\n", gnode->fqdn, gnode->port, epoch2human(gnode->failedsince));
424489 } else if (time(NULL) > curhost->failedsince + MAXFAILTIME) {
425490 /* remove server from the list */
426- printf("server removed due to long-time failure: %s:%u (failed since %s)\n", gnode->fqdn, gnode->port, ctime(&(gnode->failedsince)));
427- glist = glist_node_unchain(glist, gnode, &glistlen);
491+ printf("server removed due to long-time failure: %s:%u (failed since %s)\n", gnode->fqdn, gnode->port, epoch2human(gnode->failedsince));
492+ glist = glist_node_unchain(glist, gnode);
428493 glist_node_free(&gnode);
429494 }
430495 }
@@ -432,7 +497,7 @@
432497 continue;
433498 }
434499
435- printf("ok (%ld bytes)\n", bufflen);
500+ printf("ok (%ld bytes, IP=%s)\n", bufflen, curhost_ipaddr);
436501
437502 /* menu to glist */
438503 mlist = menu2gopherlist(buff, bufflen);
@@ -442,7 +507,7 @@
442507 * 64 bytes of data AND it port=70, then add server's IP address */
443508 if ((curhost->selector == NULL) && (bufflen >= 64) && (curhost->port == 70)) {
444509 printf(" (keeping server in list as %s)\n", curhost_ipaddr);
445- glist_addnewhost(&glist, &glistlen, curhost_ipaddr, curhost->port, 0);
510+ glist_addnewhostport(&glist, curhost_ipaddr, curhost->port, 0);
446511 }
447512 /* */
448513 glist_free(mlist);
@@ -451,19 +516,17 @@
451516 }
452517
453518 /* try adding hosts to global glist (and count 'em) */
454- i = 0;
455519 for (gnode = mlist; gnode != NULL; gnode = gnode->next) {
456- i++;
457- glist_addnewhost(&glist, &glistlen, gnode->fqdn, gnode->port, 1);
520+ glist_addnewhostport(&glist, gnode->fqdn, gnode->port, 1);
458521 }
459522
460523 /* if main menu, then check that host is pointing to himself */
461524 if (curhost->selector == NULL) {
462525 /* make sure the server points to itself */
463- if (glist_findhost(mlist, curhost->fqdn, curhost->port) == NULL) {
526+ if (glist_findhostport(mlist, curhost->fqdn, curhost->port) == NULL) {
464527 printf("ERR: main menu contains no link to self, dropping hostname '%s'\n", curhost->fqdn);
465- gnode = glist_findhost(glist, curhost->fqdn, curhost->port);
466- glist = glist_node_unchain(glist, gnode, &glistlen);
528+ gnode = glist_findhostport(glist, curhost->fqdn, curhost->port);
529+ glist = glist_node_unchain(glist, gnode);
467530 glist_free(mlist);
468531 glist_node_free(&curhost);
469532 continue;
@@ -471,11 +534,11 @@
471534 }
472535
473536 /* mark host as 'okay' */
474- gnode = glist_findhost(glist, curhost->fqdn, curhost->port);
537+ gnode = glist_findhostport(glist, curhost->fqdn, curhost->port);
475538 gnode->failedsince = 0;
476539
477540 /* choose a random entry from mlist */
478- gnode = pickrandhostfromlist(mlist, i);
541+ gnode = pickrandhostfromlist(mlist);
479542 if (gnode == NULL) {
480543 printf("pickrandhostfromlist() could not find a node candidate in list\n");
481544 glist_node_free(&curhost);
Show on old repository browser