New utility: nscd cache dumper

Message ID xnk14ydmbj.fsf@greed.delorie.com
State Superseded
Headers

Commit Message

DJ Delorie Feb. 7, 2020, 11:12 p.m. UTC
  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!
  

Comments

Andreas Schwab Feb. 8, 2020, 9:46 a.m. UTC | #1
On Feb 07 2020, DJ Delorie wrote:

> +  while ((len == -1) ? 1 : (cp < cpe))

What's wrong with (len == -1 || cp < cpe)?

Andreas.
  
Joseph Myers Feb. 10, 2020, 10:25 p.m. UTC | #2
On Fri, 7 Feb 2020, DJ Delorie wrote:

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

This needs a copyright and license notice.

If you intend to make this an installed program (with a name making it 
clear it's something to do with nscd), it also needs --help and --version 
support following the GNU Coding Standards (so output to stdout not 
stderr, exit with successful status, bug reporting link in --help output, 
copyright and license notice in --version output) - with the configured 
pkgversion / bugurl properly respected, and with that output being marked 
for translation but with the copyright year not being part of the 
translated string so the translated string doesn't need to change each 
year.
  

Patch

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);
+}