Blame nscd/nscd_stat.c

Packit 6c4009
/* Copyright (c) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <langinfo.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/socket.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
Packit 6c4009
#include "nscd.h"
Packit 6c4009
#include "dbg_log.h"
Packit 6c4009
#include "selinux.h"
Packit 6c4009
#ifdef HAVE_SELINUX
Packit 6c4009
# include <selinux/selinux.h>
Packit 6c4009
# include <selinux/avc.h>
Packit 6c4009
#endif /* HAVE_SELINUX */
Packit 6c4009
Packit 6c4009
/* We use this to make sure the receiver is the same.  The lower 16
Packit 6c4009
   bits are reserved for flags indicating compilation variants.  This
Packit 6c4009
   version needs to be updated if the definition of struct statdata
Packit 6c4009
   changes.  */
Packit 6c4009
#define STATDATA_VERSION  0x01020000U
Packit 6c4009
Packit 6c4009
#ifdef HAVE_SELINUX
Packit 6c4009
# define STATDATA_VERSION_SELINUX_FLAG 0x0001U
Packit 6c4009
#else
Packit 6c4009
# define STATDATA_VERSION_SELINUX_FLAG 0x0000U
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* All flags affecting the struct statdata layout.  */
Packit 6c4009
#define STATDATA_VERSION_FLAGS STATDATA_VERSION_SELINUX_FLAG
Packit 6c4009
Packit 6c4009
/* The full version number for struct statdata.  */
Packit 6c4009
#define STATDATA_VERSION_FULL (STATDATA_VERSION | STATDATA_VERSION_FLAGS)
Packit 6c4009
Packit 6c4009
/* Statistic data for one database.  */
Packit 6c4009
struct dbstat
Packit 6c4009
{
Packit 6c4009
  int enabled;
Packit 6c4009
  int check_file;
Packit 6c4009
  int shared;
Packit 6c4009
  int persistent;
Packit 6c4009
  size_t module;
Packit 6c4009
Packit 6c4009
  unsigned long int postimeout;
Packit 6c4009
  unsigned long int negtimeout;
Packit 6c4009
Packit 6c4009
  size_t nentries;
Packit 6c4009
  size_t maxnentries;
Packit 6c4009
  size_t maxnsearched;
Packit 6c4009
  size_t datasize;
Packit 6c4009
  size_t dataused;
Packit 6c4009
Packit 6c4009
  uintmax_t poshit;
Packit 6c4009
  uintmax_t neghit;
Packit 6c4009
  uintmax_t posmiss;
Packit 6c4009
  uintmax_t negmiss;
Packit 6c4009
Packit 6c4009
  uintmax_t rdlockdelayed;
Packit 6c4009
  uintmax_t wrlockdelayed;
Packit 6c4009
Packit 6c4009
  uintmax_t addfailed;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Record for transmitting statistics.  If this definition changes,
Packit 6c4009
   update STATDATA_VERSION above.  */
Packit 6c4009
struct statdata
Packit 6c4009
{
Packit 6c4009
  unsigned int version;		/* Must be STATDATA_VERSION_FULL.  */
Packit 6c4009
  int debug_level;
Packit 6c4009
  time_t runtime;
Packit 6c4009
  unsigned long int client_queued;
Packit 6c4009
  int nthreads;
Packit 6c4009
  int max_nthreads;
Packit 6c4009
  int paranoia;
Packit 6c4009
  time_t restart_interval;
Packit 6c4009
  unsigned int reload_count;
Packit 6c4009
  int ndbs;
Packit 6c4009
  struct dbstat dbs[lastdb];
Packit 6c4009
#ifdef HAVE_SELINUX
Packit 6c4009
  struct avc_cache_stats cstats;
Packit 6c4009
#endif /* HAVE_SELINUX */
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
send_stats (int fd, struct database_dyn dbs[lastdb])
Packit 6c4009
{
Packit 6c4009
  struct statdata data;
Packit 6c4009
  int cnt;
Packit 6c4009
Packit 6c4009
  memset (&data, 0, sizeof (data));
Packit 6c4009
Packit 6c4009
  data.version = STATDATA_VERSION_FULL;
Packit 6c4009
  data.debug_level = debug_level;
Packit 6c4009
  data.runtime = time (NULL) - start_time;
Packit 6c4009
  data.client_queued = client_queued;
Packit 6c4009
  data.nthreads = nthreads;
Packit 6c4009
  data.max_nthreads = max_nthreads;
Packit 6c4009
  data.paranoia = paranoia;
Packit 6c4009
  data.restart_interval = restart_interval;
Packit 6c4009
  data.reload_count = reload_count;
Packit 6c4009
  data.ndbs = lastdb;
Packit 6c4009
Packit 6c4009
  for (cnt = 0; cnt < lastdb; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      memset (&data.dbs[cnt], 0, sizeof (data.dbs[cnt]));
Packit 6c4009
      data.dbs[cnt].enabled = dbs[cnt].enabled;
Packit 6c4009
      data.dbs[cnt].check_file = dbs[cnt].check_file;
Packit 6c4009
      data.dbs[cnt].shared = dbs[cnt].shared;
Packit 6c4009
      data.dbs[cnt].persistent = dbs[cnt].persistent;
Packit 6c4009
      data.dbs[cnt].postimeout = dbs[cnt].postimeout;
Packit 6c4009
      data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
Packit 6c4009
      if (dbs[cnt].head != NULL)
Packit 6c4009
	{
Packit 6c4009
	  data.dbs[cnt].module = dbs[cnt].head->module;
Packit 6c4009
	  data.dbs[cnt].poshit = dbs[cnt].head->poshit;
Packit 6c4009
	  data.dbs[cnt].neghit = dbs[cnt].head->neghit;
Packit 6c4009
	  data.dbs[cnt].posmiss = dbs[cnt].head->posmiss;
Packit 6c4009
	  data.dbs[cnt].negmiss = dbs[cnt].head->negmiss;
Packit 6c4009
	  data.dbs[cnt].nentries = dbs[cnt].head->nentries;
Packit 6c4009
	  data.dbs[cnt].maxnentries = dbs[cnt].head->maxnentries;
Packit 6c4009
	  data.dbs[cnt].datasize = dbs[cnt].head->data_size;
Packit 6c4009
	  data.dbs[cnt].dataused = dbs[cnt].head->first_free;
Packit 6c4009
	  data.dbs[cnt].maxnsearched = dbs[cnt].head->maxnsearched;
Packit 6c4009
	  data.dbs[cnt].rdlockdelayed = dbs[cnt].head->rdlockdelayed;
Packit 6c4009
	  data.dbs[cnt].wrlockdelayed = dbs[cnt].head->wrlockdelayed;
Packit 6c4009
	  data.dbs[cnt].addfailed = dbs[cnt].head->addfailed;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (selinux_enabled)
Packit 6c4009
    nscd_avc_cache_stats (&data.cstats);
Packit 6c4009
Packit 6c4009
  if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
Packit 6c4009
      != sizeof (data))
Packit 6c4009
    {
Packit 6c4009
      char buf[256];
Packit 6c4009
      dbg_log (_("cannot write statistics: %s"),
Packit 6c4009
	       strerror_r (errno, buf, sizeof (buf)));
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
receive_print_stats (void)
Packit 6c4009
{
Packit 6c4009
  struct statdata data;
Packit 6c4009
  request_header req;
Packit 6c4009
  ssize_t nbytes;
Packit 6c4009
  int fd;
Packit 6c4009
  int i;
Packit 6c4009
  uid_t uid = getuid ();
Packit 6c4009
  const char *yesstr = _("yes");
Packit 6c4009
  const char *nostr = _("no");
Packit 6c4009
Packit 6c4009
  /* Find out whether there is another user but root allowed to
Packit 6c4009
     request statistics.  */
Packit 6c4009
  if (uid != 0)
Packit 6c4009
    {
Packit 6c4009
      /* User specified?  */
Packit 6c4009
      if(stat_user == NULL || stat_uid != uid)
Packit 6c4009
	{
Packit 6c4009
	  if (stat_user != NULL)
Packit 6c4009
	    error (EXIT_FAILURE, 0,
Packit 6c4009
		   _("Only root or %s is allowed to use this option!"),
Packit 6c4009
		   stat_user);
Packit 6c4009
	  else
Packit 6c4009
	    error (EXIT_FAILURE, 0,
Packit 6c4009
		   _("Only root is allowed to use this option!"));
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Open a socket to the running nscd.  */
Packit 6c4009
  fd = nscd_open_socket ();
Packit 6c4009
  if (fd == -1)
Packit 6c4009
    error (EXIT_FAILURE, 0, _("nscd not running!\n"));
Packit 6c4009
Packit 6c4009
  /* Send the request.  */
Packit 6c4009
  req.version = NSCD_VERSION;
Packit 6c4009
  req.type = GETSTAT;
Packit 6c4009
  req.key_len = 0;
Packit 6c4009
  nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
Packit 6c4009
				     MSG_NOSIGNAL));
Packit 6c4009
  if (nbytes != sizeof (request_header))
Packit 6c4009
    {
Packit 6c4009
      int err = errno;
Packit 6c4009
      close (fd);
Packit 6c4009
      error (EXIT_FAILURE, err, _("write incomplete"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Read as much data as we expect.  */
Packit 6c4009
  if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
Packit 6c4009
      || (data.version != STATDATA_VERSION_FULL
Packit 6c4009
	  /* Yes, this is an assignment!  */
Packit 6c4009
	  && (errno = EINVAL)))
Packit 6c4009
    {
Packit 6c4009
      /* Not the right version.  */
Packit 6c4009
      int err = errno;
Packit 6c4009
      close (fd);
Packit 6c4009
      error (EXIT_FAILURE, err, _("cannot read statistics data"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  printf (_("nscd configuration:\n\n%15d  server debug level\n"),
Packit 6c4009
	  data.debug_level);
Packit 6c4009
Packit 6c4009
  /* We know that we can simply subtract time_t values.  */
Packit 6c4009
  unsigned long int diff = data.runtime;
Packit 6c4009
  unsigned int ndays = 0;
Packit 6c4009
  unsigned int nhours = 0;
Packit 6c4009
  unsigned int nmins = 0;
Packit 6c4009
  if (diff > 24 * 60 * 60)
Packit 6c4009
    {
Packit 6c4009
      ndays = diff / (24 * 60 * 60);
Packit 6c4009
      diff %= 24 * 60 * 60;
Packit 6c4009
    }
Packit 6c4009
  if (diff > 60 * 60)
Packit 6c4009
    {
Packit 6c4009
      nhours = diff / (60 * 60);
Packit 6c4009
      diff %= 60 * 60;
Packit 6c4009
    }
Packit 6c4009
  if (diff > 60)
Packit 6c4009
    {
Packit 6c4009
      nmins = diff / 60;
Packit 6c4009
      diff %= 60;
Packit 6c4009
    }
Packit 6c4009
  if (ndays != 0)
Packit 6c4009
    printf (_("%3ud %2uh %2um %2lus  server runtime\n"),
Packit 6c4009
	    ndays, nhours, nmins, diff);
Packit 6c4009
  else if (nhours != 0)
Packit 6c4009
    printf (_("    %2uh %2um %2lus  server runtime\n"), nhours, nmins, diff);
Packit 6c4009
  else if (nmins != 0)
Packit 6c4009
    printf (_("        %2um %2lus  server runtime\n"), nmins, diff);
Packit 6c4009
  else
Packit 6c4009
    printf (_("            %2lus  server runtime\n"), diff);
Packit 6c4009
Packit 6c4009
  printf (_("%15d  current number of threads\n"
Packit 6c4009
	    "%15d  maximum number of threads\n"
Packit 6c4009
	    "%15lu  number of times clients had to wait\n"
Packit 6c4009
	    "%15s  paranoia mode enabled\n"
Packit 6c4009
	    "%15lu  restart internal\n"
Packit 6c4009
	    "%15u  reload count\n"),
Packit 6c4009
	  data.nthreads, data.max_nthreads, data.client_queued,
Packit 6c4009
	  data.paranoia ? yesstr : nostr,
Packit 6c4009
	  (unsigned long int) data.restart_interval, data.reload_count);
Packit 6c4009
Packit 6c4009
  for (i = 0; i < lastdb; ++i)
Packit 6c4009
    {
Packit 6c4009
      unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
Packit 6c4009
      unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
Packit 6c4009
      const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
Packit 6c4009
      const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
Packit 6c4009
      const char *shared = data.dbs[i].shared ? yesstr : nostr;
Packit 6c4009
      const char *persistent = data.dbs[i].persistent ? yesstr : nostr;
Packit 6c4009
Packit 6c4009
      if (enabled[0] == '\0')
Packit 6c4009
	/* The locale does not provide this information so we have to
Packit 6c4009
	   translate it ourself.  Since we should avoid short translation
Packit 6c4009
	   terms we artifically increase the length.  */
Packit 6c4009
	enabled = data.dbs[i].enabled ? yesstr : nostr;
Packit 6c4009
      if (check_file[0] == '\0')
Packit 6c4009
	check_file = data.dbs[i].check_file ? yesstr : nostr;
Packit 6c4009
      if (shared[0] == '\0')
Packit 6c4009
	shared = data.dbs[i].shared ? yesstr : nostr;
Packit 6c4009
      if (persistent[0] == '\0')
Packit 6c4009
	persistent = data.dbs[i].persistent ? yesstr : nostr;
Packit 6c4009
Packit 6c4009
      if (all == 0)
Packit 6c4009
	/* If nothing happened so far report a 0% hit rate.  */
Packit 6c4009
	all = 1;
Packit 6c4009
Packit 6c4009
      printf (_("\n%s cache:\n\n"
Packit 6c4009
		"%15s  cache is enabled\n"
Packit 6c4009
		"%15s  cache is persistent\n"
Packit 6c4009
		"%15s  cache is shared\n"
Packit 6c4009
		"%15zu  suggested size\n"
Packit 6c4009
		"%15zu  total data pool size\n"
Packit 6c4009
		"%15zu  used data pool size\n"
Packit 6c4009
		"%15lu  seconds time to live for positive entries\n"
Packit 6c4009
		"%15lu  seconds time to live for negative entries\n"
Packit 6c4009
		"%15" PRIuMAX "  cache hits on positive entries\n"
Packit 6c4009
		"%15" PRIuMAX "  cache hits on negative entries\n"
Packit 6c4009
		"%15" PRIuMAX "  cache misses on positive entries\n"
Packit 6c4009
		"%15" PRIuMAX "  cache misses on negative entries\n"
Packit 6c4009
		"%15lu%% cache hit rate\n"
Packit 6c4009
		"%15zu  current number of cached values\n"
Packit 6c4009
		"%15zu  maximum number of cached values\n"
Packit 6c4009
		"%15zu  maximum chain length searched\n"
Packit 6c4009
		"%15" PRIuMAX "  number of delays on rdlock\n"
Packit 6c4009
		"%15" PRIuMAX "  number of delays on wrlock\n"
Packit 6c4009
		"%15" PRIuMAX "  memory allocations failed\n"
Packit 6c4009
		"%15s  check /etc/%s for changes\n"),
Packit 6c4009
	      dbnames[i], enabled, persistent, shared,
Packit 6c4009
	      data.dbs[i].module,
Packit 6c4009
	      data.dbs[i].datasize, data.dbs[i].dataused,
Packit 6c4009
	      data.dbs[i].postimeout, data.dbs[i].negtimeout,
Packit 6c4009
	      data.dbs[i].poshit, data.dbs[i].neghit,
Packit 6c4009
	      data.dbs[i].posmiss, data.dbs[i].negmiss,
Packit 6c4009
	      (100 * hit) / all,
Packit 6c4009
	      data.dbs[i].nentries, data.dbs[i].maxnentries,
Packit 6c4009
	      data.dbs[i].maxnsearched,
Packit 6c4009
	      data.dbs[i].rdlockdelayed,
Packit 6c4009
	      data.dbs[i].wrlockdelayed,
Packit 6c4009
	      data.dbs[i].addfailed, check_file, dbnames[i]);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (selinux_enabled)
Packit 6c4009
    nscd_avc_print_stats (&data.cstats);
Packit 6c4009
Packit 6c4009
  close (fd);
Packit 6c4009
Packit 6c4009
  exit (0);
Packit 6c4009
}