Back to index

glibc  2.9
Classes | Defines | Enumerations | Functions | Variables
nscd.h File Reference
#include <pthread.h>
#include <stdbool.h>
#include <time.h>
#include <sys/uio.h>
#include "nscd-client.h"
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  database_dyn
struct  mem_in_flight
struct  mem_in_flight.block

Defines

#define DEFAULT_RELOAD_LIMIT   5
#define RESTART_INTERVAL   (60 * 60)
#define NSCD_THREAD_STACKSIZE   1024 * 1024 * (sizeof (void *) / 4)
#define MAX_STACK_USE   ((8 * NSCD_THREAD_STACKSIZE) / 10)
#define _PATH_NSCD_PASSWD_DB   "/var/db/nscd/passwd"
#define _PATH_NSCD_GROUP_DB   "/var/db/nscd/group"
#define _PATH_NSCD_HOSTS_DB   "/var/db/nscd/hosts"
#define _PATH_NSCD_SERVICES_DB   "/var/db/nscd/services"
#define _PATH_NSCD_XYZ_DB_TMP   "/var/run/nscd/dbXXXXXX"
#define BLOCK_ALIGN_LOG   3
#define BLOCK_ALIGN   (1 << BLOCK_ALIGN_LOG)
#define BLOCK_ALIGN_M1   (BLOCK_ALIGN - 1)
#define DEFAULT_MAX_DB_SIZE   (32 * 1024 * 1024)
#define DEFAULT_DATASIZE_PER_BUCKET   1024
#define DEFAULT_SUGGESTED_MODULE   211
#define CACHE_PRUNE_INTERVAL   15
#define TEMP_FAILURE_RETRY_VAL(expression)

Enumerations

enum  dbtype {
  pwddb, grpdb, hstdb, servdb,
  lastdb
}
enum  in_flight { IDX_result_data = 0, IDX_record_data = 1, IDX_last }

Functions

