New utility: nscd cache dumper
Carlos O'Donell
carlos@redhat.com
Thu Mar 12 20:43:18 GMT 2020
On 3/12/20 4:09 PM, DJ Delorie wrote:
> Andreas Schwab <schwab@suse.de> writes:
>>> + (unsigned char) *cp);
>> The cast is a no-op.
> Noted and fixed. My paranoia shines through...
>
> "Carlos O'Donell" <carlos@redhat.com> writes:
>>> +/* 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.
> Oops. Fixed.
>
>> Please add a comment documenting data_string.
> Done.
>
>> Please document what this macro does.
>>> +#define A(x) (int)((char *)&(x)-(char *)the_cache)
> Done.
>
>> 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);
> Done.
>
>> Use debug_level > 0
> Done.
>
>> Please document what this loop does in a comment.
>>> + for (i=0; i<dps->module; i++)
> Done.
>
>> GNU format please.
> Fixed. I ran it through GNU indent :-)
>
>>> + printf ("\n%08x: type %s key %p \"", A (*here),
>> Needs localization.
> "type" and "key" are names of fields, not descriptive.
>
OK. If they are field names then we do *not* want to translate htem.
>> Use debug_level > 0
>>> + if (debug_level)
> Fixed.
>
>> 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);
> Done.
>
>> Document what this macro does.
>>> +#define DSTR(str, l) cp = data_string (cp, str, l)
> Done.
>
>>> + 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.
> Do we want to make them conform with the field names more precisely, or
> make them more descriptive? I was just using abbreviated field names
> (i.e. passwd), not descriptions (i.e. password).
OK. If they are field names, then we should use those, and *not* translate them.
> (apply question to further such comments)
>
We should use field names to help those reviewing the dumped data to match the
value to the fields.
This isn't for the average user to use, but for helping with in-the-field issues.
>>> + { "print", 'p', N_("NAME"), 0,
>>> + N_("Print contents of cache file NAME") },
>> Suggest:
>>
>> "Print contents of the offline cache file NAME"
> Changed as suggested.
>
>> I want to make sure nobody uses this on a live cache file.
> :-)
>
> From: DJ Delorie <dj@redhat.com>
> Date: Wed, 19 Feb 2020 12:31:09 -0500
> Subject: nscd: add cache dumper
>
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> 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 <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)
> +
> +/* 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. */
>
--
Cheers,
Carlos.
More information about the Libc-alpha
mailing list