Back to index

glibc  2.9
Classes | Defines | Enumerations | Functions | Variables
connections.c File Reference
#include <alloca.h>
#include <assert.h>
#include <atomic.h>
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <libintl.h>
#include <pthread.h>
#include <pwd.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include "nscd.h"
#include "dbg_log.h"
#include "selinux.h"
#include <resolv/resolv.h>

Go to the source code of this file.

Classes

struct  fdlist

Defines

#define NGROUPS   32
#define have_paccept   -1
#define EXTRA_O_FLAGS   0
#define UPDATE_MAPPING_TIMEOUT   (MAPPING_TIMEOUT * 9 / 10)
#define MAX_ACCEPT_TIMEOUT   30
#define MIN_ACCEPT_TIMEOUT   5
#define MAIN_THREAD_TIMEOUT
#define ACCEPT_TIMEOUT
#define MINCONN   32
#define MAXCONN   16384

Enumerations

enum  usekey {
  use_not = 0, use_first = 16, use_begin = 32, use_end = 64,
  use_he = 1, use_he_begin = use_he | use_begin, use_he_end = use_he | use_end, use_data = 3,
  use_data_begin = use_data | use_begin, use_data_end = use_data | use_end, use_data_first = use_data_begin | use_first
}

Functions

void * xmalloc (size_t n)
void * xcalloc (size_t n, size_t s)
void * xrealloc (void *o, size_t n)
static void begin_drop_privileges (void)
static void finish_drop_privileges (void)
ssize_t writeall (int fd, const void *buf, size_t len)
static int check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap, enum usekey use, ref_t start, size_t len)
static int verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
void nscd_init (void)
void close_sockets (void)
static void invalidate_cache (char *key, int fd)
static void handle_request (int fd, request_header *req, void *key, uid_t uid, pid_t pid)
static void restart (void)
static void * __attribute__ ((__noreturn__))
static void fd_ready (int fd)
static int restart_p (time_t now)
void start_threads (void)

Variables

const char * server_user
static uid_t server_uid
static gid_t server_gid
const char * stat_user
uid_t stat_uid
static gid_tserver_groups
static int server_ngroups
static pthread_attr_t attr
const char *const serv2str [LASTREQ]
struct {
bool data_request
struct database_dyndb
reqinfo [LASTREQ]
int nthreads = -1
int max_nthreads = 32
static int sock
static int have_sock_cloexec
unsigned long int client_queued
__thread struct mem_in_flight
struct mem_in_flightmem_in_flight_list
static struct fdlistfdlist
static struct fdlistreadylist
static pthread_cond_t readylist_cond = PTHREAD_COND_INITIALIZER
static pthread_mutex_t readylist_lock = PTHREAD_MUTEX_INITIALIZER
static clockid_t timeout_clock = CLOCK_REALTIME
static unsigned long int nready
static unsigned int nconns
static time_tstarttime

Class Documentation

struct fdlist

Definition at line 1446 of file connections.c.

Collaboration diagram for fdlist:
Class Members
int fd
struct fdlist * next

Define Documentation

#define ACCEPT_TIMEOUT
#define EXTRA_O_FLAGS   0

Definition at line 509 of file connections.c.

#define have_paccept   -1

Definition at line 242 of file connections.c.

Value:
(MAX_ACCEPT_TIMEOUT * 1000                                           \
   - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * 1000 * nused) / (2 * nconns))
#define MAX_ACCEPT_TIMEOUT   30
#define MAXCONN   16384
#define MIN_ACCEPT_TIMEOUT   5
#define MINCONN   32
#define NGROUPS   32

Definition at line 73 of file connections.c.

#define UPDATE_MAPPING_TIMEOUT   (MAPPING_TIMEOUT * 9 / 10)

Enumeration Type Documentation

enum usekey
Enumerator:
use_not 
use_first 
use_begin 
use_end 
use_he 
use_he_begin 
use_he_end 
use_data 
use_data_begin 
use_data_end 
use_data_first 

