Mercurial > notdcc
comparison dblist/dblist.c @ 0:c7f6b056b673
First import of vendor version
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 13:49:58 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c7f6b056b673 |
---|---|
1 /* Distributed Checksum Clearinghouse | |
2 * | |
3 * database lister | |
4 * | |
5 * Copyright (c) 2008 by Rhyolite Software, LLC | |
6 * | |
7 * This agreement is not applicable to any entity which sells anti-spam | |
8 * solutions to others or provides an anti-spam solution as part of a | |
9 * security solution sold to other entities, or to a private network | |
10 * which employs the DCC or uses data provided by operation of the DCC | |
11 * but does not provide corresponding data to other users. | |
12 * | |
13 * Permission to use, copy, modify, and distribute this software without | |
14 * changes for any purpose with or without fee is hereby granted, provided | |
15 * that the above copyright notice and this permission notice appear in all | |
16 * copies and any distributed versions or copies are either unchanged | |
17 * or not called anything similar to "DCC" or "Distributed Checksum | |
18 * Clearinghouse". | |
19 * | |
20 * Parties not eligible to receive a license under this agreement can | |
21 * obtain a commercial license to use DCC by contacting Rhyolite Software | |
22 * at sales@rhyolite.com. | |
23 * | |
24 * A commercial license would be for Distributed Checksum and Reputation | |
25 * Clearinghouse software. That software includes additional features. This | |
26 * free license for Distributed ChecksumClearinghouse Software does not in any | |
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse | |
28 * software | |
29 * | |
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL | |
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | |
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC | |
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES | |
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
37 * SOFTWARE. | |
38 * | |
39 * Rhyolite Software DCC 1.3.103-1.137 $Revision$ | |
40 */ | |
41 | |
42 #include "srvr_defs.h" | |
43 #include "dcc_xhdr.h" | |
44 #include "dcc_ck.h" | |
45 #include <signal.h> | |
46 #if HAVE_BOOTTIME | |
47 #include <sys/sysctl.h> | |
48 #endif | |
49 | |
50 static DCC_EMSG dcc_emsg; | |
51 | |
52 static int verbose; | |
53 #define VERBOSE_HASH 3 | |
54 static u_char no_hash; | |
55 static u_char no_data; | |
56 static u_char matching; | |
57 | |
58 static DCC_CLNT_CTXT *ctxt; | |
59 static DCC_OP_RESP aop_resp; | |
60 static DCC_SRVR_NM srvr; | |
61 static const ID_TBL *srvr_clnt_tbl; | |
62 | |
63 static struct { | |
64 DCC_CK_TYPES type; | |
65 DCC_SUM sum; | |
66 u_char type_only; | |
67 } search_cksums[16]; | |
68 static int num_search_cksums; | |
69 | |
70 static struct { | |
71 DCC_TS lo; | |
72 DCC_TS hi; | |
73 } search_ts[16]; | |
74 static int num_search_ts; | |
75 | |
76 DCC_SRVR_ID search_ids[16]; | |
77 static int num_search_ids; | |
78 | |
79 static DB_PTR page_offset; | |
80 static DB_PTR dbaddr; | |
81 static int max_pathlen; | |
82 | |
83 static DB_HOFF hash_fsize; | |
84 static char dcc_db_nm[] = DB_DCC_NAME; | |
85 static char grey_db_nm[] = DB_GREY_NAME; | |
86 static DCC_PATH hash_nm; | |
87 static char *def_argv[2]; | |
88 static const char *homedir; | |
89 | |
90 static const DB_VERSION_BUF version_buf = DB_VERSION_STR; | |
91 static const u_char hash_magic[sizeof(((HASH_CTL*)0)->s.magic) | |
92 ] = HASH_MAGIC_STR; | |
93 | |
94 static void rel_db(void); | |
95 static void sigterm(int); | |
96 static int save_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS); | |
97 static void list_cleaned(const DB_PARMS *); | |
98 static void list_flod(void); | |
99 static int fd_hash = -1; | |
100 static int fd_db = -1; | |
101 static struct stat hash_sb, db_sb; | |
102 static void list_db(void); | |
103 static u_char open_db(void); | |
104 static void open_hash(void); | |
105 static void list_hash(void); | |
106 | |
107 | |
108 static void NRATTRIB | |
109 usage(void) | |
110 { | |
111 dcc_logbad(EX_USAGE, "usage: [-vVHD] [-G on | off] [-h homedir]\n" | |
112 " [-s server-ID[,server-addr][,server-port]]\n" | |
113 " [-C '[type] [h1 h2 h3 h4]'] [-I server-Id] [-A dbptr]" | |
114 " [-L pathlen]\n" | |
115 " [-P pages] [-T timestamp] [file file2 ...]"); | |
116 } | |
117 | |
118 | |
119 | |
120 int NRATTRIB | |
121 main(int argc, char **argv) | |
122 { | |
123 u_char print_version = 0; | |
124 char hostname[DCC_MAXDOMAINLEN]; | |
125 int file_num; | |
126 DCC_CK_TYPES type; | |
127 char tbuf[80]; | |
128 const char *cp, *cp0; | |
129 struct timeval tv1, tv2; | |
130 int us; | |
131 struct tm tm; | |
132 char *p; | |
133 u_long l; | |
134 int i; | |
135 | |
136 dcc_syslog_init(0, argv[0], 0); | |
137 | |
138 while ((i = getopt(argc, argv, "vVHDG:h:s:C:I:A:L:P:T:")) != -1) { | |
139 switch (i) { | |
140 case 'v': | |
141 ++verbose; | |
142 break; | |
143 | |
144 case 'V': | |
145 fprintf(stderr, DCC_VERSION"\n"); | |
146 print_version = 1; | |
147 break; | |
148 | |
149 case 'G': | |
150 if (!strcasecmp(optarg, "on")) { | |
151 grey_on = 1; | |
152 } else if (!strcasecmp(optarg, "off")) { | |
153 grey_on = 0; | |
154 } else { | |
155 usage(); | |
156 } | |
157 break; | |
158 | |
159 case 'h': | |
160 homedir = optarg; | |
161 break; | |
162 | |
163 case 's': | |
164 l = strtoul(optarg, &p, 10); | |
165 if ((*p != '\0' && *p != ',') | |
166 || l < DCC_SRVR_ID_MIN | |
167 || l > DCC_SRVR_ID_MAX) | |
168 dcc_logbad(EX_USAGE, "invalid DCC ID \"-s %s\"", | |
169 optarg); | |
170 srvr.clnt_id = l; | |
171 if (*p != '\0') { | |
172 ++p; | |
173 p += strspn(p, DCC_WHITESPACE); | |
174 } | |
175 hostname[0] = '\0'; | |
176 srvr.port = 0; | |
177 if (*p == '\0') | |
178 break; | |
179 cp = dcc_parse_nm_port(dcc_emsg, p, srvr.port, | |
180 hostname, sizeof(hostname), | |
181 &srvr.port, 0, 0, 0, 0); | |
182 if (!cp) | |
183 dcc_logbad(EX_USAGE, "%s", dcc_emsg); | |
184 cp += strspn(cp, DCC_WHITESPACE); | |
185 if (*cp != '\0') | |
186 dcc_logbad(EX_USAGE, | |
187 "unrecognized port number in" | |
188 "\"-s %s\"", optarg); | |
189 if (hostname[0] != '\0') | |
190 BUFCPY(srvr.hostname, hostname); | |
191 break; | |
192 | |
193 case 'H': | |
194 no_hash = 1; | |
195 break; | |
196 | |
197 case 'D': | |
198 no_data = 1; | |
199 break; | |
200 | |
201 case 'C': | |
202 if (num_search_cksums >= DIM(search_cksums)) { | |
203 dcc_error_msg("too many -C checksums"); | |
204 break; | |
205 } | |
206 matching = 1; | |
207 cp0 = optarg; | |
208 cp = dcc_parse_word(0, tbuf, sizeof(tbuf), | |
209 optarg, "checksum type", 0, 0); | |
210 if (!cp) | |
211 exit(1); | |
212 if (!strcasecmp(tbuf, "hex")) { | |
213 /* ignore "hex" */ | |
214 cp0 = cp; | |
215 cp = dcc_parse_word(0, tbuf, sizeof(tbuf), | |
216 cp, "checksum type", | |
217 0, 0); | |
218 if (!cp) | |
219 dcc_logbad(EX_USAGE, | |
220 "unrecognized checksum" | |
221 " \"-C %s\"", optarg); | |
222 } | |
223 if (*cp == '\0') { | |
224 /* allow bare checksum type */ | |
225 type = dcc_str2type_del(tbuf, -1); | |
226 if (type == DCC_CK_INVALID) | |
227 dcc_logbad(EX_USAGE, | |
228 "unrecognized checksum type" | |
229 " \"-C %s\"", optarg); | |
230 search_cksums[num_search_cksums].type = type; | |
231 memset(search_cksums[num_search_cksums].sum, 0, | |
232 sizeof(DCC_SUM)); | |
233 search_cksums[num_search_cksums].type_only = 1; | |
234 ++num_search_cksums; | |
235 break; | |
236 } | |
237 /* allow missing checksum type */ | |
238 strtoul(tbuf, &p, 16); | |
239 if (*p == '\0') { | |
240 if (0 >= dcc_parse_hex_ck(dcc_emsg, 0, | |
241 "-", DCC_CK_FLOD_PATH, | |
242 cp0, 0, save_cksum)) | |
243 dcc_logbad(EX_USAGE, "%s", dcc_emsg); | |
244 } else { | |
245 type = dcc_str2type_del(tbuf, -1); | |
246 if (type == DCC_CK_FLOD_PATH) | |
247 dcc_logbad(EX_USAGE, | |
248 "unrecognized checksum type" | |
249 " \"-C %s\"", optarg); | |
250 if (0 >= dcc_parse_hex_ck(dcc_emsg, 0, | |
251 tbuf, type, | |
252 cp, 0, save_cksum)) | |
253 dcc_logbad(EX_USAGE, "%s", dcc_emsg); | |
254 } | |
255 break; | |
256 | |
257 case 'I': | |
258 if (num_search_ids >= DIM(search_ids)) { | |
259 dcc_error_msg("too many -I IDs"); | |
260 break; | |
261 } | |
262 search_ids[num_search_ids] = strtoul(optarg, &p, 10); | |
263 if (search_ids[num_search_ids] > DCC_SRVR_ID_MAX | |
264 || *p != '\0') | |
265 dcc_logbad(EX_USAGE, | |
266 "invalid server-ID \"-I %s\"", | |
267 optarg); | |
268 ++num_search_ids; | |
269 matching = 1; | |
270 break; | |
271 | |
272 case 'A': | |
273 dbaddr = strtoul(optarg, &p, 16); | |
274 if (*p != '\0') | |
275 dcc_logbad(EX_USAGE, | |
276 "invalid database address \"%s\"", | |
277 optarg); | |
278 matching = 1; | |
279 break; | |
280 | |
281 case 'L': | |
282 max_pathlen = strtoul(optarg, &p, 10); | |
283 if (*p != '\0') | |
284 dcc_logbad(EX_USAGE, | |
285 "invalid path length \"%s\"", | |
286 optarg); | |
287 matching = 1; | |
288 break; | |
289 | |
290 case 'P': | |
291 page_offset = strtoul(optarg, &p, 10); | |
292 if (*p != '\0') | |
293 dcc_logbad(EX_USAGE, | |
294 "invalid number of pages \"%s\"", | |
295 optarg); | |
296 matching = 1; | |
297 break; | |
298 | |
299 case 'T': | |
300 if (num_search_ts >= DIM(search_ts)) { | |
301 dcc_error_msg("too many -T timestamps"); | |
302 break; | |
303 } | |
304 memset(&tm, 0, sizeof(tm)); | |
305 i = sscanf(optarg, "%d/%d/%d %d:%d:%d.%d%c", | |
306 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, | |
307 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, | |
308 &us, tbuf); | |
309 if (i < 6 || i > 7 | |
310 || tm.tm_mon <= 0) | |
311 dcc_logbad(EX_USAGE,"bad timestamp \"%s\"", | |
312 optarg); | |
313 --tm.tm_mon; | |
314 tm.tm_year += 100; | |
315 tv1.tv_sec = DCC_TIMEGM(&tm); | |
316 if (tv1.tv_sec < 0) | |
317 dcc_logbad(EX_USAGE, "invalid timestamp \"%s\"", | |
318 optarg); | |
319 tv2.tv_sec = tv1.tv_sec; | |
320 if (i == 7) { | |
321 if (us >= DCC_US) | |
322 dcc_logbad(EX_USAGE, | |
323 "invalid microseconds" | |
324 " in \"%s\"", | |
325 optarg); | |
326 tv1.tv_usec = us; | |
327 tv2.tv_usec = us; | |
328 } else { | |
329 tv1.tv_usec = 0; | |
330 tv2.tv_usec = DCC_US-1; | |
331 } | |
332 dcc_timeval2ts(&search_ts[num_search_ts].lo, &tv1, 0); | |
333 dcc_timeval2ts(&search_ts[num_search_ts].hi, &tv2, 0); | |
334 ++num_search_ts; | |
335 matching = 1; | |
336 break; | |
337 | |
338 default: | |
339 usage(); | |
340 } | |
341 } | |
342 argc -= optind; | |
343 argv += optind; | |
344 def_argv[0] = grey_on ? grey_db_nm : dcc_db_nm; | |
345 if (argc == 0) { | |
346 if (print_version) | |
347 exit(EX_OK); | |
348 argv = def_argv; | |
349 argc = 1; | |
350 } | |
351 | |
352 dcc_clnt_unthread_init(); | |
353 if (!dcc_cdhome(dcc_emsg, homedir, 1)) | |
354 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
355 | |
356 flod_mmap_path_set(); | |
357 | |
358 if (matching) { | |
359 if (no_data && no_hash) | |
360 dcc_logbad(EX_USAGE, | |
361 "patterns need data or hash table"); | |
362 if (!no_data && !no_hash) | |
363 no_hash = 1; | |
364 } | |
365 | |
366 if (dbaddr != 0 && page_offset != 0) | |
367 dcc_logbad(EX_USAGE, "-P and -A are incompatible"); | |
368 | |
369 if (srvr.clnt_id != 0) { | |
370 if (argc != 1) | |
371 dcc_logbad(EX_USAGE, "lock only one file"); | |
372 | |
373 i = load_ids(dcc_emsg, srvr.clnt_id, &srvr_clnt_tbl, 1); | |
374 if (i <= 0) | |
375 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
376 memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd, | |
377 sizeof(srvr.passwd)); | |
378 if (hostname[0] == '\0') | |
379 strcpy(srvr.hostname, DCC_SRVR_NM_DEF_HOST); | |
380 if (srvr.port == 0) | |
381 srvr.port = DCC_GREY2PORT(grey_on); | |
382 | |
383 i = DCC_CLNT_FG_SLOW; | |
384 if (grey_on) | |
385 i |= DCC_CLNT_FG_GREY; | |
386 ctxt = dcc_tmp_clnt_init(dcc_emsg, 0, &srvr, 0, i, 0); | |
387 if (!ctxt) | |
388 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
389 if (!lock_dbclean(dcc_emsg, *argv)) | |
390 dcc_logbad(dcc_ex_code, "%s: dbclean running?", | |
391 dcc_emsg); | |
392 | |
393 atexit(rel_db); | |
394 signal(SIGALRM, sigterm); | |
395 signal(SIGHUP, sigterm); | |
396 signal(SIGTERM, sigterm); | |
397 signal(SIGINT, sigterm); | |
398 if (!dcc_aop_persist(dcc_emsg, ctxt, | |
399 grey_on ? DCC_CLNT_FG_GREY : 0, | |
400 verbose != 0, | |
401 DCC_AOP_DB_UNLOAD, 0, 60*5, &aop_resp)) | |
402 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
403 } | |
404 | |
405 for (file_num = 1; *argv != 0; ++argv, ++file_num) { | |
406 if (fd_db >= 0) | |
407 close(fd_db); | |
408 if (fd_hash >= 0) | |
409 close(fd_hash); | |
410 | |
411 BUFCPY(db_nm, *argv); | |
412 snprintf(hash_nm, sizeof(hash_nm), "%s"DB_HASH_SUFFIX, db_nm); | |
413 | |
414 if (file_num != 1) | |
415 fputc('\n', stdout); | |
416 if (verbose || argc > 1) | |
417 printf(" %s\n", db_nm); | |
418 | |
419 /* try to open the hash table and the database | |
420 * fail only if we cannot open the database */ | |
421 open_hash(); | |
422 if (!open_db()) | |
423 continue; | |
424 | |
425 /* print the header of the database followed by its contents */ | |
426 list_db(); | |
427 list_hash(); | |
428 } | |
429 | |
430 exit(EX_OK); | |
431 } | |
432 | |
433 | |
434 | |
435 static void | |
436 rel_db(void) | |
437 { | |
438 if (!ctxt) | |
439 return; | |
440 if (!dcc_aop_persist(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, | |
441 1, DCC_AOP_DB_UNLOAD, 1, 60*5, &aop_resp)) | |
442 dcc_error_msg("%s", dcc_emsg); | |
443 unlock_dbclean(); | |
444 ctxt = 0; | |
445 } | |
446 | |
447 | |
448 | |
449 static void | |
450 sigterm(int sig UATTRIB) | |
451 { | |
452 rel_db(); | |
453 } | |
454 | |
455 | |
456 | |
457 static int | |
458 save_cksum(DCC_EMSG emsg UATTRIB, DCC_WF *wf UATTRIB, | |
459 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB) | |
460 { | |
461 search_cksums[num_search_cksums].type = type; | |
462 memcpy(search_cksums[num_search_cksums].sum, sum, sizeof(DCC_SUM)); | |
463 search_cksums[num_search_cksums].type_only = 0; | |
464 ++num_search_cksums; | |
465 return 1; | |
466 } | |
467 | |
468 | |
469 | |
470 #define RCD_PAT "%-27s %-8.8s %-10.10s %7s "L_HWPAT(8)"\n" | |
471 #define RCD_PAT1(s) RCD_PAT, s, "", "", "" | |
472 | |
473 static DB_HDR hdr_buf; | |
474 | |
475 static enum {NO_LB, /* no label */ | |
476 WHITE_LB, /* whitelist section labelled */ | |
477 DATE_LB /* normal section labelled */ | |
478 } last_lb = NO_LB; | |
479 static u_char printed_rcd; | |
480 static int rcds, white_rcds, sums, white_sums; | |
481 | |
482 | |
483 static u_char | |
484 open_db(void) | |
485 { | |
486 int i; | |
487 | |
488 fd_db = open(db_nm, O_RDONLY, 0); | |
489 if (fd_db < 0) { | |
490 dcc_error_msg("open(%s): %s", db_nm, ERROR_STR()); | |
491 return 0; | |
492 } | |
493 | |
494 i = read_db(dcc_emsg, &hdr_buf, sizeof(hdr_buf), fd_db, 0, db_nm); | |
495 if (i != sizeof(hdr_buf)) { | |
496 if (i < 0) | |
497 dcc_error_msg("%s", dcc_emsg); | |
498 else | |
499 dcc_error_msg("found only %d bytes of magic in %s", | |
500 i, db_nm); | |
501 return 0; | |
502 } | |
503 | |
504 if (memcmp(hdr_buf.p.version, version_buf, | |
505 sizeof(hdr_buf.p.version))) { | |
506 dcc_error_msg("%s contains the wrong magic \"%.*s\"", | |
507 db_nm, ISZ(version_buf), hdr_buf.p.version); | |
508 } | |
509 if (0 > fstat(fd_db, &db_sb)) { | |
510 dcc_error_msg("stat(%s): %s", db_nm, ERROR_STR()); | |
511 return 0; | |
512 } | |
513 | |
514 if (db_sb.st_size == sizeof(hdr_buf)) { | |
515 dcc_error_msg("%s contains no checksums",db_nm); | |
516 return 0; | |
517 } | |
518 | |
519 if ((DB_PTR)db_sb.st_size < hdr_buf.p.db_csize) { | |
520 dcc_error_msg("%s says it contains "L_DPAT | |
521 " bytes instead of "OFF_DPAT, | |
522 db_nm, hdr_buf.p.db_csize, db_sb.st_size); | |
523 } | |
524 | |
525 db_pagesize = hdr_buf.p.pagesize; | |
526 db_hash_page_len = db_pagesize/sizeof(HASH_ENTRY); | |
527 | |
528 return 1; | |
529 } | |
530 | |
531 | |
532 | |
533 static void | |
534 list_db_entry(DB_PTR rcd_link, const DB_RCD *rcd) | |
535 { | |
536 const DB_RCD_CK *rcd_ck; | |
537 DB_PTR rcd_prev; | |
538 DCC_TGTS tgts; | |
539 DCC_CK_TYPES type; | |
540 char ts_buf[40], id_buf[30]; | |
541 char tgts_buf[20]; | |
542 char ck_buf[sizeof(DCC_SUM)*3+2]; | |
543 u_char rpt_match, kept; | |
544 int i; | |
545 | |
546 /* usually skip padding */ | |
547 if (rcd->fgs_num_cks == 0) { | |
548 if (verbose > 1) { | |
549 printf(RCD_PAT1(" page padding"), rcd_link); | |
550 printed_rcd = 1; | |
551 } | |
552 return; | |
553 } | |
554 | |
555 rpt_match = 0; | |
556 | |
557 /* skip until the desired first address */ | |
558 if (dbaddr != 0) { | |
559 if (rcd_link < dbaddr) | |
560 return; | |
561 rpt_match = 1; | |
562 } | |
563 | |
564 /* if we have target server-IDs, display only their reports */ | |
565 if (num_search_ids > 0) { | |
566 for (i = 0; i < num_search_ids; ++i) { | |
567 if (search_ids[i] == DB_RCD_ID(rcd)) { | |
568 rpt_match = 1; | |
569 goto got_id; | |
570 } | |
571 } | |
572 return; | |
573 got_id:; | |
574 } | |
575 | |
576 /* if we have target checksums, display only reports containing them */ | |
577 if (num_search_cksums > 0) { | |
578 for (i = 0; i < num_search_cksums; ++i) { | |
579 for (rcd_ck = rcd->cks; | |
580 rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)]; | |
581 ++rcd_ck) { | |
582 type = search_cksums[i].type; | |
583 if ((DB_CK_TYPE(rcd_ck) == type | |
584 || type == DCC_CK_FLOD_PATH) | |
585 && (search_cksums[i].type_only | |
586 || !memcmp(search_cksums[i].sum, | |
587 rcd_ck->sum, | |
588 sizeof(DCC_SUM)))) { | |
589 rpt_match = 1; | |
590 goto got_ck; | |
591 } | |
592 } | |
593 } | |
594 return; | |
595 got_ck:; | |
596 } | |
597 | |
598 if (num_search_ts > 0 | |
599 && DB_RCD_ID(rcd) != DCC_ID_WHITE) { | |
600 for (i = 0; i < num_search_ts; ++i) { | |
601 if (!dcc_ts_older_ts(&rcd->ts, | |
602 &search_ts[i].lo) | |
603 && !dcc_ts_newer_ts(&rcd->ts, | |
604 &search_ts[i].hi)) { | |
605 rpt_match = 1; | |
606 goto got_ts; | |
607 } | |
608 } | |
609 return; | |
610 got_ts:; | |
611 } | |
612 | |
613 if (max_pathlen != 0 | |
614 && DB_RCD_ID(rcd) != DCC_ID_WHITE) { | |
615 DCC_FLOD_PATH_ID *id; | |
616 DCC_SRVR_ID psrvr; | |
617 int pathlen = 0; | |
618 | |
619 for (rcd_ck = rcd->cks; | |
620 rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)] | |
621 && pathlen < max_pathlen; | |
622 ++rcd_ck) { | |
623 if (DB_CK_TYPE(rcd_ck) != DCC_CK_FLOD_PATH) | |
624 break; | |
625 id = (DCC_FLOD_PATH_ID *)&rcd_ck->sum; | |
626 for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++id) { | |
627 psrvr = ((id->hi<<8) | id->lo); | |
628 if (psrvr == DCC_ID_INVALID) | |
629 break; | |
630 ++pathlen; | |
631 } | |
632 } | |
633 if (pathlen < max_pathlen) | |
634 return; | |
635 rpt_match = 1; | |
636 } | |
637 | |
638 ++rcds; | |
639 if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { | |
640 ++white_rcds; | |
641 if (last_lb != WHITE_LB) { | |
642 last_lb = WHITE_LB; | |
643 strcpy(ts_buf, "\n"DCC_XHDR_ID_WHITE); | |
644 } else { | |
645 ts_buf[0] = '\0'; | |
646 } | |
647 } else { | |
648 if (last_lb != DATE_LB) { | |
649 last_lb = DATE_LB; | |
650 if (rpt_match || verbose > 0) | |
651 putchar('\n'); | |
652 } | |
653 if (rpt_match || verbose > 0) | |
654 ts2str(ts_buf, sizeof(ts_buf), &rcd->ts); | |
655 } | |
656 | |
657 /* display separator between whitelist and ordinary entries | |
658 * along with the timestamp and the rest of the first line | |
659 * of a report */ | |
660 if (rpt_match | |
661 || verbose >= 2 | |
662 || (verbose > 0 && DB_RCD_ID(rcd) != DCC_ID_WHITE)) { | |
663 if (last_lb == DATE_LB) { | |
664 tgts = DB_TGTS_RCD_RAW(rcd); | |
665 printf(RCD_PAT, ts_buf, | |
666 (tgts == 0) | |
667 ? "deleted" | |
668 : dcc_tgts2str(tgts_buf, sizeof(tgts_buf), | |
669 tgts, grey_on), | |
670 id2str(id_buf, sizeof(id_buf), | |
671 rcd->srvr_id_auth), | |
672 DB_RCD_TRIMMED(rcd) ? "trimmed" | |
673 : DB_RCD_SUMRY(rcd) ? "summary" | |
674 : DB_RCD_DELAY(rcd) ? "delayed" | |
675 : "", | |
676 rcd_link); | |
677 } else { | |
678 printf(RCD_PAT1(ts_buf), rcd_link); | |
679 } | |
680 printed_rcd = 1; | |
681 } | |
682 | |
683 /* display a report */ | |
684 for (rcd_ck = rcd->cks; | |
685 rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)]; | |
686 ++rcd_ck) { | |
687 ++sums; | |
688 /* always count whitelist entries, | |
689 * but display only as requested */ | |
690 if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { | |
691 ++white_sums; | |
692 if (verbose < 2 && !rpt_match) | |
693 continue; | |
694 } else { | |
695 if (verbose < 1 && !rpt_match) | |
696 continue; | |
697 } | |
698 | |
699 /* decode the special checksum that is a path */ | |
700 if (DB_CK_TYPE(rcd_ck)== DCC_CK_FLOD_PATH) { | |
701 if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { | |
702 int lno, fno; | |
703 memcpy(&lno, rcd_ck->sum, sizeof(lno)); | |
704 fno = rcd_ck->sum[sizeof(lno)]; | |
705 if (fno == 0) { | |
706 printf(" line #%d\n", lno); | |
707 } else { | |
708 printf(" line #%d" | |
709 " included file #%d\n", | |
710 lno, fno); | |
711 } | |
712 | |
713 } else { | |
714 DCC_SRVR_ID psrvr; | |
715 DCC_FLOD_PATH_ID *path_id, *path_id_lim; | |
716 const char *s; | |
717 | |
718 path_id=(DCC_FLOD_PATH_ID *)rcd_ck->sum; | |
719 path_id_lim = path_id+DCC_NUM_FLOD_PATH; | |
720 s = " path: "; | |
721 do { | |
722 psrvr = ((path_id->hi<<8) | |
723 | path_id->lo); | |
724 if (psrvr == DCC_ID_INVALID) | |
725 break; | |
726 printf("%s%d", s, psrvr); | |
727 s = "<-"; | |
728 } while (++path_id < path_id_lim); | |
729 printf("%s\n", s); | |
730 } | |
731 continue; | |
732 } | |
733 | |
734 kept = (!DB_TEST_NOKEEP(hdr_buf.p.nokeep_cks, | |
735 DB_CK_TYPE(rcd_ck)) | |
736 || DB_RCD_ID(rcd) == DCC_ID_WHITE); | |
737 | |
738 printf(" %c%-12.12s %-10.10s %-31s", | |
739 DB_CK_OBS(rcd_ck) ? '*' : ' ', | |
740 DB_TYPE2STR(DB_CK_TYPE(rcd_ck)), | |
741 !kept | |
742 ? "" : dcc_tgts2str(tgts_buf, sizeof(tgts_buf), | |
743 DB_TGTS_CK(rcd_ck), grey_on), | |
744 dcc_ck2str(ck_buf, sizeof(ck_buf), | |
745 DB_CK_TYPE(rcd_ck), rcd_ck->sum, | |
746 DB_RCD_ID(rcd))); | |
747 rcd_prev = DB_PTR_EX(rcd_ck->prev); | |
748 if (rcd_prev == DB_PTR_NULL) | |
749 printf(" %8s", ""); | |
750 else if (DB_PTR_IS_BAD(rcd_prev)) | |
751 printf(" bogus "L_HWPAT(8), rcd_prev); | |
752 else | |
753 printf(" "L_HWPAT(8), rcd_prev); | |
754 if (db_hash_len != 0 | |
755 && kept) | |
756 printf(" %x", db_hash(DB_CK_TYPE(rcd_ck), rcd_ck->sum)); | |
757 putchar('\n'); | |
758 } | |
759 } | |
760 | |
761 | |
762 | |
763 static void | |
764 list_db(void) | |
765 { | |
766 DB_RCD rcd; | |
767 int rcd_len; | |
768 DB_PTR rcd_lim, rcd_link; | |
769 | |
770 if (fd_db < 0) | |
771 return; | |
772 | |
773 /* print the header of the database */ | |
774 if (verbose > 0) { | |
775 list_cleaned(&hdr_buf.p); | |
776 list_flod(); | |
777 } | |
778 | |
779 if (no_data) | |
780 return; | |
781 | |
782 last_lb = NO_LB; | |
783 printed_rcd = 0; | |
784 rcds = 0; | |
785 white_rcds = 0; | |
786 sums = 0; | |
787 white_sums = 0; | |
788 | |
789 /* list the records in the database */ | |
790 if (dbaddr != 0) { | |
791 if ((DB_PTR)db_sb.st_size <= dbaddr) { | |
792 page_offset = 0; | |
793 } else { | |
794 page_offset = ((db_sb.st_size - dbaddr + db_pagesize -1) | |
795 / db_pagesize); | |
796 } | |
797 } | |
798 if (page_offset == 0) { | |
799 rcd_link = DB_PTR_BASE; | |
800 } else { | |
801 rcd_link = db_sb.st_size / hdr_buf.p.pagesize; | |
802 if (rcd_link < page_offset) | |
803 rcd_link = 0; | |
804 else | |
805 rcd_link -= page_offset; | |
806 rcd_link *= hdr_buf.p.pagesize; | |
807 if (rcd_link < DB_PTR_BASE) | |
808 rcd_link = DB_PTR_BASE; | |
809 } | |
810 rcd_lim = ((verbose > 2) | |
811 ? (DB_PTR)db_sb.st_size : hdr_buf.p.db_csize); | |
812 read_rcd_invalidate(0); | |
813 while (rcd_link < rcd_lim) { | |
814 rcd_len = read_rcd(dcc_emsg, &rcd, fd_db, rcd_link, db_nm); | |
815 if (rcd_len <= 0) { | |
816 if (rcd_len == 0) | |
817 break; | |
818 /* ignore fragmentary reports at the end */ | |
819 if (rcd_link > hdr_buf.p.db_csize - DB_RCD_HDR_LEN) { | |
820 printf(RCD_PAT1(" page padding"), rcd_link); | |
821 printed_rcd = 1; | |
822 break; | |
823 } | |
824 dcc_error_msg("%s", dcc_emsg); | |
825 read_rcd_invalidate(0); | |
826 return; | |
827 } | |
828 | |
829 | |
830 list_db_entry(rcd_link, &rcd); | |
831 rcd_link += rcd_len; | |
832 } | |
833 | |
834 if (verbose || matching) { | |
835 /* print address after the last record, | |
836 * but only if we printed a record */ | |
837 if (printed_rcd) | |
838 printf(RCD_PAT1(""), rcd_link); | |
839 putchar('\n'); | |
840 } | |
841 if (!matching) { | |
842 printf("%7d records containing %d checksums\n", | |
843 rcds, sums); | |
844 if (!grey_on && rcds != white_rcds) | |
845 printf("%7d non-whitelist records containing" | |
846 " %d checksums\n", | |
847 rcds-white_rcds, sums-white_sums); | |
848 } | |
849 read_rcd_invalidate(0); | |
850 } | |
851 | |
852 | |
853 | |
854 static const char * | |
855 print_rate(char *buf, u_int buf_len, | |
856 const DB_PARMS *parms, u_char hash_or_db) | |
857 { | |
858 double rate; | |
859 | |
860 rate = db_add_rate(parms, hash_or_db); | |
861 | |
862 if (rate <= 0.0) | |
863 return "?"; | |
864 | |
865 return size2str(buf, buf_len, rate * (24*60*60*1.0), !hash_or_db); | |
866 } | |
867 | |
868 | |
869 | |
870 static const char * | |
871 secs2str(char *buf, u_int buf_len, u_int32_t secs) | |
872 { | |
873 int days, minutes, hours; | |
874 | |
875 days = secs / (24*60*60); | |
876 secs %= (24*60*60); | |
877 hours = secs / (60*60); | |
878 secs %= (60*60); | |
879 minutes = secs / 60; | |
880 secs %= 60; | |
881 | |
882 if (hours == 0 && minutes == 0 | |
883 && (secs == 0 || (days != 0 && secs < 15 && verbose < 3))) { | |
884 snprintf(buf, buf_len, "%d day%s", | |
885 days, (days > 1) ? "s" : " "); | |
886 return buf; | |
887 } | |
888 | |
889 if (days == 0 && minutes == 0 && secs == 0) { | |
890 snprintf(buf, buf_len, "%d hour%s", | |
891 hours, (hours > 1) ? "s" : " "); | |
892 return buf; | |
893 } | |
894 | |
895 if (days == 0 && hours == 0) { | |
896 snprintf(buf, buf_len, "%02d:%02d", | |
897 minutes, secs); | |
898 return buf; | |
899 } | |
900 | |
901 if (days == 0) { | |
902 snprintf(buf, buf_len, "%d:%02d:%02d", | |
903 hours, minutes, secs); | |
904 return buf; | |
905 } | |
906 | |
907 snprintf(buf, buf_len, "%d %d:%02d:%02d", | |
908 days, hours, minutes, secs); | |
909 return buf; | |
910 } | |
911 | |
912 | |
913 | |
914 static const char * | |
915 ex_ts2str(char *buf, u_int buf_len, const DCC_TS *ts) | |
916 { | |
917 static DCC_TS never; | |
918 | |
919 if (!memcmp(&ts, &never, sizeof(never))) { | |
920 STRLCPY(buf, "never ", buf_len); | |
921 return buf; | |
922 } | |
923 return ts2str(buf, buf_len, ts); | |
924 } | |
925 | |
926 | |
927 | |
928 /* display the expiration information in the database header */ | |
929 static void | |
930 list_cleaned(const DB_PARMS *parms) | |
931 { | |
932 #define CLEANED_PAT " %12s %c %17.17s %17.17s %10s %10s" | |
933 struct tm tm; | |
934 char time_buf[32]; | |
935 char db_rate[10], hash_rate[10], entries_buf[10]; | |
936 DCC_CK_TYPES type; | |
937 char spam_ts_buf[18]; | |
938 char all_ts_buf[18]; | |
939 char allsecs_buf[20]; | |
940 char spamsecs_buf[20]; | |
941 | |
942 printf(" %s%s%spage size %#-8x s/n %s\n", | |
943 (parms->flags & DB_PARM_FG_GREY) ? "greylist " : "", | |
944 (parms->flags & DB_PARM_FG_CLEARED) ? "cleared ": "", | |
945 (parms->flags & DB_PARM_EXP_SET) ? "dbclean -e/-E ": "", | |
946 parms->pagesize, ts2str_err(&parms->sn)); | |
947 | |
948 DCC_GMTIME_R(&parms->cleared, &tm); | |
949 strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S UTC", &tm); | |
950 printf(" created %s", time_buf); | |
951 if (parms->cleaned_cron == 0) { | |
952 printf("; never properly cleaned"); | |
953 } else { | |
954 DCC_GMTIME_R(&parms->cleaned_cron, &tm); | |
955 strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); | |
956 printf("; cleaned %s", time_buf); | |
957 } | |
958 putchar('\n'); | |
959 if (parms->cleaned > parms->cleaned_cron) { | |
960 DCC_GMTIME_R(&parms->cleaned, &tm); | |
961 strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); | |
962 printf(" failsafe cleaned %s\n", time_buf); | |
963 } | |
964 | |
965 if (verbose > 3) { | |
966 printf(" db_csize="L_DPAT" old="L_DPAT" added="L_DPAT"\n", | |
967 parms->db_csize, parms->old_db_csize, parms->db_added); | |
968 printf(" hash_used=%d old=%d added=%d old_kept_cks=%d\n", | |
969 parms->hash_used, parms->old_hash_used, | |
970 parms->hash_added, parms->old_kept_cks); | |
971 printf(" rate_secs=%d \"%.*s\"\n", | |
972 (int)parms->rate_secs, | |
973 ISZ(parms->version), parms->version); | |
974 } | |
975 | |
976 printf(" added %s database bytes/day and %s hash entries/day\n", | |
977 print_rate(db_rate, sizeof(db_rate), parms, 0), | |
978 print_rate(hash_rate, sizeof(hash_rate), parms, 1)); | |
979 | |
980 if (db_hash_len > 0 | |
981 && parms->hash_used >= DB_HADDR_BASE) | |
982 printf(" %.0f%% of %s hash entries used\n", | |
983 HADDR2LEN(parms->hash_used) * 100.0 | |
984 / HADDR2LEN(db_hash_len), | |
985 size2str(entries_buf, sizeof(entries_buf), | |
986 HADDR2LEN(db_hash_len), 0)); | |
987 | |
988 if (parms->flags & DB_PARM_FG_GREY) | |
989 printf(CLEANED_PAT, | |
990 "", ' ', "", "", | |
991 "window", "white"); | |
992 else | |
993 printf(CLEANED_PAT, | |
994 "", ' ', "non-bulk expired", "bulk expired ", | |
995 "non ", "bulk"); | |
996 for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) { | |
997 if ((type == DCC_CK_SRVR_ID | |
998 || DB_TEST_NOKEEP(parms->nokeep_cks, type)) | |
999 && verbose < 3) | |
1000 continue; | |
1001 if (parms->ex_secs[type].all == DB_EXPIRE_SECS_MAX) { | |
1002 STRLCPY(allsecs_buf, "never", sizeof(allsecs_buf)); | |
1003 STRLCPY(all_ts_buf, "- ", sizeof(all_ts_buf)); | |
1004 } else { | |
1005 secs2str(allsecs_buf, sizeof(allsecs_buf), | |
1006 parms->ex_secs[type].all); | |
1007 ex_ts2str(all_ts_buf, sizeof(all_ts_buf), | |
1008 &parms->ex_all[type]); | |
1009 } | |
1010 if (parms->ex_secs[type].spam == DB_EXPIRE_SECS_MAX) { | |
1011 STRLCPY(spamsecs_buf, "never", sizeof(spamsecs_buf)); | |
1012 STRLCPY(spam_ts_buf, "- ", sizeof(spam_ts_buf)); | |
1013 } else { | |
1014 secs2str(spamsecs_buf, sizeof(spamsecs_buf), | |
1015 parms->ex_secs[type].spam); | |
1016 ex_ts2str(spam_ts_buf, sizeof(spam_ts_buf), | |
1017 &parms->ex_spam[type]); | |
1018 } | |
1019 printf("\n"CLEANED_PAT, | |
1020 DB_TYPE2STR(type), | |
1021 DB_TEST_NOKEEP(parms->nokeep_cks, type) ? '*' : ' ', | |
1022 all_ts_buf, spam_ts_buf, | |
1023 allsecs_buf, spamsecs_buf); | |
1024 } | |
1025 #undef CLEANED_PAT | |
1026 } | |
1027 | |
1028 | |
1029 | |
1030 static void | |
1031 list_flod(void) | |
1032 { | |
1033 FLOD_MMAP *mp; | |
1034 DCC_PATH path; | |
1035 char hostname[40], fg_buf[60]; | |
1036 u_char first; | |
1037 | |
1038 /* display the flood map only for default database */ | |
1039 if (strcmp(fnm2abs_err(path, db_nm), DB_NM2PATH_ERR(def_argv[0]))) { | |
1040 putchar('\n'); | |
1041 } else if (!flod_mmap(dcc_emsg, 0, 0, 0, 1)) { | |
1042 dcc_error_msg("\n\n%s", dcc_emsg); | |
1043 } else if (strcmp(flod_mmaps->magic, FLOD_MMAP_MAGIC)) { | |
1044 dcc_error_msg("\n\n%s contains the wrong magic \"%.*s\"", | |
1045 flod_mmap_path, | |
1046 ISZ(flod_mmaps->magic), flod_mmaps->magic); | |
1047 if (!flod_unmap(dcc_emsg, 0)) | |
1048 dcc_error_msg("%s", dcc_emsg); | |
1049 } else { | |
1050 first = 1; | |
1051 fputs("\n\n ", stdout); | |
1052 fputs(flod_mmap_path, stdout); | |
1053 printf(" s/n %s\n delay position "L_HWPAT(8)"\n", | |
1054 ts2str_err(&flod_mmaps->sn), flod_mmaps->delay_pos); | |
1055 for (mp = flod_mmaps->mmaps; | |
1056 mp <= LAST(flod_mmaps->mmaps); | |
1057 ++mp) { | |
1058 if (mp->rem_hostname[0] == '\0') | |
1059 continue; | |
1060 if (first) { | |
1061 first = 0; | |
1062 printf("%32s %5s %9s %s\n", | |
1063 "peer", "", "ID", "position"); | |
1064 } | |
1065 printf("%38s %9d "L_HWPAT(8)"%s\n", | |
1066 dcc_host_portname(hostname, sizeof(hostname), | |
1067 mp->rem_hostname, | |
1068 mp->rem_portname), | |
1069 mp->rem_id, | |
1070 mp->confirm_pos, | |
1071 flodmap_fg(fg_buf, sizeof(fg_buf), " ", mp)); | |
1072 if (mp->rem_su.sa.sa_family != AF_UNSPEC | |
1073 && verbose > 1) | |
1074 printf("%40s\n", | |
1075 dcc_su2str3(hostname, sizeof(hostname), | |
1076 &mp->rem_su, | |
1077 DCC_GREY2PORT(grey_on))); | |
1078 } | |
1079 if (!flod_unmap(dcc_emsg, 0)) | |
1080 dcc_error_msg("%s", dcc_emsg); | |
1081 } | |
1082 } | |
1083 | |
1084 | |
1085 | |
1086 static void | |
1087 open_hash(void) | |
1088 { | |
1089 db_hash_len = 0; | |
1090 fd_hash = open(hash_nm, O_RDONLY, 0); | |
1091 if (0 > fd_hash) { | |
1092 dcc_error_msg("open(%s): %s", hash_nm, ERROR_STR()); | |
1093 return; | |
1094 } | |
1095 if (0 > fstat(fd_hash, &hash_sb)) { | |
1096 dcc_error_msg("stat(%s): %s", hash_nm, ERROR_STR()); | |
1097 close(fd_hash); | |
1098 fd_hash = -1; | |
1099 return; | |
1100 } | |
1101 hash_fsize = hash_sb.st_size; | |
1102 db_hash_len = hash_fsize/sizeof(HASH_ENTRY); | |
1103 if ((hash_fsize % sizeof(HASH_ENTRY)) != 0) { | |
1104 dcc_error_msg("%s has size "L_DPAT", not a multiple of %d", | |
1105 hash_nm, hash_fsize, ISZ(HASH_ENTRY)); | |
1106 db_hash_len = 0; | |
1107 close(fd_hash); | |
1108 fd_hash = -1; | |
1109 return; | |
1110 } | |
1111 if (db_hash_len < MIN_HASH_ENTRIES) { | |
1112 dcc_error_msg("%s has too few records, "L_DPAT" bytes", | |
1113 hash_nm, hash_fsize); | |
1114 db_hash_len = 0; | |
1115 close(fd_hash); | |
1116 fd_hash = -1; | |
1117 return; | |
1118 } | |
1119 | |
1120 db_hash_divisor = get_db_hash_divisor(db_hash_len); | |
1121 } | |
1122 | |
1123 | |
1124 | |
1125 #define HASH_MAP_LEN (1024*1024) | |
1126 #define HASH_MAP_NUM 16 | |
1127 typedef struct hash_map { | |
1128 struct hash_map *fwd, *bak; | |
1129 HASH_ENTRY *buf; | |
1130 DB_HADDR base; | |
1131 DB_HADDR lim; | |
1132 DB_HOFF offset; | |
1133 DB_HOFF size; | |
1134 } HASH_MAP; | |
1135 static HASH_MAP hash_maps[HASH_MAP_NUM]; | |
1136 static HASH_MAP *hash_map_newest; | |
1137 | |
1138 | |
1139 static u_char | |
1140 hash_munmap(HASH_MAP *mp) | |
1141 { | |
1142 if (!mp->buf) | |
1143 return 1; | |
1144 | |
1145 if (0 > munmap((void *)mp->buf, mp->size)) { | |
1146 dcc_error_msg("munmap(%s,"L_DPAT"): %s", | |
1147 hash_nm, mp->size, ERROR_STR()); | |
1148 return 0; | |
1149 } | |
1150 mp->buf = 0; | |
1151 return 1; | |
1152 } | |
1153 | |
1154 | |
1155 | |
1156 static u_char | |
1157 hash_map_clear(void) | |
1158 { | |
1159 HASH_MAP *mp; | |
1160 int i; | |
1161 | |
1162 mp = hash_maps; | |
1163 for (i = 0; i < DIM(hash_maps); ++i, ++mp) { | |
1164 if (i == DIM(hash_maps)-1) | |
1165 mp->fwd = hash_maps; | |
1166 else | |
1167 mp->fwd = mp+1; | |
1168 if (i == 0) | |
1169 mp->bak = LAST(hash_maps); | |
1170 else | |
1171 mp->bak = mp-1; | |
1172 } | |
1173 hash_map_newest = hash_maps; | |
1174 | |
1175 for (mp = hash_maps; mp <= LAST(hash_maps); ++mp) { | |
1176 if (!hash_munmap(mp)) | |
1177 return 0; | |
1178 } | |
1179 | |
1180 return 1; | |
1181 } | |
1182 | |
1183 | |
1184 | |
1185 static void | |
1186 hash_map_ref(HASH_MAP *mp) | |
1187 { | |
1188 if (hash_map_newest != mp) { | |
1189 mp->fwd->bak = mp->bak; | |
1190 mp->bak->fwd = mp->fwd; | |
1191 mp->fwd = hash_map_newest; | |
1192 mp->bak = hash_map_newest->bak; | |
1193 mp->fwd->bak = mp; | |
1194 mp->bak->fwd = mp; | |
1195 hash_map_newest = mp; | |
1196 } | |
1197 } | |
1198 | |
1199 | |
1200 | |
1201 static const void * | |
1202 haddr_mmap(DB_HADDR haddr) | |
1203 { | |
1204 HASH_MAP *mp; | |
1205 void *p; | |
1206 int i; | |
1207 | |
1208 for (i = 0, mp = hash_map_newest; | |
1209 i < DIM(hash_maps); | |
1210 ++i, mp = mp->fwd) { | |
1211 if (!mp->buf) | |
1212 continue; | |
1213 if (haddr >= mp->base | |
1214 && haddr < mp->lim) { | |
1215 hash_map_ref(mp); | |
1216 return mp->buf + (haddr - mp->base); | |
1217 } | |
1218 } | |
1219 | |
1220 mp = hash_map_newest->bak; | |
1221 hash_munmap(mp); | |
1222 | |
1223 mp->base = haddr - haddr%HASH_MAP_LEN; | |
1224 mp->offset = mp->base*sizeof(HASH_ENTRY); | |
1225 mp->size = hash_fsize - mp->offset; | |
1226 if (mp->size > HASH_MAP_LEN*ISZ(HASH_ENTRY)) | |
1227 mp->size = HASH_MAP_LEN*ISZ(HASH_ENTRY); | |
1228 mp->lim = mp->base + mp->size/sizeof(HASH_ENTRY); | |
1229 p = mmap(0, mp->size, PROT_READ, MAP_SHARED, fd_hash, mp->offset); | |
1230 if (p != MAP_FAILED) { | |
1231 mp->buf = p; | |
1232 hash_map_ref(mp); | |
1233 return mp->buf + (haddr - mp->base); | |
1234 } | |
1235 dcc_error_msg("mmap(%s,%d,%d): %s", | |
1236 hash_nm, (int)mp->size, (int)mp->offset, | |
1237 ERROR_STR()); | |
1238 return 0; | |
1239 } | |
1240 | |
1241 | |
1242 | |
1243 static void | |
1244 list_hash(void) | |
1245 { | |
1246 #define HEAD() (headed ? 1 : (headed = 1, printf("\n %s\n", hash_nm))) | |
1247 const HASH_ENTRY *entry; | |
1248 const HASH_CTL *ctl; | |
1249 time_t secs; | |
1250 struct tm tm; | |
1251 char time_buf[30]; | |
1252 DB_HADDR collisions, chains, chain_lens; | |
1253 int max_chain_len, chain_len; | |
1254 DB_HADDR free_fwd, free_bak; | |
1255 DB_HADDR fwd, bak, haddr; | |
1256 DB_HADDR db_hash_used_stored; | |
1257 DB_PTR rcd_link; | |
1258 DCC_CK_TYPES type; | |
1259 DB_RCD rcd; | |
1260 int rcd_len; | |
1261 u_char headed, clean; | |
1262 int i; | |
1263 | |
1264 if (fd_hash < 0) | |
1265 return; | |
1266 | |
1267 headed = 0; | |
1268 | |
1269 if (!hash_map_clear()) | |
1270 return; | |
1271 | |
1272 read_rcd_invalidate(DB_RCD_LEN_MAX); | |
1273 | |
1274 ctl = haddr_mmap(0); | |
1275 if (!ctl) | |
1276 return; | |
1277 if (memcmp(ctl->s.magic, &hash_magic, sizeof(hash_magic))) { | |
1278 HEAD(); | |
1279 dcc_error_msg(" contains the wrong magic"); | |
1280 return; | |
1281 } | |
1282 | |
1283 if (verbose > VERBOSE_HASH) { | |
1284 HEAD(); | |
1285 printf(" magic: \"%.*s\"\n", | |
1286 ISZ(ctl->s.magic), ctl->s.magic); | |
1287 } | |
1288 | |
1289 if (srvr.clnt_id != 0) { | |
1290 clean = 0; | |
1291 } else { | |
1292 clean = (ctl->s.flags & HASH_CTL_FG_CLEAN) != 0; | |
1293 if (!clean) { | |
1294 HEAD(); | |
1295 printf(" not closed\n"); | |
1296 } | |
1297 } | |
1298 secs = ctl->s.synced; | |
1299 if (verbose >= VERBOSE_HASH) { | |
1300 DCC_GMTIME_R(&secs, &tm); | |
1301 strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); | |
1302 printf(" synced %s\n", time_buf); | |
1303 if (ctl->s.flags & HASH_CTL_FG_NOSYNC) | |
1304 printf(" unsafe after next system reboot\n"); | |
1305 } | |
1306 | |
1307 free_fwd = ctl->s.free_fwd; | |
1308 free_bak = ctl->s.free_bak; | |
1309 if (DB_HADDR_INVALID(ctl->s.free_fwd) | |
1310 && (ctl->s.free_fwd != FREE_HADDR_END | |
1311 || ctl->s.free_fwd != ctl->s.free_bak)) { | |
1312 HEAD(); | |
1313 dcc_error_msg(" broken free list head of %#x", | |
1314 ctl->s.free_fwd); | |
1315 } | |
1316 if (DB_HADDR_INVALID(ctl->s.free_bak) | |
1317 && (ctl->s.free_bak != FREE_HADDR_END | |
1318 || ctl->s.free_fwd != ctl->s.free_bak)) { | |
1319 HEAD(); | |
1320 dcc_error_msg(" broken free list tail of %#x", | |
1321 ctl->s.free_bak); | |
1322 } | |
1323 if (verbose > VERBOSE_HASH) | |
1324 printf(" free: %x, %x\n", free_fwd, free_bak); | |
1325 | |
1326 if (db_hash_len != ctl->s.len | |
1327 && (ctl->s.len != 0 || verbose >= VERBOSE_HASH)) { | |
1328 HEAD(); | |
1329 dcc_error_msg(" has %d entries but claims %d", | |
1330 HADDR2LEN(db_hash_len), HADDR2LEN(ctl->s.len)); | |
1331 } | |
1332 db_hash_used_stored = ctl->s.used; | |
1333 if (ctl->s.used > db_hash_len) { | |
1334 HEAD(); | |
1335 dcc_error_msg(" contains only %d entries but %d used", | |
1336 HADDR2LEN(ctl->s.len), HADDR2LEN((ctl->s.used))); | |
1337 } | |
1338 if (ctl->s.used == db_hash_len) { | |
1339 HEAD(); | |
1340 dcc_error_msg(" overflows with %d entries", | |
1341 HADDR2LEN(db_hash_len)); | |
1342 } | |
1343 if (ctl->s.db_csize != hdr_buf.p.db_csize | |
1344 && (clean || verbose >= VERBOSE_HASH)) { | |
1345 HEAD(); | |
1346 dcc_error_msg(" claims %s contains "L_DPAT | |
1347 " bytes instead of "L_DPAT, | |
1348 db_nm, ctl->s.db_csize, hdr_buf.p.db_csize); | |
1349 } | |
1350 if (ctl->s.divisor != get_db_hash_divisor(db_hash_len)) { | |
1351 HEAD(); | |
1352 dcc_error_msg(" built with hash divisor %d instead of %d", | |
1353 ctl->s.divisor, get_db_hash_divisor(db_hash_len)); | |
1354 } | |
1355 if (verbose >= VERBOSE_HASH) { | |
1356 printf(" hash length=%#x=%d used=%#x=%d\n", | |
1357 ctl->s.len, ctl->s.len, | |
1358 ctl->s.used, ctl->s.used); | |
1359 printf(" db_csize="L_HPAT"="L_DPAT"\n", | |
1360 ctl->s.db_csize, ctl->s.db_csize); | |
1361 } | |
1362 | |
1363 if (no_hash) { | |
1364 hash_map_clear(); | |
1365 return; | |
1366 } | |
1367 | |
1368 db_hash_used = DB_HADDR_BASE; | |
1369 collisions = 0; | |
1370 chains = 0; | |
1371 chain_lens = 0; | |
1372 max_chain_len = 1; | |
1373 for (haddr = DB_HADDR_BASE; haddr < db_hash_len; ++haddr) { | |
1374 entry = haddr_mmap(haddr); | |
1375 if (!entry) | |
1376 break; | |
1377 | |
1378 fwd = DB_HADDR_EX(entry->fwd); | |
1379 bak = DB_HADDR_EX(entry->bak); | |
1380 rcd_link = DB_HPTR_EX(entry->rcd); | |
1381 | |
1382 /* deal with a free entry */ | |
1383 if (HE_IS_FREE(entry)) { | |
1384 if (rcd_link != DB_PTR_NULL) | |
1385 dcc_error_msg("free hash table data link at" | |
1386 " %x to "L_HPAT, | |
1387 haddr, rcd_link); | |
1388 if (haddr == free_fwd | |
1389 && bak != FREE_HADDR_END) | |
1390 dcc_error_msg("bad 1st free hash bak link %x", | |
1391 bak); | |
1392 else if (haddr != free_fwd | |
1393 && (DB_HADDR_INVALID(bak) || bak >= haddr)) | |
1394 dcc_error_msg("bad hash bak link at %x", | |
1395 haddr); | |
1396 if (haddr == free_bak | |
1397 && fwd != FREE_HADDR_END) | |
1398 dcc_error_msg("bad last free hash fwd link %x", | |
1399 fwd); | |
1400 else if (haddr != free_bak | |
1401 && (DB_HADDR_INVALID(fwd) || fwd <= haddr)) | |
1402 dcc_error_msg("bad hash fwd link at %x", | |
1403 haddr); | |
1404 if (verbose >= VERBOSE_HASH) | |
1405 printf(" %6x: %6x %6x\n", haddr, fwd, bak); | |
1406 continue; | |
1407 } | |
1408 | |
1409 if (haddr == free_fwd && clean) | |
1410 dcc_error_msg("start of free list at %x not free", | |
1411 haddr); | |
1412 if (haddr == free_bak && clean) | |
1413 dcc_error_msg("end of free list at %x not free", | |
1414 haddr); | |
1415 | |
1416 /* deal with a used entry */ | |
1417 ++db_hash_used; | |
1418 if (DB_PTR_IS_BAD(rcd_link)) | |
1419 dcc_error_msg("bad hash table data link at" | |
1420 " %x to "L_HPAT, | |
1421 haddr, rcd_link); | |
1422 if (DB_HADDR_INVALID(fwd) && fwd != DB_HADDR_NULL) | |
1423 dcc_error_msg("bad hash fwd link at %x to %x", | |
1424 haddr, fwd); | |
1425 if (DB_HADDR_INVALID(bak) && bak != DB_HADDR_NULL) | |
1426 dcc_error_msg("bad hash bak link at %x to %x", | |
1427 haddr, bak); | |
1428 if (verbose >= VERBOSE_HASH) | |
1429 printf(" %6x: %6x %6x "L_HWPAT(8)" %s\n", | |
1430 haddr, fwd, bak, rcd_link, | |
1431 DB_TYPE2STR(HE_TYPE(entry))); | |
1432 | |
1433 if (bak != DB_HADDR_NULL) { | |
1434 ++collisions; | |
1435 } else { | |
1436 ++chains; | |
1437 bak = haddr; | |
1438 chain_len = 1; | |
1439 while (!DB_HADDR_INVALID(fwd)) { | |
1440 if (++chain_len > 500) { | |
1441 dcc_error_msg("possible hash chain loop" | |
1442 " starting at %x" | |
1443 " continuing through %x", | |
1444 haddr, fwd); | |
1445 break; | |
1446 } | |
1447 entry = haddr_mmap(fwd); | |
1448 if (!entry) | |
1449 break; | |
1450 if (HE_IS_FREE(entry) | |
1451 || DB_HADDR_EX(entry->bak) != bak) { | |
1452 dcc_error_msg("broken hash chain" | |
1453 " starting at %x at %x", | |
1454 haddr, fwd); | |
1455 break; | |
1456 } | |
1457 bak = fwd; | |
1458 fwd = DB_HADDR_EX(entry->fwd); | |
1459 } | |
1460 chain_lens += chain_len; | |
1461 if (max_chain_len < chain_len) | |
1462 max_chain_len = chain_len; | |
1463 } | |
1464 | |
1465 if (matching) { | |
1466 if (num_search_cksums > 0) { | |
1467 for (i = 0; i < num_search_cksums; ++i) { | |
1468 type = search_cksums[i].type; | |
1469 if (type == HE_TYPE(entry) | |
1470 || type == DCC_CK_FLOD_PATH) | |
1471 break; | |
1472 } | |
1473 if (i >= num_search_cksums) | |
1474 continue; | |
1475 } | |
1476 rcd_len = read_rcd(dcc_emsg, &rcd, | |
1477 fd_db, rcd_link, db_nm); | |
1478 if (rcd_len <= 0) { | |
1479 if (rcd_len == 0) | |
1480 dcc_error_msg("bogus hash table data" | |
1481 " link at %x to "L_HPAT, | |
1482 haddr, rcd_link); | |
1483 else | |
1484 dcc_error_msg("%s", dcc_emsg); | |
1485 } else { | |
1486 list_db_entry(rcd_link, &rcd); | |
1487 } | |
1488 } | |
1489 } | |
1490 | |
1491 hash_map_clear(); | |
1492 | |
1493 if (db_hash_used_stored > db_hash_used) { | |
1494 dcc_error_msg("%s should have %d entries but has only %d", | |
1495 hash_nm, | |
1496 HADDR2LEN(db_hash_used_stored), | |
1497 HADDR2LEN(db_hash_used)); | |
1498 } else if (db_hash_used_stored < db_hash_used | |
1499 && (clean || verbose >= VERBOSE_HASH)) { | |
1500 dcc_error_msg("%s should have %d filled entries but has %d", | |
1501 hash_nm, | |
1502 HADDR2LEN(db_hash_used_stored), | |
1503 HADDR2LEN(db_hash_used)); | |
1504 } | |
1505 | |
1506 if (verbose >= VERBOSE_HASH) | |
1507 putchar('\n'); | |
1508 printf("%7d hash entries total %d or %.0f%% used %d free\n" | |
1509 "%7d modulus %.2f%% collisions", | |
1510 HADDR2LEN(db_hash_len), | |
1511 HADDR2LEN(db_hash_used), | |
1512 (HADDR2LEN(db_hash_used)*100.0) / HADDR2LEN(db_hash_len), | |
1513 HADDR2LEN(db_hash_len) - HADDR2LEN(db_hash_used), | |
1514 db_hash_divisor, | |
1515 collisions*100.0/HADDR2LEN(db_hash_len)); | |
1516 if (chains != 0) | |
1517 printf(" %7d hash chains\n" | |
1518 " %d max length %.2f average length", | |
1519 chains, max_chain_len, chain_lens*1.0/chains); | |
1520 fputc('\n', stdout); | |
1521 } |