Correct copying.
[kopensolaris-gnu/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 }