Update.
[glibc.git] / nis / nis_cache.c
blobe10b8c07fd3212743cdb82bb96293759ed6ca789
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <syslog.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/mman.h>
26 #include <rpcsvc/nis.h>
27 #include <rpcsvc/nis_cache.h>
28 #include <bits/libc-lock.h>
30 #include "nis_intern.h"
32 static struct timeval TIMEOUT = {10, 0};
34 #define HEADER_MAGIC 0x07021971
35 #define SPACER_MAGIC 0x07654321
37 #define CACHE_VERSION 0x00000001
39 struct cache_header
41 u_long magic; /* Magic number */
42 u_long vers; /* Cache file format version */
43 u_short tcp_port; /* tcp port of nis_cachemgr */
44 u_short udp_port; /* udp port of nis_cachemgr */
45 u_long entries; /* Number of cached objs. */
46 off_t used; /* How many space are used ? */
48 typedef struct cache_header cache_header;
50 struct cache_spacer
52 u_long magic; /* Magic number */
53 u_long hashval;
54 time_t ctime; /* time we have created this object */
55 time_t ttl; /* time to life of this object */
56 off_t next_offset;
58 typedef struct cache_spacer cache_spacer;
60 static int cache_fd = -1;
61 static int clnt_sock;
62 static caddr_t maddr = NULL;
63 static size_t msize;
64 static CLIENT *cache_clnt = NULL;
66 /* If there is no cachemgr, we shouldn't use NIS_SHARED_DIRCACHE, if
67 there is no NIS_SHARED_DIRCACHE, we couldn't use nis_cachemgr.
68 So, if the clnt_call to nis_cachemgr fails, we also close the cache file.
69 But another thread could read the cache => lock the cache_fd and cache_clnt
70 variables with the same lock */
71 __libc_lock_define_initialized (static, mgrlock)
73 /* close file handles and nis_cachemgr connection */
74 static void
75 __cache_close (void)
77 if (cache_fd != -1)
79 close (cache_fd);
80 cache_fd = -1;
82 if (cache_clnt != NULL)
84 clnt_destroy (cache_clnt);
85 close (clnt_sock);
86 cache_clnt = NULL;
90 /* open the cache file and connect to nis_cachemgr */
91 static bool_t
92 __cache_open (void)
94 struct sockaddr_in sin;
95 cache_header hptr;
97 if ((cache_fd = open (CACHEFILE, O_RDONLY)) == -1)
98 return FALSE;
100 if (read (cache_fd, &hptr, sizeof (cache_header)) == -1
101 || lseek (cache_fd, 0, SEEK_SET) < 0)
103 close (cache_fd);
104 cache_fd = -1;
105 return FALSE;
107 if (hptr.magic != HEADER_MAGIC)
109 close (cache_fd);
110 cache_fd = -1;
111 syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
112 return FALSE;
115 memset (&sin, '\0', sizeof (sin));
116 sin.sin_family = AF_INET;
117 clnt_sock = RPC_ANYSOCK;
118 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
119 sin.sin_port = htons (hptr.tcp_port);
120 cache_clnt = clnttcp_create (&sin, CACHEPROG, CACHE_VER_1, &clnt_sock, 0, 0);
121 if (cache_clnt == NULL)
123 close (cache_fd);
124 cache_fd = -1;
125 return FALSE;
127 /* If the program exists, close the socket */
128 if (fcntl (clnt_sock, F_SETFD, FD_CLOEXEC) == -1)
129 perror (_("fcntl: F_SETFD"));
130 return TRUE;
133 /* Ask the cache manager to update directory 'name'
134 for us (because the ttl has expired). */
135 static nis_error
136 __cache_refresh (nis_name name)
138 char clnt_res = 0;
139 nis_error result = NIS_SUCCESS;
141 __libc_lock_lock (mgrlock);
143 if (cache_clnt == NULL)
144 result = NIS_FAIL;
145 else if (clnt_call (cache_clnt, NIS_CACHE_REFRESH_ENTRY,
146 (xdrproc_t) xdr_wrapstring, (caddr_t) &name,
147 (xdrproc_t) xdr_void, &clnt_res, TIMEOUT)
148 != RPC_SUCCESS)
150 __cache_close ();
151 result = NIS_FAIL;
154 __libc_lock_unlock (mgrlock);
156 return result;
159 static nis_error
160 __cache_find (const_nis_name name, directory_obj **obj)
162 unsigned long hash;
163 struct cache_header *hptr;
164 struct cache_spacer *cs;
165 struct directory_obj *dir;
166 XDR xdrs;
167 caddr_t addr, ptr;
168 time_t now = time (NULL);
170 if (maddr == NULL)
171 return NIS_FAIL;
173 hash = __nis_hash (name, strlen(name));
174 hptr = (cache_header *)maddr;
175 if ((hptr->magic != HEADER_MAGIC) || (hptr->vers != CACHE_VERSION))
177 syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
178 return NIS_SYSTEMERROR;
180 cs = (cache_spacer *)(maddr + sizeof (cache_header));
181 while (cs->next_offset)
183 if (cs->magic != SPACER_MAGIC)
185 syslog (LOG_ERR, _("NIS+: cache file is corrupt!"));
186 return NIS_SYSTEMERROR;
188 if (cs->hashval == hash)
190 if ((now - cs->ctime) > cs->ttl)
191 return NIS_CACHEEXPIRED;
192 dir = calloc (1, sizeof (directory_obj));
193 addr = (caddr_t)cs + sizeof (cache_spacer);
194 xdrmem_create (&xdrs, addr, cs->next_offset, XDR_DECODE);
195 xdr_directory_obj (&xdrs, dir);
196 xdr_destroy (&xdrs);
197 *obj = dir;
198 return NIS_SUCCESS;
200 ptr = (caddr_t)cs;
201 ptr += cs->next_offset + sizeof (struct cache_spacer);
202 cs = (struct cache_spacer *)ptr;
204 return NIS_NOTFOUND;
207 static directory_obj *
208 internal_cache_search (const_nis_name name)
210 directory_obj *dir;
211 nis_error res;
212 int second_refresh = 0;
213 struct stat s;
215 if (cache_fd == -1)
216 if (__cache_open () == FALSE)
217 return NULL;
219 again:
220 /* This lock is for nis_cachemgr, so it couldn't write a new cache
221 file if we reading it */
222 if (__nis_lock_cache () == -1)
223 return NULL;
225 if (maddr != NULL)
226 munmap (maddr, msize);
227 if (fstat (cache_fd, &s) < 0)
228 maddr = MAP_FAILED;
229 else
231 msize = s.st_size;
232 maddr = mmap (0, msize, PROT_READ, MAP_SHARED, cache_fd, 0);
234 if (maddr == MAP_FAILED)
236 __nis_unlock_cache ();
237 return NULL;
240 res = __cache_find (name, &dir);
242 munmap (maddr, msize);
243 maddr = NULL;
244 /* Allow nis_cachemgr to write a new cachefile */
245 __nis_unlock_cache ();
247 switch(res)
249 case NIS_CACHEEXPIRED:
250 if (second_refresh)
252 __cache_close ();
253 syslog (LOG_WARNING,
254 _("NIS+: nis_cachemgr failed to refresh object for us"));
255 return NULL;
257 ++second_refresh;
258 if (__cache_refresh ((char *) name) != NIS_SUCCESS)
259 return NULL;
260 goto again;
261 break;
262 case NIS_SUCCESS:
263 return dir;
264 default:
265 return NULL;
269 directory_obj *
270 __cache_search (const_nis_name name)
272 directory_obj *dir;
274 __libc_lock_lock (mgrlock);
276 dir = internal_cache_search (name);
278 __libc_lock_unlock (mgrlock);
280 return dir;
283 nis_error
284 __cache_add (fd_result *fd)
286 char clnt_res = 0;
287 nis_error result = NIS_SUCCESS;
289 __libc_lock_lock (mgrlock);
291 if (cache_clnt == NULL)
292 if (__cache_open () == FALSE)
293 result = NIS_FAIL;
295 if (cache_clnt != NULL &&
296 (clnt_call (cache_clnt, NIS_CACHE_ADD_ENTRY, (xdrproc_t) xdr_fd_result,
297 (caddr_t)fd, (xdrproc_t) xdr_void, &clnt_res, TIMEOUT)
298 != RPC_SUCCESS))
300 __cache_close ();
301 result = NIS_RPCERROR;
304 __libc_lock_unlock (mgrlock);
306 return result;