0
|
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 } |