Logo Search packages:      
Sourcecode: dcc version File versions  Download package

dblist.c

/* Distributed Checksum Clearinghouse
 *
 * database lister
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.59 $Revision$
 */

#include "srvr_defs.h"
#include "dcc_xhdr.h"
#include "dcc_ck.h"

static DCC_EMSG dcc_emsg;

static int verbose;
#define VERBOSE_HASH 3
static u_char no_hash;
static u_char no_data;
static u_char matching;

static struct {
    DCC_CK_TYPES type;
    DCC_SUM sum;
} search_cksums[16];
static int num_search_cksums;

static DCC_TS search_ts[16];
static int num_search_ts;

DCC_SRVR_ID search_ids[16];
static int num_search_ids;

static DB_PTR page_offset;
static DB_PTR dbaddr;
static int max_pathlen;

static DB_HADDR hash_fsize;
static char dcc_db_nm[] = DB_DCC_NAME;
static char grey_db_nm[] = DB_GREY_NAME;
static char *def_argv[2];
static const char *homedir;

static const DB_VERSION_BUF version_buf = DB_VERSION_STR;

static const char hash_magic[sizeof(HASH_ENTRY)] = DB_HASH_MAGIC;


static int save_cksum(DCC_EMSG, DCC_WF *, const char *, int, \
                  DCC_CK_TYPES, DCC_SUM, DCC_TGTS);
static void list_cleaned(const DB_MAGIC *);
static void list_flod(void);
static DB_PTR list_db(void);
static int open_hash(const char *);
static void list_hash(int, const char *);


static void NRATTRIB
usage(void)
{
      dcc_logbad(EX_USAGE, "usage: [-vVHD] [-G on | off] [-h homedir]"
               " [-C 'type h1 h2 h3 h4']\n"
               "   [-I server-Id] [-A dbptr] [-L pathlen] [-P pages]"
               " [-T timestamp]\n"
               "   [file file2 ...]");
}



int NRATTRIB
main(int argc, char **argv)
{
      int file_num;
      DCC_PATH hash_nm;
      int hash_fd;
      u_char print_version = 0;
      char tbuf[80];
      const char *cp;
      struct timeval tv;
      int usecs;
      struct tm tm;
      char *p;
      int i;

      dcc_syslog_init(0, argv[0], 0);
      dup2(1, 2);             /* put error messages in context */

      while ((i = getopt(argc, argv, "vVHDG:h:C:I:A:L:P:T:")) != EOF) {
            switch (i) {
            case 'v':
                  ++verbose;
                  break;

            case 'V':
                  fprintf(stderr, DCC_VERSION"\n");
                  print_version = 1;
                  break;

            case 'H':
                  no_hash = 1;
                  break;

            case 'D':
                  no_data = 1;
                  break;

            case 'G':
                  if (!strcasecmp(optarg, "on")) {
                        grey_on = 1;
                  } else if (!strcasecmp(optarg, "off")) {
                        grey_on = 0;
                  } else {
                        usage();
                  }
                  break;

            case 'h':
                  homedir = optarg;
                  break;

            case 'C':
                  if (num_search_cksums >= DIM(search_cksums)) {
                        dcc_error_msg("too many -C checksums");
                        break;
                  }
                  no_hash = 1;
                  matching = 1;
                  cp = dcc_parse_word(0, tbuf, sizeof(tbuf),
                                  optarg, "checksum type", 0, 0);
                  if (!cp)
                        exit(1);
                  if (!strcasecmp(tbuf, "hex")) {
                        cp = dcc_parse_word(0, tbuf, sizeof(tbuf),
                                        cp, "checksum type",
                                        0, 0);
                        if (!cp)
                              exit(1);
                  }
                  if (!dcc_parse_hex_ck(dcc_emsg, 0, 0, 0,
                                    tbuf, cp, 0, save_cksum))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
                  break;

            case 'I':
                  if (num_search_ids >= DIM(search_ids)) {
                        dcc_error_msg("too many -I IDs");
                        break;
                  }
                  if (!dcc_get_srvr_id(dcc_emsg,
                                   &search_ids[num_search_ids],
                                   optarg, 0, 0, 0))
                        dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
                  ++num_search_ids;
                  no_hash = 1;
                  matching = 1;
                  break;

            case 'A':
                  dbaddr = strtoul(optarg, &p, 0);
                  if (*p != '\0')
                        dcc_logbad(EX_USAGE,
                                 "invalid database address \"%s\"",
                                 optarg);
                  no_hash = 1;
                  matching = 1;
                  break;

            case 'L':
                  max_pathlen = strtoul(optarg, &p, 0);
                  if (*p != '\0')
                        dcc_logbad(EX_USAGE,
                                 "invalid path length \"%s\"",
                                 optarg);
                  no_hash = 1;
                  matching = 1;
                  break;

            case 'P':
                  page_offset = strtoul(optarg, &p, 0);
                  if (*p != '\0')
                        dcc_logbad(EX_USAGE,
                                 "invalid number of pages \"%s\"",
                                 optarg);
                  no_hash = 1;
                  matching = 1;
                  break;

            case 'T':
                  if (num_search_ts >= DIM(search_ts)) {
                        dcc_error_msg("too many -I timestamps");
                        break;
                  }
                  memset(&tm, 0, sizeof(tm));
                  if (7 != sscanf(optarg, "%d/%d/%d %d:%d:%d.%d",
                              &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
                              &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
                              &usecs)
                      || tm.tm_mon <= 0)
                        dcc_logbad(EX_USAGE,"bad timestamp \"%s\"",
                                 optarg);
                  --tm.tm_mon;
                  tm.tm_year += 100;
                  tv.tv_sec = mktime(&tm);
                  if (tv.tv_sec == -1
                      || usecs < 0
                      || usecs > 1000000)
                        dcc_logbad(EX_USAGE,"invalid timestamp \"%s\"",
                                 optarg);
                  tv.tv_usec = usecs;
                  dcc_timeval2ts(search_ts[num_search_ts++], &tv, 0);
                  no_hash = 1;
                  matching = 1;
                  break;

            default:
                  usage();
            }
      }
      argc -= optind;
      argv += optind;
      def_argv[0] = grey_on ? grey_db_nm : dcc_db_nm;
      if (argc == 0) {
            if (print_version)
                  exit(EX_OK);
            argv = def_argv;
            argc = 1;
      }

      dcc_clnt_unthread_init();

      if (!dcc_cdhome(dcc_emsg, homedir))
            dcc_error_msg("%s", dcc_emsg);

      if (num_search_cksums != 0) {
            if (no_data)
                  dcc_logbad(EX_USAGE, "-C and -D are incompatible");
      }

      if (dbaddr != 0 && page_offset != 0)
            dcc_logbad(EX_USAGE, "-P and -A are incompatible");

      for (file_num = 1; *argv != 0; ++argv, ++file_num) {
            BUFCPY(db_nm, *argv);
            snprintf(hash_nm, sizeof(hash_nm), "%s"DB_HASH_SUFFIX, db_nm);

            if (file_num != 1)
                  fputc('\n', stdout);
            if (verbose || argc > 1)
                  printf("  %s\n", DCC_NM2PATH(db_nm));

            hash_fd = open_hash(hash_nm);
            db_csize = list_db();
            if (!db_csize)
                  continue;

            if (hash_fd >= 0)
                  list_hash(hash_fd, hash_nm);

            close(hash_fd);
      }

      exit(EX_OK);
}



