New utility: nscd cache dumper

Carlos O'Donell carlos@redhat.com
Thu Mar 12 15:51:13 GMT 2020


On 3/11/20 4:48 PM, DJ Delorie wrote:
> Here's a version that's built into nscd.  It doesn't support live dumps
> but that can be added later if there's a need.
> 
>   $ nscd -p /var/db/nscd/hosts
>

Looking forward to the next version.

> From a53749f8f7d51b50bb7164dec41eb81ce09eee21 Mon Sep 17 00:00:00 2001
> From: DJ Delorie <dj@redhat.com>
> Date: Wed, 19 Feb 2020 12:31:09 -0500
> Subject: nscd: add cache dumper
> 
> 
> 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

OK.

>  
>  ifeq ($(build-nscd)$(have-thread-library),yesyes)
>  
> diff --git a/nscd/cachedumper.c b/nscd/cachedumper.c
> new file mode 100644
> index 0000000000..9f565477ef
> --- /dev/null
> +++ b/nscd/cachedumper.c
> @@ -0,0 +1,359 @@
> +/* Copyright (c) 2020 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.

Remove incorrect contributed by line.

> +
> +   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 <https://www.gnu.org/licenses/>.  */
> +
> +/* cachedumper - dump a human-readable representation of a cache file.  */
> +
> +#include <ctype.h>
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <libintl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <arpa/inet.h>
> +#include <getopt.h>
> +
> +#include "nscd.h"
> +#include "dbg_log.h"
> +
> +static void *the_cache;
> +
> +#define NO_REF ((ref_t)-1)
> +

Please add a comment documenting data_string.

> +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,
> +		(unsigned char) *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;
> +

Please document what this macro does.

> +#define A(x) (int)((char *)&(x)-(char *)the_cache)

> +

Please document what this macro does.

> +#define DPS(f) printf("%08x: %24s : %10d %08x\n", A (dps->f), #f, (int)dps->f, (int)dps->f);
> +

Avoid boolean coercion.

Use debug_level > 0

> +  if (debug_level)
> +    {
> +      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))];
> +

Please document what this loop does in a comment.

> +  for (i=0; i<dps->module; i++)

GNU format please.

> +    {
> +      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),

Needs localization.

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

Use debug_level > 0

> +	  if (debug_level)
> +	    {

Please document what this macro does.

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

Document what this macro does.

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

Several of these need localization.

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

Needs localization.

> +		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 [");

Needs localization.

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

Needs localization.

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

Needs localization.

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

Need localization?

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

Needs localization.

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

OK.

> +}
> diff --git a/nscd/nscd.c b/nscd/nscd.c
> index 310111ae6d..36a27d4c16 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 cache file NAME") },

Suggest:

"Print contents of the offline cache file NAME"

I want to make sure nobody uses this on a live cache file.

>    { "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);

OK.

> +
>    /* 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;

OK.

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

OK.

>  
>  /* Special version of TEMP_FAILURE_RETRY for functions returning error
>     values.  */
> 


-- 
Cheers,
Carlos.



More information about the Libc-alpha mailing list