Update.
[glibc.git] / nscd / pwdcache.c
blob12d18ddacc916743b0adca956d595cc7b96175d8
1 /* Copyright (c) 1998 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
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 <errno.h>
21 #include <malloc.h>
22 #include <pthread.h>
23 #include <pwd.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <rpcsvc/nis.h>
27 #include <sys/types.h>
29 #include "dbg_log.h"
30 #include "nscd.h"
32 static unsigned long int modulo = 211;
33 static unsigned long int postimeout = 600;
34 static unsigned long int negtimeout = 20;
36 static unsigned long int poshit = 0;
37 static unsigned long int posmiss = 0;
38 static unsigned long int neghit = 0;
39 static unsigned long int negmiss = 0;
41 struct pwdhash
43 time_t create;
44 struct pwdhash *next;
45 struct passwd *pwd;
47 typedef struct pwdhash pwdhash;
49 struct uidhash
51 struct uidhash *next;
52 struct passwd *pwptr;
54 typedef struct uidhash uidhash;
56 struct neghash
58 time_t create;
59 struct neghash *next;
60 char *key;
62 typedef struct neghash neghash;
64 static pwdhash *pwdtbl;
65 static uidhash *uidtbl;
66 static neghash *negtbl;
68 static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
69 static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
71 static void *pwdtable_update (void *);
72 static void *negtable_update (void *);
74 void
75 get_pw_stat (stat_response_header *stat)
77 stat->pw_poshit = poshit;
78 stat->pw_posmiss = posmiss;
79 stat->pw_neghit = neghit;
80 stat->pw_negmiss = negmiss;
81 stat->pw_size = modulo;
82 stat->pw_posttl = postimeout;
83 stat->pw_negttl = negtimeout;
86 void
87 set_pwd_modulo (unsigned long int mod)
89 modulo = mod;
92 void
93 set_pos_pwd_ttl (unsigned long int ttl)
95 postimeout = ttl;
98 void
99 set_neg_pwd_ttl (unsigned long int ttl)
101 negtimeout = ttl;
105 cache_pwdinit ()
107 pthread_attr_t attr;
108 pthread_t thread;
110 pwdtbl = calloc (modulo, sizeof (pwdhash));
111 if (pwdtbl == NULL)
112 return -1;
113 uidtbl = calloc (modulo, sizeof (uidhash));
114 if (uidtbl == NULL)
115 return -1;
116 negtbl = calloc (modulo, sizeof (neghash));
117 if (negtbl == NULL)
118 return -1;
120 pthread_attr_init (&attr);
121 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
123 pthread_create (&thread, NULL, pwdtable_update, &attr);
124 pthread_create (&thread, NULL, negtable_update, &attr);
126 pthread_attr_destroy (&attr);
128 return 0;
131 static struct passwd *
132 save_pwd (struct passwd *src)
134 struct passwd *dest;
135 size_t name_len = strlen (src->pw_name) + 1;
136 size_t passwd_len = strlen (src->pw_gecos) + 1;
137 size_t gecos_len = strlen (src->pw_dir) + 1;
138 size_t dir_len = strlen (src->pw_dir) + 1;
139 size_t shell_len = strlen (src->pw_shell) + 1;
140 char *cp;
142 dest = malloc (sizeof (struct passwd)
143 + name_len + passwd_len + gecos_len + dir_len + shell_len);
144 if (dest == NULL)
145 return NULL;
147 cp = (char *) (dest + 1);
148 dest->pw_name = cp;
149 cp = mempcpy (cp, src->pw_name, name_len) + 1;
150 dest->pw_passwd = cp;
151 cp = mempcpy (cp, src->pw_passwd, passwd_len) + 1;
152 dest->pw_uid = src->pw_uid;
153 dest->pw_gid = src->pw_gid;
154 dest->pw_gecos = cp;
155 cp = mempcpy (cp, src->pw_gecos, gecos_len) + 1;
156 dest->pw_dir = cp;
157 cp = mempcpy (cp, src->pw_dir, dir_len) + 1;
158 dest->pw_shell = cp;
159 mempcpy (cp, src->pw_shell, shell_len);
161 return dest;
164 static void
165 free_pwd (struct passwd *src)
167 free (src);
170 static int
171 add_cache (struct passwd *pwd)
173 pwdhash *work;
174 uidhash *uidwork;
175 unsigned long int hash = __nis_hash (pwd->pw_name,
176 strlen (pwd->pw_name)) % modulo;
178 if (debug_flag)
179 dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name);
181 work = &pwdtbl[hash];
183 if (pwdtbl[hash].pwd == NULL)
184 pwdtbl[hash].pwd = save_pwd (pwd);
185 else
187 while (work->next != NULL)
188 work = work->next;
190 work->next = calloc (1, sizeof (pwdhash));
191 work->next->pwd = save_pwd (pwd);
192 work = work->next;
194 /* Set a pointer from the pwuid hash table to the pwname hash table */
195 time (&work->create);
196 uidwork = &uidtbl[pwd->pw_uid % modulo];
197 if (uidwork->pwptr == NULL)
198 uidwork->pwptr = work->pwd;
199 else
201 while (uidwork->next != NULL)
202 uidwork = uidwork->next;
204 uidwork->next = calloc (1, sizeof (uidhash));
205 uidwork->next->pwptr = work->pwd;
207 return 0;
210 static struct passwd *
211 cache_search_name (const char *name)
213 pwdhash *work;
214 unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
216 work = &pwdtbl[hash];
218 while (work->pwd != NULL)
220 if (strcmp (work->pwd->pw_name, name) == 0)
221 return work->pwd;
222 if (work->next != NULL)
223 work = work->next;
224 else
225 return NULL;
227 return NULL;
230 static struct passwd *
231 cache_search_uid (uid_t uid)
233 uidhash *work;
235 work = &uidtbl[uid % modulo];
237 while (work->pwptr != NULL)
239 if (work->pwptr->pw_uid == uid)
240 return work->pwptr;
241 if (work->next != NULL)
242 work = work->next;
243 else
244 return NULL;
246 return NULL;
249 static int
250 add_negcache (char *key)
252 neghash *work;
253 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
255 if (debug_flag)
256 dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash);
258 work = &negtbl[hash];
260 if (negtbl[hash].key == NULL)
262 negtbl[hash].key = strdup (key);
263 negtbl[hash].next = NULL;
265 else
267 while (work->next != NULL)
268 work = work->next;
270 work->next = calloc (1, sizeof (neghash));
271 work->next->key = strdup (key);
272 work = work->next;
275 time (&work->create);
277 return 0;
280 static int
281 cache_search_neg (const char *key)
283 neghash *work;
284 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
286 if (debug_flag)
287 dbg_log (_("pwd_cache_search_neg (%s|%ld)"), key, hash);
289 work = &negtbl[hash];
291 while (work->key != NULL)
293 if (strcmp (work->key, key) == 0)
294 return 1;
295 if (work->next != NULL)
296 work = work->next;
297 else
298 return 0;
300 return 0;
303 void *
304 cache_getpwnam (void *v_param)
306 struct passwd *pwd;
307 param_t *param = (param_t *)v_param;
309 pthread_rwlock_rdlock (&pwdlock);
310 pwd = cache_search_name (param->key);
312 /* I don't like it to hold the read only lock longer, but it is
313 necessary to avoid to much malloc/free/strcpy. */
315 if (pwd != NULL)
317 if (debug_flag)
318 dbg_log (_("Found \"%s\" in cache !"), param->key);
320 ++poshit;
321 pw_send_answer (param->conn, pwd);
322 close_socket (param->conn);
324 pthread_rwlock_unlock (&pwdlock);
326 else
328 int status;
329 int buflen = 1024;
330 char *buffer = calloc (1, buflen);
331 struct passwd resultbuf;
333 if (debug_flag)
334 dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
336 pthread_rwlock_unlock (&pwdlock);
338 pthread_rwlock_rdlock (&neglock);
339 status = cache_search_neg (param->key);
340 pthread_rwlock_unlock (&neglock);
342 if (status == 0)
344 while (buffer != NULL
345 && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
346 != 0)
347 && errno == ERANGE)
349 errno = 0;
350 buflen += 1024;
351 buffer = realloc (buffer, buflen);
354 if (buffer != NULL && pwd != NULL)
356 struct passwd *tmp;
358 ++posmiss;
359 pthread_rwlock_wrlock (&pwdlock);
360 /* While we are waiting on the lock, somebody else could
361 add this entry. */
362 tmp = cache_search_name (param->key);
363 if (tmp == NULL)
364 add_cache (pwd);
365 pthread_rwlock_unlock (&pwdlock);
367 else
369 ++negmiss;
370 pthread_rwlock_wrlock (&neglock);
371 add_negcache (param->key);
372 pthread_rwlock_unlock (&neglock);
375 else
376 ++neghit;
377 pw_send_answer (param->conn, pwd);
378 close_socket (param->conn);
379 if (buffer != NULL)
380 free (buffer);
382 free (param->key);
383 free (param);
384 return NULL;
387 void *
388 cache_pw_disabled (void *v_param)
390 param_t *param = (param_t *)v_param;
392 if (debug_flag)
393 dbg_log (_("\tpasswd cache is disabled\n"));
395 pw_send_disabled (param->conn);
396 return NULL;
399 void *
400 cache_getpwuid (void *v_param)
402 param_t *param = (param_t *)v_param;
403 struct passwd *pwd, resultbuf;
404 uid_t uid = strtol (param->key, NULL, 10);
406 pthread_rwlock_rdlock (&pwdlock);
407 pwd = cache_search_uid (uid);
409 /* I don't like it to hold the read only lock longer, but it is
410 necessary to avoid to much malloc/free/strcpy. */
412 if (pwd != NULL)
414 if (debug_flag)
415 dbg_log (_("Found \"%d\" in cache !"), uid);
417 ++poshit;
418 pw_send_answer (param->conn, pwd);
419 close_socket (param->conn);
421 pthread_rwlock_unlock (&pwdlock);
423 else
425 int buflen = 1024;
426 char *buffer = malloc (buflen);
427 int status;
429 if (debug_flag)
430 dbg_log (_("Doesn't found \"%d\" in cache !"), uid);
432 pthread_rwlock_unlock (&pwdlock);
434 pthread_rwlock_rdlock (&neglock);
435 status = cache_search_neg (param->key);
436 pthread_rwlock_unlock (&neglock);
438 if (status == 0)
440 while (buffer != NULL
441 && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
442 && errno == ERANGE)
444 errno = 0;
445 buflen += 1024;
446 buffer = realloc (buffer, buflen);
449 if (buffer != NULL && pwd != NULL)
451 struct passwd *tmp;
453 ++posmiss;
454 pthread_rwlock_wrlock (&pwdlock);
455 /* While we are waiting on the lock, somebody else could
456 add this entry. */
457 tmp = cache_search_uid (uid);
458 if (tmp == NULL)
459 add_cache (pwd);
460 pthread_rwlock_unlock (&pwdlock);
462 else
464 ++negmiss;
465 pthread_rwlock_wrlock (&neglock);
466 add_negcache (param->key);
467 pthread_rwlock_unlock (&neglock);
470 else
471 ++neghit;
473 pw_send_answer (param->conn, pwd);
474 close_socket (param->conn);
475 if (buffer != NULL)
476 free (buffer);
478 free (param->key);
479 free (param);
480 return NULL;
483 static void *
484 pwdtable_update (void *v)
486 time_t now;
487 int i;
489 sleep (20);
491 while (!do_shutdown)
493 if (debug_flag > 2)
494 dbg_log (_("(pwdtable_update) Wait for write lock!"));
496 pthread_rwlock_wrlock (&pwdlock);
498 if (debug_flag > 2)
499 dbg_log (_("(pwdtable_update) Have write lock"));
501 time (&now);
502 for (i = 0; i < modulo; ++i)
504 pwdhash *work = &pwdtbl[i];
506 while (work && work->pwd)
508 if ((now - work->create) >= postimeout)
510 uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo];
512 if (debug_flag)
513 dbg_log (_("Give \"%s\" free"), work->pwd->pw_name);
515 while (uh != NULL && uh->pwptr)
517 if (uh->pwptr->pw_uid == work->pwd->pw_uid)
519 if (debug_flag)
520 dbg_log (_("Give uid for \"%s\" free"),
521 work->pwd->pw_name);
522 if (uh->next != NULL)
524 uidhash *tmp = uh->next;
525 uh->pwptr = tmp->pwptr;
526 uh->next = tmp->next;
527 free (tmp);
529 else
530 uh->pwptr = NULL;
532 uh = uh->next;
535 free_pwd (work->pwd);
536 if (work->next != NULL)
538 pwdhash *tmp = work->next;
539 work->create = tmp->create;
540 work->next = tmp->next;
541 work->pwd = tmp->pwd;
542 free (tmp);
544 else
545 work->pwd = NULL;
547 work = work->next;
550 if (debug_flag > 2)
551 dbg_log (_("(pwdtable_update) Release wait lock"));
552 pthread_rwlock_unlock (&pwdlock);
553 sleep (20);
555 return NULL;
558 static void *
559 negtable_update (void *v)
561 time_t now;
562 int i;
564 sleep (30);
566 while (!do_shutdown)
568 if (debug_flag > 2)
569 dbg_log (_("(negpwdtable_update) Wait for write lock!"));
571 pthread_rwlock_wrlock (&neglock);
573 if (debug_flag > 2)
574 dbg_log (_("(negpwdtable_update) Have write lock"));
576 time (&now);
577 for (i = 0; i < modulo; ++i)
579 neghash *work = &negtbl[i];
581 while (work && work->key)
583 if ((now - work->create) >= negtimeout)
585 if (debug_flag)
586 dbg_log (_("Give \"%s\" free"), work->key);
588 free (work->key);
590 if (work->next != NULL)
592 neghash *tmp = work->next;
593 work->create = tmp->create;
594 work->next = tmp->next;
595 work->key = tmp->key;
596 free (tmp);
598 else
599 work->key = NULL;
601 work = work->next;
604 if (debug_flag > 2)
605 dbg_log (_("(negpwdtable_update) Release wait lock"));
607 pthread_rwlock_unlock (&neglock);
608 sleep (10);
610 return NULL;