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