void termination_handler (int signum) __attribute__((__noreturn__))
int nscd_open_socket (void)
void nscd_init (void)
void close_sockets (void)
void start_threads (void) __attribute__((__noreturn__))
int nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
void send_stats (int fd, struct database_dyn dbs[lastdb])
int receive_print_stats (void) __attribute__((__noreturn__))
struct dataheadcache_search (request_type, void *key, size_t len, struct database_dyn *table, uid_t owner)
int cache_add (int type, const void *key, size_t len, struct datahead *packet, bool first, struct database_dyn *table, uid_t owner, bool prune_wakeup)
time_t prune_cache (struct database_dyn *table, time_t now, int fd)
void addpwbyname (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void addpwbyuid (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdpwbyname (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void readdpwbyuid (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addgrbyname (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void addgrbygid (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdgrbyname (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void readdgrbygid (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addhstbyname (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void addhstbyaddr (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void addhstbynamev6 (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdhstbyname (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void readdhstbyaddr (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void readdhstbynamev6 (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addhstai (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdinitgroups (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addservbyname (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdservbyname (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void addservbyport (struct database_dyn *db, int fd, request_header *req, void *key, uid_t uid)
void readdservbyport (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
void * mempool_alloc (struct database_dyn *db, size_t len, enum in_flight idx)
void gc (struct database_dyn *db)
int setup_thread (struct database_dyn *db)

Variables

struct database_dyn[lastdbattribute_hidden
const char *const dbnames [lastdb]
const char *const serv2str [LASTREQ]
int nthreads
int max_nthreads
const char * server_user
const char * stat_user
uid_t stat_uid
time_t start_time
unsigned long int client_queued
const size_t block_align
unsigned int reload_count
uintptr_t pagesize_m1
int paranoia
time_t restart_time
time_t restart_interval
const char * oldcwd
uid_t old_uid
gid_t old_gid
__thread struct mem_in_flight attribute_tls_model_ie
struct mem_in_flightmem_in_flight_list

Class Documentation

struct database_dyn

Definition at line 67 of file nscd.h.

Collaboration diagram for database_dyn:
Class Members
int check_file
int clear_cache
char * data
const char * db_filename
struct iovec * disabled_iov
int enabled
time_t file_mtime
const char filename
struct database_pers_head * head
int inotify_descr
bool last_alloc_failed
pthread_rwlock_t lock
size_t max_db_size
pthread_mutex_t memlock
size_t memsize
bool mmap_used
unsigned long int negtimeout
int persistent
unsigned long int postimeout
int propagate
pthread_cond_t prune_cond
pthread_mutex_t prune_lock
int reset_res
int ro_fd
int shared
size_t suggested_module
time_t wakeup_time
int wr_fd
struct mem_in_flight

Definition at line 196 of file nscd.h.

Collaboration diagram for mem_in_flight:
Class Members
struct mem_in_flight block
struct mem_in_flight * next
struct mem_in_flight.block

Definition at line 198 of file nscd.h.

Class Members
nscd_ssize_t blocklen
nscd_ssize_t blockoff
int dbidx

Define Documentation

#define _PATH_NSCD_GROUP_DB   "/var/db/nscd/group"

Definition at line 107 of file nscd.h.

#define _PATH_NSCD_HOSTS_DB   "/var/db/nscd/hosts"

Definition at line 108 of file nscd.h.

#define _PATH_NSCD_PASSWD_DB   "/var/db/nscd/passwd"

Definition at line 106 of file nscd.h.

#define _PATH_NSCD_SERVICES_DB   "/var/db/nscd/services"

Definition at line 109 of file nscd.h.

#define _PATH_NSCD_XYZ_DB_TMP   "/var/run/nscd/dbXXXXXX"

Definition at line 112 of file nscd.h.

#define BLOCK_ALIGN   (1 << BLOCK_ALIGN_LOG)

Definition at line 116 of file nscd.h.

#define BLOCK_ALIGN_LOG   3

Definition at line 115 of file nscd.h.

#define BLOCK_ALIGN_M1   (BLOCK_ALIGN - 1)

Definition at line 117 of file nscd.h.

#define CACHE_PRUNE_INTERVAL   15

Definition at line 131 of file nscd.h.

#define DEFAULT_DATASIZE_PER_BUCKET   1024

Definition at line 123 of file nscd.h.

#define DEFAULT_MAX_DB_SIZE   (32 * 1024 * 1024)

Definition at line 120 of file nscd.h.

#define DEFAULT_RELOAD_LIMIT   5

Definition at line 51 of file nscd.h.

#define DEFAULT_SUGGESTED_MODULE   211

Definition at line 126 of file nscd.h.

#define MAX_STACK_USE   ((8 * NSCD_THREAD_STACKSIZE) / 10)

Definition at line 63 of file nscd.h.

#define NSCD_THREAD_STACKSIZE   1024 * 1024 * (sizeof (void *) / 4)

Definition at line 59 of file nscd.h.

#define RESTART_INTERVAL   (60 * 60)

Definition at line 55 of file nscd.h.

Value:
(__extension__                                                       \
    ({ long int __result;                                            \
       do __result = (long int) (expression);                               \
       while (__result == EINTR);                                    \
       __result; }))

Definition at line 313 of file nscd.h.


Enumeration Type Documentation

enum dbtype
Enumerator:
pwddb 
grpdb 
hstdb 
servdb 
lastdb 

Definition at line 36 of file nscd.h.

enum in_flight
Enumerator:
IDX_result_data 
IDX_record_data 
IDX_last 

Definition at line 189 of file nscd.h.

  {
    IDX_result_data = 0,
    /* Keep the IDX_record_data entry last at all times.  */
    IDX_record_data = 1,
    IDX_last
  };

Function Documentation

void addgrbygid ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 508 of file grpcache.c.

{
  char *ep;
  gid_t gid = strtoul ((char *) key, &ep, 10);

  if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
    {
      if (debug_level > 0)
        dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);

      errno = EINVAL;
      return;
    }

  union keytype u = { .g = gid };

  addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addgrbyname ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 483 of file grpcache.c.

{
  union keytype u = { .v = key };

  addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addhstai ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 573 of file aicache.c.

{
  addhstaiX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addhstbyaddr ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 550 of file hstcache.c.

{
  addhstbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addhstbyaddrv6 ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 594 of file hstcache.c.

{
  addhstbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addhstbyname ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 528 of file hstcache.c.

{
  addhstbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addhstbynamev6 ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 572 of file hstcache.c.

{
  addhstbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addinitgroups ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 423 of file initgrcache.c.

{
  addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addpwbyname ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 489 of file pwdcache.c.

{
  union keytype u = { .v = key };

  addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addpwbyuid ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 514 of file pwdcache.c.

{
  char *ep;
  uid_t uid = strtoul ((char *) key, &ep, 10);

  if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
    {
      if (debug_level > 0)
        dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);

      errno = EINVAL;
      return;
    }

  union keytype u = { .u = uid };

  addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addservbyname ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 435 of file servicescache.c.

{
  addservbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void addservbyport ( struct database_dyn db,
int  fd,
request_header req,
void *  key,
uid_t  uid 
)

Definition at line 457 of file servicescache.c.

{
  addservbyX (db, fd, req, key, uid, NULL, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int cache_add ( int  type,
const void *  key,
size_t  len,
struct datahead packet,
bool  first,
struct database_dyn table,
uid_t  owner,
bool  prune_wakeup 
)

Definition at line 136 of file cache.c.

{
  if (__builtin_expect (debug_level >= 2, 0))
    {
      const char *str;
      char buf[INET6_ADDRSTRLEN + 1];
      if (type == GETHOSTBYADDR || type == GETHOSTBYADDRv6)
       str = inet_ntop (type == GETHOSTBYADDR ? AF_INET : AF_INET6,
                      key, buf, sizeof (buf));
      else
       str = key;

      dbg_log (_("add new entry \"%s\" of type %s for %s to cache%s"),
              str, serv2str[type], dbnames[table - dbs],
              first ? _(" (first)") : "");
    }

  unsigned long int hash = __nis_hash (key, len) % table->head->module;
  struct hashentry *newp;

  newp = mempool_alloc (table, sizeof (struct hashentry), IDX_record_data);
  /* If we cannot allocate memory, just do not do anything.  */
  if (newp == NULL)
    {
      ++table->head->addfailed;

      /* If necessary mark the entry as unusable so that lookups will
        not use it.  */
      if (first)
       packet->usable = false;

      /* Mark the in-flight memory as unused.  */
      for (enum in_flight idx = 0; idx < IDX_record_data; ++idx)
       mem_in_flight.block[idx].dbidx = -1;

      return -1;
    }

  newp->type = type;
  newp->first = first;
  newp->len = len;
  newp->key = (char *) key - table->data;
  assert (newp->key + newp->len <= table->head->first_free);
  newp->owner = owner;
  newp->packet = (char *) packet - table->data;
  assert ((newp->packet & BLOCK_ALIGN_M1) == 0);

  /* Put the new entry in the first position.  */
  do
    newp->next = table->head->array[hash];
  while (atomic_compare_and_exchange_bool_acq (&table->head->array[hash],
                                          (ref_t) ((char *) newp
                                                 - table->data),
                                          (ref_t) newp->next));

  /* Update the statistics.  */
  if (packet->notfound)
    ++table->head->negmiss;
  else if (first)
    ++table->head->posmiss;

  /* We depend on this value being correct and at least as high as the
     real number of entries.  */
  atomic_increment (&table->head->nentries);

  /* It does not matter that we are not loading the just increment
     value, this is just for statistics.  */
  unsigned long int nentries = table->head->nentries;
  if (nentries > table->head->maxnentries)
    table->head->maxnentries = nentries;

  if (table->persistent)
    // XXX async OK?
    msync ((void *) table->head,
          (char *) &table->head->array[hash] - (char *) table->head
          + sizeof (ref_t), MS_ASYNC);

  /* We do not have to worry about the pruning thread if we are
     re-adding the data since this is done by the pruning thread.  We
     also do not have to do anything in case this is not the first
     time the data is entered since different data heads all have the
     same timeout.  */
  if (first && prune_wakeup)
    {
      /* Perhaps the prune thread for the table is not running in a long
        time.  Wake it if necessary.  */
      pthread_mutex_lock (&table->prune_lock);
      time_t next_wakeup = table->wakeup_time;
      bool do_wakeup = false;
      if (next_wakeup > packet->timeout + CACHE_PRUNE_INTERVAL)
       {
         table->wakeup_time = packet->timeout;
         do_wakeup = true;
       }
      pthread_mutex_unlock (&table->prune_lock);
      if (do_wakeup)
       pthread_cond_signal (&table->prune_cond);
    }

  /* Mark the in-flight memory as unused.  */
  for (enum in_flight idx = 0; idx < IDX_last; ++idx)
    mem_in_flight.block[idx].dbidx = -1;

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct datahead* cache_search ( request_type  ,
void *  key,
size_t  len,
struct database_dyn table,
uid_t  owner 
) [read]

Definition at line 73 of file cache.c.

{
  unsigned long int hash = __nis_hash (key, len) % table->head->module;

  unsigned long int nsearched = 0;
  struct datahead *result = NULL;

  ref_t work = table->head->array[hash];
  while (work != ENDREF)
    {
      ++nsearched;

      struct hashentry *here = (struct hashentry *) (table->data + work);

      if (type == here->type && len == here->len
         && memcmp (key, table->data + here->key, len) == 0
         && here->owner == owner)
       {
         /* We found the entry.  Increment the appropriate counter.  */
         struct datahead *dh
           = (struct datahead *) (table->data + here->packet);

         /* See whether we must ignore the entry.  */
         if (dh->usable)
           {
             /* We do not synchronize the memory here.  The statistics
               data is not crucial, we synchronize only once in a while
               in the cleanup threads.  */
             if (dh->notfound)
              ++table->head->neghit;
             else
              {
                ++table->head->poshit;

                if (dh->nreloads != 0)
                  dh->nreloads = 0;
              }

             result = dh;
             break;
           }
       }

      work = here->next;
    }

  if (nsearched > table->head->maxnsearched)
    table->head->maxnsearched = nsearched;

  return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void close_sockets ( void  )

Definition at line 948 of file connections.c.

{
  close (sock);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void gc ( struct database_dyn db)

Definition at line 114 of file mem.c.

{
  /* We need write access.  */
  pthread_rwlock_wrlock (&db->lock);

  /* And the memory handling lock.  */
  pthread_mutex_lock (&db->memlock);

  /* We need an array representing the data area.  All memory
     allocation is BLOCK_ALIGN aligned so this is the level at which
     we have to look at the memory.  We use a mark and sweep algorithm
     where the marks are placed in this array.  */
  assert (db->head->first_free % BLOCK_ALIGN == 0);

  BITMAP_T *mark;
  bool mark_use_malloc;
  /* In prune_cache we are also using a dynamically allocated array.
     If the array in the caller is too large we have malloc'ed it.  */
  size_t stack_used = sizeof (bool) * db->head->module;
  if (__builtin_expect (stack_used > MAX_STACK_USE, 0))
    stack_used = 0;
  size_t nmark = (db->head->first_free / BLOCK_ALIGN + BITS - 1) / BITS;
  size_t memory_needed = nmark * sizeof (BITMAP_T);
  if (stack_used + memory_needed <= MAX_STACK_USE)
    {
      mark = (BITMAP_T *) alloca (memory_needed);
      mark_use_malloc = false;
      memset (mark, '\0', memory_needed);
      stack_used += memory_needed;
    }
  else
    {
      mark = (BITMAP_T *) xcalloc (1, memory_needed);
      mark_use_malloc = true;
    }

  /* Create an array which can hold pointer to all the entries in hash
     entries.  */
  memory_needed = 2 * db->head->nentries * sizeof (struct hashentry *);
  struct hashentry **he;
  struct hashentry **he_data;
  bool he_use_malloc;
  if (stack_used + memory_needed <= MAX_STACK_USE)
    {
      he = alloca (db->head->nentries * sizeof (struct hashentry *));
      he_data = alloca (db->head->nentries * sizeof (struct hashentry *));
      he_use_malloc = false;
      stack_used += memory_needed;
    }
  else
    {
      he = xmalloc (memory_needed);
      he_data = &he[db->head->nentries * sizeof (struct hashentry *)];
      he_use_malloc = true;
    }

  size_t cnt = 0;
  for (size_t idx = 0; idx < db->head->module; ++idx)
    {
      ref_t *prevp = &db->head->array[idx];
      ref_t run = *prevp;

      while (run != ENDREF)
       {
         assert (cnt < db->head->nentries);
         he[cnt] = (struct hashentry *) (db->data + run);

         he[cnt]->prevp = prevp;
         prevp = &he[cnt]->next;

         /* This is the hash entry itself.  */
         markrange (mark, run, sizeof (struct hashentry));

         /* Add the information for the data itself.  We do this
            only for the one special entry marked with FIRST.  */
         if (he[cnt]->first)
           {
             struct datahead *dh
              = (struct datahead *) (db->data + he[cnt]->packet);
             markrange (mark, he[cnt]->packet, dh->allocsize);
           }

         run = he[cnt]->next;

         ++cnt;
       }
    }
  assert (cnt == db->head->nentries);

  /* Go through the list of in-flight memory blocks.  */
  struct mem_in_flight *mrunp = mem_in_flight_list;
  while (mrunp != NULL)
    {
      /* NB: There can be no race between this test and another thread
        setting the field to the index we are looking for because
        this would require the other thread to also have the memlock
        for the database.

       NB2: we do not have to look at latter blocks (higher indices) if
       earlier blocks are not in flight.  They are always allocated in
       sequence.  */
      for (enum in_flight idx = IDX_result_data;
          idx < IDX_last && mrunp->block[idx].dbidx == db - dbs; ++idx)
       {
         assert (mrunp->block[idx].blockoff >= 0);
         assert (mrunp->block[idx].blocklen < db->memsize);
         assert (mrunp->block[idx].blockoff
                + mrunp->block[0].blocklen <= db->memsize);
         markrange (mark, mrunp->block[idx].blockoff,
                   mrunp->block[idx].blocklen);
       }

      mrunp = mrunp->next;
    }

  /* Sort the entries by the addresses of the referenced data.  All
     the entries pointing to the same DATAHEAD object will have the
     same key.  Stability of the sorting is unimportant.  */
  memcpy (he_data, he, cnt * sizeof (struct hashentry *));
  qsort (he_data, cnt, sizeof (struct hashentry *), sort_he_data);

  /* Sort the entries by their address.  */
  qsort (he, cnt, sizeof (struct hashentry *), sort_he);

#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
  struct obstack ob;
  obstack_init (&ob);

  /* Determine the highest used address.  */
  size_t high = nmark;
  while (high > 0 && mark[high - 1] == 0)
    --high;

  /* No memory used.  */
  if (high == 0)
    {
      db->head->first_free = 0;
      goto out;
    }

  /* Determine the highest offset.  */
  BITMAP_T mask = HIGHBIT;
  ref_t highref = (high * BITS - 1) * BLOCK_ALIGN;
  while ((mark[high - 1] & mask) == 0)
    {
      mask >>= 1;
      highref -= BLOCK_ALIGN;
    }

  /* Now we can iterate over the MARK array and find bits which are not
     set.  These represent memory which can be recovered.  */
  size_t byte = 0;
  /* Find the first gap.  */
  while (byte < high && mark[byte] == ALLBITS)
    ++byte;

  if (byte == high
      || (byte == high - 1 && (mark[byte] & ~(mask | (mask - 1))) == 0))
    /* No gap.  */
    goto out;

  mask = 1;
  cnt = 0;
  while ((mark[byte] & mask) != 0)
    {
      ++cnt;
      mask <<= 1;
    }
  ref_t off_free = (byte * BITS + cnt) * BLOCK_ALIGN;
  assert (off_free <= db->head->first_free);

  struct hashentry **next_hash = he;
  struct hashentry **next_data = he_data;

  /* Skip over the hash entries in the first block which does not get
     moved.  */
  while (next_hash < &he[db->head->nentries]
        && *next_hash < (struct hashentry *) (db->data + off_free))
    ++next_hash;

  while (next_data < &he_data[db->head->nentries]
        && (*next_data)->packet < off_free)
    ++next_data;


  /* Now we start modifying the data.  Make sure all readers of the
     data are aware of this and temporarily don't use the data.  */
  ++db->head->gc_cycle;
  assert ((db->head->gc_cycle & 1) == 1);


  /* We do not perform the move operations right away since the
     he_data array is not sorted by the address of the data.  */
  struct moveinfo
  {
    void *from;
    void *to;
    size_t size;
    struct moveinfo *next;
  } *moves = NULL;

  while (byte < high)
    {
      /* Search for the next filled block.  BYTE is the index of the
        entry in MARK, MASK is the bit, and CNT is the bit number.
        OFF_FILLED is the corresponding offset.  */
      if ((mark[byte] & ~(mask - 1)) == 0)
       {
         /* No other bit set in the same element of MARK.  Search in the
            following memory.  */
         do
           ++byte;
         while (byte < high && mark[byte] == 0);

         if (byte == high)
           /* That was it.  */
           break;

         mask = 1;
         cnt = 0;
       }
      /* Find the exact bit.  */
      while ((mark[byte] & mask) == 0)
       {
         ++cnt;
         mask <<= 1;
       }

      ref_t off_alloc = (byte * BITS + cnt) * BLOCK_ALIGN;
      assert (off_alloc <= db->head->first_free);

      /* Find the end of the used area.  */
      if ((mark[byte] & ~(mask - 1)) == (BITMAP_T) ~(mask - 1))
       {
         /* All other bits set.  Search the next bytes in MARK.  */
         do
           ++byte;
         while (byte < high && mark[byte] == ALLBITS);

         mask = 1;
         cnt = 0;
       }
      if (byte < high)
       {
         /* Find the exact bit.  */
         while ((mark[byte] & mask) != 0)
           {
             ++cnt;
             mask <<= 1;
           }
       }

      ref_t off_allocend = (byte * BITS + cnt) * BLOCK_ALIGN;
      assert (off_allocend <= db->head->first_free);
      /* Now we know that we can copy the area from OFF_ALLOC to
        OFF_ALLOCEND (not included) to the memory starting at
        OFF_FREE.  First fix up all the entries for the
        displacement.  */
      ref_t disp = off_alloc - off_free;

      struct moveinfo *new_move;
      if (stack_used + sizeof (*new_move) <= MAX_STACK_USE)
       {
         new_move = alloca (sizeof (*new_move));
         stack_used += sizeof (*new_move);
       }
      else
       new_move = obstack_alloc (&ob, sizeof (*new_move));
      new_move->from = db->data + off_alloc;
      new_move->to = db->data + off_free;
      new_move->size = off_allocend - off_alloc;
      /* Create a circular list to be always able to append at the end.  */
      if (moves == NULL)
       moves = new_move->next = new_move;
      else
       {
         new_move->next = moves->next;
         moves = moves->next = new_move;
       }

      /* The following loop will prepare to move this much data.  */
      off_free += off_allocend - off_alloc;

      while (off_alloc < off_allocend)
       {
         /* Determine whether the next entry is for a hash entry or
            the data.  */
         if ((struct hashentry *) (db->data + off_alloc) == *next_hash)
           {
             /* Just correct the forward reference.  */
             *(*next_hash++)->prevp -= disp;

             off_alloc += ((sizeof (struct hashentry) + BLOCK_ALIGN_M1)
                         & ~BLOCK_ALIGN_M1);
           }
         else
           {
             assert (next_data < &he_data[db->head->nentries]);
             assert ((*next_data)->packet == off_alloc);

             struct datahead *dh = (struct datahead *) (db->data + off_alloc);
             do
              {
                assert ((*next_data)->key >= (*next_data)->packet);
                assert ((*next_data)->key + (*next_data)->len
                       <= (*next_data)->packet + dh->allocsize);

                (*next_data)->packet -= disp;
                (*next_data)->key -= disp;
                ++next_data;
              }
             while (next_data < &he_data[db->head->nentries]
                   && (*next_data)->packet == off_alloc);

             off_alloc += (dh->allocsize + BLOCK_ALIGN_M1) & ~BLOCK_ALIGN_M1;
           }
       }
      assert (off_alloc == off_allocend);

      assert (off_alloc <= db->head->first_free);
      if (off_alloc == db->head->first_free)
       /* We are done, that was the last block.  */
       break;
    }
  assert (next_hash == &he[db->head->nentries]);
  assert (next_data == &he_data[db->head->nentries]);

  /* Now perform the actual moves.  */
  if (moves != NULL)
    {
      struct moveinfo *runp = moves->next;
      do
       {
         assert ((char *) runp->to >= db->data);
         assert ((char *) runp->to + runp->size
                <= db->data  + db->head->first_free);
         assert ((char *) runp->from >= db->data);
         assert ((char *) runp->from + runp->size
                <= db->data  + db->head->first_free);

         /* The regions may overlap.  */
         memmove (runp->to, runp->from, runp->size);
         runp = runp->next;
       }
      while (runp != moves->next);

      if (__builtin_expect (debug_level >= 3, 0))
       dbg_log (_("freed %zu bytes in %s cache"),
               db->head->first_free
               - ((char *) moves->to + moves->size - db->data),
               dbnames[db - dbs]);

      /* The byte past the end of the last copied block is the next
        available byte.  */
      db->head->first_free = (char *) moves->to + moves->size - db->data;

      /* Consistency check.  */
      if (__builtin_expect (debug_level >= 3, 0))
       {
         for (size_t idx = 0; idx < db->head->module; ++idx)
           {
             ref_t run = db->head->array[idx];
             size_t cnt = 0;

             while (run != ENDREF)
              {
                if (run + sizeof (struct hashentry) > db->head->first_free)
                  {
                    dbg_log ("entry %zu in hash bucket %zu out of bounds: "
                            "%" PRIu32 "+%zu > %zu\n",
                            cnt, idx, run, sizeof (struct hashentry),
                            (size_t) db->head->first_free);
                    break;
                  }

                struct hashentry *he = (struct hashentry *) (db->data + run);

                if (he->key + he->len > db->head->first_free)
                  dbg_log ("key of entry %zu in hash bucket %zu out of "
                          "bounds: %" PRIu32 "+%zu > %zu\n",
                          cnt, idx, he->key, (size_t) he->len,
                          (size_t) db->head->first_free);

                if (he->packet + sizeof (struct datahead)
                    > db->head->first_free)
                  dbg_log ("packet of entry %zu in hash bucket %zu out of "
                          "bounds: %" PRIu32 "+%zu > %zu\n",
                          cnt, idx, he->packet, sizeof (struct datahead),
                          (size_t) db->head->first_free);
                else
                  {
                    struct datahead *dh = (struct datahead *) (db->data
                                                         + he->packet);
                    if (he->packet + dh->allocsize
                       > db->head->first_free)
                     dbg_log ("full key of entry %zu in hash bucket %zu "
                             "out of bounds: %" PRIu32 "+%zu > %zu",
                             cnt, idx, he->packet, (size_t) dh->allocsize,
                             (size_t) db->head->first_free);
                  }

                run = he->next;
                ++cnt;
              }
           }
       }
    }

  /* Make sure the data on disk is updated.  */
  if (db->persistent)
    msync (db->head, db->data + db->head->first_free - (char *) db->head,
          MS_ASYNC);


  /* Now we are done modifying the data.  */
  ++db->head->gc_cycle;
  assert ((db->head->gc_cycle & 1) == 0);

  /* We are done.  */
 out:
  pthread_mutex_unlock (&db->memlock);
  pthread_rwlock_unlock (&db->lock);

  if (he_use_malloc)
    free (he);
  if (mark_use_malloc)
    free (mark);

  obstack_free (&ob, NULL);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void* mempool_alloc ( struct database_dyn db,
size_t  len,
enum in_flight  idx 
)

Definition at line 548 of file mem.c.

{
  /* Make sure LEN is a multiple of our maximum alignment so we can
     keep track of used memory is multiples of this alignment value.  */
  if ((len & BLOCK_ALIGN_M1) != 0)
    len += BLOCK_ALIGN - (len & BLOCK_ALIGN_M1);

  pthread_mutex_lock (&db->memlock);

  assert ((db->head->first_free & BLOCK_ALIGN_M1) == 0);

  bool tried_resize = false;
  void *res;
 retry:
  res = db->data + db->head->first_free;

  if (__builtin_expect (db->head->first_free + len > db->head->data_size, 0))
    {
      if (! tried_resize)
       {
         /* Try to resize the database.  Grow size of 1/8th.  */
         size_t oldtotal = (sizeof (struct database_pers_head)
                          + roundup (db->head->module * sizeof (ref_t),
                                   ALIGN)
                          + db->head->data_size);
         size_t new_data_size = (db->head->data_size
                              + MAX (2 * len, db->head->data_size / 8));
         size_t newtotal = (sizeof (struct database_pers_head)
                          + roundup (db->head->module * sizeof (ref_t), ALIGN)
                          + new_data_size);
         if (newtotal > db->max_db_size)
           {
             new_data_size -= newtotal - db->max_db_size;
             newtotal = db->max_db_size;
           }

         if (db->mmap_used && newtotal > oldtotal
             /* We only have to adjust the file size.  The new pages
               become magically available.  */
             && TEMP_FAILURE_RETRY_VAL (posix_fallocate (db->wr_fd, oldtotal,
                                                   newtotal
                                                   - oldtotal)) == 0)
           {
             db->head->data_size = new_data_size;
             tried_resize = true;
             goto retry;
           }
       }

      if (! db->last_alloc_failed)
       {
         dbg_log (_("no more memory for database '%s'"), dbnames[db - dbs]);

         db->last_alloc_failed = true;
       }

      /* No luck.  */
      res = NULL;
    }
  else
    {
      /* Remember that we have allocated this memory.  */
      assert (idx >= 0 && idx < IDX_last);
      mem_in_flight.block[idx].dbidx = db - dbs;
      mem_in_flight.block[idx].blocklen = len;
      mem_in_flight.block[idx].blockoff = db->head->first_free;

      db->head->first_free += len;

      db->last_alloc_failed = false;

    }

  pthread_mutex_unlock (&db->memlock);

  return res;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void nscd_init ( void  )

Definition at line 515 of file connections.c.

{
  /* Look up unprivileged uid/gid/groups before we start listening on the
     socket  */
  if (server_user != NULL)
    begin_drop_privileges ();

  if (nthreads == -1)
    /* No configuration for this value, assume a default.  */
    nthreads = 4;

#ifdef HAVE_INOTIFY
  /* Use inotify to recognize changed files.  */
  inotify_fd = inotify_init1 (IN_NONBLOCK);
# ifndef __ASSUME_IN_NONBLOCK
  if (inotify_fd == -1 && errno == ENOSYS)
    {
      inotify_fd = inotify_init ();
      if (inotify_fd != -1)
       fcntl (inotify_fd, F_SETFL, O_RDONLY | O_NONBLOCK);
    }
# endif
#endif

  for (size_t cnt = 0; cnt < lastdb; ++cnt)
    if (dbs[cnt].enabled)
      {
       pthread_rwlock_init (&dbs[cnt].lock, NULL);
       pthread_mutex_init (&dbs[cnt].memlock, NULL);

       if (dbs[cnt].persistent)
         {
           /* Try to open the appropriate file on disk.  */
           int fd = open (dbs[cnt].db_filename, O_RDWR | EXTRA_O_FLAGS);
           if (fd != -1)
             {
              char *msg = NULL;
              struct stat64 st;
              void *mem;
              size_t total;
              struct database_pers_head head;
              ssize_t n = TEMP_FAILURE_RETRY (read (fd, &head,
                                                sizeof (head)));
              if (n != sizeof (head) || fstat64 (fd, &st) != 0)
                {
                fail_db_errno:
                  /* The code is single-threaded at this point so
                     using strerror is just fine.  */
                  msg = strerror (errno);
                fail_db:
                  dbg_log (_("invalid persistent database file \"%s\": %s"),
                          dbs[cnt].db_filename, msg);
                  unlink (dbs[cnt].db_filename);
                }
              else if (head.module == 0 && head.data_size == 0)
                {
                  /* The file has been created, but the head has not
                     been initialized yet.  */
                  msg = _("uninitialized header");
                  goto fail_db;
                }
              else if (head.header_size != (int) sizeof (head))
                {
                  msg = _("header size does not match");
                  goto fail_db;
                }
              else if ((total = (sizeof (head)
                               + roundup (head.module * sizeof (ref_t),
                                         ALIGN)
                               + head.data_size))
                      > st.st_size
                      || total < sizeof (head))
                {
                  msg = _("file size does not match");
                  goto fail_db;
                }
              /* Note we map with the maximum size allowed for the
                 database.  This is likely much larger than the
                 actual file size.  This is OK on most OSes since
                 extensions of the underlying file will
                 automatically translate more pages available for
                 memory access.  */
              else if ((mem = mmap (NULL, dbs[cnt].max_db_size,
                                  PROT_READ | PROT_WRITE,
                                  MAP_SHARED, fd, 0))
                      == MAP_FAILED)
                goto fail_db_errno;
              else if (!verify_persistent_db (mem, &head, cnt))
                {
                  munmap (mem, total);
                  msg = _("verification failed");
                  goto fail_db;
                }
              else
                {
                  /* Success.  We have the database.  */
                  dbs[cnt].head = mem;
                  dbs[cnt].memsize = total;
                  dbs[cnt].data = (char *)
                    &dbs[cnt].head->array[roundup (dbs[cnt].head->module,
                                               ALIGN / sizeof (ref_t))];
                  dbs[cnt].mmap_used = true;

                  if (dbs[cnt].suggested_module > head.module)
                    dbg_log (_("suggested size of table for database %s larger than the persistent database's table"),
                            dbnames[cnt]);

                  dbs[cnt].wr_fd = fd;
                  fd = -1;
                  /* We also need a read-only descriptor.  */
                  if (dbs[cnt].shared)
                    {
                     dbs[cnt].ro_fd = open (dbs[cnt].db_filename,
                                          O_RDONLY | EXTRA_O_FLAGS);
                     if (dbs[cnt].ro_fd == -1)
                       dbg_log (_("\
cannot create read-only descriptor for \"%s\"; no mmap"),
                               dbs[cnt].db_filename);
                    }

                  // XXX Shall we test whether the descriptors actually
                  // XXX point to the same file?
                }

              /* Close the file descriptors in case something went
                 wrong in which case the variable have not been
                 assigned -1.  */
              if (fd != -1)
                close (fd);
             }
         }

       if (dbs[cnt].head == NULL)
         {
           /* No database loaded.  Allocate the data structure,
              possibly on disk.  */
           struct database_pers_head head;
           size_t total = (sizeof (head)
                         + roundup (dbs[cnt].suggested_module
                                   * sizeof (ref_t), ALIGN)
                         + (dbs[cnt].suggested_module
                            * DEFAULT_DATASIZE_PER_BUCKET));

           /* Try to create the database.  If we do not need a
              persistent database create a temporary file.  */
           int fd;
           int ro_fd = -1;
           if (dbs[cnt].persistent)
             {
              fd = open (dbs[cnt].db_filename,
                        O_RDWR | O_CREAT | O_EXCL | O_TRUNC | EXTRA_O_FLAGS,
                        S_IRUSR | S_IWUSR);
              if (fd != -1 && dbs[cnt].shared)
                ro_fd = open (dbs[cnt].db_filename,
                            O_RDONLY | EXTRA_O_FLAGS);
             }
           else
             {
              char fname[] = _PATH_NSCD_XYZ_DB_TMP;
              fd = mkostemp (fname, EXTRA_O_FLAGS);

              /* We do not need the file name anymore after we
                 opened another file descriptor in read-only mode.  */
              if (fd != -1)
                {
                  if (dbs[cnt].shared)
                    ro_fd = open (fname, O_RDONLY | EXTRA_O_FLAGS);

                  unlink (fname);
                }
             }

           if (fd == -1)
             {
              if (errno == EEXIST)
                {
                  dbg_log (_("database for %s corrupted or simultaneously used; remove %s manually if necessary and restart"),
                          dbnames[cnt], dbs[cnt].db_filename);
                  // XXX Correct way to terminate?
                  exit (1);
                }

              if  (dbs[cnt].persistent)
                dbg_log (_("cannot create %s; no persistent database used"),
                        dbs[cnt].db_filename);
              else
                dbg_log (_("cannot create %s; no sharing possible"),
                        dbs[cnt].db_filename);

              dbs[cnt].persistent = 0;
              // XXX remember: no mmap
             }
           else
             {
              /* Tell the user if we could not create the read-only
                 descriptor.  */
              if (ro_fd == -1 && dbs[cnt].shared)
                dbg_log (_("\
cannot create read-only descriptor for \"%s\"; no mmap"),
                        dbs[cnt].db_filename);

              /* Before we create the header, initialiye the hash
                 table.  So that if we get interrupted if writing
                 the header we can recognize a partially initialized
                 database.  */
              size_t ps = sysconf (_SC_PAGESIZE);
              char tmpbuf[ps];
              assert (~ENDREF == 0);
              memset (tmpbuf, '\xff', ps);

              size_t remaining = dbs[cnt].suggested_module * sizeof (ref_t);
              off_t offset = sizeof (head);

              size_t towrite;
              if (offset % ps != 0)
                {
                  towrite = MIN (remaining, ps - (offset % ps));
                  if (pwrite (fd, tmpbuf, towrite, offset) != towrite)
                    goto write_fail;
                  offset += towrite;
                  remaining -= towrite;
                }

              while (remaining > ps)
                {
                  if (pwrite (fd, tmpbuf, ps, offset) == -1)
                    goto write_fail;
                  offset += ps;
                  remaining -= ps;
                }

              if (remaining > 0
                  && pwrite (fd, tmpbuf, remaining, offset) != remaining)
                goto write_fail;

              /* Create the header of the file.  */
              struct database_pers_head head =
                {
                  .version = DB_VERSION,
                  .header_size = sizeof (head),
                  .module = dbs[cnt].suggested_module,
                  .data_size = (dbs[cnt].suggested_module
                              * DEFAULT_DATASIZE_PER_BUCKET),
                  .first_free = 0
                };
              void *mem;

              if ((TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head)))
                   != sizeof (head))
                  || (TEMP_FAILURE_RETRY_VAL (posix_fallocate (fd, 0, total))
                     != 0)
                  || (mem = mmap (NULL, dbs[cnt].max_db_size,
                                PROT_READ | PROT_WRITE,
                                MAP_SHARED, fd, 0)) == MAP_FAILED)
                {
                write_fail:
                  unlink (dbs[cnt].db_filename);
                  dbg_log (_("cannot write to database file %s: %s"),
                          dbs[cnt].db_filename, strerror (errno));
                  dbs[cnt].persistent = 0;
                }
              else
                {
                  /* Success.  */
                  dbs[cnt].head = mem;
                  dbs[cnt].data = (char *)
                    &dbs[cnt].head->array[roundup (dbs[cnt].head->module,
                                               ALIGN / sizeof (ref_t))];
                  dbs[cnt].memsize = total;
                  dbs[cnt].mmap_used = true;

                  /* Remember the descriptors.  */
                  dbs[cnt].wr_fd = fd;
                  dbs[cnt].ro_fd = ro_fd;
                  fd = -1;
                  ro_fd = -1;
                }

              if (fd != -1)
                close (fd);
              if (ro_fd != -1)
                close (ro_fd);
             }
         }

#if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
       /* We do not check here whether the O_CLOEXEC provided to the
          open call was successful or not.  The two fcntl calls are
          only performed once each per process start-up and therefore
          is not noticeable at all.  */
       if (paranoia
           && ((dbs[cnt].wr_fd != -1
               && fcntl (dbs[cnt].wr_fd, F_SETFD, FD_CLOEXEC) == -1)
              || (dbs[cnt].ro_fd != -1
                  && fcntl (dbs[cnt].ro_fd, F_SETFD, FD_CLOEXEC) == -1)))
         {
           dbg_log (_("\
cannot set socket to close on exec: %s; disabling paranoia mode"),
                   strerror (errno));
           paranoia = 0;
         }
#endif

       if (dbs[cnt].head == NULL)
         {
           /* We do not use the persistent database.  Just
              create an in-memory data structure.  */
           assert (! dbs[cnt].persistent);

           dbs[cnt].head = xmalloc (sizeof (struct database_pers_head)
                                 + (dbs[cnt].suggested_module
                                   * sizeof (ref_t)));
           memset (dbs[cnt].head, '\0', sizeof (struct database_pers_head));
           assert (~ENDREF == 0);
           memset (dbs[cnt].head->array, '\xff',
                  dbs[cnt].suggested_module * sizeof (ref_t));
           dbs[cnt].head->module = dbs[cnt].suggested_module;
           dbs[cnt].head->data_size = (DEFAULT_DATASIZE_PER_BUCKET
                                   * dbs[cnt].head->module);
           dbs[cnt].data = xmalloc (dbs[cnt].head->data_size);
           dbs[cnt].head->first_free = 0;

           dbs[cnt].shared = 0;
           assert (dbs[cnt].ro_fd == -1);
         }

       dbs[cnt].inotify_descr = -1;
       if (dbs[cnt].check_file)
         {
#ifdef HAVE_INOTIFY
           if (inotify_fd < 0
              || (dbs[cnt].inotify_descr
                  = inotify_add_watch (inotify_fd, dbs[cnt].filename,
                                    IN_DELETE_SELF | IN_MODIFY)) < 0)
             /* We cannot notice changes in the main thread.  */
#endif
             {
              /* We need the modification date of the file.  */
              struct stat64 st;

              if (stat64 (dbs[cnt].filename, &st) < 0)
                {
                  /* We cannot stat() the file, disable file checking.  */
                  dbg_log (_("cannot stat() file `%s': %s"),
                          dbs[cnt].filename, strerror (errno));
                  dbs[cnt].check_file = 0;
                }
              else
                dbs[cnt].file_mtime = st.st_mtime;
             }
         }

#ifdef HAVE_INOTIFY
       if (cnt == hstdb && inotify_fd >= -1)
         /* We also monitor the resolver configuration file.  */
         resolv_conf_descr = inotify_add_watch (inotify_fd,
                                           _PATH_RESCONF,
                                           IN_DELETE_SELF | IN_MODIFY);
#endif
      }

  /* Create the socket.  */
#ifndef __ASSUME_SOCK_CLOEXEC
  sock = -1;
  if (have_sock_cloexec >= 0)
#endif
    {
      sock = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
#ifndef __ASSUME_SOCK_CLOEXEC
      if (have_sock_cloexec == 0)
       have_sock_cloexec = sock != -1 || errno != EINVAL ? 1 : -1;
#endif
    }
#ifndef __ASSUME_SOCK_CLOEXEC
  if (have_sock_cloexec < 0)
    sock = socket (AF_UNIX, SOCK_STREAM, 0);
#endif
  if (sock < 0)
    {
      dbg_log (_("cannot open socket: %s"), strerror (errno));
      exit (errno == EACCES ? 4 : 1);
    }
  /* Bind a name to the socket.  */
  struct sockaddr_un sock_addr;
  sock_addr.sun_family = AF_UNIX;
  strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
  if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
    {
      dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
      exit (errno == EACCES ? 4 : 1);
    }

#ifndef __ASSUME_SOCK_CLOEXEC
  if (have_sock_cloexec < 0)
    {
      /* We don't want to get stuck on accept.  */
      int fl = fcntl (sock, F_GETFL);
      if (fl == -1 || fcntl (sock, F_SETFL, fl | O_NONBLOCK) == -1)
       {
         dbg_log (_("cannot change socket to nonblocking mode: %s"),
                 strerror (errno));
         exit (1);
       }

      /* The descriptor needs to be closed on exec.  */
      if (paranoia && fcntl (sock, F_SETFD, FD_CLOEXEC) == -1)
       {
         dbg_log (_("cannot set socket to close on exec: %s"),
                 strerror (errno));
         exit (1);
       }
    }
#endif

  /* Set permissions for the socket.  */
  chmod (_PATH_NSCDSOCKET, DEFFILEMODE);

  /* Set the socket up to accept connections.  */
  if (listen (sock, SOMAXCONN) < 0)
    {
      dbg_log (_("cannot enable socket to accept connections: %s"),
              strerror (errno));
      exit (1);
    }

  /* Change to unprivileged uid/gid/groups if specifed in config file */
  if (server_user != NULL)
    finish_drop_privileges ();
}

Here is the call graph for this function:

Here is the caller graph for this function:

int nscd_open_socket ( void  )

Definition at line 418 of file nscd.c.

{
  struct sockaddr_un addr;
  int sock;

  sock = socket (PF_UNIX, SOCK_STREAM, 0);
  if (sock < 0)
    return -1;

  addr.sun_family = AF_UNIX;
  assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
  strcpy (addr.sun_path, _PATH_NSCDSOCKET);
  if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
    {
      close (sock);
      return -1;
    }

  return sock;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int nscd_parse_file ( const char *  fname,
struct database_dyn  dbs[lastdb] 
)

Definition at line 62 of file nscd_conf.c.

{
  FILE *fp;
  char *line, *cp, *entry, *arg1, *arg2;
  size_t len;
  int cnt;
  const unsigned int initial_error_message_count = error_message_count;

  /* Open the configuration file.  */
  fp = fopen (fname, "r");
  if (fp == NULL)
    return -1;

  /* The stream is not used by more than one thread.  */
  (void) __fsetlocking (fp, FSETLOCKING_BYCALLER);

  line = NULL;
  len = 0;

  do
    {
      ssize_t n = getline (&line, &len, fp);
      if (n < 0)
       break;
      if (line[n - 1] == '\n')
       line[n - 1] = '\0';

      /* Because the file format does not know any form of quoting we
        can search forward for the next '#' character and if found
        make it terminating the line.  */
      *strchrnul (line, '#') = '\0';

      /* If the line is blank it is ignored.  */
      if (line[0] == '\0')
       continue;

      entry = line;
      while (isspace (*entry) && *entry != '\0')
       ++entry;
      cp = entry;
      while (!isspace (*cp) && *cp != '\0')
       ++cp;
      arg1 = cp;
      ++arg1;
      *cp = '\0';
      if (strlen (entry) == 0)
       error (0, 0, _("Parse error: %s"), line);
      while (isspace (*arg1) && *arg1 != '\0')
       ++arg1;
      cp = arg1;
      while (!isspace (*cp) && *cp != '\0')
       ++cp;
      arg2 = cp;
      ++arg2;
      *cp = '\0';
      if (strlen (arg2) > 0)
       {
         while (isspace (*arg2) && *arg2 != '\0')
           ++arg2;
         cp = arg2;
         while (!isspace (*cp) && *cp != '\0')
           ++cp;
         *cp = '\0';
       }

      if (strcmp (entry, "positive-time-to-live") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           dbs[idx].postimeout = atol (arg2);
       }
      else if (strcmp (entry, "negative-time-to-live") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           dbs[idx].negtimeout = atol (arg2);
       }
      else if (strcmp (entry, "suggested-size") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           dbs[idx].suggested_module
             = atol (arg2) ?: DEFAULT_SUGGESTED_MODULE;
       }
      else if (strcmp (entry, "enable-cache") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           {
             if (strcmp (arg2, "no") == 0)
              dbs[idx].enabled = 0;
             else if (strcmp (arg2, "yes") == 0)
              dbs[idx].enabled = 1;
           }
       }
      else if (strcmp (entry, "check-files") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           {
             if (strcmp (arg2, "no") == 0)
              dbs[idx].check_file = 0;
             else if (strcmp (arg2, "yes") == 0)
              dbs[idx].check_file = 1;
           }
       }
      else if (strcmp (entry, "max-db-size") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           dbs[idx].max_db_size = atol (arg2) ?: DEFAULT_MAX_DB_SIZE;
       }
      else if (strcmp (entry, "logfile") == 0)
       set_logfile (arg1);
      else if (strcmp (entry, "debug-level") == 0)
       {
         int level = atoi (arg1);
         if (level > 0)
           debug_level = level;
       }
      else if (strcmp (entry, "threads") == 0)
       {
         if (nthreads == -1)
           nthreads = MAX (atol (arg1), lastdb);
       }
      else if (strcmp (entry, "max-threads") == 0)
       {
         max_nthreads = MAX (atol (arg1), lastdb);
       }
      else if (strcmp (entry, "server-user") == 0)
        {
          if (!arg1)
            error (0, 0, _("Must specify user name for server-user option"));
          else
            server_user = xstrdup (arg1);
        }
      else if (strcmp (entry, "stat-user") == 0)
        {
          if (arg1 == NULL)
            error (0, 0, _("Must specify user name for stat-user option"));
          else
           {
             stat_user = xstrdup (arg1);

             struct passwd *pw = getpwnam (stat_user);
             if (pw != NULL)
              stat_uid = pw->pw_uid;
           }
        }
      else if (strcmp (entry, "persistent") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           {
             if (strcmp (arg2, "no") == 0)
              dbs[idx].persistent = 0;
             else if (strcmp (arg2, "yes") == 0)
              dbs[idx].persistent = 1;
           }
       }
      else if (strcmp (entry, "shared") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           {
             if (strcmp (arg2, "no") == 0)
              dbs[idx].shared = 0;
             else if (strcmp (arg2, "yes") == 0)
              dbs[idx].shared = 1;
           }
       }
      else if (strcmp (entry, "reload-count") == 0)
       {
         if (strcasecmp (arg1, "unlimited") == 0)
           reload_count = UINT_MAX;
         else
           {
             unsigned int count = strtoul (arg1, NULL, 0);
             if (count > UINT8_MAX - 1)
              reload_count = UINT_MAX;
             else if (count >= 0)
           reload_count = count;
             else
              error (0, 0, _("invalid value for 'reload-count': %u"), count);
           }
       }
      else if (strcmp (entry, "paranoia") == 0)
       {
         if (strcmp (arg1, "no") == 0)
           paranoia = 0;
         else if (strcmp (arg1, "yes") == 0)
           paranoia = 1;
       }
      else if (strcmp (entry, "restart-interval") == 0)
       {
         if (arg1 != NULL)
           restart_interval = atol (arg1);
         else
            error (0, 0, _("Must specify value for restart-interval option"));
       }
      else if (strcmp (entry, "auto-propagate") == 0)
       {
         int idx = find_db (arg1);
         if (idx >= 0)
           {
             if (strcmp (arg2, "no") == 0)
              dbs[idx].propagate = 0;
             else if (strcmp (arg2, "yes") == 0)
              dbs[idx].propagate = 1;
           }
       }
      else
       error (0, 0, _("Unknown option: %s %s %s"), entry, arg1, arg2);
    }
  while (!feof_unlocked (fp));

  if (paranoia)
    {
      restart_time = time (NULL) + restart_interval;

      /* Save the old current workding directory if we are in paranoia
        mode.  We have to change back to it.  */
      oldcwd = get_current_dir_name ();
      if (oldcwd == NULL)
       {
         error (0, 0, _("\
cannot get current working directory: %s; disabling paranoia mode"),
                 strerror (errno));
         paranoia = 0;
       }
    }

  /* Enforce sanity.  */
  if (max_nthreads < nthreads)
    max_nthreads = nthreads;

  for (cnt = 0; cnt < lastdb; ++cnt)
    {
      size_t datasize = (sizeof (struct database_pers_head)
                      + roundup (dbs[cnt].suggested_module
                                * sizeof (ref_t), ALIGN)
                      + (dbs[cnt].suggested_module
                         * DEFAULT_DATASIZE_PER_BUCKET));
      if (datasize > dbs[cnt].max_db_size)
       {
         error (0, 0, _("maximum file size for %s database too small"),
                 dbnames[cnt]);
         dbs[cnt].max_db_size = datasize;
       }

    }

  /* Free the buffer.  */
  free (line);
  /* Close configuration file.  */
  fclose (fp);

  return error_message_count != initial_error_message_count;
}

Here is the call graph for this function:

Here is the caller graph for this function:

time_t prune_cache ( struct database_dyn table,
time_t  now,
int  fd 
)

Definition at line 257 of file cache.c.

{
  size_t cnt = table->head->module;

  /* If this table is not actually used don't do anything.  */
  if (cnt == 0)
    {
      if (fd != -1)
       {
         /* Reply to the INVALIDATE initiator.  */
         int32_t resp = 0;
         writeall (fd, &resp, sizeof (resp));
       }

      /* No need to do this again anytime soon.  */
      return 24 * 60 * 60;
    }

  /* If we check for the modification of the underlying file we invalidate
     the entries also in this case.  */
  if (table->inotify_descr < 0 && table->check_file && now != LONG_MAX)
    {
      struct stat64 st;

      if (stat64 (table->filename, &st) < 0)
       {
         char buf[128];
         /* We cannot stat() the file, disable file checking if the
             file does not exist.  */
         dbg_log (_("cannot stat() file `%s': %s"),
                 table->filename, strerror_r (errno, buf, sizeof (buf)));
         if (errno == ENOENT)
           table->check_file = 0;
       }
      else
       {
         if (st.st_mtime != table->file_mtime)
           {
             /* The file changed.  Invalidate all entries.  */
             now = LONG_MAX;
             table->file_mtime = st.st_mtime;
           }
       }
    }

  /* We run through the table and find values which are not valid anymore.

     Note that for the initial step, finding the entries to be removed,
     we don't need to get any lock.  It is at all timed assured that the
     linked lists are set up correctly and that no second thread prunes
     the cache.  */
  bool *mark;
  size_t memory_needed = cnt * sizeof (bool);
  bool mark_use_alloca;
  if (__builtin_expect (memory_needed <= MAX_STACK_USE, 1))
    {
      mark = alloca (cnt * sizeof (bool));
      memset (mark, '\0', memory_needed);
      mark_use_alloca = true;
    }
  else
    {
      mark = xcalloc (1, memory_needed);
      mark_use_alloca = false;
    }
  size_t first = cnt + 1;
  size_t last = 0;
  char *const data = table->data;
  bool any = false;

  if (__builtin_expect (debug_level > 2, 0))
    dbg_log (_("pruning %s cache; time %ld"),
            dbnames[table - dbs], (long int) now);

#define NO_TIMEOUT LONG_MAX
  time_t next_timeout = NO_TIMEOUT;
  do
    {
      ref_t run = table->head->array[--cnt];

      while (run != ENDREF)
       {
         struct hashentry *runp = (struct hashentry *) (data + run);
         struct datahead *dh = (struct datahead *) (data + runp->packet);

         /* Some debug support.  */
         if (__builtin_expect (debug_level > 2, 0))
           {
             char buf[INET6_ADDRSTRLEN];
             const char *str;

             if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
              {
                inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
                          data + runp->key, buf, sizeof (buf));
                str = buf;
              }
             else
              str = data + runp->key;

             dbg_log (_("considering %s entry \"%s\", timeout %" PRIu64),
                     serv2str[runp->type], str, dh->timeout);
           }

         /* Check whether the entry timed out.  */
         if (dh->timeout < now)
           {
             /* This hash bucket could contain entries which need to
               be looked at.  */
             mark[cnt] = true;

             first = MIN (first, cnt);
             last = MAX (last, cnt);

             /* We only have to look at the data of the first entries
               since the count information is kept in the data part
               which is shared.  */
             if (runp->first)
              {

                /* At this point there are two choices: we reload the
                   value or we discard it.  Do not change NRELOADS if
                   we never not reload the record.  */
                if ((reload_count != UINT_MAX
                     && __builtin_expect (dh->nreloads >= reload_count, 0))
                    /* We always remove negative entries.  */
                    || dh->notfound
                    /* Discard everything if the user explicitly
                      requests it.  */
                    || now == LONG_MAX)
                  {
                    /* Remove the value.  */
                    dh->usable = false;

                    /* We definitely have some garbage entries now.  */
                    any = true;
                  }
                else
                  {
                    /* Reload the value.  We do this only for the
                      initially used key, not the additionally
                      added derived value.  */
                    assert (runp->type < LASTREQ
                           && readdfcts[runp->type] != NULL);

                    readdfcts[runp->type] (table, runp, dh);

                    /* If the entry has been replaced, we might need
                      cleanup.  */
                    any |= !dh->usable;
                  }
              }
           }
         else
           {
             assert (dh->usable);
             next_timeout = MIN (next_timeout, dh->timeout);
           }

         run = runp->next;
       }
    }
  while (cnt > 0);

  if (__builtin_expect (fd != -1, 0))
    {
      /* Reply to the INVALIDATE initiator that the cache has been
        invalidated.  */
      int32_t resp = 0;
      writeall (fd, &resp, sizeof (resp));
    }

  if (first <= last)
    {
      struct hashentry *head = NULL;

      /* Now we have to get the write lock since we are about to modify
        the table.  */
      if (__builtin_expect (pthread_rwlock_trywrlock (&table->lock) != 0, 0))
       {
         ++table->head->wrlockdelayed;
         pthread_rwlock_wrlock (&table->lock);
       }

      while (first <= last)
       {
         if (mark[first])
           {
             ref_t *old = &table->head->array[first];
             ref_t run = table->head->array[first];

             assert (run != ENDREF);
             do
              {
                struct hashentry *runp = (struct hashentry *) (data + run);
                struct datahead *dh
                  = (struct datahead *) (data + runp->packet);

                if (! dh->usable)
                  {
                    /* We need the list only for debugging but it is
                      more costly to avoid creating the list than
                      doing it.  */
                    runp->dellist = head;
                    head = runp;

                    /* No need for an atomic operation, we have the
                      write lock.  */
                    --table->head->nentries;

                    run = *old = runp->next;
                  }
                else
                  {
                    old = &runp->next;
                    run = runp->next;
                  }
              }
             while (run != ENDREF);
           }

         ++first;
       }

      /* It's all done.  */
      pthread_rwlock_unlock (&table->lock);

      /* Make sure the data is saved to disk.  */
      if (table->persistent)
       msync (table->head,
              data + table->head->first_free - (char *) table->head,
              MS_ASYNC);

      /* One extra pass if we do debugging.  */
      if (__builtin_expect (debug_level > 0, 0))
       {
         struct hashentry *runp = head;

         while (runp != NULL)
           {
             char buf[INET6_ADDRSTRLEN];
             const char *str;

             if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
              {
                inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
                          data + runp->key, buf, sizeof (buf));
                str = buf;
              }
             else
              str = data + runp->key;

             dbg_log ("remove %s entry \"%s\"", serv2str[runp->type], str);

             runp = runp->dellist;
           }
       }
    }

  if (__builtin_expect (! mark_use_alloca, 0))
    free (mark);

  /* Run garbage collection if any entry has been removed or replaced.  */
  if (any)
    gc (table);

  /* If there is no entry in the database and we therefore have no new
     timeout value, tell the caller to wake up in 24 hours.  */
  return next_timeout == NO_TIMEOUT ? 24 * 60 * 60 : next_timeout - now;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void readdgrbygid ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 530 of file grpcache.c.

{
  char *ep;
  gid_t gid = strtoul (db->data + he->key, &ep, 10);

  /* Since the key has been added before it must be OK.  */
  assert (*(db->data + he->key) != '\0' && *ep == '\0');

  request_header req =
    {
      .type = GETGRBYGID,
      .key_len = he->len
    };
  union keytype u = { .g = gid };

  addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdgrbyname ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 493 of file grpcache.c.

{
  request_header req =
    {
      .type = GETGRBYNAME,
      .key_len = he->len
    };
  union keytype u = { .v = db->data + he->key };

  addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdhstai ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 581 of file aicache.c.

{
  request_header req =
    {
      .type = GETAI,
      .key_len = he->len
    };

  addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdhstbyaddr ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 558 of file hstcache.c.

{
  request_header req =
    {
      .type = GETHOSTBYADDR,
      .key_len = he->len
    };

  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdhstbyaddrv6 ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 602 of file hstcache.c.

{
  request_header req =
    {
      .type = GETHOSTBYADDRv6,
      .key_len = he->len
    };

  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdhstbyname ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 536 of file hstcache.c.

{
  request_header req =
    {
      .type = GETHOSTBYNAME,
      .key_len = he->len
    };

  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdhstbynamev6 ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 580 of file hstcache.c.

{
  request_header req =
    {
      .type = GETHOSTBYNAMEv6,
      .key_len = he->len
    };

  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdinitgroups ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 431 of file initgrcache.c.

{
  request_header req =
    {
      .type = INITGROUPS,
      .key_len = he->len
    };

  addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdpwbyname ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 499 of file pwdcache.c.

{
  request_header req =
    {
      .type = GETPWBYNAME,
      .key_len = he->len
    };
  union keytype u = { .v = db->data + he->key };

  addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdpwbyuid ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 536 of file pwdcache.c.

{
  char *ep;
  uid_t uid = strtoul (db->data + he->key, &ep, 10);

  /* Since the key has been added before it must be OK.  */
  assert (*(db->data + he->key) != '\0' && *ep == '\0');

  request_header req =
    {
      .type = GETPWBYUID,
      .key_len = he->len
    };
  union keytype u = { .u = uid };

  addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdservbyname ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 443 of file servicescache.c.

{
  request_header req =
    {
      .type = GETSERVBYNAME,
      .key_len = he->len
    };

  addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

void readdservbyport ( struct database_dyn db,
struct hashentry he,
struct datahead dh 
)

Definition at line 465 of file servicescache.c.

{
  request_header req =
    {
      .type = GETSERVBYPORT,
      .key_len = he->len
    };

  addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}

Here is the call graph for this function:

Definition at line 148 of file nscd_stat.c.

{
  struct statdata data;
  request_header req;
  ssize_t nbytes;
  int fd;
  int i;
  uid_t uid = getuid ();
  const char *yesstr = _("yes");
  const char *nostr = _("no");

  /* Find out whether there is another user but root allowed to
     request statistics.  */
  if (uid != 0)
    {
      /* User specified?  */
      if(stat_user == NULL || stat_uid != uid)
       {
         if (stat_user != NULL)
           error (EXIT_FAILURE, 0,
                 _("Only root or %s is allowed to use this option!"),
                 stat_user);
         else
           error (EXIT_FAILURE, 0,
                 _("Only root is allowed to use this option!"));
       }
    }

  /* Open a socket to the running nscd.  */
  fd = nscd_open_socket ();
  if (fd == -1)
    error (EXIT_FAILURE, 0, _("nscd not running!\n"));

  /* Send the request.  */
  req.version = NSCD_VERSION;
  req.type = GETSTAT;
  req.key_len = 0;
  nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
                                 MSG_NOSIGNAL));
  if (nbytes != sizeof (request_header))
    {
      int err = errno;
      close (fd);
      error (EXIT_FAILURE, err, _("write incomplete"));
    }

  /* Read as much data as we expect.  */
  if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
      || (memcmp (data.version, compilation, sizeof (compilation)) != 0
         /* Yes, this is an assignment!  */
         && (errno = EINVAL)))
    {
      /* Not the right version.  */
      int err = errno;
      close (fd);
      error (EXIT_FAILURE, err, _("cannot read statistics data"));
    }

  printf (_("nscd configuration:\n\n%15d  server debug level\n"),
         data.debug_level);

  /* We know that we can simply subtract time_t values.  */
  unsigned long int diff = data.runtime;
  unsigned int ndays = 0;
  unsigned int nhours = 0;
  unsigned int nmins = 0;
  if (diff > 24 * 60 * 60)
    {
      ndays = diff / (24 * 60 * 60);
      diff %= 24 * 60 * 60;
    }
  if (diff > 60 * 60)
    {
      nhours = diff / (60 * 60);
      diff %= 60 * 60;
    }
  if (diff > 60)
    {
      nmins = diff / 60;
      diff %= 60;
    }
  if (ndays != 0)
    printf (_("%3ud %2uh %2um %2lus  server runtime\n"),
           ndays, nhours, nmins, diff);
  else if (nhours != 0)
    printf (_("    %2uh %2um %2lus  server runtime\n"), nhours, nmins, diff);
  else if (nmins != 0)
    printf (_("        %2um %2lus  server runtime\n"), nmins, diff);
  else
    printf (_("            %2lus  server runtime\n"), diff);

  printf (_("%15d  current number of threads\n"
           "%15d  maximum number of threads\n"
           "%15lu  number of times clients had to wait\n"
           "%15s  paranoia mode enabled\n"
           "%15lu  restart internal\n"),
         data.nthreads, data.max_nthreads, data.client_queued,
         data.paranoia ? yesstr : nostr,
         (unsigned long int) data.restart_interval);

  for (i = 0; i < lastdb; ++i)
    {
      unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
      unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
      const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
      const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
      const char *shared = data.dbs[i].shared ? yesstr : nostr;
      const char *persistent = data.dbs[i].persistent ? yesstr : nostr;

      if (enabled[0] == '\0')
       /* The locale does not provide this information so we have to
          translate it ourself.  Since we should avoid short translation
          terms we artifically increase the length.  */
       enabled = data.dbs[i].enabled ? yesstr : nostr;
      if (check_file[0] == '\0')
       check_file = data.dbs[i].check_file ? yesstr : nostr;
      if (shared[0] == '\0')
       shared = data.dbs[i].shared ? yesstr : nostr;
      if (persistent[0] == '\0')
       persistent = data.dbs[i].persistent ? yesstr : nostr;

      if (all == 0)
       /* If nothing happened so far report a 0% hit rate.  */
       all = 1;

      printf (_("\n%s cache:\n\n"
              "%15s  cache is enabled\n"
              "%15s  cache is persistent\n"
              "%15s  cache is shared\n"
              "%15zu  suggested size\n"
              "%15zu  total data pool size\n"
              "%15zu  used data pool size\n"
              "%15lu  seconds time to live for positive entries\n"
              "%15lu  seconds time to live for negative entries\n"
              "%15" PRIuMAX "  cache hits on positive entries\n"
              "%15" PRIuMAX "  cache hits on negative entries\n"
              "%15" PRIuMAX "  cache misses on positive entries\n"
              "%15" PRIuMAX "  cache misses on negative entries\n"
              "%15lu%% cache hit rate\n"
              "%15zu  current number of cached values\n"
              "%15zu  maximum number of cached values\n"
              "%15zu  maximum chain length searched\n"
              "%15" PRIuMAX "  number of delays on rdlock\n"
              "%15" PRIuMAX "  number of delays on wrlock\n"
              "%15" PRIuMAX "  memory allocations failed\n"
              "%15s  check /etc/%s for changes\n"),
             dbnames[i], enabled, persistent, shared,
             data.dbs[i].module,
             data.dbs[i].datasize, data.dbs[i].dataused,
             data.dbs[i].postimeout, data.dbs[i].negtimeout,
             data.dbs[i].poshit, data.dbs[i].neghit,
             data.dbs[i].posmiss, data.dbs[i].negmiss,
             (100 * hit) / all,
             data.dbs[i].nentries, data.dbs[i].maxnentries,
             data.dbs[i].maxnsearched,
             data.dbs[i].rdlockdelayed,
             data.dbs[i].wrlockdelayed,
             data.dbs[i].addfailed, check_file, dbnames[i]);
    }

  if (selinux_enabled)
    nscd_avc_print_stats (&data.cstats);

  close (fd);

  exit (0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void send_stats ( int  fd,
struct database_dyn  dbs[lastdb] 
)

Definition at line 92 of file nscd_stat.c.

{
  struct statdata data;
  int cnt;

  memcpy (data.version, compilation, sizeof (compilation));
  data.debug_level = debug_level;
  data.runtime = time (NULL) - start_time;
  data.client_queued = client_queued;
  data.nthreads = nthreads;
  data.max_nthreads = max_nthreads;
  data.paranoia = paranoia;
  data.restart_interval = restart_interval;
  data.ndbs = lastdb;

  for (cnt = 0; cnt < lastdb; ++cnt)
    {
      memset (&data.dbs[cnt], 0, sizeof (data.dbs[cnt]));
      data.dbs[cnt].enabled = dbs[cnt].enabled;
      data.dbs[cnt].check_file = dbs[cnt].check_file;
      data.dbs[cnt].shared = dbs[cnt].shared;
      data.dbs[cnt].persistent = dbs[cnt].persistent;
      data.dbs[cnt].postimeout = dbs[cnt].postimeout;
      data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
      if (dbs[cnt].head != NULL)
       {
         data.dbs[cnt].module = dbs[cnt].head->module;
         data.dbs[cnt].poshit = dbs[cnt].head->poshit;
         data.dbs[cnt].neghit = dbs[cnt].head->neghit;
         data.dbs[cnt].posmiss = dbs[cnt].head->posmiss;
         data.dbs[cnt].negmiss = dbs[cnt].head->negmiss;
         data.dbs[cnt].nentries = dbs[cnt].head->nentries;
         data.dbs[cnt].maxnentries = dbs[cnt].head->maxnentries;
         data.dbs[cnt].datasize = dbs[cnt].head->data_size;
         data.dbs[cnt].dataused = dbs[cnt].head->first_free;
         data.dbs[cnt].maxnsearched = dbs[cnt].head->maxnsearched;
         data.dbs[cnt].rdlockdelayed = dbs[cnt].head->rdlockdelayed;
         data.dbs[cnt].wrlockdelayed = dbs[cnt].head->wrlockdelayed;
         data.dbs[cnt].addfailed = dbs[cnt].head->addfailed;
       }
    }

  if (selinux_enabled)
    nscd_avc_cache_stats (&data.cstats);

  if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
      != sizeof (data))
    {
      char buf[256];
      dbg_log (_("cannot write statistics: %s"),
              strerror_r (errno, buf, sizeof (buf)));
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int setup_thread ( struct database_dyn db)

Definition at line 24 of file nscd_setup_thread.c.

{
  /* Nothing.  */
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void start_threads ( void  )

Definition at line 2155 of file connections.c.

{
  /* Initialize the conditional variable we will use.  The only
     non-standard attribute we might use is the clock selection.  */
  pthread_condattr_t condattr;
  pthread_condattr_init (&condattr);

#if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0 \
    && defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
  /* Determine whether the monotonous clock is available.  */
  struct timespec dummy;
# if _POSIX_MONOTONIC_CLOCK == 0
  if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
# endif
# if _POSIX_CLOCK_SELECTION == 0
    if (sysconf (_SC_CLOCK_SELECTION) > 0)
# endif
      if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0
         && pthread_condattr_setclock (&condattr, CLOCK_MONOTONIC) == 0)
       timeout_clock = CLOCK_MONOTONIC;
#endif

  /* Create the attribute for the threads.  They are all created
     detached.  */
  pthread_attr_init (&attr);
  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
  /* Use 1MB stacks, twice as much for 64-bit architectures.  */
  pthread_attr_setstacksize (&attr, NSCD_THREAD_STACKSIZE);

  /* We allow less than LASTDB threads only for debugging.  */
  if (debug_level == 0)
    nthreads = MAX (nthreads, lastdb);

  /* Create the threads which prune the databases.  */
  // XXX Ideally this work would be done by some of the worker threads.
  // XXX But this is problematic since we would need to be able to wake
  // XXX them up explicitly as well as part of the group handling the
  // XXX ready-list.  This requires an operation where we can wait on
  // XXX two conditional variables at the same time.  This operation
  // XXX does not exist (yet).
  for (long int i = 0; i < lastdb; ++i)
    {
      /* Initialize the conditional variable.  */
      if (pthread_cond_init (&dbs[i].prune_cond, &condattr) != 0)
       {
         dbg_log (_("could not initialize conditional variable"));
         exit (1);
       }

      pthread_t th;
      if (dbs[i].enabled
         && pthread_create (&th, &attr, nscd_run_prune, (void *) i) != 0)
       {
         dbg_log (_("could not start clean-up thread; terminating"));
         exit (1);
       }
    }

  pthread_condattr_destroy (&condattr);

  for (long int i = 0; i < nthreads; ++i)
    {
      pthread_t th;
      if (pthread_create (&th, &attr, nscd_run_worker, NULL) != 0)
       {
         if (i == 0)
           {
             dbg_log (_("could not start any worker thread; terminating"));
             exit (1);
           }

         break;
       }
    }

  /* Determine how much room for descriptors we should initially
     allocate.  This might need to change later if we cap the number
     with MAXCONN.  */
  const long int nfds = sysconf (_SC_OPEN_MAX);
#define MINCONN 32
#define MAXCONN 16384
  if (nfds == -1 || nfds > MAXCONN)
    nconns = MAXCONN;
  else if (nfds < MINCONN)
    nconns = MINCONN;
  else
    nconns = nfds;

  /* We need memory to pass descriptors on to the worker threads.  */
  fdlist = (struct fdlist *) xcalloc (nconns, sizeof (fdlist[0]));
  /* Array to keep track when connection was accepted.  */
  starttime = (time_t *) xcalloc (nconns, sizeof (starttime[0]));

  /* In the main thread we execute the loop which handles incoming
     connections.  */
#ifdef HAVE_EPOLL
  int efd = epoll_create (100);
  if (efd != -1)
    {
      main_loop_epoll (efd);
      close (efd);
    }
#endif

  main_loop_poll ();
}

Here is the call graph for this function:

void termination_handler ( int  signum)

Definition at line 442 of file nscd.c.

{
  close_sockets ();

  /* Clean up the file created by 'bind'.  */
  unlink (_PATH_NSCDSOCKET);

  /* Clean up pid file.  */
  unlink (_PATH_NSCDPID);

  // XXX Terminate threads.

  /* Synchronize memory.  */
  for (int cnt = 0; cnt < lastdb; ++cnt)
    {
      if (!dbs[cnt].enabled)
       continue;

      /* Make sure nobody keeps using the database.  */
      dbs[cnt].head->timestamp = 0;

      if (dbs[cnt].persistent)
       // XXX async OK?
       msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
    }

  /* Shutdown the SELinux AVC.  */
  if (selinux_enabled)
    nscd_avc_destroy ();

  _exit (EXIT_SUCCESS);
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

Definition at line 25 of file init-first.c.

unsigned long int client_queued

Definition at line 246 of file connections.c.

Definition at line 41 of file nscd_conf.c.

Definition at line 224 of file connections.c.

Definition at line 251 of file connections.c.

Definition at line 222 of file connections.c.

Definition at line 85 of file nscd.c.

Definition at line 84 of file nscd.c.

const char* oldcwd

Definition at line 83 of file nscd.c.

Definition at line 78 of file nscd.c.

Definition at line 80 of file nscd.c.

unsigned int reload_count

Definition at line 45 of file cache.c.

Definition at line 82 of file nscd.c.

Definition at line 81 of file nscd.c.

Definition at line 83 of file connections.c.

Definition at line 66 of file connections.c.

Definition at line 76 of file nscd.c.

Definition at line 70 of file connections.c.

Definition at line 69 of file connections.c.