]> sourceware.org Git - glibc.git/blob - nscd/pwdcache.c
Update.
[glibc.git] / nscd / pwdcache.c
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.
4
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.
9
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.
14
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. */
19
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>
28
29 #include "dbg_log.h"
30 #include "nscd.h"
31
32 static unsigned long int modulo = 211;
33 static unsigned long int postimeout = 600;
34 static unsigned long int negtimeout = 20;
35
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;
40
41 struct pwdhash
42 {
43 time_t create;
44 struct pwdhash *next;
45 struct passwd *pwd;
46 };
47 typedef struct pwdhash pwdhash;
48
49 struct uidhash
50 {
51 struct uidhash *next;
52 struct passwd *pwptr;
53 };
54 typedef struct uidhash uidhash;
55
56 struct neghash
57 {
58 time_t create;
59 struct neghash *next;
60 char *key;
61 };
62 typedef struct neghash neghash;
63
64 static pwdhash *pwdtbl;
65 static uidhash *uidtbl;
66 static neghash *negtbl;
67
68 static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
69 static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
70
71 static void *pwdtable_update (void *);
72 static void *negtable_update (void *);
73
74 void
75 get_pw_stat (stat_response_header *stat)
76 {
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;
84 }
85
86 void
87 set_pwd_modulo (unsigned long int mod)
88 {
89 modulo = mod;
90 }
91
92 void
93 set_pos_pwd_ttl (unsigned long int ttl)
94 {
95 postimeout = ttl;
96 }
97
98 void
99 set_neg_pwd_ttl (unsigned long int ttl)
100 {
101 negtimeout = ttl;
102 }
103
104 int
105 cache_pwdinit ()
106 {
107 pthread_attr_t attr;
108 pthread_t thread;
109
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;
119
120 pthread_attr_init (&attr);
121 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
122
123 pthread_create (&thread, NULL, pwdtable_update, &attr);
124 pthread_create (&thread, NULL, negtable_update, &attr);
125
126 pthread_attr_destroy (&attr);
127
128 return 0;
129 }
130
131 static struct passwd *
132 save_pwd (struct passwd *src)
133 {
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;
141
142 dest = malloc (sizeof (struct passwd)
143 + name_len + passwd_len + gecos_len + dir_len + shell_len);
144 if (dest == NULL)
145 return NULL;
146
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);
160
161 return dest;
162 }
163
164 static void
165 free_pwd (struct passwd *src)
166 {
167 free (src);
168 }
169
170 static int
171 add_cache (struct passwd *pwd)
172 {
173 pwdhash *work;
174 uidhash *uidwork;
175 unsigned long int hash = __nis_hash (pwd->pw_name,
176 strlen (pwd->pw_name)) % modulo;
177
178 if (debug_flag)
179 dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name);
180
181 work = &pwdtbl[hash];
182
183 if (pwdtbl[hash].pwd == NULL)
184 pwdtbl[hash].pwd = save_pwd (pwd);
185 else
186 {
187 while (work->next != NULL)
188 work = work->next;
189
190 work->next = calloc (1, sizeof (pwdhash));
191 work->next->pwd = save_pwd (pwd);
192 work = work->next;
193 }
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
200 {
201 while (uidwork->next != NULL)
202 uidwork = uidwork->next;
203
204 uidwork->next = calloc (1, sizeof (uidhash));
205 uidwork->next->pwptr = work->pwd;
206 }
207 return 0;
208 }
209
210 static struct passwd *
211 cache_search_name (const char *name)
212 {
213 pwdhash *work;
214 unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
215
216 work = &pwdtbl[hash];
217
218 while (work->pwd != NULL)
219 {
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;
226 }
227 return NULL;
228 }
229
230 static struct passwd *
231 cache_search_uid (uid_t uid)
232 {
233 uidhash *work;
234
235 work = &uidtbl[uid % modulo];
236
237 while (work->pwptr != NULL)
238 {
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;
245 }
246 return NULL;
247 }
248
249 static int
250 add_negcache (char *key)
251 {
252 neghash *work;
253 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
254
255 if (debug_flag)
256 dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash);
257
258 work = &negtbl[hash];
259
260 if (negtbl[hash].key == NULL)
261 {
262 negtbl[hash].key = strdup (key);
263 negtbl[hash].next = NULL;
264 }
265 else
266 {
267 while (work->next != NULL)
268 work = work->next;
269
270 work->next = calloc (1, sizeof (neghash));
271 work->next->key = strdup (key);
272 work = work->next;
273 }
274
275 time (&work->create);
276
277 return 0;
278 }
279
280 static int
281 cache_search_neg (const char *key)
282 {
283 neghash *work;
284 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
285
286 if (debug_flag)
287 dbg_log (_("pwd_cache_search_neg (%s|%ld)"), key, hash);
288
289 work = &negtbl[hash];
290
291 while (work->key != NULL)
292 {
293 if (strcmp (work->key, key) == 0)
294 return 1;
295 if (work->next != NULL)
296 work = work->next;
297 else
298 return 0;
299 }
300 return 0;
301 }
302
303 void *
304 cache_getpwnam (void *v_param)
305 {
306 struct passwd *pwd;
307 param_t *param = (param_t *)v_param;
308
309 pthread_rwlock_rdlock (&pwdlock);
310 pwd = cache_search_name (param->key);
311
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. */
314
315 if (pwd != NULL)
316 {
317 if (debug_flag)
318 dbg_log (_("Found \"%s\" in cache !"), param->key);
319
320 ++poshit;
321 pw_send_answer (param->conn, pwd);
322 close_socket (param->conn);
323
324 pthread_rwlock_unlock (&pwdlock);
325 }
326 else
327 {
328 int status;
329 int buflen = 1024;
330 char *buffer = calloc (1, buflen);
331 struct passwd resultbuf;
332
333 if (debug_flag)
334 dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
335
336 pthread_rwlock_unlock (&pwdlock);
337
338 pthread_rwlock_rdlock (&neglock);
339 status = cache_search_neg (param->key);
340 pthread_rwlock_unlock (&neglock);
341
342 if (status == 0)
343 {
344 while (buffer != NULL
345 && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
346 != 0)
347 && errno == ERANGE)
348 {
349 errno = 0;
350 buflen += 1024;
351 buffer = realloc (buffer, buflen);
352 }
353
354 if (buffer != NULL && pwd != NULL)
355 {
356 struct passwd *tmp;
357
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);
366 }
367 else
368 {
369 ++negmiss;
370 pthread_rwlock_wrlock (&neglock);
371 add_negcache (param->key);
372 pthread_rwlock_unlock (&neglock);
373 }
374 }
375 else
376 ++neghit;
377 pw_send_answer (param->conn, pwd);
378 close_socket (param->conn);
379 if (buffer != NULL)
380 free (buffer);
381 }
382 free (param->key);
383 free (param);
384 return NULL;
385 }
386
387 void *
388 cache_pw_disabled (void *v_param)
389 {
390 param_t *param = (param_t *)v_param;
391
392 if (debug_flag)
393 dbg_log (_("\tpasswd cache is disabled\n"));
394
395 pw_send_disabled (param->conn);
396 return NULL;
397 }
398
399 void *
400 cache_getpwuid (void *v_param)
401 {
402 param_t *param = (param_t *)v_param;
403 struct passwd *pwd, resultbuf;
404 uid_t uid = strtol (param->key, NULL, 10);
405
406 pthread_rwlock_rdlock (&pwdlock);
407 pwd = cache_search_uid (uid);
408
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. */
411
412 if (pwd != NULL)
413 {
414 if (debug_flag)
415 dbg_log (_("Found \"%d\" in cache !"), uid);
416
417 ++poshit;
418 pw_send_answer (param->conn, pwd);
419 close_socket (param->conn);
420
421 pthread_rwlock_unlock (&pwdlock);
422 }
423 else
424 {
425 int buflen = 1024;
426 char *buffer = malloc (buflen);
427 int status;
428
429 if (debug_flag)
430 dbg_log (_("Doesn't found \"%d\" in cache !"), uid);
431
432 pthread_rwlock_unlock (&pwdlock);
433
434 pthread_rwlock_rdlock (&neglock);
435 status = cache_search_neg (param->key);
436 pthread_rwlock_unlock (&neglock);
437
438 if (status == 0)
439 {
440 while (buffer != NULL
441 && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
442 && errno == ERANGE)
443 {
444 errno = 0;
445 buflen += 1024;
446 buffer = realloc (buffer, buflen);
447 }
448
449 if (buffer != NULL && pwd != NULL)
450 {
451 struct passwd *tmp;
452
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);
461 }
462 else
463 {
464 ++negmiss;
465 pthread_rwlock_wrlock (&neglock);
466 add_negcache (param->key);
467 pthread_rwlock_unlock (&neglock);
468 }
469 }
470 else
471 ++neghit;
472
473 pw_send_answer (param->conn, pwd);
474 close_socket (param->conn);
475 if (buffer != NULL)
476 free (buffer);
477 }
478 free (param->key);
479 free (param);
480 return NULL;
481 }
482
483 static void *
484 pwdtable_update (void *v)
485 {
486 time_t now;
487 int i;
488
489 sleep (20);
490
491 while (!do_shutdown)
492 {
493 if (debug_flag > 2)
494 dbg_log (_("(pwdtable_update) Wait for write lock!"));
495
496 pthread_rwlock_wrlock (&pwdlock);
497
498 if (debug_flag > 2)
499 dbg_log (_("(pwdtable_update) Have write lock"));
500
501 time (&now);
502 for (i = 0; i < modulo; ++i)
503 {
504 pwdhash *work = &pwdtbl[i];
505
506 while (work && work->pwd)
507 {
508 if ((now - work->create) >= postimeout)
509 {
510 uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo];
511
512 if (debug_flag)
513 dbg_log (_("Give \"%s\" free"), work->pwd->pw_name);
514
515 while (uh != NULL && uh->pwptr)
516 {
517 if (uh->pwptr->pw_uid == work->pwd->pw_uid)
518 {
519 if (debug_flag)
520 dbg_log (_("Give uid for \"%s\" free"),
521 work->pwd->pw_name);
522 if (uh->next != NULL)
523 {
524 uidhash *tmp = uh->next;
525 uh->pwptr = tmp->pwptr;
526 uh->next = tmp->next;
527 free (tmp);
528 }
529 else
530 uh->pwptr = NULL;
531 }
532 uh = uh->next;
533 }
534
535 free_pwd (work->pwd);
536 if (work->next != NULL)
537 {
538 pwdhash *tmp = work->next;
539 work->create = tmp->create;
540 work->next = tmp->next;
541 work->pwd = tmp->pwd;
542 free (tmp);
543 }
544 else
545 work->pwd = NULL;
546 }
547 work = work->next;
548 }
549 }
550 if (debug_flag > 2)
551 dbg_log (_("(pwdtable_update) Release wait lock"));
552 pthread_rwlock_unlock (&pwdlock);
553 sleep (20);
554 }
555 return NULL;
556 }
557
558 static void *
559 negtable_update (void *v)
560 {
561 time_t now;
562 int i;
563
564 sleep (30);
565
566 while (!do_shutdown)
567 {
568 if (debug_flag > 2)
569 dbg_log (_("(negpwdtable_update) Wait for write lock!"));
570
571 pthread_rwlock_wrlock (&neglock);
572
573 if (debug_flag > 2)
574 dbg_log (_("(negpwdtable_update) Have write lock"));
575
576 time (&now);
577 for (i = 0; i < modulo; ++i)
578 {
579 neghash *work = &negtbl[i];
580
581 while (work && work->key)
582 {
583 if ((now - work->create) >= negtimeout)
584 {
585 if (debug_flag)
586 dbg_log (_("Give \"%s\" free"), work->key);
587
588 free (work->key);
589
590 if (work->next != NULL)
591 {
592 neghash *tmp = work->next;
593 work->create = tmp->create;
594 work->next = tmp->next;
595 work->key = tmp->key;
596 free (tmp);
597 }
598 else
599 work->key = NULL;
600 }
601 work = work->next;
602 }
603 }
604 if (debug_flag > 2)
605 dbg_log (_("(negpwdtable_update) Release wait lock"));
606
607 pthread_rwlock_unlock (&neglock);
608 sleep (10);
609 }
610 return NULL;
611 }
This page took 0.069795 seconds and 6 git commands to generate.