static int
save_cksum(DCC_EMSG emsg UATTRIB, DCC_WF *wf UATTRIB,
         const char *fnm UATTRIB, int lineno UATTRIB,
         DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB)
{
      search_cksums[num_search_cksums].type = type;
      memcpy(search_cksums[num_search_cksums].sum, sum, sizeof(DCC_SUM));
      ++num_search_cksums;
      return 1;
}



static DB_PTR                       /* size of contents or 0=failed */
list_db(void)
{
#define RCD_PAT "%-27s %-8.8s %-10.10s %7s      "L_H8PAT"\n"
#define RCD_PAT1(s) RCD_PAT, s,  "", "", ""
      int fd;
      DB_MAGIC magic_buf;
      struct stat sb;
      DB_RCD rcd;
      DB_RCD_CK *rcd_ck;
      int rcd_len, rcds, white_rcds, sums, white_sums;
      DB_PTR rcd_lim, rcd_link, rcd_prev;
      DCC_TGTS tgts;
      char ts_buf[40], id_buf[30];
      char tgts_buf[20];
      char ck_buf[sizeof(DCC_SUM)*3+2];
      enum {NO_LB,                  /* no label */
            WHITE_LB,         /* whitelist section labelled */
            DATE_LB                 /* normal section labelled */
      } last_lb = NO_LB;
      u_char rpt_match, kept, printed_rcd;
      int i;

      fd = open(db_nm, O_RDONLY, 0);
      if (fd < 0) {
            dcc_error_msg("open(%s): %s", DCC_NM2PATH(db_nm), ERROR_STR());
            close(fd);
            return 0;
      }

      i = read_db(dcc_emsg, &magic_buf, sizeof(magic_buf),
                fd, 0, db_nm);
      if (i != sizeof(magic_buf)) {
            if (i < 0)
                  dcc_error_msg("%s", dcc_emsg);
            else
                  dcc_error_msg("found only %d bytes of magic in %s",
                              i, DCC_NM2PATH(db_nm));
            close(fd);
            return 0;
      }

      if (memcmp(magic_buf.s.version, version_buf,
               sizeof(magic_buf.s.version))) {
            dcc_error_msg("%s contains the wrong magic \"%.*s\"",
                        DCC_NM2PATH(db_nm),
                        ISZ(version_buf), magic_buf.s.version);
      }
      if (0 > fstat(fd, &sb)) {
            dcc_error_msg("stat(%s): %s",
                        DCC_NM2PATH(db_nm), ERROR_STR());
            close(fd);
            return 0;
      }
      if ((DB_PTR)sb.st_size < magic_buf.s.db_csize) {
            dcc_error_msg("%s says it contains "L_DPAT
                        " bytes instead of "OFF_DPAT,
                        DCC_NM2PATH(db_nm),
                        magic_buf.s.db_csize, sb.st_size);
      }

      if (verbose > 0) {
            list_cleaned(&magic_buf);
            list_flod();
      }

      if (sb.st_size == sizeof(magic_buf)) {
            dcc_error_msg("%s contains no checksums",
                        DCC_NM2PATH(db_nm));
            close(fd);
            return 0;
      }

      if (no_data)
            return magic_buf.s.db_csize;

      /* list the records in the database */
      rcds = 0;
      white_rcds = 0;
      sums = 0;
      white_sums = 0;
      if (dbaddr != 0) {
            page_offset = sb.st_size - dbaddr;
            if (page_offset <= 0) {
                  page_offset = 0;
            } else {
                  page_offset = ((page_offset + magic_buf.s.page_size -1)
                               / magic_buf.s.page_size);
            }
      }
      if (page_offset == 0) {
            rcd_link = DB_PTR_BASE;
      } else {
            rcd_link = sb.st_size / magic_buf.s.page_size;
            if (rcd_link < page_offset)
                  rcd_link = 0;
            else
                  rcd_link -= page_offset;
            rcd_link *= magic_buf.s.page_size;
            if (rcd_link < DB_PTR_BASE)
                  rcd_link = DB_PTR_BASE;
      }
      rcd_lim = ((verbose > 2)
               ? (DB_PTR)sb.st_size : (DB_PTR)magic_buf.s.db_csize);
      printed_rcd = 0;
      read_rcd_invalidate();
      while (rcd_link < rcd_lim) {
            rcd_len = read_rcd(dcc_emsg, &rcd, fd, rcd_link, db_nm);
            if (rcd_len <= 0) {
                  if (rcd_len == 0)
                        break;
                  /* ignore fragmentary reports at the end */
                  if (rcd_link > magic_buf.s.db_csize - DB_RCD_PAD) {
                        printf(RCD_PAT1("    page padding"), rcd_link);
                        printed_rcd = 1;
                        break;
                  }
                  dcc_error_msg("%s", dcc_emsg);
                  close(fd);
                  return 0;
            }

            /* usually skip padding */
            if (rcd.fgs_num_cks == 0) {
                  if (verbose > 1) {
                        printf(RCD_PAT1("    page padding"), rcd_link);
                        printed_rcd = 1;
                  }
                  rcd_link += rcd_len;
                  continue;
            }

            /* skip until the desired first address */
            if (rcd_link < dbaddr) {
                  rcd_link += rcd_len;
                  continue;
            }

            rpt_match = 0;

            /* if we have a target checksum, display only its reports */
            if (num_search_ids > 0) {
                  for (i = 0; i < num_search_ids; ++i) {
                        if (search_ids[i] == DB_RCD_ID(&rcd)) {
                              rpt_match = 1;
                              goto got_id;
                        }
                  }
                  rcd_link += rcd_len;
                  continue;
got_id:;
            }

            if (num_search_cksums > 0) {
                  for (i = 0; i >= 0 && i < num_search_cksums; ++i) {
                        for (rcd_ck = rcd.cks;
                             rcd_ck < &rcd.cks[DB_NUM_CKS(&rcd)];
                             ++rcd_ck) {
                              if (DB_CK_TYPE(rcd_ck)
                                  == search_cksums[i].type
                                  && !memcmp(search_cksums[i].sum,
                                           rcd_ck->sum,
                                           sizeof(DCC_SUM))) {
                                  rpt_match = 1;
                                  goto got_ck;
                              }
                        }
                  }
                  rcd_link += rcd_len;
                  continue;
got_ck:;
            }

            if (num_search_ts > 0
                && DB_RCD_ID(&rcd) != DCC_ID_WHITE) {
                  for (i = 0; i < num_search_ts; ++i) {
                        if (!memcmp(rcd.ts, search_ts[i],
                                  sizeof(rcd.ts))) {
                              rpt_match = 1;
                              goto got_ts;
                        }
                  }
                  rcd_link += rcd_len;
                  continue;
got_ts:;
            }

            if (max_pathlen != 0
                && DB_RCD_ID(&rcd) != DCC_ID_WHITE) {
                  DCC_FLOD_PATH_ID *id;
                  DCC_SRVR_ID psrvr;
                  int pathlen = 0;

                  for (rcd_ck = rcd.cks;
                       rcd_ck < &rcd.cks[DB_NUM_CKS(&rcd)]
                       && pathlen < max_pathlen;
                       ++rcd_ck) {
                        if (DB_CK_TYPE(rcd_ck) != DCC_CK_FLOD_PATH)
                              break;
                        id = (DCC_FLOD_PATH_ID *)&rcd_ck->sum;
                        for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++id) {
                              psrvr = ((id->hi<<8) | id->lo);
                              if (psrvr == DCC_ID_INVALID)
                                  break;
                              ++pathlen;
                        }
                  }
                  if (pathlen < max_pathlen) {
                        rcd_link += rcd_len;
                        continue;
                  }
                  rpt_match = 1;
            }

            ++rcds;
            if (DB_RCD_ID(&rcd) == DCC_ID_WHITE) {
                  ++white_rcds;
                  if (last_lb != WHITE_LB) {
                        last_lb = WHITE_LB;
                        strcpy(ts_buf, "\n"DCC_XHDR_ID_WHITE);
                  } else {
                        ts_buf[0] = '\0';
                  }
            } else {
                  if (last_lb != DATE_LB) {
                        last_lb = DATE_LB;
                        if (rpt_match || verbose > 0)
                              putchar('\n');
                  }
                  if (rpt_match || verbose > 0)
                        dcc_ts2str(ts_buf, sizeof(ts_buf), rcd.ts);
            }

            /* display separator between whitelist and ordinary entries
             * along with the timestamp and the rest of the first line
             * of a report */
            if (rpt_match
                || verbose >= 2
                || (verbose > 0 && DB_RCD_ID(&rcd) != DCC_ID_WHITE)) {
                  if (last_lb == DATE_LB) {
                        tgts = DB_TGTS_RCD_RAW(&rcd);
                        printf(RCD_PAT, ts_buf,
                               (tgts == 0)
                               ? "deleted"
                               : dcc_cnt2str(tgts_buf, sizeof(tgts_buf),
                                         tgts, grey_on),
                               dcc_srvr_id2str(id_buf, sizeof(id_buf),
                                           DB_RCD_ID(&rcd)),
                               DB_RCD_TRIMMED(&rcd) ? "trimmed"
                               : DB_RCD_SUMRY(&rcd) ? "summary"
                               : DB_RCD_DELAY(&rcd) ? "delayed"
                               : "",
                               rcd_link);
                  } else {
                        printf(RCD_PAT1(ts_buf), rcd_link);
                  }
                  printed_rcd = 1;
            }

            /* display a report */
            for (rcd_ck = rcd.cks;
                 rcd_ck < &rcd.cks[DB_NUM_CKS(&rcd)];
                 ++rcd_ck) {
                  ++sums;
                  /* always count whitelist entries,
                   * but display only as requested */
                  if (DB_RCD_ID(&rcd) == DCC_ID_WHITE) {
                        ++white_sums;
                        if (verbose < 2 && !rpt_match)
                              continue;
                  } else {
                        if (verbose < 1 && !rpt_match)
                              continue;
                  }

                  /* decode the special checksum that is a path */
                  if (DB_CK_TYPE(rcd_ck)== DCC_CK_FLOD_PATH) {
                        DCC_SRVR_ID psrvr;
                        DCC_FLOD_PATH_ID *path_id, *path_id_lim;
                        const char *s;

                        path_id = (DCC_FLOD_PATH_ID *)rcd_ck->sum;
                        path_id_lim = path_id+DCC_NUM_FLOD_PATH;
                        s = "     path: ";
                        do {
                              psrvr = (path_id->hi<<8) | path_id->lo;
                              if (psrvr == DCC_ID_INVALID)
                                  break;
                              printf("%s%d", s, psrvr);
                              s = "<-";
                        } while (++path_id < path_id_lim);
                        printf("%s\n", s);
                        continue;
                  }

                  kept = (!DB_TEST_NOKEEP(magic_buf.s.nokeep_cks,
                                    DB_CK_TYPE(rcd_ck))
                        || DB_RCD_ID(&rcd) == DCC_ID_WHITE);

                  printf(" %c%-12.12s %-10.10s %-31s",
                         DB_CK_OBS(rcd_ck) ? '*' : ' ',
                         dcc_type2str_err(DB_CK_TYPE(rcd_ck), 0, 1),
                         !kept
                         ? "" : dcc_cnt2str(tgts_buf, sizeof(tgts_buf),
                                      DB_TGTS_CK(rcd_ck), grey_on),
                         dcc_ck2str(ck_buf, sizeof(ck_buf),
                                DB_CK_TYPE(rcd_ck), rcd_ck->sum));
                  rcd_prev = DB_PTR_EX(rcd_ck->prev);
                  if (rcd_prev == DB_PTR_NULL)
                        printf(" %8s", "");
                  else if (DB_PTR_IS_BAD_FULL(rcd_prev))
                        printf(" bogus "L_H8PAT, rcd_prev);
                  else
                        printf(" "L_H8PAT, rcd_prev);
                  if (db_hash_len != 0
                      && kept)
                        printf(" %x", db_hash(DB_CK_TYPE(rcd_ck),
                                          rcd_ck->sum));
                  putchar('\n');
            }

            rcd_link += rcd_len;
      }

      close(fd);
      if (verbose || matching) {
            /* print address after the last record,
             * but only if we printed a record */
            if (printed_rcd)
                  printf(RCD_PAT1(""), rcd_link);
            putchar('\n');
      }
      if (!matching) {
            printf("%7d records containing %d checksums\n",
                   rcds, sums);
            if (!grey_on)
                  printf("%7d non-whitelist records containing"
                         " %d checksums\n",
                         rcds-white_rcds, sums-white_sums);
      }
      return magic_buf.s.db_csize;
}



