This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
New utility: nscd cache dumper
- From: DJ Delorie <dj at redhat dot com>
- To: libc-alpha at sourceware dot org
- Date: Fri, 07 Feb 2020 18:12:00 -0500
- Subject: New utility: nscd cache dumper
First pass. Dumps contents of nscd cache files (/var/db/nscd/* on
Fedora). I'm open to tweaks to what's included with -v (verbose)
and/or -x (extra).
Enjoy!
diff --git a/nscd/Makefile b/nscd/Makefile
index e12b9f11f1..d2d91830d0 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -46,6 +46,8 @@ install-sbin := nscd
extra-objs = $(nscd-modules:=.o)
+others += cachedumper
+
endif
all-nscd-modules := $(nscd-modules) selinux
@@ -101,3 +103,6 @@ $(objpfx)nscd: $(shared-thread-library)
else
$(objpfx)nscd: $(static-thread-library)
endif
+
+cache_dumper-modules := cachedumper
+
diff --git a/nscd/cachedumper.c b/nscd/cachedumper.c
new file mode 100644
index 0000000000..4b276f8a73
--- /dev/null
+++ b/nscd/cachedumper.c
@@ -0,0 +1,395 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+
+#include "nscd-client.h"
+
+void *the_cache;
+
+int verbose = 0;
+int extended = 0;
+int noesc = 0;
+
+#define NO_REF ((ref_t)-1)
+
+/* Map request type to a string. */
+const char *const serv2str[LASTREQ] =
+{
+ [GETPWBYNAME] = "GETPWBYNAME",
+ [GETPWBYUID] = "GETPWBYUID",
+ [GETGRBYNAME] = "GETGRBYNAME",
+ [GETGRBYGID] = "GETGRBYGID",
+ [GETHOSTBYNAME] = "GETHOSTBYNAME",
+ [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
+ [GETHOSTBYADDR] = "GETHOSTBYADDR",
+ [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
+ [SHUTDOWN] = "SHUTDOWN",
+ [GETSTAT] = "GETSTAT",
+ [INVALIDATE] = "INVALIDATE",
+ [GETFDPW] = "GETFDPW",
+ [GETFDGR] = "GETFDGR",
+ [GETFDHST] = "GETFDHST",
+ [GETAI] = "GETAI",
+ [INITGROUPS] = "INITGROUPS",
+ [GETSERVBYNAME] = "GETSERVBYNAME",
+ [GETSERVBYPORT] = "GETSERVBYPORT",
+ [GETFDSERV] = "GETFDSERV",
+ [GETNETGRENT] = "GETNETGRENT",
+ [INNETGR] = "INNETGR",
+ [GETFDNETGR] = "GETFDNETGR"
+};
+
+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) ? 1 : (cp < cpe))
+ {
+ if (isgraph (*cp))
+ putchar (*cp);
+ else
+ if (noesc)
+ printf ("<%02x>",
+ (unsigned char) *cp);
+ else
+ printf ("\033[%dm<%02x>\033[0m",
+ *cp % 6 + 31,
+ (unsigned char) *cp);
+ if (len == -1 && *cp == 0)
+ return cp + 1;
+
+ ++ cp;
+ if (++oops > 1000)
+ break;
+ }
+ return cp;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ int fd;
+ int i, o;
+
+ while ((o = getopt (argc, argv, "vxph")) != -1)
+ switch (o)
+ {
+ case 'v':
+ verbose ++;
+ break;
+ case 'x':
+ extended ++;
+ break;
+ case 'p':
+ noesc ++;
+ break;
+ case '?':
+ case 'h':
+ fprintf (stderr, "Usage: %s [-v] [-x] [-p] /var/db/nscd/<file>\n", argv[0]);
+ fprintf (stderr, " -v = verbose, may be given multiple times\n");
+ fprintf (stderr, " -x = print extra data after record (twice,"
+ " prints all data of record)\n");
+ fprintf (stderr, " -p = plain text, no colors\n");
+ exit (1);
+ }
+
+ if (stat (argv[optind], &st) < 0)
+ {
+ perror (argv [optind]);
+ exit (1);
+ }
+
+ fd = open (argv[optind], 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;
+
+#define A(x) (int)((char *)&(x)-(char *)the_cache)
+
+#define DPS(f) printf("%08x: %24s : %10d %08x\n", A (dps->f), #f, (int)dps->f, (int)dps->f);
+
+ if (verbose)
+ {
+ 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))];
+
+ for (i=0; i<dps->module; i++)
+ {
+ ref_t r = dps->array[i];
+ if (r == NO_REF)
+ continue;
+
+ if (verbose > 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 (verbose)
+ {
+#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 (extended > 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);
+
+#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 (extended && 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);
+}