/* Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk , 1997. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include "nis_intern.h" 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) { directory_obj *dir; __libc_lock_lock (mgrlock); dir = internal_cache_search (name); __libc_lock_unlock (mgrlock); return dir; } nis_error __cache_add (fd_result *fd) { 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; }