const char *
secs2str(char *buf, u_int buf_len, u_int32_t secs)
{
      const char *dim;

      if (!buf_len)
            return 0;
      if (secs == 0) {
            STRLIMCPY(buf, buf_len, "never");
      } else {
            if (secs % (24*60*60) == 0) {
                  dim = " day";
                  secs /= (24*60*60);
            } else if (secs % (60*60) == 0) {
                  dim = " hour";
                  secs /= (60*60);
            } else {
                  dim = "";
            }
            snprintf(buf, buf_len, "%d%s%s",
                   secs, dim, (secs > 1 && dim) ? "s" : "");
      }
      return buf;
}



/* display the expiration information in the database header */
static void
list_cleaned(const DB_MAGIC *magic)
{
#define CLEANED_PAT     "     %12s %c %17.17s  %8s  %8s  %5s"
#define CLEANED_PAT_VB3 "     %12s %c %17.17s  %8s  %8s  %5s %8s"
      DCC_CK_TYPES type;
      char allsecs_buf[20];
      char spamsecs_buf[20];
      char tgts_buf[20];
      char flod_thold_buf[20];

      printf("     %s%spage size %#-8x s/n %s\n",
             (magic->s.flags & DB_MAGIC_ST_GREY)
             ? "Greylist  " : "",
             !(magic->s.flags & DB_MAGIC_ST_SELF_CLEAN) ? ""
             : !(magic->s.flags & DB_MAGIC_ST_SELF_CLEAN2) ? "self-cleaned  "
             : "twice self-cleaned  ",
             magic->s.page_size, dcc_ts2str_err(magic->s.sn));

      if (magic->s.flags & DB_MAGIC_ST_GREY)
            printf(CLEANED_PAT,
                   "", ' ', "expired    ", "window", "pass", "threshold");
      else
            printf(CLEANED_PAT,
                   "", ' ', "expired    ", "", "bulk  ", "threshold");
      for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
            if ((type == DCC_CK_SRVR_ID
                 || DB_TEST_NOKEEP(magic->s.nokeep_cks, type))
                && verbose < 3)
                  continue;
            secs2str(allsecs_buf, sizeof(allsecs_buf),
                   magic->s.ex_secs[type].all);
            if (magic->s.ex_secs[type].all != magic->s.ex_secs[type].spam
                || verbose >= 3) {
                  secs2str(spamsecs_buf, sizeof(spamsecs_buf),
                         magic->s.ex_secs[type].spam);
                  if ((magic->s.flags & DB_MAGIC_ST_GREY)
                      && verbose < 3) {
                        tgts_buf[0] = '\0';
                  } else {
                        dcc_cnt2str(tgts_buf, sizeof(tgts_buf),
                                  magic->s.ex_secs[type].clean_thold,
                                  grey_on);
                  }
            } else {
                  spamsecs_buf[0] = '\0';
                  tgts_buf[0] = '\0';
            }
            dcc_cnt2str(flod_thold_buf, sizeof(flod_thold_buf),
                      magic->s.flod_tholds[type], grey_on);
            printf(verbose >= 3 ? "\n"CLEANED_PAT_VB3 : "\n"CLEANED_PAT,
                   dcc_type2str_err(type, 0, 1),
                   DB_TEST_NOKEEP(magic->s.nokeep_cks, type) ? '*' : ' ',
                   dcc_ts2str_err(magic->s.ex_ts[type].all),
                   allsecs_buf, spamsecs_buf, tgts_buf, flod_thold_buf);
      }
