3f3f4ba0ecab44994f5dfc51f6abf8e4fa0d9acf
[platform/upstream/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   gidtbl = 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   if (debug_flag)
183     dbg_log (_("grp_add_cache (%s)"), grp->gr_name);
184
185   work = &grptbl[hash];
186
187   if (grptbl[hash].grp == NULL)
188     grptbl[hash].grp = save_grp (grp);
189   else
190     {
191       while (work->next != NULL)
192         work = work->next;
193
194       work->next = calloc (1, sizeof (grphash));
195       work->next->grp = save_grp (grp);
196       work = work->next;
197     }
198
199   time (&work->create);
200   gidtbl[grp->gr_gid % modulo].grptr = work;
201
202   return 0;
203 }
204
205 static struct group *
206 cache_search_name (const char *name)
207 {
208   grphash *work;
209   unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
210
211   work = &grptbl[hash];
212
213   while (work->grp != NULL)
214     {
215       if (strcmp (work->grp->gr_name, name) == 0)
216         return work->grp;
217       if (work->next != NULL)
218         work = work->next;
219       else
220         return NULL;
221     }
222   return NULL;
223 }
224
225 static struct group *
226 cache_search_gid (gid_t gid)
227 {
228   gidhash *work;
229
230   work = &gidtbl[gid % modulo];
231
232   while (work->grptr != NULL)
233     {
234       if (work->grptr->grp->gr_gid == gid)
235         return work->grptr->grp;
236       if (work->next != NULL)
237         work = work->next;
238       else
239         return NULL;
240     }
241   return NULL;
242 }
243
244 static int
245 add_negcache (char *key)
246 {
247   neghash *work;
248   unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
249
250   if (debug_flag)
251     dbg_log (_("grp_add_netgache (%s|%ld)"), key, hash);
252
253   work = &negtbl[hash];
254
255   if (negtbl[hash].key == NULL)
256     {
257       negtbl[hash].key = strdup (key);
258       negtbl[hash].next = NULL;
259     }
260   else
261     {
262       while (work->next != NULL)
263         work = work->next;
264
265       work->next = calloc (1, sizeof (neghash));
266       work->next->key = strdup (key);
267       work = work->next;
268     }
269
270   time (&work->create);
271   return 0;
272 }
273
274 static int
275 cache_search_neg (const char *key)
276 {
277   neghash *work;
278   unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
279
280   if (debug_flag)
281     dbg_log (_("grp_cache_search_neg (%s|%ld)"), key, hash);
282
283   work = &negtbl[hash];
284
285   while (work->key != NULL)
286     {
287       if (strcmp (work->key, key) == 0)
288         return 1;
289       if (work->next != NULL)
290         work = work->next;
291       else
292         return 0;
293     }
294   return 0;
295 }
296
297 void *
298 cache_getgrnam (void *v_param)
299 {
300   param_t *param = (param_t *)v_param;
301   struct group *grp;
302
303   pthread_rwlock_rdlock (&grplock);
304   grp = cache_search_name (param->key);
305
306   /* I don't like it to hold the read only lock longer, but it is
307      necessary to avoid to much malloc/free/strcpy.  */
308
309   if (grp != NULL)
310     {
311       if (debug_flag)
312         dbg_log (_("Found \"%s\" in cache !"), param->key);
313
314       ++poshit;
315       gr_send_answer (param->conn, grp);
316       close_socket (param->conn);
317
318       pthread_rwlock_unlock (&grplock);
319     }
320   else
321     {
322       int status;
323       int buflen = 1024;
324       char *buffer = calloc (1, buflen);
325       struct group resultbuf;
326
327       if (debug_flag)
328         dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
329
330       pthread_rwlock_unlock (&grplock);
331
332       pthread_rwlock_rdlock (&neglock);
333       status = cache_search_neg (param->key);
334       pthread_rwlock_unlock (&neglock);
335
336       if (status == 0)
337         {
338           while (buffer != NULL
339                  && (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp)
340                      != 0)
341                  && errno == ERANGE)
342             {
343               errno = 0;
344               buflen += 1024;
345               buffer = realloc (buffer, buflen);
346             }
347
348           if (buffer != NULL && grp != NULL)
349             {
350               struct group *tmp;
351
352               ++poshit;
353               pthread_rwlock_wrlock (&grplock);
354               /* While we are waiting on the lock, somebody else could
355                  add this entry.  */
356               tmp = cache_search_name (param->key);
357               if (tmp == NULL)
358                 add_cache (grp);
359               pthread_rwlock_unlock (&grplock);
360             }
361           else
362             {
363               pthread_rwlock_wrlock (&neglock);
364               add_negcache (param->key);
365               ++negmiss;
366               pthread_rwlock_unlock (&neglock);
367             }
368         }
369       else
370         ++neghit;
371
372       gr_send_answer (param->conn, grp);
373       close_socket (param->conn);
374       if (buffer != NULL)
375         free (buffer);
376     }
377   free (param->key);
378   free (param);
379   return NULL;
380 }
381
382 void *
383 cache_gr_disabled (void *v_param)
384 {
385   param_t *param = (param_t *)v_param;
386
387   if (debug_flag)
388     dbg_log (_("\tgroup cache is disabled\n"));
389
390   gr_send_disabled (param->conn);
391   return NULL;
392 }
393
394 void *
395 cache_getgrgid (void *v_param)
396 {
397   param_t *param = (param_t *)v_param;
398   struct group *grp, resultbuf;
399   gid_t gid = strtol (param->key, NULL, 10);
400
401   pthread_rwlock_rdlock (&grplock);
402   grp = cache_search_gid (gid);
403
404   /* I don't like it to hold the read only lock longer, but it is
405      necessary to avoid to much malloc/free/strcpy.  */
406
407   if (grp != NULL)
408     {
409       if (debug_flag)
410         dbg_log (_("Found \"%d\" in cache !"), gid);
411
412       ++poshit;
413       gr_send_answer (param->conn, grp);
414       close_socket (param->conn);
415
416       pthread_rwlock_unlock (&grplock);
417     }
418   else
419     {
420       int buflen = 1024;
421       char *buffer = malloc (buflen);
422       int status;
423
424       if (debug_flag)
425         dbg_log (_("Doesn't found \"%d\" in cache !"), gid);
426
427       pthread_rwlock_unlock (&grplock);
428
429       pthread_rwlock_rdlock (&neglock);
430       status = cache_search_neg (param->key);
431       pthread_rwlock_unlock (&neglock);
432
433       if (status == 0)
434         {
435           while (buffer != NULL
436                  && (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0)
437                  && errno == ERANGE)
438             {
439               errno = 0;
440               buflen += 1024;
441               buffer = realloc (buffer, buflen);
442             }
443
444           if (buffer != NULL && grp != NULL)
445             {
446               struct group *tmp;
447
448               ++posmiss;
449               pthread_rwlock_wrlock (&grplock);
450               /* While we are waiting on the lock, somebody else could
451                  add this entry.  */
452               tmp = cache_search_gid (gid);
453               if (tmp == NULL)
454                 add_cache (grp);
455               pthread_rwlock_unlock (&grplock);
456             }
457           else
458             {
459               ++negmiss;
460               pthread_rwlock_wrlock (&neglock);
461               add_negcache (param->key);
462               pthread_rwlock_unlock (&neglock);
463             }
464         }
465       else
466         ++neghit;
467
468       gr_send_answer (param->conn, grp);
469       close_socket (param->conn);
470       if (buffer != NULL)
471         free (buffer);
472     }
473   free (param->key);
474   free (param);
475   return NULL;
476 }
477
478 void *
479 grptable_update (void *v)
480 {
481   time_t now;
482   int i;
483
484   sleep (20);
485
486   while (!do_shutdown)
487     {
488       if (debug_flag > 2)
489         dbg_log (_("(grptable_update) Wait for write lock!"));
490
491       pthread_rwlock_wrlock (&grplock);
492
493       if (debug_flag > 2)
494         dbg_log (_("(grptable_update) Have write lock"));
495
496       time (&now);
497       for (i = 0; i < modulo; ++i)
498         {
499           grphash *work = &grptbl[i];
500
501           while (work && work->grp)
502             {
503               if ((now - work->create) >= postimeout)
504                 {
505                   gidhash *uh = &gidtbl[work->grp->gr_gid % modulo];
506
507                   if (debug_flag)
508                     dbg_log (_("Give \"%s\" free"), work->grp->gr_name);
509
510                   while (uh && uh->grptr)
511                     {
512                       if (uh->grptr->grp->gr_gid == work->grp->gr_gid)
513                         {
514                           if (debug_flag > 3)
515                             dbg_log (_("Give gid for \"%s\" free"),
516                                      work->grp->gr_name);
517                           if (uh->next != NULL)
518                             {
519                               gidhash *tmp = uh->next;
520                               uh->grptr = tmp->grptr;
521                               uh->next = tmp->next;
522                               free (tmp);
523                             }
524                           else
525                             uh->grptr = NULL;
526                         }
527                       uh = uh->next;
528                     }
529
530                   free_grp (work->grp);
531                   if (work->next != NULL)
532                     {
533                       grphash *tmp = work->next;
534                       work->create = tmp->create;
535                       work->next = tmp->next;
536                       work->grp = tmp->grp;
537                       free (tmp);
538                     }
539                   else
540                     work->grp = NULL;
541                 }
542               work = work->next;
543             }
544         }
545       if (debug_flag > 2)
546         dbg_log (_("(pwdtable_update) Release wait lock\n"));
547       pthread_rwlock_unlock (&grplock);
548       sleep (20);
549     }
550   return NULL;
551 }
552
553 void *
554 negtable_update (void *v)
555 {
556   time_t now;
557   int i;
558
559   sleep (30);
560
561   while (!do_shutdown)
562     {
563       if (debug_flag > 2)
564         dbg_log (_("(negtable_update) Wait for write lock!"));
565
566       pthread_rwlock_wrlock (&neglock);
567
568       if (debug_flag > 2)
569         dbg_log (_("(negtable_update) Have write lock"));
570
571       time (&now);
572       for (i = 0; i < modulo; ++i)
573         {
574           neghash *work = &negtbl[i];
575
576           while (work && work->key)
577             {
578               if ((now - work->create) >= negtimeout)
579                 {
580                   if (debug_flag)
581                     dbg_log (_("Give \"%s\" free"), work->key);
582
583                   free (work->key);
584
585                   if (work->next != NULL)
586                     {
587                       neghash *tmp = work->next;
588                       work->create = tmp->create;
589                       work->next = tmp->next;
590                       work->key = tmp->key;
591                       free (tmp);
592                     }
593                   else
594                     work->key = NULL;
595                 }
596               work = work->next;
597             }
598         }
599       if (debug_flag > 2)
600         dbg_log (_("(negtable_update) Release wait lock"));
601       pthread_rwlock_unlock (&neglock);
602       sleep (10);
603     }
604   return NULL;
605 }