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