#undef CLEANED_PAT
}



static void
list_flod(void)
{
      FLOD_MMAP *mp;
      DCC_PATH path;
      u_char first;

      /* display the flood map only for default database */
      if (strcmp(dcc_fnm2path(path, db_nm), DCC_NM2PATH(def_argv[0]))) {
            putchar('\n');
      } else if (!flod_mmap(dcc_emsg, 0, 0, 0, 1)) {
            dcc_error_msg("\n\n%s", dcc_emsg);
      } else if (strcmp(flod_mmaps->magic, FLOD_MMAP_MAGIC)) {
            dcc_error_msg("\n\n%s contains the wrong magic \"%.*s\"",
                        FLOD_MMAP_PATH(grey_on),
                        ISZ(flod_mmaps->magic), flod_mmaps->magic);
            if (!flod_unmap(dcc_emsg, 0))
                  dcc_error_msg("%s", dcc_emsg);
      } else {
            first = 1;
            fputs("\n\n  ", stdout);
            fputs(FLOD_MMAP_NM(grey_on), stdout);
            printf("  s/n %s   delay position "L_HPAT"\n",
                   dcc_ts2str_err(flod_mmaps->sn),
                   flod_mmaps->delay_pos);
            for (mp = flod_mmaps->mmaps;
                 mp <= LAST(flod_mmaps->mmaps);
                 ++mp) {
                  if (mp->hostname[0] == '\0')
                        continue;
                  if (first) {
                        first = 0;
                        printf("%32s %5s %9s %s\n",
                               "peer", "", "ID", "position");
                  }
                  printf("%32.*s,%-5.*s %9d "L_H8PAT"%s%s%s%s%s\n",
                         ISZ(mp->hostname), mp->hostname,
                         ISZ(mp->portname), mp->portname,
                         mp->rem_id,
                         mp->confirm_pos,
                         (mp->flags & OFLOD_MMAP_FG_REWINDING)
                         ? "  rewinding" : "",
                         ((mp->flags & OFLOD_MMAP_FG_NEED_REWIND)
                        ? "  need rewind"
                        : (mp->flags & OFLOD_MMAP_FG_FFWD_IN)
                        ? "  need FFWD"
                        : ""),
                         (mp->flags & OFLOD_MMAP_FG_PASSWD_NEXT)
                         ? "  alt-password" : "",
                         (mp->flags & OFLOD_MMAP_FG_IPv4)
                         ? "  IPv4" : "",
                         (mp->flags & OFLOD_MMAP_FG_IPv6)
                         ? "  IPv6" : "");

            }
            if (!flod_unmap(dcc_emsg, 0))
                  dcc_error_msg("%s", dcc_emsg);
      }
}