Definition at line 292 of file connections.c.

  {
    use_not = 0,
    /* The following three are not really used, they are symbolic constants.  */
    use_first = 16,
    use_begin = 32,
    use_end = 64,

    use_he = 1,
    use_he_begin = use_he | use_begin,
    use_he_end = use_he | use_end,
#if SEPARATE_KEY
    use_key = 2,
    use_key_begin = use_key | use_begin,
    use_key_end = use_key | use_end,
    use_key_first = use_key_begin | use_first,
#endif
    use_data = 3,
    use_data_begin = use_data | use_begin,
    use_data_end = use_data | use_end,
    use_data_first = use_data_begin | use_first
  };

Function Documentation

static void* __attribute__ ( (__noreturn__)  ) [static]

Definition at line 1471 of file connections.c.

{
  const long int my_number = (long int) p;
  assert (dbs[my_number].enabled);

  int dont_need_update = setup_thread (&dbs[my_number]);

  time_t now = time (NULL);

  /* We are running.  */
  dbs[my_number].head->timestamp = now;

  struct timespec prune_ts;
  if (__builtin_expect (clock_gettime (timeout_clock, &prune_ts) == -1, 0))
    /* Should never happen.  */
    abort ();

  /* Compute the initial timeout time.  Prevent all the timers to go
     off at the same time by adding a db-based value.  */
  prune_ts.tv_sec += CACHE_PRUNE_INTERVAL + my_number;
  dbs[my_number].wakeup_time = now + CACHE_PRUNE_INTERVAL + my_number;

  pthread_mutex_t *prune_lock = &dbs[my_number].prune_lock;
  pthread_cond_t *prune_cond = &dbs[my_number].prune_cond;

  pthread_mutex_lock (prune_lock);
  while (1)
    {
      /* Wait, but not forever.  */
      int e = 0;
      if (! dbs[my_number].clear_cache)
       e = pthread_cond_timedwait (prune_cond, prune_lock, &prune_ts);
      assert (__builtin_expect (e == 0 || e == ETIMEDOUT, 1));

      time_t next_wait;
      now = time (NULL);
      if (e == ETIMEDOUT || now >= dbs[my_number].wakeup_time
         || dbs[my_number].clear_cache)
       {
         /* We will determine the new timout values based on the
            cache content.  Should there be concurrent additions to
            the cache which are not accounted for in the cache
            pruning we want to know about it.  Therefore set the
            timeout to the maximum.  It will be descreased when adding
            new entries to the cache, if necessary.  */
         if (sizeof (time_t) == sizeof (long int))
           dbs[my_number].wakeup_time = LONG_MAX;
         else
           dbs[my_number].wakeup_time = INT_MAX;

         /* Unconditionally reset the flag.  */
         time_t prune_now = dbs[my_number].clear_cache ? LONG_MAX : now;
         dbs[my_number].clear_cache = 0;

         pthread_mutex_unlock (prune_lock);

         next_wait = prune_cache (&dbs[my_number], prune_now, -1);

         next_wait = MAX (next_wait, CACHE_PRUNE_INTERVAL);
         /* If clients cannot determine for sure whether nscd is running
            we need to wake up occasionally to update the timestamp.
            Wait 90% of the update period.  */
#define UPDATE_MAPPING_TIMEOUT (MAPPING_TIMEOUT * 9 / 10)
         if (__builtin_expect (! dont_need_update, 0))
           {
             next_wait = MIN (UPDATE_MAPPING_TIMEOUT, next_wait);
             dbs[my_number].head->timestamp = now;
           }

         pthread_mutex_lock (prune_lock);

         /* Make it known when we will wake up again.  */
         if (now + next_wait < dbs[my_number].wakeup_time)
           dbs[my_number].wakeup_time = now + next_wait;
         else
           next_wait = dbs[my_number].wakeup_time - now;
       }
      else
       /* The cache was just pruned.  Do not do it again now.  Just
          use the new timeout value.  */
       next_wait = dbs[my_number].wakeup_time - now;

      if (clock_gettime (timeout_clock, &prune_ts) == -1)
       /* Should never happen.  */
       abort ();

      /* Compute next timeout time.  */
      prune_ts.tv_sec += next_wait;
    }
}

