]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/grpcache.c
Update.
[thirdparty/glibc.git] / nscd / grpcache.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 <grp.h>
22 #include <pthread.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <rpcsvc/nis.h>
26 #include <sys/types.h>
27
28 #include "dbg_log.h"
29 #include "nscd.h"
30
31 static unsigned long modulo = 211;
32 static unsigned long postimeout = 3600;
33 static unsigned long negtimeout = 60;
34
35 static unsigned long poshit = 0;
36 static unsigned long posmiss = 0;
37 static unsigned long neghit = 0;
38 static unsigned long negmiss = 0;
39
40 struct grphash
41 {
42 time_t create;
43 struct grphash *next;
44 struct group *grp;
45 };
46 typedef struct grphash grphash;
47
48 struct gidhash
49 {
50 struct gidhash *next;
51 struct grphash *grptr;
52 };
53 typedef struct gidhash gidhash;
54
55 struct neghash
56 {
57 time_t create;
58 struct neghash *next;
59 char *key;
60 };
61 typedef struct neghash neghash;
62
63 static grphash *grptbl;
64 static gidhash *gidtbl;
65 static neghash *negtbl;
66
67 static pthread_rwlock_t grplock = PTHREAD_RWLOCK_INITIALIZER;
68 static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
69
70 static void *grptable_update (void *);
71 static void *negtable_update (void *);
72
73 void
74 get_gr_stat (stat_response_header *stat)
75 {
76 stat->gr_poshit = poshit;
77 stat->gr_posmiss = posmiss;
78 stat->gr_neghit = neghit;
79 stat->gr_negmiss = negmiss;
80 stat->gr_size = modulo;
81 stat->gr_posttl = postimeout;
82 stat->gr_negttl = negtimeout;
83 }
84
85 void
86 set_grp_modulo (unsigned long mod)
87 {
88 modulo = mod;
89 }
90
91 void
92 set_pos_grp_ttl (unsigned long ttl)
93 {
94 postimeout = ttl;
95 }
96
97 void
98 set_neg_grp_ttl (unsigned long ttl)
99 {
100 negtimeout = ttl;
101 }
102
103 int
104 cache_grpinit ()
105 {
106 pthread_attr_t attr;
107 pthread_t thread;
108
109 grptbl = calloc (modulo, sizeof (grphash));
110 if (grptbl == NULL)
111 return -1;
112 calloc (modulo, sizeof (grphash));
113 if (gidtbl == NULL)
114 return -1;
115 negtbl = calloc (modulo, sizeof (neghash));
116 if (negtbl == NULL)
117 return -1;
118
119 pthread_attr_init (&attr);
120 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
121
122 pthread_create (&thread, NULL, grptable_update, &attr);
123 pthread_create (&thread, NULL, negtable_update, &attr);
124
125 pthread_attr_destroy (&attr);
126
127 return 0;
128 }
129
130 static struct group *
131 save_grp (struct group *src)
132 {
133 struct group *dest;
134 unsigned long int l;
135
136 dest = calloc (1, sizeof (struct group));
137 dest->gr_name = strdup (src->gr_name);
138 dest->gr_passwd = strdup (src->gr_passwd);
139 dest->gr_gid = src->gr_gid;
140
141 /* How many members does this group have? */
142 l = 0;
143 while (src->gr_mem[l])
144 ++l;
145
146 dest->gr_mem = calloc (1, sizeof (char *) * (l+1));
147 l = 0;
148 while (src->gr_mem[l])
149 {
150 dest->gr_mem[l] = strdup (src->gr_mem[l]);
151 ++l;
152 }
153
154 return dest;
155 }
156
157 static void
158 free_grp (struct group *src)
159 {
160 unsigned long int l;
161
162 free (src->gr_name);
163 free (src->gr_passwd);
164
165 l = 0;
166 while (src->gr_mem[l])
167 {
168 free (src->gr_mem[l]);
169 ++l;
170 }
171 free (src->gr_mem);
172 free (src);
173 }
174
175 static int
176 add_cache (struct group *grp)
177 {
178 grphash *work;
179 unsigned long int hash = __nis_hash (grp->gr_name,
180 strlen (grp->gr_name)) % modulo;
181
182 work = &grptbl[hash];
183
184 if (grptbl[hash].grp == NULL)
185 grptbl[hash].grp = save_grp (grp);
186 else
187 {
188 while (work->next != NULL)
189 work = work->next;
190
191 work->next = calloc (1, sizeof (grphash));
192 work->next->grp = save_grp (grp);
193 work = work->next;
194 }
195
196 time (&work->create);
197 gidtbl[grp->gr_gid % modulo].grptr = work;
198
199 return 0;
200 }
201
202 static struct group *
203 cache_search_name (const char *name)
204 {
205 grphash *work;
206 unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
207
208 work = &grptbl[hash];
209
210 while (work->grp != NULL)
211 {
212 if (strcmp (work->grp->gr_name, name) == 0)
213 return work->grp;
214 if (work->next != NULL)
215 work = work->next;
216 else
217 return NULL;
218 }
219 return NULL;
220 }
221
222 static struct group *
223 cache_search_gid (gid_t gid)
224 {
225 gidhash *work;
226
227 work = &gidtbl[gid % modulo];
228
229 while (work->grptr != NULL)
230 {
231 if (work->grptr->grp->gr_gid == gid)
232 return work->grptr->grp;
233 if (work->next != NULL)
234 work = work->next;
235 else
236 return NULL;
237 }
238 return NULL;
239 }
240
241 static int
242 add_negcache (char *key)
243 {
244 neghash *work;
245 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
246
247 work = &negtbl[hash];
248
249 if (negtbl[hash].key == NULL)
250 negtbl[hash].key = strdup (key);
251 else
252 {
253 while (work->next != NULL)
254 work = work->next;
255
256 work->next = calloc (1, sizeof (neghash));
257 work->next->key = strdup (key);
258 work = work->next;
259 }
260
261 time (&work->create);
262 return 0;
263 }
264
265 static int
266 cache_search_neg (const char *key)
267 {
268 neghash *work;
269 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
270
271 work = &negtbl[hash];
272
273 while (work->key != NULL)
274 {
275 if (strcmp (work->key, key) == 0)
276 return 1;
277 if (work->next != NULL)
278 work = work->next;
279 else
280 return 0;
281 }
282 return 0;
283 }
284
285 void *
286 cache_getgrnam (void *v_param)
287 {
288 param_t *param = (param_t *)v_param;
289 struct group *grp, resultbuf;
290
291 pthread_rwlock_rdlock (&grplock);
292 grp = cache_search_name (param->key);
293
294 /* I don't like it to hold the read only lock longer, but it is
295 necessary to avoid to much malloc/free/strcpy. */
296
297 if (grp)
298 {
299 if (debug_flag)
300 dbg_log (_("Found \"%s\" in cache !"), param->key);
301
302 ++poshit;
303 gr_send_answer (param->conn, grp);
304 close_socket (param->conn);
305
306 pthread_rwlock_unlock (&grplock);
307 }
308 else
309 {
310 int buflen = 1024;
311 char *buffer = calloc (1, buflen);
312 int status;
313
314 if (debug_flag)
315 dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
316
317 pthread_rwlock_unlock (&grplock);
318
319 pthread_rwlock_rdlock (&neglock);
320 status = cache_search_neg (param->key);
321 pthread_rwlock_unlock (&neglock);
322
323 if (status == 0)
324 {
325 while (buffer != NULL
326 && (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp)
327 != 0)
328 && errno == ERANGE)
329 {
330 errno = 0;
331 buflen += 1024;
332 buffer = realloc (buffer, buflen);
333 }
334
335 if (buffer != NULL && grp != NULL)
336 {
337 struct group *tmp;
338
339 ++poshit;
340 pthread_rwlock_wrlock (&grplock);
341 /* While we are waiting on the lock, somebody else could
342 add this entry. */
343 tmp = cache_search_name (param->key);
344 if (tmp == NULL)
345 add_cache (grp);
346 pthread_rwlock_unlock (&grplock);
347 }
348 else
349 {
350 pthread_rwlock_wrlock (&neglock);
351 add_negcache (param->key);
352 ++negmiss;
353 pthread_rwlock_unlock (&neglock);
354 }
355 }
356 else
357 ++neghit;
358
359 gr_send_answer (param->conn, grp);
360 close_socket (param->conn);
361 if (buffer != NULL)
362 free (buffer);
363 }
364 free (param->key);
365 free (param);
366 return NULL;
367 }
368
369 void *
370 cache_gr_disabled (void *v_param)
371 {
372 param_t *param = (param_t *)v_param;
373
374 gr_send_disabled (param->conn);
375 return NULL;
376 }
377
378 void *
379 cache_getgrgid (void *v_param)
380 {
381 param_t *param = (param_t *)v_param;
382 struct group *grp, resultbuf;
383 gid_t gid = strtol (param->key, NULL, 10);
384
385 pthread_rwlock_rdlock (&grplock);
386 grp = cache_search_gid (gid);
387
388 /* I don't like it to hold the read only lock longer, but it is
389 necessary to avoid to much malloc/free/strcpy. */
390
391 if (grp != NULL)
392 {
393 if (debug_flag)
394 dbg_log (_("Found \"%d\" in cache !\n"), gid);
395
396 ++poshit;
397 gr_send_answer (param->conn, grp);
398 close_socket (param->conn);
399
400 pthread_rwlock_unlock (&grplock);
401 }
402 else
403 {
404 int buflen = 1024;
405 char *buffer = malloc (buflen);
406 int status;
407
408 if (debug_flag)
409 dbg_log (_("Doesn't found \"%d\" in cache !\n"), gid);
410
411 pthread_rwlock_unlock (&grplock);
412
413 pthread_rwlock_rdlock (&neglock);
414 status = cache_search_neg (param->key);
415 pthread_rwlock_unlock (&neglock);
416
417 if (status == 0)
418 {
419 while (buffer != NULL
420 && (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0)
421 && errno == ERANGE)
422 {
423 errno = 0;
424 buflen += 1024;
425 buffer = realloc (buffer, buflen);
426 }
427
428 if (buffer != NULL && grp != NULL)
429 {
430 struct group *tmp;
431
432 ++posmiss;
433 pthread_rwlock_wrlock (&grplock);
434 /* While we are waiting on the lock, somebody else could
435 add this entry. */
436 tmp = cache_search_gid (gid);
437 if (tmp == NULL)
438 add_cache (grp);
439 pthread_rwlock_unlock (&grplock);
440 }
441 else
442 {
443 ++negmiss;
444 pthread_rwlock_wrlock (&neglock);
445 add_negcache (param->key);
446 pthread_rwlock_unlock (&neglock);
447 }
448 }
449 else
450 ++neghit;
451
452 gr_send_answer (param->conn, grp);
453 close_socket (param->conn);
454 if (buffer != NULL)
455 free (buffer);
456 }
457 free (param->key);
458 free (param);
459 return NULL;
460 }
461
462 void *
463 grptable_update (void *v)
464 {
465 time_t now;
466 int i;
467
468 sleep (20);
469
470 while (!do_shutdown)
471 {
472 if (debug_flag > 2)
473 dbg_log (_("(grptable_update) Wait for write lock!"));
474
475 pthread_rwlock_wrlock (&grplock);
476
477 if (debug_flag > 2)
478 dbg_log (_("(grptable_update) Have write lock"));
479
480 time (&now);
481 for (i = 0; i < modulo; ++i)
482 {
483 grphash *work = &grptbl[i];
484
485 while (work && work->grp)
486 {
487 if ((now - work->create) >= postimeout)
488 {
489 gidhash *uh = &gidtbl[work->grp->gr_gid % modulo];
490
491 if (debug_flag)
492 dbg_log (_("Give \"%s\" free"), work->grp->gr_name);
493
494 while (uh && uh->grptr)
495 {
496 if (uh->grptr->grp->gr_gid == work->grp->gr_gid)
497 {
498 if (debug_flag > 3)
499 dbg_log (_("Give gid for \"%s\" free"),
500 work->grp->gr_name);
501 if (uh->next != NULL)
502 {
503 gidhash *tmp = uh->next;
504 uh->grptr = tmp->grptr;
505 uh->next = tmp->next;
506 free (tmp);
507 }
508 else
509 uh->grptr = NULL;
510 }
511 uh = uh->next;
512 }
513
514 free_grp (work->grp);
515 if (work->next != NULL)
516 {
517 grphash *tmp = work->next;
518 work->create = tmp->create;
519 work->next = tmp->next;
520 work->grp = tmp->grp;
521 free (tmp);
522 }
523 else
524 work->grp = NULL;
525 }
526 work = work->next;
527 }
528 }
529 if (debug_flag > 2)
530 dbg_log (_("(pwdtable_update) Release wait lock\n"));
531 pthread_rwlock_unlock (&grplock);
532 sleep (20);
533 }
534 return NULL;
535 }
536
537 void *
538 negtable_update (void *v)
539 {
540 time_t now;
541 int i;
542
543 sleep (30);
544
545 while (!do_shutdown)
546 {
547 if (debug_flag > 2)
548 dbg_log (_("(negtable_update) Wait for write lock!"));
549
550 pthread_rwlock_wrlock (&neglock);
551
552 if (debug_flag > 2)
553 dbg_log (_("(negtable_update) Have write lock"));
554
555 time (&now);
556 for (i = 0; i < modulo; ++i)
557 {
558 neghash *work = &negtbl[i];
559
560 while (work && work->key)
561 {
562 if ((now - work->create) >= negtimeout)
563 {
564 if (debug_flag)
565 dbg_log (_("Give \"%s\" free"), work->key);
566
567 free (work->key);
568
569 if (work->next != NULL)
570 {
571 neghash *tmp = work->next;
572 work->create = tmp->create;
573 work->next = tmp->next;
574 work->key = tmp->key;
575 free (tmp);
576 }
577 else
578 work->key = NULL;
579 }
580 work = work->next;
581 }
582 }
583 if (debug_flag > 2)
584 dbg_log (_("(negtable_update) Release wait lock"));
585 pthread_rwlock_unlock (&neglock);
586 sleep (10);
587 }
588 return NULL;
589 }