static int
open_hash(const char *hash_nm)
{
      struct stat sb;
      int hash_fd;

      db_hash_len = 0;
      hash_fd = open(hash_nm, O_RDONLY, 0);
      if (0 > hash_fd) {
            dcc_error_msg("open(%s): %s",
                        DCC_NM2PATH(hash_nm), ERROR_STR());
            return -1;
      }
      if (0 > fstat(hash_fd, &sb)) {
            dcc_error_msg("stat(%s): %s",
                        DCC_NM2PATH(hash_nm), ERROR_STR());
            close(hash_fd);
            return -1;
      }
      hash_fsize = sb.st_size;
      db_hash_len = hash_fsize/sizeof(HASH_ENTRY);
      if ((hash_fsize % sizeof(HASH_ENTRY)) != 0) {
            dcc_error_msg("%s has size %u, not a multiple of %d",
                        DCC_NM2PATH(hash_nm),
                        hash_fsize, ISZ(HASH_ENTRY));
            db_hash_len = 0;
            close(hash_fd);
            return -1;
      }
      if (db_hash_len < MIN_HASH_ENTRIES) {
            dcc_error_msg("%s has too few records, %d bytes",
                        DCC_NM2PATH(hash_nm), hash_fsize);
            db_hash_len = 0;
            close(hash_fd);
            return -1;
      }

      return hash_fd;
}



