From dcf46d3fe5ad8e93f25ad90771484aa22c2ca4fc Mon Sep 17 00:00:00 2001 From: DJ Delorie Date: Wed, 19 Feb 2020 12:31:09 -0500 Subject: nscd: add cache dumper --- nscd/Makefile | 2 +- nscd/cachedumper.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nscd/nscd.c | 13 ++ nscd/nscd.h | 2 + 4 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 nscd/cachedumper.c (limited to 'nscd') diff --git a/nscd/Makefile b/nscd/Makefile index e12b9f11f1..87c571c228 100644 --- a/nscd/Makefile +++ b/nscd/Makefile @@ -36,7 +36,7 @@ nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \ getsrvbynm_r getsrvbypt_r servicescache \ dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \ xmalloc xstrdup aicache initgrcache gai res_hconf \ - netgroupcache + netgroupcache cachedumper ifeq ($(build-nscd)$(have-thread-library),yesyes) diff --git a/nscd/cachedumper.c b/nscd/cachedumper.c new file mode 100644 index 0000000000..c5bd370918 --- /dev/null +++ b/nscd/cachedumper.c @@ -0,0 +1,373 @@ +/* Copyright (c) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +/* cachedumper - dump a human-readable representation of a cache file. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nscd.h" +#include "dbg_log.h" + +static void *the_cache; + +#define NO_REF ((ref_t)-1) + +/* Given a chunk of raw data CP of length LEN, print it in a hopefully + user-readable format, including colorizing non-readable characters. + STR prefixes it, if non-NULL. If LEN is -1, CP is + NUL-terminated. */ +unsigned char * +data_string (unsigned char *cp, const char *str, int len) +{ + int oops = 0; + unsigned char *cpe = cp + len; + printf ("%s", str); + while (len == -1 || cp < cpe) + { + if (isgraph (*cp)) + putchar (*cp); + else + printf ("\033[%dm<%02x>\033[0m", *cp % 6 + 31, *cp); + if (len == -1 && *cp == 0) + return cp + 1; + + ++cp; + if (++oops > 1000) + break; + } + return cp; +} + +void +nscd_print_cache (const char *name) +{ + struct stat st; + int fd; + int i; + + if (stat (name, &st) < 0) + { + perror (name); + exit (1); + } + + fd = open (name, O_RDONLY); + + the_cache = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + + struct database_pers_head *dps = (struct database_pers_head *) the_cache; + + /* Shortcut for "print the cache offset (address) of X in the + cache". */ +#define A(x) (int)((char *)&(x)-(char *)the_cache) + + /* Common code for "print field DPS->F, it's offset, and contents". */ +#define DPS(f) printf("%08x: %24s : %10d %08x\n", A (dps->f), #f, (int)dps->f, (int)dps->f); + + if (debug_level > 0) + { + DPS (version); + DPS (header_size); + DPS (gc_cycle); + DPS (nscd_certainly_running); + DPS (timestamp); + DPS (module); + DPS (data_size); + DPS (first_free); + DPS (nentries); + DPS (maxnentries); + DPS (maxnsearched); + DPS (poshit); + DPS (neghit); + DPS (posmiss); + DPS (negmiss); + DPS (rdlockdelayed); + DPS (wrlockdelayed); + DPS (addfailed); + printf ("\n"); + } + + + char *data = (char *) &dps->array[roundup (dps->module, + ALIGN / sizeof (ref_t))]; + + /* Loop through each entry in the hash table, which is of size + dps->module. Raw data is stored after the hash table in the + cache file. */ + for (i = 0; i < dps->module; i++) + { + ref_t r = dps->array[i]; + if (r == NO_REF) + continue; + + if (debug_level > 2) + printf ("hash[%4d] = 0x%x\n", i, r); + + while (r != NO_REF) + { + struct hashentry *here = (struct hashentry *) (data + r); + + unsigned char *key = (unsigned char *) data + here->key; + + printf ("\n%08x: type %s key %p \"", A (*here), + serv2str[here->type], key); + + data_string (key, "", here->len); + + struct datahead *dh = (struct datahead *) (data + here->packet); + printf ("\" (len:%d) Data %08lx\n", here->len, + (char *) dh - (char *) the_cache); + + if (debug_level > 0) + { +/* Common code for printing fields in struct DATAHEAD DH. */ +#define DH(f) printf ("%08x; %24s : %10d %08x\n", A (dh->f), #f, (int)dh->f, (int)dh->f); + DH (allocsize); + DH (recsize); + DH (timeout); + DH (notfound); + DH (nreloads); + DH (usable); + DH (unused); + DH (ttl); + } + + unsigned char *cp = (unsigned char *) (&dh->data[0]); + unsigned char *cpe = + (unsigned char *) (&dh->data[0]) + dh->allocsize; + + + int i; + uint32_t *grplens; + + if (debug_level > 1) + { + data_string (cp, _(" - all data: "), cpe - cp); + printf ("\n"); + } + + /* These two are common to all responses. */ + printf ("V%d F%d", + dh->data[0].pwdata.version, dh->data[0].pwdata.found); + +/* Shortcut for the common case where we iterate through + fixed-length strings stored in the data portion of the + cache. CP is updated to point to the next string. */ +#define DSTR(str, l) cp = data_string (cp, str, l) + + switch (here->type) + { + case GETPWBYNAME: + case GETPWBYUID: + { + pw_response_header *pw = &(dh->data[0].pwdata); + cp += sizeof (*pw); + DSTR (" name ", pw->pw_name_len); + DSTR (" passwd ", pw->pw_passwd_len); + printf (" uid %d gid %d", pw->pw_uid, pw->pw_gid); + DSTR (" gecos ", pw->pw_gecos_len); + DSTR (" dir ", pw->pw_dir_len); + DSTR (" shell ", pw->pw_shell_len); + DSTR (" byuid ", -1); + DSTR (" key ", -1); + printf ("\n"); + } + break; + + case GETGRBYNAME: + case GETGRBYGID: + { + gr_response_header *gr = &(dh->data[0].grdata); + cp += sizeof (*gr); + grplens = (uint32_t *) cp; + cp += gr->gr_mem_cnt * sizeof (uint32_t); + DSTR (" name ", gr->gr_name_len); + DSTR (" passwd ", gr->gr_passwd_len); + printf (" gid %d members %d [ ", (int) gr->gr_gid, + (int) gr->gr_mem_cnt); + for (i = 0; i < gr->gr_mem_cnt; i++) + DSTR (" ", grplens[i]); + DSTR (" ] bygid ", -1); + DSTR (" key ", -1); + printf ("\n"); + } + break; + + case GETHOSTBYADDR: + case GETHOSTBYADDRv6: + case GETHOSTBYNAME: + case GETHOSTBYNAMEv6: + { + hst_response_header *hst = &(dh->data[0].hstdata); + printf (" addrtype %d error %d", hst->h_addrtype, hst->error); + cp += sizeof (*hst); + DSTR (" name ", hst->h_name_len); + uint32_t *aliases_len = (uint32_t *) cp; + cp += hst->h_aliases_cnt * sizeof (uint32_t); + uint32_t *addrs = (uint32_t *) cp; + cp += hst->h_length * hst->h_addr_list_cnt; + + if (hst->h_aliases_cnt) + { + printf (" aliases ["); + for (i = 0; i < hst->h_aliases_cnt; i++) + DSTR (" ", aliases_len[i]); + printf (" ]"); + } + if (hst->h_addr_list_cnt) + { + char buf[INET6_ADDRSTRLEN]; + printf (" addresses ["); + for (i = 0; i < hst->h_addr_list_cnt; i++) + { + inet_ntop (hst->h_addrtype, addrs, buf, sizeof (buf)); + printf (" %s", buf); + addrs += hst->h_length; + } + printf (" ]"); + } + + printf ("\n"); + } + break; + + case GETAI: + { + ai_response_header *ai = &(dh->data[0].aidata); + printf (" naddrs %d addrslen %d canonlen %d error %d [", + ai->naddrs, ai->addrslen, ai->canonlen, ai->error); + cp += sizeof (*ai); + unsigned char *addrs = cp; + unsigned char *families = cp + ai->addrslen; + cp = families + ai->naddrs; + char buf[INET6_ADDRSTRLEN]; + + for (i = 0; i < ai->naddrs; i++) + { + switch (*families) + { + case AF_INET: + inet_ntop (*families, addrs, buf, sizeof (buf)); + printf (" %s", buf); + addrs += 4; + break; + case AF_INET6: + inet_ntop (*families, addrs, buf, sizeof (buf)); + printf (" %s", buf); + addrs += 16; + break; + } + families++; + } + DSTR (" ] canon ", ai->canonlen); + DSTR (" key ", -1); + printf ("\n"); + } + break; + + case INITGROUPS: + { + initgr_response_header *ig = &(dh->data[0].initgrdata); + printf (" nresults %d groups [", (int) ig->ngrps); + cp += sizeof (*ig); + grplens = (uint32_t *) cp; + cp += ig->ngrps * sizeof (uint32_t); + for (i = 0; i < ig->ngrps; i++) + printf (" %d", grplens[i]); + DSTR (" ] key ", -1); + printf ("\n"); + } + break; + + case GETSERVBYNAME: + case GETSERVBYPORT: + { + serv_response_header *serv = &(dh->data[0].servdata); + printf (" alias_cnt %d port %d (stored as %d)", + serv->s_aliases_cnt, + ((serv->s_port & 0xff00) >> 8) | ((serv-> + s_port & 0xff) << + 8), serv->s_port); + cp += sizeof (*serv); + DSTR (" name ", serv->s_name_len); + DSTR (" proto ", serv->s_proto_len); + if (serv->s_aliases_cnt) + { + uint32_t *alias_len = (uint32_t *) cp; + printf (" aliases ["); + cp += sizeof (uint32_t) * serv->s_aliases_cnt; + for (i = 0; i < serv->s_aliases_cnt; i++) + DSTR (" ", alias_len[i]); + printf (" ]"); + } + printf ("\n"); + } + break; + + case GETNETGRENT: + { + netgroup_response_header *ng = &(dh->data[0].netgroupdata); + printf (" nresults %d len %d\n", + (int) ng->nresults, (int) ng->result_len); + cp += sizeof (*ng); + for (i = 0; i < ng->nresults; i++) + { + DSTR (" (", -1); + DSTR (",", -1); + DSTR (",", -1); + printf (")"); + } + printf ("\n"); + } + break; + + case INNETGR: + { + innetgroup_response_header *ing = + &(dh->data[0].innetgroupdata); + printf (" result %d\n", ing->result); + } + break; + + default: + break; + } + + if (debug_level > 2 && cp && cp < cpe) + { + printf (_(" - remaining data %p: "), cp); + data_string (cp, "", cpe - cp); + printf ("\n"); + } + + + r = here->next; + } + } + + munmap (the_cache, st.st_size); + + exit (0); +} diff --git a/nscd/nscd.c b/nscd/nscd.c index 310111ae6d..4731146fad 100644 --- a/nscd/nscd.c +++ b/nscd/nscd.c @@ -77,6 +77,8 @@ static run_modes run_mode = RUN_DAEMONIZE; static const char *conffile = _PATH_NSCDCONF; +static const char *print_cache = NULL; + time_t start_time; uintptr_t pagesize_m1; @@ -106,6 +108,8 @@ static const struct argp_option options[] = N_("Read configuration data from NAME") }, { "debug", 'd', NULL, 0, N_("Do not fork and display messages on the current tty") }, + { "print", 'p', N_("NAME"), 0, + N_("Print contents of the offline cache file NAME") }, { "foreground", 'F', NULL, 0, N_("Do not fork, but otherwise behave like a daemon") }, { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") }, @@ -157,6 +161,11 @@ main (int argc, char **argv) exit (1); } + /* Print the contents of the indicated cache file. */ + if (print_cache != NULL) + /* Does not return. */ + nscd_print_cache (print_cache); + /* Read the configuration file. */ if (nscd_parse_file (conffile, dbs) != 0) /* We couldn't read the configuration file. We don't start the @@ -404,6 +413,10 @@ parse_opt (int key, char *arg, struct argp_state *state) run_mode = RUN_DEBUG; break; + case 'p': + print_cache = arg; + break; + case 'F': run_mode = RUN_FOREGROUND; break; diff --git a/nscd/nscd.h b/nscd/nscd.h index 5f6327c192..11ab27fb54 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -364,6 +364,8 @@ extern void gc (struct database_dyn *db); /* nscd_setup_thread.c */ extern int setup_thread (struct database_dyn *db); +/* cachedumper.c */ +extern void nscd_print_cache (const char *name); /* Special version of TEMP_FAILURE_RETRY for functions returning error values. */ -- cgit 1.4.1