(root)/
glibc-2.38/
nscd/
nscd_stat.c
       1  /* Copyright (c) 1998-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <error.h>
      20  #include <inttypes.h>
      21  #include <langinfo.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <sys/socket.h>
      26  #include <unistd.h>
      27  #include <libintl.h>
      28  
      29  #include "nscd.h"
      30  #include "dbg_log.h"
      31  #include "selinux.h"
      32  #ifdef HAVE_SELINUX
      33  # include <selinux/selinux.h>
      34  # include <selinux/avc.h>
      35  #endif /* HAVE_SELINUX */
      36  
      37  /* We use this to make sure the receiver is the same.  The lower 16
      38     bits are reserved for flags indicating compilation variants.  This
      39     version needs to be updated if the definition of struct statdata
      40     changes.  */
      41  #define STATDATA_VERSION  0x01020000U
      42  
      43  #ifdef HAVE_SELINUX
      44  # define STATDATA_VERSION_SELINUX_FLAG 0x0001U
      45  #else
      46  # define STATDATA_VERSION_SELINUX_FLAG 0x0000U
      47  #endif
      48  
      49  /* All flags affecting the struct statdata layout.  */
      50  #define STATDATA_VERSION_FLAGS STATDATA_VERSION_SELINUX_FLAG
      51  
      52  /* The full version number for struct statdata.  */
      53  #define STATDATA_VERSION_FULL (STATDATA_VERSION | STATDATA_VERSION_FLAGS)
      54  
      55  /* Statistic data for one database.  */
      56  struct dbstat
      57  {
      58    int enabled;
      59    int check_file;
      60    int shared;
      61    int persistent;
      62    size_t module;
      63  
      64    unsigned long int postimeout;
      65    unsigned long int negtimeout;
      66  
      67    size_t nentries;
      68    size_t maxnentries;
      69    size_t maxnsearched;
      70    size_t datasize;
      71    size_t dataused;
      72  
      73    uintmax_t poshit;
      74    uintmax_t neghit;
      75    uintmax_t posmiss;
      76    uintmax_t negmiss;
      77  
      78    uintmax_t rdlockdelayed;
      79    uintmax_t wrlockdelayed;
      80  
      81    uintmax_t addfailed;
      82  };
      83  
      84  /* Record for transmitting statistics.  If this definition changes,
      85     update STATDATA_VERSION above.  */
      86  struct statdata
      87  {
      88    unsigned int version;		/* Must be STATDATA_VERSION_FULL.  */
      89    int debug_level;
      90    time_t runtime;
      91    unsigned long int client_queued;
      92    int nthreads;
      93    int max_nthreads;
      94    int paranoia;
      95    time_t restart_interval;
      96    unsigned int reload_count;
      97    int ndbs;
      98    struct dbstat dbs[lastdb];
      99  #ifdef HAVE_SELINUX
     100    struct avc_cache_stats cstats;
     101  #endif /* HAVE_SELINUX */
     102  };
     103  
     104  
     105  void
     106  send_stats (int fd, struct database_dyn dbs[lastdb])
     107  {
     108    struct statdata data;
     109    int cnt;
     110  
     111    memset (&data, 0, sizeof (data));
     112  
     113    data.version = STATDATA_VERSION_FULL;
     114    data.debug_level = debug_level;
     115    data.runtime = time (NULL) - start_time;
     116    data.client_queued = client_queued;
     117    data.nthreads = nthreads;
     118    data.max_nthreads = max_nthreads;
     119    data.paranoia = paranoia;
     120    data.restart_interval = restart_interval;
     121    data.reload_count = reload_count;
     122    data.ndbs = lastdb;
     123  
     124    for (cnt = 0; cnt < lastdb; ++cnt)
     125      {
     126        memset (&data.dbs[cnt], 0, sizeof (data.dbs[cnt]));
     127        data.dbs[cnt].enabled = dbs[cnt].enabled;
     128        data.dbs[cnt].check_file = dbs[cnt].check_file;
     129        data.dbs[cnt].shared = dbs[cnt].shared;
     130        data.dbs[cnt].persistent = dbs[cnt].persistent;
     131        data.dbs[cnt].postimeout = dbs[cnt].postimeout;
     132        data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
     133        if (dbs[cnt].head != NULL)
     134  	{
     135  	  data.dbs[cnt].module = dbs[cnt].head->module;
     136  	  data.dbs[cnt].poshit = dbs[cnt].head->poshit;
     137  	  data.dbs[cnt].neghit = dbs[cnt].head->neghit;
     138  	  data.dbs[cnt].posmiss = dbs[cnt].head->posmiss;
     139  	  data.dbs[cnt].negmiss = dbs[cnt].head->negmiss;
     140  	  data.dbs[cnt].nentries = dbs[cnt].head->nentries;
     141  	  data.dbs[cnt].maxnentries = dbs[cnt].head->maxnentries;
     142  	  data.dbs[cnt].datasize = dbs[cnt].head->data_size;
     143  	  data.dbs[cnt].dataused = dbs[cnt].head->first_free;
     144  	  data.dbs[cnt].maxnsearched = dbs[cnt].head->maxnsearched;
     145  	  data.dbs[cnt].rdlockdelayed = dbs[cnt].head->rdlockdelayed;
     146  	  data.dbs[cnt].wrlockdelayed = dbs[cnt].head->wrlockdelayed;
     147  	  data.dbs[cnt].addfailed = dbs[cnt].head->addfailed;
     148  	}
     149      }
     150  
     151    if (selinux_enabled)
     152      nscd_avc_cache_stats (&data.cstats);
     153  
     154    if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
     155        != sizeof (data))
     156      {
     157        char buf[256];
     158        dbg_log (_("cannot write statistics: %s"),
     159  	       strerror_r (errno, buf, sizeof (buf)));
     160      }
     161  }
     162  
     163  
     164  int
     165  receive_print_stats (void)
     166  {
     167    struct statdata data;
     168    request_header req;
     169    ssize_t nbytes;
     170    int fd;
     171    int i;
     172    uid_t uid = getuid ();
     173    const char *yesstr = _("yes");
     174    const char *nostr = _("no");
     175  
     176    /* Find out whether there is another user but root allowed to
     177       request statistics.  */
     178    if (uid != 0)
     179      {
     180        /* User specified?  */
     181        if(stat_user == NULL || stat_uid != uid)
     182  	{
     183  	  if (stat_user != NULL)
     184  	    error (EXIT_FAILURE, 0,
     185  		   _("Only root or %s is allowed to use this option!"),
     186  		   stat_user);
     187  	  else
     188  	    error (EXIT_FAILURE, 0,
     189  		   _("Only root is allowed to use this option!"));
     190  	}
     191      }
     192  
     193    /* Open a socket to the running nscd.  */
     194    fd = nscd_open_socket ();
     195    if (fd == -1)
     196      error (EXIT_FAILURE, 0, _("nscd not running!\n"));
     197  
     198    /* Send the request.  */
     199    req.version = NSCD_VERSION;
     200    req.type = GETSTAT;
     201    req.key_len = 0;
     202    nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
     203  				     MSG_NOSIGNAL));
     204    if (nbytes != sizeof (request_header))
     205      {
     206        int err = errno;
     207        close (fd);
     208        error (EXIT_FAILURE, err, _("write incomplete"));
     209      }
     210  
     211    /* Read as much data as we expect.  */
     212    if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
     213        || (data.version != STATDATA_VERSION_FULL
     214  	  /* Yes, this is an assignment!  */
     215  	  && (errno = EINVAL)))
     216      {
     217        /* Not the right version.  */
     218        int err = errno;
     219        close (fd);
     220        error (EXIT_FAILURE, err, _("cannot read statistics data"));
     221      }
     222  
     223    printf (_("nscd configuration:\n\n%15d  server debug level\n"),
     224  	  data.debug_level);
     225  
     226    /* We know that we can simply subtract time_t values.  */
     227    unsigned long int diff = data.runtime;
     228    unsigned int ndays = 0;
     229    unsigned int nhours = 0;
     230    unsigned int nmins = 0;
     231    if (diff > 24 * 60 * 60)
     232      {
     233        ndays = diff / (24 * 60 * 60);
     234        diff %= 24 * 60 * 60;
     235      }
     236    if (diff > 60 * 60)
     237      {
     238        nhours = diff / (60 * 60);
     239        diff %= 60 * 60;
     240      }
     241    if (diff > 60)
     242      {
     243        nmins = diff / 60;
     244        diff %= 60;
     245      }
     246    if (ndays != 0)
     247      printf (_("%3ud %2uh %2um %2lus  server runtime\n"),
     248  	    ndays, nhours, nmins, diff);
     249    else if (nhours != 0)
     250      printf (_("    %2uh %2um %2lus  server runtime\n"), nhours, nmins, diff);
     251    else if (nmins != 0)
     252      printf (_("        %2um %2lus  server runtime\n"), nmins, diff);
     253    else
     254      printf (_("            %2lus  server runtime\n"), diff);
     255  
     256    printf (_("%15d  current number of threads\n"
     257  	    "%15d  maximum number of threads\n"
     258  	    "%15lu  number of times clients had to wait\n"
     259  	    "%15s  paranoia mode enabled\n"
     260  	    "%15lu  restart internal\n"
     261  	    "%15u  reload count\n"),
     262  	  data.nthreads, data.max_nthreads, data.client_queued,
     263  	  data.paranoia ? yesstr : nostr,
     264  	  (unsigned long int) data.restart_interval, data.reload_count);
     265  
     266    for (i = 0; i < lastdb; ++i)
     267      {
     268        unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
     269        unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
     270        const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
     271        const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
     272        const char *shared = data.dbs[i].shared ? yesstr : nostr;
     273        const char *persistent = data.dbs[i].persistent ? yesstr : nostr;
     274  
     275        if (enabled[0] == '\0')
     276  	/* The locale does not provide this information so we have to
     277  	   translate it ourself.  Since we should avoid short translation
     278  	   terms we artificially increase the length.  */
     279  	enabled = data.dbs[i].enabled ? yesstr : nostr;
     280        if (check_file[0] == '\0')
     281  	check_file = data.dbs[i].check_file ? yesstr : nostr;
     282        if (shared[0] == '\0')
     283  	shared = data.dbs[i].shared ? yesstr : nostr;
     284        if (persistent[0] == '\0')
     285  	persistent = data.dbs[i].persistent ? yesstr : nostr;
     286  
     287        if (all == 0)
     288  	/* If nothing happened so far report a 0% hit rate.  */
     289  	all = 1;
     290  
     291        printf (_("\n%s cache:\n\n"
     292  		"%15s  cache is enabled\n"
     293  		"%15s  cache is persistent\n"
     294  		"%15s  cache is shared\n"
     295  		"%15zu  suggested size\n"
     296  		"%15zu  total data pool size\n"
     297  		"%15zu  used data pool size\n"
     298  		"%15lu  seconds time to live for positive entries\n"
     299  		"%15lu  seconds time to live for negative entries\n"
     300  		"%15" PRIuMAX "  cache hits on positive entries\n"
     301  		"%15" PRIuMAX "  cache hits on negative entries\n"
     302  		"%15" PRIuMAX "  cache misses on positive entries\n"
     303  		"%15" PRIuMAX "  cache misses on negative entries\n"
     304  		"%15lu%% cache hit rate\n"
     305  		"%15zu  current number of cached values\n"
     306  		"%15zu  maximum number of cached values\n"
     307  		"%15zu  maximum chain length searched\n"
     308  		"%15" PRIuMAX "  number of delays on rdlock\n"
     309  		"%15" PRIuMAX "  number of delays on wrlock\n"
     310  		"%15" PRIuMAX "  memory allocations failed\n"
     311  		"%15s  check /etc/%s for changes\n"),
     312  	      dbnames[i], enabled, persistent, shared,
     313  	      data.dbs[i].module,
     314  	      data.dbs[i].datasize, data.dbs[i].dataused,
     315  	      data.dbs[i].postimeout, data.dbs[i].negtimeout,
     316  	      data.dbs[i].poshit, data.dbs[i].neghit,
     317  	      data.dbs[i].posmiss, data.dbs[i].negmiss,
     318  	      (100 * hit) / all,
     319  	      data.dbs[i].nentries, data.dbs[i].maxnentries,
     320  	      data.dbs[i].maxnsearched,
     321  	      data.dbs[i].rdlockdelayed,
     322  	      data.dbs[i].wrlockdelayed,
     323  	      data.dbs[i].addfailed, check_file, dbnames[i]);
     324      }
     325  
     326    if (selinux_enabled)
     327      nscd_avc_print_stats (&data.cstats);
     328  
     329    close (fd);
     330  
     331    exit (0);
     332  }