#define HASH_MAP_LEN    (1024*1024)
#define HASH_MAP_NUM    16
typedef struct hash_map {
    struct hash_map *fwd, *bak;
    HASH_ENTRY    *buf;
    DB_HADDR      base;
    DB_HADDR      lim;
    off_t   offset;
    int           size;
} HASH_MAP;
static HASH_MAP hash_maps[HASH_MAP_NUM];
static HASH_MAP *hash_map_newest;
static int hash_map_fd;
static const char *hash_map_nm;


static u_char
hash_munmap(HASH_MAP *mp)
{
      if (!mp->buf)
            return 1;

      if (0 > munmap((void *)mp->buf, mp->size)) {
            dcc_error_msg("munmap(%s,%d): %s",
                        hash_map_nm, mp->size, ERROR_STR());
            return 0;
      }
      mp->buf = 0;
      return 1;
}



static u_char
hash_map_clear(int hash_fd, const char *hash_nm)
{
      HASH_MAP *mp;
      int i;

      hash_map_fd = hash_fd;
      hash_map_nm = hash_nm;

      mp = hash_maps;
      for (i = 0; i < DIM(hash_maps); ++i, ++mp) {

            if (i == DIM(hash_maps)-1)
                  mp->fwd = hash_maps;
            else
                  mp->fwd = mp+1;
            if (i == 0)
                  mp->bak = LAST(hash_maps);
            else
                  mp->bak = mp-1;
      }
      hash_map_newest = hash_maps;

      for (mp = hash_maps; mp <= LAST(hash_maps); ++mp) {
            if (!hash_munmap(mp))
                  return 0;
      }

      return 1;
}