Here is the call graph for this function:

static void begin_drop_privileges ( void  ) [static]

Definition at line 2267 of file connections.c.

{
  struct passwd *pwd = getpwnam (server_user);

  if (pwd == NULL)
    {
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      error (EXIT_FAILURE, 0, _("Failed to run nscd as user '%s'"),
            server_user);
    }

  server_uid = pwd->pw_uid;
  server_gid = pwd->pw_gid;

  /* Save the old UID/GID if we have to change back.  */
  if (paranoia)
    {
      old_uid = getuid ();
      old_gid = getgid ();
    }

  if (getgrouplist (server_user, server_gid, NULL, &server_ngroups) == 0)
    {
      /* This really must never happen.  */
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      error (EXIT_FAILURE, errno, _("initial getgrouplist failed"));
    }

  server_groups = (gid_t *) xmalloc (server_ngroups * sizeof (gid_t));

  if (getgrouplist (server_user, server_gid, server_groups, &server_ngroups)
      == -1)
    {
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      error (EXIT_FAILURE, errno, _("getgrouplist failed"));
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int check_use ( const char *  data,
nscd_ssize_t  first_free,
uint8_t usemap,
enum usekey  use,
ref_t  start,
size_t  len 
) [static]

Definition at line 317 of file connections.c.

{
  assert (len >= 2);

  if (start > first_free || start + len > first_free
      || (start & BLOCK_ALIGN_M1))
    return 0;

  if (usemap[start] == use_not)
    {
      /* Add the start marker.  */
      usemap[start] = use | use_begin;
      use &= ~use_first;

      while (--len > 0)
       if (usemap[++start] != use_not)
         return 0;
       else
         usemap[start] = use;

      /* Add the end marker.  */
      usemap[start] = use | use_end;
    }
  else if ((usemap[start] & ~use_first) == ((use | use_begin) & ~use_first))
    {
      /* Hash entries can't be shared.  */
      if (use == use_he)
       return 0;

      usemap[start] |= (use & use_first);
      use &= ~use_first;

      while (--len > 1)
       if (usemap[++start] != use)
         return 0;

      if (usemap[++start] != (use | use_end))
       return 0;
    }
  else
    /* Points to a wrong object or somewhere in the middle.  */
    return 0;

  return 1;
}

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:

static void fd_ready ( int  fd) [static]

Definition at line 1714 of file connections.c.

{
  pthread_mutex_lock (&readylist_lock);

  /* Find an empty entry in FDLIST.  */
  size_t inner;
  for (inner = 0; inner < nconns; ++inner)
    if (fdlist[inner].next == NULL)
      break;
  assert (inner < nconns);

  fdlist[inner].fd = fd;

  if (readylist == NULL)
    readylist = fdlist[inner].next = &fdlist[inner];
  else
    {
      fdlist[inner].next = readylist->next;
      readylist = readylist->next = &fdlist[inner];
    }

  bool do_signal = true;
  if (__builtin_expect (nready == 0, 0))
    {
      ++client_queued;
      do_signal = false;

      /* Try to start another thread to help out.  */
      pthread_t th;
      if (nthreads < max_nthreads
         && pthread_create (&th, &attr, nscd_run_worker,
                          (void *) (long int) nthreads) == 0)
       {
         /* We got another thread.  */
         ++nthreads;
         /* The new thread might need a kick.  */
         do_signal = true;
       }

    }

  pthread_mutex_unlock (&readylist_lock);

  /* Tell one of the worker threads there is work to do.  */
  if (do_signal)
    pthread_cond_signal (&readylist_cond);
}

Here is the call graph for this function:

static void finish_drop_privileges ( void  ) [static]

Definition at line 2309 of file connections.c.

{
#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
  /* We need to preserve the capabilities to connect to the audit daemon.  */
  cap_t new_caps = preserve_capabilities ();
#endif

  if (setgroups (server_ngroups, server_groups) == -1)
    {
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      error (EXIT_FAILURE, errno, _("setgroups failed"));
    }

  int res;
  if (paranoia)
    res = setresgid (server_gid, server_gid, old_gid);
  else
    res = setgid (server_gid);
  if (res == -1)
    {
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      perror ("setgid");
      exit (4);
    }

  if (paranoia)
    res = setresuid (server_uid, server_uid, old_uid);
  else
    res = setuid (server_uid);
  if (res == -1)
    {
      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
      perror ("setuid");
      exit (4);
    }

#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
  /* Remove the temporary capabilities.  */
  install_real_capabilities (new_caps);
#endif
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void handle_request ( int  fd,
request_header req,
void *  key,
uid_t  uid,
pid_t  pid 
) [static]

Definition at line 1042 of file connections.c.

{
  if (__builtin_expect (req->version, NSCD_VERSION) != NSCD_VERSION)
    {
      if (debug_level > 0)
       dbg_log (_("\
cannot handle old request version %d; current version is %d"),
               req->version, NSCD_VERSION);
      return;
    }

  /* Perform the SELinux check before we go on to the standard checks.  */
  if (selinux_enabled && nscd_request_avc_has_perm (fd, req->type) != 0)
    {
      if (debug_level > 0)
       {
#ifdef SO_PEERCRED
# ifdef PATH_MAX
         char buf[PATH_MAX];
# else
         char buf[4096];
# endif

         snprintf (buf, sizeof (buf), "/proc/%ld/exe", (long int) pid);
         ssize_t n = readlink (buf, buf, sizeof (buf) - 1);

         if (n <= 0)
           dbg_log (_("\
request from %ld not handled due to missing permission"), (long int) pid);
         else
           {
             buf[n] = '\0';
             dbg_log (_("\
request from '%s' [%ld] not handled due to missing permission"),
                     buf, (long int) pid);
           }
#else
         dbg_log (_("request not handled due to missing permission"));
#endif
       }
      return;
    }

  struct database_dyn *db = reqinfo[req->type].db;

  /* See whether we can service the request from the cache.  */
  if (__builtin_expect (reqinfo[req->type].data_request, true))
    {
      if (__builtin_expect (debug_level, 0) > 0)
       {
         if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
           {
             char buf[INET6_ADDRSTRLEN];

             dbg_log ("\t%s (%s)", serv2str[req->type],
                     inet_ntop (req->type == GETHOSTBYADDR
                              ? AF_INET : AF_INET6,
                              key, buf, sizeof (buf)));
           }
         else
           dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
       }

      /* Is this service enabled?  */
      if (__builtin_expect (!db->enabled, 0))
       {
         /* No, sent the prepared record.  */
         if (TEMP_FAILURE_RETRY (send (fd, db->disabled_iov->iov_base,
                                   db->disabled_iov->iov_len,
                                   MSG_NOSIGNAL))
             != (ssize_t) db->disabled_iov->iov_len
             && __builtin_expect (debug_level, 0) > 0)
           {
             /* We have problems sending the result.  */
             char buf[256];
             dbg_log (_("cannot write result: %s"),
                     strerror_r (errno, buf, sizeof (buf)));
           }

         return;
       }

      /* Be sure we can read the data.  */
      if (__builtin_expect (pthread_rwlock_tryrdlock (&db->lock) != 0, 0))
       {
         ++db->head->rdlockdelayed;
         pthread_rwlock_rdlock (&db->lock);
       }

      /* See whether we can handle it from the cache.  */
      struct datahead *cached;
      cached = (struct datahead *) cache_search (req->type, key, req->key_len,
                                           db, uid);
      if (cached != NULL)
       {
         /* Hurray it's in the cache.  */
         ssize_t nwritten;

#ifdef HAVE_SENDFILE
         if (__builtin_expect (db->mmap_used, 1))
           {
             assert (db->wr_fd != -1);
             assert ((char *) cached->data > (char *) db->data);
             assert ((char *) cached->data - (char *) db->head
                    + cached->recsize
                    <= (sizeof (struct database_pers_head)
                       + db->head->module * sizeof (ref_t)
                       + db->head->data_size));
             nwritten = sendfileall (fd, db->wr_fd,
                                  (char *) cached->data
                                  - (char *) db->head, cached->recsize);
# ifndef __ASSUME_SENDFILE
             if (nwritten == -1 && errno == ENOSYS)
              goto use_write;
# endif
           }
         else
# ifndef __ASSUME_SENDFILE
         use_write:
# endif
#endif
           nwritten = writeall (fd, cached->data, cached->recsize);

         if (nwritten != cached->recsize
             && __builtin_expect (debug_level, 0) > 0)
           {
             /* We have problems sending the result.  */
             char buf[256];
             dbg_log (_("cannot write result: %s"),
                     strerror_r (errno, buf, sizeof (buf)));
           }

         pthread_rwlock_unlock (&db->lock);

         return;
       }

      pthread_rwlock_unlock (&db->lock);
    }
  else if (__builtin_expect (debug_level, 0) > 0)
    {
      if (req->type == INVALIDATE)
       dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
      else
       dbg_log ("\t%s", serv2str[req->type]);
    }

  /* Handle the request.  */
  switch (req->type)
    {
    case GETPWBYNAME:
      addpwbyname (db, fd, req, key, uid);
      break;

    case GETPWBYUID:
      addpwbyuid (db, fd, req, key, uid);
      break;

    case GETGRBYNAME:
      addgrbyname (db, fd, req, key, uid);
      break;

    case GETGRBYGID:
      addgrbygid (db, fd, req, key, uid);
      break;

    case GETHOSTBYNAME:
      addhstbyname (db, fd, req, key, uid);
      break;

    case GETHOSTBYNAMEv6:
      addhstbynamev6 (db, fd, req, key, uid);
      break;

    case GETHOSTBYADDR:
      addhstbyaddr (db, fd, req, key, uid);
      break;

    case GETHOSTBYADDRv6:
      addhstbyaddrv6 (db, fd, req, key, uid);
      break;

    case GETAI:
      addhstai (db, fd, req, key, uid);
      break;

    case INITGROUPS:
      addinitgroups (db, fd, req, key, uid);
      break;

    case GETSERVBYNAME:
      addservbyname (db, fd, req, key, uid);
      break;

    case GETSERVBYPORT:
      addservbyport (db, fd, req, key, uid);
      break;

    case GETSTAT:
    case SHUTDOWN:
    case INVALIDATE:
      {
       /* Get the callers credentials.  */
#ifdef SO_PEERCRED
       struct ucred caller;
       socklen_t optlen = sizeof (caller);

       if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
         {
           char buf[256];

           dbg_log (_("error getting caller's id: %s"),
                   strerror_r (errno, buf, sizeof (buf)));
           break;
         }

       uid = caller.uid;
#else
       /* Some systems have no SO_PEERCRED implementation.  They don't
          care about security so we don't as well.  */
       uid = 0;
#endif
      }

      /* Accept shutdown, getstat and invalidate only from root.  For
        the stat call also allow the user specified in the config file.  */
      if (req->type == GETSTAT)
       {
         if (uid == 0 || uid == stat_uid)
           send_stats (fd, dbs);
       }
      else if (uid == 0)
       {
         if (req->type == INVALIDATE)
           invalidate_cache (key, fd);
         else
           termination_handler (0);
       }
      break;

    case GETFDPW:
    case GETFDGR:
    case GETFDHST:
    case GETFDSERV:
#ifdef SCM_RIGHTS
      send_ro_fd (reqinfo[req->type].db, key, fd);
#endif
      break;

    default:
      /* Ignore the command, it's nothing we know.  */
      break;
    }
}

Here is the call graph for this function:

static void invalidate_cache ( char *  key,
int  fd 
) [static]

Definition at line 955 of file connections.c.

{
  dbtype number;
  int32_t resp;

  for (number = pwddb; number < lastdb; ++number)
    if (strcmp (key, dbnames[number]) == 0)
      {
       if (dbs[number].reset_res)
         res_init ();

       break;
      }

  if (number == lastdb)
    {
      resp = EINVAL;
      writeall (fd, &resp, sizeof (resp));
      return;
    }

  if (dbs[number].enabled)
    {
      pthread_mutex_lock (&dbs[number].prune_lock);
      prune_cache (&dbs[number], LONG_MAX, fd);
      pthread_mutex_unlock (&dbs[number].prune_lock);
    }
  else
    {
      resp = 0;
      writeall (fd, &resp, sizeof (resp));
    }
}

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:

static void restart ( void  ) [static]

Definition at line 1300 of file connections.c.

{
  /* First determine the parameters.  We do not use the parameters
     passed to main() since in case nscd is started by running the
     dynamic linker this will not work.  Yes, this is not the usual
     case but nscd is part of glibc and we occasionally do this.  */
  size_t buflen = 1024;
  char *buf = alloca (buflen);
  size_t readlen = 0;
  int fd = open ("/proc/self/cmdline", O_RDONLY);
  if (fd == -1)
    {
      dbg_log (_("\
cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
              strerror (errno));

      paranoia = 0;
      return;
    }

  while (1)
    {
      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + readlen,
                                       buflen - readlen));
      if (n == -1)
       {
         dbg_log (_("\
cannot read /proc/self/cmdline: %s; disabling paranoia mode"),
                 strerror (errno));

         close (fd);
         paranoia = 0;
         return;
       }

      readlen += n;

      if (readlen < buflen)
       break;

      /* We might have to extend the buffer.  */
      size_t old_buflen = buflen;
      char *newp = extend_alloca (buf, buflen, 2 * buflen);
      buf = memmove (newp, buf, old_buflen);
    }

  close (fd);

  /* Parse the command line.  Worst case scenario: every two
     characters form one parameter (one character plus NUL).  */
  char **argv = alloca ((readlen / 2 + 1) * sizeof (argv[0]));
  int argc = 0;

  char *cp = buf;
  while (cp < buf + readlen)
    {
      argv[argc++] = cp;
      cp = (char *) rawmemchr (cp, '\0') + 1;
    }
  argv[argc] = NULL;

  /* Second, change back to the old user if we changed it.  */
  if (server_user != NULL)
    {
      if (setresuid (old_uid, old_uid, old_uid) != 0)
       {
         dbg_log (_("\
cannot change to old UID: %s; disabling paranoia mode"),
                 strerror (errno));

         paranoia = 0;
         return;
       }

      if (setresgid (old_gid, old_gid, old_gid) != 0)
       {
         dbg_log (_("\
cannot change to old GID: %s; disabling paranoia mode"),
                 strerror (errno));

         setuid (server_uid);
         paranoia = 0;
         return;
       }
    }

  /* Next change back to the old working directory.  */
  if (chdir (oldcwd) == -1)
    {
      dbg_log (_("\
cannot change to old working directory: %s; disabling paranoia mode"),
              strerror (errno));

      if (server_user != NULL)
       {
         setuid (server_uid);
         setgid (server_gid);
       }
      paranoia = 0;
      return;
    }

  /* Synchronize memory.  */
  int32_t certainly[lastdb];
  for (int cnt = 0; cnt < lastdb; ++cnt)
    if (dbs[cnt].enabled)
      {
       /* Make sure nobody keeps using the database.  */
       dbs[cnt].head->timestamp = 0;
       certainly[cnt] = dbs[cnt].head->nscd_certainly_running;
       dbs[cnt].head->nscd_certainly_running = 0;

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

  /* The preparations are done.  */
  execv ("/proc/self/exe", argv);

  /* If we come here, we will never be able to re-exec.  */
  dbg_log (_("re-exec failed: %s; disabling paranoia mode"),
          strerror (errno));

  if (server_user != NULL)
    {
      setuid (server_uid);
      setgid (server_gid);
    }
  if (chdir ("/") != 0)
    dbg_log (_("cannot change current working directory to \"/\": %s"),
            strerror (errno));
  paranoia = 0;

  /* Reenable the databases.  */
  time_t now = time (NULL);
  for (int cnt = 0; cnt < lastdb; ++cnt)
    if (dbs[cnt].enabled)
      {
       dbs[cnt].head->timestamp = now;
       dbs[cnt].head->nscd_certainly_running = certainly[cnt];
      }
}

Here is the call graph for this function:

static int restart_p ( time_t  now) [inline, static]

Definition at line 1765 of file connections.c.

{
  return (paranoia && readylist == NULL && nready == nthreads
         && now >= restart_time);
}
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:

static int verify_persistent_db ( void *  mem,
struct database_pers_head readhead,
int  dbnr 
) [static]

Definition at line 367 of file connections.c.

{
  assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb);

  time_t now = time (NULL);

  struct database_pers_head *head = mem;
  struct database_pers_head head_copy = *head;

  /* Check that the header that was read matches the head in the database.  */
  if (memcmp (head, readhead, sizeof (*head)) != 0)
    return 0;

  /* First some easy tests: make sure the database header is sane.  */
  if (head->version != DB_VERSION
      || head->header_size != sizeof (*head)
      /* We allow a timestamp to be one hour ahead of the current time.
        This should cover daylight saving time changes.  */
      || head->timestamp > now + 60 * 60 + 60
      || (head->gc_cycle & 1)
      || head->module == 0
      || (size_t) head->module > INT32_MAX / sizeof (ref_t)
      || (size_t) head->data_size > INT32_MAX - head->module * sizeof (ref_t)
      || head->first_free < 0
      || head->first_free > head->data_size
      || (head->first_free & BLOCK_ALIGN_M1) != 0
      || head->maxnentries < 0
      || head->maxnsearched < 0)
    return 0;

  uint8_t *usemap = calloc (head->first_free, 1);
  if (usemap == NULL)
    return 0;

  const char *data = (char *) &head->array[roundup (head->module,
                                              ALIGN / sizeof (ref_t))];

  nscd_ssize_t he_cnt = 0;
  for (nscd_ssize_t cnt = 0; cnt < head->module; ++cnt)
    {
      ref_t trail = head->array[cnt];
      ref_t work = trail;
      int tick = 0;

      while (work != ENDREF)
       {
         if (! check_use (data, head->first_free, usemap, use_he, work,
                        sizeof (struct hashentry)))
           goto fail;

         /* Now we know we can dereference the record.  */
         struct hashentry *here = (struct hashentry *) (data + work);

         ++he_cnt;

         /* Make sure the record is for this type of service.  */
         if (here->type >= LASTREQ
             || reqinfo[here->type].db != &dbs[dbnr])
           goto fail;

         /* Validate boolean field value.  */
         if (here->first != false && here->first != true)
           goto fail;

         if (here->len < 0)
           goto fail;

         /* Now the data.  */
         if (here->packet < 0
             || here->packet > head->first_free
             || here->packet + sizeof (struct datahead) > head->first_free)
           goto fail;

         struct datahead *dh = (struct datahead *) (data + here->packet);

         if (! check_use (data, head->first_free, usemap,
                        use_data | (here->first ? use_first : 0),
                        here->packet, dh->allocsize))
           goto fail;

         if (dh->allocsize < sizeof (struct datahead)
             || dh->recsize > dh->allocsize
             || (dh->notfound != false && dh->notfound != true)
             || (dh->usable != false && dh->usable != true))
           goto fail;

         if (here->key < here->packet + sizeof (struct datahead)
             || here->key > here->packet + dh->allocsize
             || here->key + here->len > here->packet + dh->allocsize)
           {
#if SEPARATE_KEY
             /* If keys can appear outside of data, this should be done
               instead.  But gc doesn't mark the data in that case.  */
             if (! check_use (data, head->first_free, usemap,
                            use_key | (here->first ? use_first : 0),
                            here->key, here->len))
#endif
              goto fail;
           }

         work = here->next;

         if (work == trail)
           /* A circular list, this must not happen.  */
           goto fail;
         if (tick)
           trail = ((struct hashentry *) (data + trail))->next;
         tick = 1 - tick;
       }
    }

  if (he_cnt != head->nentries)
    goto fail;

  /* See if all data and keys had at least one reference from
     he->first == true hashentry.  */
  for (ref_t idx = 0; idx < head->first_free; ++idx)
    {
#if SEPARATE_KEY
      if (usemap[idx] == use_key_begin)
       goto fail;
#endif
      if (usemap[idx] == use_data_begin)
       goto fail;
    }

  /* Finally, make sure the database hasn't changed since the first test.  */
  if (memcmp (mem, &head_copy, sizeof (*head)) != 0)
    goto fail;

  free (usemap);
  return 1;

fail:
  free (usemap);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

ssize_t writeall ( int  fd,
const void *  buf,
size_t  len 
)

Definition at line 255 of file connections.c.

{
  size_t n = len;
  ssize_t ret;
  do
    {
      ret = TEMP_FAILURE_RETRY (send (fd, buf, n, MSG_NOSIGNAL));
      if (ret <= 0)
       break;
      buf = (const char *) buf + ret;
      n -= ret;
    }
  while (n > 0);
  return ret < 0 ? ret : len - n;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void* xcalloc ( size_t  n,
size_t  s 
)

Definition at line 91 of file xmalloc.c.

{
  VOID *p;

  p = calloc (n, s);
  if (p == 0)
    p = fixup_null_alloc (n);
  return p;
}
void* xmalloc ( size_t  n)

Definition at line 77 of file xmalloc.c.

{
  VOID *p;

  p = malloc (n);
  if (p == 0)
    p = fixup_null_alloc (n);
  return p;
}
void* xrealloc ( void *  o,
size_t  n 
)

Variable Documentation

pthread_attr_t attr [static]

Definition at line 77 of file connections.c.

unsigned long int client_queued

Definition at line 246 of file connections.c.

struct fdlist* fdlist [static]

Definition at line 1452 of file connections.c.

Definition at line 240 of file connections.c.

Definition at line 224 of file connections.c.

__thread struct mem_in_flight

Definition at line 249 of file connections.c.

Definition at line 251 of file connections.c.

unsigned int nconns [static]

Definition at line 1711 of file connections.c.

unsigned long int nready [static]

Definition at line 1466 of file connections.c.

int nthreads = -1

Definition at line 222 of file connections.c.

struct fdlist* readylist [static]

Definition at line 1454 of file connections.c.

Definition at line 1459 of file connections.c.

Definition at line 1460 of file connections.c.

struct { ... } reqinfo[LASTREQ] [static]
Initial value:
{
  [GETPWBYNAME] = "GETPWBYNAME",
  [GETPWBYUID] = "GETPWBYUID",
  [GETGRBYNAME] = "GETGRBYNAME",
  [GETGRBYGID] = "GETGRBYGID",
  [GETHOSTBYNAME] = "GETHOSTBYNAME",
  [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
  [GETHOSTBYADDR] = "GETHOSTBYADDR",
  [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
  [SHUTDOWN] = "SHUTDOWN",
  [GETSTAT] = "GETSTAT",
  [INVALIDATE] = "INVALIDATE",
  [GETFDPW] = "GETFDPW",
  [GETFDGR] = "GETFDGR",
  [GETFDHST] = "GETFDHST",
  [GETAI] = "GETAI",
  [INITGROUPS] = "INITGROUPS",
  [GETSERVBYNAME] = "GETSERVBYNAME",
  [GETSERVBYPORT] = "GETSERVBYPORT",
  [GETFDSERV] = "GETFDSERV"
}

Definition at line 83 of file connections.c.

gid_t server_gid [static]

Definition at line 68 of file connections.c.

gid_t* server_groups [static]

Definition at line 71 of file connections.c.

int server_ngroups [static]

Definition at line 75 of file connections.c.

uid_t server_uid [static]

Definition at line 67 of file connections.c.

Definition at line 66 of file connections.c.

int sock [static]

Definition at line 227 of file connections.c.

time_t* starttime [static]

Definition at line 1773 of file connections.c.

Definition at line 70 of file connections.c.

Definition at line 69 of file connections.c.

clockid_t timeout_clock = CLOCK_REALTIME [static]

Definition at line 1463 of file connections.c.