about summary refs log tree commit diff
path: root/nis/nis_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'nis/nis_cache.c')
-rw-r--r--nis/nis_cache.c275
1 files changed, 269 insertions, 6 deletions
diff --git a/nis/nis_cache.c b/nis/nis_cache.c
index 8e1d583003..4c0ea7b186 100644
--- a/nis/nis_cache.c
+++ b/nis/nis_cache.c
@@ -20,25 +20,288 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <rpcsvc/nis.h>
-#include <rpcsvc/nislib.h>
 #include <rpcsvc/nis_cache.h>
 #include <bits/libc-lock.h>
 
 #include "nis_intern.h"
 
-/* XXX Only dummy functions in the moment. The real implementation
-       will follow, if we have a working nis_cachemgr */
+static struct timeval TIMEOUT = {10, 0};
+
+#define HEADER_MAGIC  0x07021971
+#define SPACER_MAGIC  0x07654321
+
+#define CACHE_VERSION 0x00000001
+
+struct cache_header
+{
+  u_long magic;               /* Magic number */
+  u_long vers;                /* Cache file format version */
+  u_short tcp_port;           /* tcp port of nis_cachemgr */
+  u_short udp_port;           /* udp port of nis_cachemgr */
+  u_long entries;             /* Number of cached objs. */
+  off_t used;                 /* How many space are used ? */
+};
+typedef struct cache_header cache_header;
+
+struct cache_spacer
+{
+  u_long magic;                /* Magic number */
+  u_long hashval;
+  time_t ctime;                /* time we have created this object */
+  time_t ttl;                  /* time to life of this object */
+  off_t next_offset;
+};
+typedef struct cache_spacer cache_spacer;
+
+static int cache_fd = -1;
+static int clnt_sock;
+static caddr_t maddr = NULL;
+static size_t msize;
+static CLIENT *cache_clnt = NULL;
+
+/* If there is no cachemgr, we shouldn't use NIS_SHARED_DIRCACHE, if
+   there is no NIS_SHARED_DIRCACHE, we couldn't use nis_cachemgr.
+   So, if the clnt_call to nis_cachemgr fails, we also close the cache file.
+   But another thread could read the cache => lock the cache_fd and cache_clnt
+   variables with the same lock */
+__libc_lock_define_initialized (static, mgrlock)
+
+/* close file handles and nis_cachemgr connection */
+static void
+__cache_close (void)
+{
+  if (cache_fd != -1)
+    {
+      close (cache_fd);
+      cache_fd = -1;
+    }
+  if (cache_clnt != NULL)
+    {
+      clnt_destroy (cache_clnt);
+      close (clnt_sock);
+      cache_clnt = NULL;
+    }
+}
+
+/* open the cache file and connect to nis_cachemgr */
+static bool_t
+__cache_open (void)
+{
+  struct sockaddr_in sin;
+  cache_header hptr;
+
+  if ((cache_fd = open (CACHEFILE, O_RDONLY)) == -1)
+    return FALSE;
+
+  if (read (cache_fd, &hptr, sizeof (cache_header)) == -1
+      || lseek (cache_fd, 0, SEEK_SET) < 0)
+    {
+      close (cache_fd);
+      cache_fd = -1;
+      return FALSE;
+    }
+  if (hptr.magic != HEADER_MAGIC)
+    {
+      close (cache_fd);
+      cache_fd = -1;
+      syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
+      return FALSE;
+    }
+
+  memset (&sin, '\0', sizeof (sin));
+  sin.sin_family = AF_INET;
+  clnt_sock = RPC_ANYSOCK;
+  sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+  sin.sin_port = htons (hptr.tcp_port);
+  cache_clnt = clnttcp_create (&sin, CACHEPROG, CACHE_VER_1, &clnt_sock, 0, 0);
+  if (cache_clnt == NULL)
+    {
+      close (cache_fd);
+      cache_fd = -1;
+      return FALSE;
+    }
+  /* If the program exists, close the socket */
+  if (fcntl (clnt_sock, F_SETFD, FD_CLOEXEC) == -1)
+    perror (_("fcntl: F_SETFD"));
+  return TRUE;
+}
+
+/* Ask the cache manager to update directory 'name'
+   for us (because the ttl has expired). */
+static nis_error
+__cache_refresh (nis_name name)
+{
+  char clnt_res = 0;
+  nis_error result = NIS_SUCCESS;
+
+  __libc_lock_lock (mgrlock);
+
+  if (cache_clnt == NULL)
+    result = NIS_FAIL;
+  else if (clnt_call (cache_clnt, NIS_CACHE_REFRESH_ENTRY,
+		      (xdrproc_t) xdr_wrapstring, (caddr_t) name,
+		      (xdrproc_t) xdr_void, &clnt_res, TIMEOUT)
+	   != RPC_SUCCESS)
+    {
+      __cache_close ();
+      result = NIS_FAIL;
+    }
+
+  __libc_lock_unlock (mgrlock);
+
+  return result;
+}
+
+static nis_error
+__cache_find (const_nis_name name, directory_obj **obj)
+{
+  unsigned long hash;
+  struct cache_header *hptr;
+  struct cache_spacer *cs;
+  struct directory_obj *dir;
+  XDR xdrs;
+  caddr_t addr, ptr;
+  time_t now = time (NULL);
+
+  if (maddr == NULL)
+    return NIS_FAIL;
+
+  hash = __nis_hash (name, strlen(name));
+  hptr = (cache_header *)maddr;
+  if ((hptr->magic != HEADER_MAGIC) || (hptr->vers != CACHE_VERSION))
+    {
+      syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
+      return NIS_SYSTEMERROR;
+    }
+  cs = (cache_spacer *)(maddr + sizeof (cache_header));
+  while (cs->next_offset)
+    {
+      if (cs->magic != SPACER_MAGIC)
+	{
+	  syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
+	  return NIS_SYSTEMERROR;
+	}
+      if (cs->hashval == hash)
+	{
+	  if ((now - cs->ctime) > cs->ttl)
+	    return NIS_CACHEEXPIRED;
+	  dir = calloc (1, sizeof (directory_obj));
+	  addr = (caddr_t)cs + sizeof (cache_spacer);
+	  xdrmem_create (&xdrs, addr, cs->next_offset, XDR_DECODE);
+	  xdr_directory_obj (&xdrs, dir);
+	  xdr_destroy (&xdrs);
+	  *obj = dir;
+	  return NIS_SUCCESS;
+	}
+      ptr = (caddr_t)cs;
+      ptr += cs->next_offset + sizeof (struct cache_spacer);
+      cs = (struct cache_spacer *)ptr;
+    }
+  return NIS_NOTFOUND;
+}
+
+static directory_obj *
+internal_cache_search (const_nis_name name)
+{
+  directory_obj *dir;
+  nis_error res;
+  int second_refresh = 0;
+  struct stat s;
+
+  if (cache_fd == -1)
+    if (__cache_open () == FALSE)
+      return NULL;
+
+ again:
+  /* This lock is for nis_cachemgr, so it couldn't write a new cache
+     file if we reading it */
+  if (__nis_lock_cache () == -1)
+    return NULL;
+
+  if (maddr != NULL)
+    munmap (maddr, msize);
+  if (fstat (cache_fd, &s) < 0)
+    maddr = MAP_FAILED;
+  else
+    {
+      msize = s.st_size;
+      maddr = mmap (0, msize, PROT_READ, MAP_SHARED, cache_fd, 0);
+    }
+  if (maddr == MAP_FAILED)
+    {
+      __nis_unlock_cache ();
+      return NULL;
+    }
+
+  res = __cache_find (name, &dir);
+
+  munmap (maddr, msize);
+  maddr = NULL;
+  /* Allow nis_cachemgr to write a new cachefile */
+  __nis_unlock_cache ();
+
+  switch(res)
+    {
+    case NIS_CACHEEXPIRED:
+      if (second_refresh)
+	{
+	  __cache_close ();
+	  syslog (LOG_WARNING,
+		  _("NIS+: nis_cachemgr failed to refresh object for us"));
+	  return NULL;
+	}
+      ++second_refresh;
+      if (__cache_refresh ((char *) name) != NIS_SUCCESS)
+	return NULL;
+      goto again;
+      break;
+    case NIS_SUCCESS:
+      return dir;
+    default:
+      return NULL;
+    }
+}
+
 directory_obj *
 __cache_search (const_nis_name name)
 {
-  return NULL;
+  directory_obj *dir;
+
+  __libc_lock_lock (mgrlock);
+
+  dir = internal_cache_search (name);
+
+  __libc_lock_unlock (mgrlock);
+
+  return dir;
 }
 
-nis_error 
+nis_error
 __cache_add (fd_result *fd)
 {
-  return NIS_FAIL;
+  char clnt_res = 0;
+  nis_error result = NIS_SUCCESS;
+
+  __libc_lock_lock (mgrlock);
+
+  if (cache_clnt == NULL)
+    if (__cache_open () == FALSE)
+      result = NIS_FAIL;
+
+  if (cache_clnt != NULL &&
+      (clnt_call (cache_clnt, NIS_CACHE_ADD_ENTRY, (xdrproc_t) xdr_fd_result,
+		  (caddr_t)fd, (xdrproc_t) xdr_void, &clnt_res, TIMEOUT)
+       != RPC_SUCCESS))
+    {
+      __cache_close ();
+      result = NIS_RPCERROR;
+    }
+
+  __libc_lock_unlock (mgrlock);
+
+  return result;
 }