static void
hash_map_ref(HASH_MAP *mp)
{
      if (hash_map_newest != mp) {
            mp->fwd->bak = mp->bak;
            mp->bak->fwd = mp->fwd;
            mp->fwd = hash_map_newest;
            mp->bak = hash_map_newest->bak;
            mp->fwd->bak = mp;
            mp->bak->fwd = mp;
            hash_map_newest = mp;
      }
}



static HASH_ENTRY *
entry_mmap(DB_HADDR haddr)
{
      HASH_MAP *mp;
      void *p;
      int i;

      for (i = 0, mp = hash_map_newest;
           i < DIM(hash_maps);
           ++i, mp = mp->fwd) {
            if (!mp->buf)
                  continue;
            if (haddr >= mp->base
                && haddr < mp->lim) {
                  hash_map_ref(mp);
                  return mp->buf + (haddr - mp->base);
            }
      }

      mp = hash_map_newest->bak;
      hash_munmap(mp);

      mp->base = haddr -  haddr%HASH_MAP_LEN;
      mp->offset = mp->base*sizeof(HASH_ENTRY);
      mp->size = hash_fsize - mp->offset;
      if (mp->size > HASH_MAP_LEN*ISZ(HASH_ENTRY))
            mp->size = HASH_MAP_LEN*ISZ(HASH_ENTRY);
      mp->lim = mp->base + mp->size/sizeof(HASH_ENTRY);
      p = mmap(0, mp->size, PROT_READ, MAP_SHARED, hash_map_fd, mp->offset);
      if (p != MAP_FAILED) {
            mp->buf = p;
            hash_map_ref(mp);
            return mp->buf + (haddr - mp->base);
      }
      dcc_error_msg("mmap(%s,%d,%d): %s",
                  hash_map_nm, (int)mp->size, (int)mp->offset,
                  ERROR_STR());
      return 0;
}



static void
list_hash(int hash_fd, const char *hash_nm)
{
      HASH_ENTRY *entry = 0;
      int collisions, chains, chain_lens, max_chain_len, chain_len;
      DB_HADDR fwd, bak, rcd_num;
      DB_HADDR db_hash_used_stored, db_hash_len_stored;
      DB_PTR rcd, db_csize_stored;

      if (verbose >= VERBOSE_HASH)
            printf("\n %s\n", DCC_NM2PATH(hash_nm));

      if (!hash_map_clear(hash_fd, hash_nm))
            return;

      if (no_hash)
            return;

      db_hash_used_stored = 0;
      db_hash_used = DB_HADDR_MIN;
      collisions = 0;
      chains = 0;
      chain_lens = 0;
      max_chain_len = 1;
      for (rcd_num = 0; rcd_num < db_hash_len; ++rcd_num) {
            entry = entry_mmap(rcd_num);
            if (!entry)
                  break;

            /* deal with the special, first entries */
            if (rcd_num == DB_HADDR_MAGIC) {
                  if (memcmp(entry, hash_magic, sizeof(entry))) {
                        dcc_error_msg("%s contains the wrong magic",
                                    DCC_NM2PATH(hash_nm));
                  }
                  if (verbose > VERBOSE_HASH) {
                        printf("     magic: \"%.*s\"\n",
                               ISZ(entry), (char*)entry);
                  }
                  continue;
            }

            fwd = DB_HADDR_EX(entry->fwd);
            bak = DB_HADDR_EX(entry->bak);

            if (rcd_num == DB_HADDR_FREE) {
                  if ((DB_HADDR_INVALID(fwd) && fwd != DB_HADDR_FREE)
                      || (DB_HADDR_INVALID(bak) && bak != DB_HADDR_FREE)
                      || DB_HPTR_EX(entry->rcd) != DB_PTR_NULL
                      || !HE_IS_FREE(entry)) {
                        dcc_error_msg("%s free list broken",
                                    DCC_NM2PATH(hash_nm));
                  }
                  if (verbose == VERBOSE_HASH) {
                        printf("    first free: %x\n",
                               fwd);
                  } else if (verbose > VERBOSE_HASH) {
                        printf("      free: %6x %6x\n",
                               fwd, bak);
                  }
                  continue;
            }

            if (rcd_num == DB_HADDR_SIZES) {
                  if (verbose > VERBOSE_HASH) {
                        printf("     sizes: %6x %6x\n",
                               fwd, bak);
                  }
                  db_hash_len_stored = DB_HADDR_EX(entry->HASH_STORE_LEN);
                  if (db_hash_len != db_hash_len_stored
                      && (db_hash_len_stored != 0
                        || verbose > VERBOSE_HASH)) {
                        dcc_error_msg("%s has %d entries but claims %d",
                                    DCC_NM2PATH(hash_nm),
                                    HASH_LEN_EXT(db_hash_len),
                                    HASH_LEN_EXT(db_hash_len_stored));
                  }
                  db_hash_used_stored = DB_HADDR_EX(entry
                                          ->HASH_STORE_USED);
                  if (db_hash_used_stored > db_hash_len) {
                        dcc_error_msg("%s contains only %d"
                                    " entries but %d used",
                              DCC_NM2PATH(hash_nm),
                              HASH_LEN_EXT(db_hash_len),
                              HASH_LEN_EXT((db_hash_used_stored)));
                  }
                  if (db_hash_used_stored == db_hash_len) {
                        dcc_error_msg("%s overflows with %d entries",
                                    DCC_NM2PATH(hash_nm),
                                    HASH_LEN_EXT(db_hash_len));
                  }
                  db_csize_stored=DB_HPTR_EX(entry->HASH_STORE_DB_CSIZE);
                  if (db_csize_stored != db_csize) {
                        dcc_error_msg("%s claims %s contains "L_DPAT
                                    " bytes instead of "L_DPAT,
                                    DCC_NM2PATH(hash_nm), db_nm,
                                    db_csize_stored, db_csize);
                  }
                  continue;
            }

            /* deal with a free entry */
            if (HE_IS_FREE(entry)) {
                  if (verbose >= VERBOSE_HASH)
                        printf("    %6x: %6x %6x\n",
                               rcd_num, fwd, bak);
                  continue;
            }

            /* deal with a used entry */
            rcd = DB_HPTR_EX(entry->rcd);
            ++db_hash_used;
            if (DB_PTR_IS_BAD_FULL(rcd))
                  dcc_error_msg("bad hash table data link at"
                              " %x to "L_HPAT,
                              rcd_num, rcd);
            if (DB_HADDR_INVALID(fwd) && fwd != DB_HADDR_NULL)
                  dcc_error_msg("bad hash forward link at %x to %x",
                              rcd_num, fwd);
            if (DB_HADDR_INVALID(bak) && bak != DB_HADDR_NULL)
                  dcc_error_msg("bad hash back link at %x to %x",
                              rcd_num, bak);
            if (verbose >= VERBOSE_HASH)
                  printf("    %6x: %6x %6x "L_H8PAT" %s\n",
                         rcd_num, fwd, bak, rcd,
                         dcc_type2str_err(HE_TYPE(entry), 0, 1));
            if (bak != DB_HADDR_NULL) {
                  ++collisions;
            } else {
                  ++chains;
                  bak = rcd_num;
                  chain_len = 1;
                  while (!DB_HADDR_INVALID(fwd)) {
                        if (++chain_len > 100) {
                              dcc_error_msg("possible hash chain loop"
                                          " starting at %x"
                                          " continuing through %x",
                                          rcd_num, fwd);
                              break;
                        }
                        entry = entry_mmap(fwd);
                        if (!entry)
                              break;
                        if (HE_IS_FREE(entry)
                            || DB_HADDR_EX(entry->bak) != bak) {
                              dcc_error_msg("broken hash chain"
                                          " starting at %x at %x",
                                          rcd_num, fwd);
                              break;
                        }
                        bak = fwd;
                        fwd = DB_HADDR_EX(entry->fwd);
                  }
                  chain_lens += chain_len;
                  if (max_chain_len < chain_len)
                        max_chain_len = chain_len;
            }
      }

      hash_map_clear(0, 0);

      if (db_hash_used_stored != db_hash_used) {
            if (db_hash_used_stored == 0)
                  dcc_error_msg("%s was not cleanly closed but has"
                              " %d entries",
                              DCC_NM2PATH(hash_nm),
                              HASH_LEN_EXT(db_hash_used));
            else
                  dcc_error_msg("%s claims to have filled"
                              " %d entries but has %d",
                              DCC_NM2PATH(hash_nm),
                              HASH_LEN_EXT(db_hash_used_stored),
                              HASH_LEN_EXT(db_hash_used));
      }

      if (verbose >= VERBOSE_HASH)
            putchar('\n');
      printf("%7d hash entries total  %d or %d%% used  %d free"
             "  %.2f%% collisions\n",
             HASH_LEN_EXT(db_hash_len),
             HASH_LEN_EXT(db_hash_used),
             (HASH_LEN_EXT(db_hash_used)*100) / HASH_LEN_EXT(db_hash_len),
             HASH_LEN_EXT(db_hash_len) - HASH_LEN_EXT(db_hash_used),
             collisions*100.0/HASH_LEN_EXT(db_hash_len));
      if (chains != 0)
            printf("%7d hash chains  max length %d  average length %.1f\n",
                   chains, max_chain_len, chain_lens*1.0/chains);
}

Generated by  Doxygen 1.6.0   Back to index