chiark / gitweb /
Major overhaul. Now uses DSA signatures rather than the bogus symmetric
[become] / src / userdb.c
1 /* -*-c-*-
2  *
3  * $Id: userdb.c,v 1.9 2003/10/12 00:14:55 mdw Exp $
4  *
5  * User database management
6  *
7  * (c) 1998 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: userdb.c,v $
32  * Revision 1.9  2003/10/12 00:14:55  mdw
33  * Major overhaul.  Now uses DSA signatures rather than the bogus symmetric
34  * encrypt-and-hope thing.  Integrated with mLib and Catacomb.
35  *
36  * Revision 1.8  1998/06/08 11:21:22  mdw
37  * Fixed bug in password and group file reading: strtok doesn't handle
38  * double colons nicely.
39  *
40  * Revision 1.7  1998/04/23 13:27:46  mdw
41  * Switch to using the ypstuff interface to YP server.
42  *
43  * Revision 1.6  1998/01/12 16:46:33  mdw
44  * Fix copyright date.
45  *
46  * Revision 1.5  1997/09/17  10:24:08  mdw
47  * Use `uid_t' instead of `int' for uids and gids.  Not quite sure why I
48  * didn't do this before.
49  *
50  * Revision 1.4  1997/08/20  16:24:58  mdw
51  * Patch memory leak.  Rename `userdb_reinit' to `userdb_end' for more
52  * sensible restart.
53  *
54  * Revision 1.3  1997/08/07 09:44:29  mdw
55  * Read NIS-based passwords from the YP server directly, rather than using
56  * `popen(ypcat)', which is probably both slower and less secure.
57  *
58  * Revision 1.2  1997/08/04 10:24:26  mdw
59  * Sources placed under CVS control.
60  *
61  * Revision 1.1  1997/07/21  13:47:43  mdw
62  * Initial revision
63  *
64  */
65
66 /*----- Header files ------------------------------------------------------*/
67
68 /* --- ANSI headers --- */
69
70 #include <ctype.h>
71 #include <errno.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75
76 /* --- Unix headers --- */
77
78 #include "config.h"
79
80 #include <sys/types.h>
81
82 #include <grp.h>
83 #include <pwd.h>
84 #include <unistd.h>
85
86 /* --- mLib headers --- */
87
88 #include <mLib/alloc.h>
89 #include <mLib/sym.h>
90 #include <mLib/trace.h>
91
92 /* --- Local headers --- */
93
94 #include "become.h"
95 #include "userdb.h"
96 #include "ypstuff.h"
97
98 /*----- Type definitions --------------------------------------------------*/
99
100 /* --- A map link --- */
101
102 typedef struct userdb__node {
103   struct userdb__node *next;
104   void *rec;
105 } userdb__node;
106
107 /* --- A reference to a real record --- */
108
109 typedef struct userdb__sym {
110   sym_base _base;
111   void *rec;
112 } userdb__sym;
113
114 /* --- A name- and number-mapping --- */
115
116 typedef struct userdb__map {
117   sym_table nmap;
118   sym_table idmap;
119   userdb__node *list;
120 } userdb__map;
121
122 /*----- Static variables --------------------------------------------------*/
123
124 static userdb__map userdb__users;       /* Map of user info blocks */
125 static sym_iter userdb__useri;          /* Iterator for users */
126 static userdb__map userdb__groups;      /* Map of group info blocks */
127 static sym_iter userdb__groupi;         /* Iterator for groups */
128
129 /*----- Map management functions ------------------------------------------*/
130
131 /* --- @userdb__createMap@ --- *
132  *
133  * Arguments:   @userdb__map *m@ = pointer to a map block
134  *
135  * Returns:     ---
136  *
137  * Use:         Initialises a map table.
138  */
139
140 static void userdb__createMap(userdb__map *m)
141 {
142   sym_create(&m->nmap);
143   sym_create(&m->idmap);
144   m->list = 0;
145 }
146
147 /* --- @userdb__addToMap@ --- *
148  *
149  * Arguments:   @userdb__map *m@ = pointer to the map block
150  *              @const char *name@ = pointer to the item's name
151  *              @uid_t id@ = the item's id number
152  *              @void *rec@ = pointer to the actual record
153  *
154  * Returns:     ---
155  *
156  * Use:         Adds an item to the given map.
157  */
158
159 static void userdb__addToMap(userdb__map *m,
160                              const char *name,
161                              uid_t id, void *rec)
162 {
163   unsigned f;
164   userdb__sym *s;
165   userdb__node *n;
166
167   s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
168   if (!f)
169     s->rec = rec;
170
171   s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
172   if (!f)
173     s->rec = rec;
174
175   n = xmalloc(sizeof(*n));
176   n->rec = rec;
177   n->next = m->list;
178   m->list = n;
179 }
180
181 /* --- @userdb__byName@ --- *
182  *
183  * Arguments:   @userdb__map *m@ = pointer to a map block
184  *              @const char *name@ = name to look up
185  *
186  * Returns:     A pointer to the appropriate block, or zero if not found.
187  *
188  * Use:         Looks up a name in a mapping and returns the result.
189  */
190
191 static void *userdb__byName(userdb__map *m, const char *name)
192 {
193   userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
194   return (s ? s->rec : 0);
195 }
196
197 /* --- @userdb__byId@ --- *
198  *
199  * Arguments:   @userdb__map *m@ = pointer to a map block
200  *              @uid_t id@ = id number to find
201  *
202  * Returns:     A pointer to the appropriate block, or zero if not found.
203  *
204  * Use:         Looks up an ID in a mapping, and returns the result.
205  */
206
207 static void *userdb__byId(userdb__map *m, uid_t id)
208 {
209   userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
210   return (s ? s->rec : 0);
211 }
212
213 /* --- @userdb__clearMap@ --- *
214  *
215  * Arguments:   @userdb__map *m@ = pointer to a map block
216  *              @void (*freerec)(void *rec)@ = pointer to a free-record proc
217  *
218  * Returns:     ---
219  *
220  * Use:         Clears a map, emptying it and releasing the memory it
221  *              occupied.
222  */
223
224 static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
225 {
226   userdb__node *n, *t;
227
228   sym_destroy(&m->nmap);
229   sym_destroy(&m->idmap);
230
231   for (n = m->list; n; n = t) {
232     t = n->next;
233     freerec(n->rec);
234     free(n);
235   }
236 }
237
238 /*----- User and group block management -----------------------------------*/
239
240 /* --- @userdb__dumpUser@ --- *
241  *
242  * Arguments:   @const struct passwd *pw@ = pointer to a user block
243  *
244  * Returns:     ---
245  *
246  * Use:         Writes a user's informationt to a stream.
247  */
248
249 #ifndef NTRACE
250
251 static void userdb__dumpUser(const struct passwd *pw)
252 {
253   trace(TRACE_DEBUG,
254         "debug: name `%s' passwd `%s' uid %i gid %i",
255         pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid);
256   trace(TRACE_DEBUG,
257         "debug: ... gecos `%s' home `%s' shell `%s'",
258         pw->pw_gecos, pw->pw_dir, pw->pw_shell);
259 }
260
261 #endif
262
263 /* --- @userdb__split@ --- *
264  *
265  * Arguments:   @char *p@ = pointer to string
266  *              @char **v@ = pointer to vector to fill in
267  *              @int sz@ = maximum number of fields to split
268  *
269  * Returns:     Number of fields extracted.
270  *
271  * Use:         Splits a string into fields at colon characters.
272  */
273
274 static int userdb__split(char *p, char **v, int sz)
275 {
276   int count = 0;
277
278   *v++ = p; sz--; count++;
279   if (!sz)
280     goto done;
281   while (*p) {
282     if (*p++ == ':') {
283       p[-1] = 0;
284       *v++ = p; sz--; count++;
285       if (!sz)
286         goto done;
287     }
288   }
289   while (sz--)
290     *v++ = 0;
291
292 done:
293   return (count);
294 }
295
296 /* --- @userdb_copyUser@ --- *
297  *
298  * Arguments:   @struct passwd *pw@ = pointer to block to copy
299  *
300  * Returns:     Pointer to the copy.
301  *
302  * Use:         Copies a user block.  The copy is `deep' so all the strings
303  *              are copied too.  Free the copy with @userdb_freeUser@ when
304  *              you don't want it any more.
305  */
306
307 struct passwd *userdb_copyUser(struct passwd *pw)
308 {
309   struct passwd *npw;
310
311   if (!pw)
312     return (0);
313
314   npw = xmalloc(sizeof(*npw));
315
316   npw->pw_name = xstrdup(pw->pw_name);
317   npw->pw_passwd = xstrdup(pw->pw_passwd);
318   npw->pw_uid = pw->pw_uid;
319   npw->pw_gid = pw->pw_gid;
320   npw->pw_gecos = xstrdup(pw->pw_gecos);
321   npw->pw_dir = xstrdup(pw->pw_dir);
322   npw->pw_shell = xstrdup(pw->pw_shell);
323
324   return (npw);
325 }
326
327 /* --- @userdb__buildUser@ --- *
328  *
329  * Arguments:   @char *s@ = pointer to user string
330  *
331  * Returns:     Pointer to a user block.
332  *
333  * Use:         Converts a line from a user file into a password entry.
334  *              Note that the string is corrupted by @strtok@ while it gets
335  *              parsed.
336  */
337
338 static struct passwd *userdb__buildUser(char *s)
339 {
340   struct passwd *pw = xmalloc(sizeof(*pw));
341   char *v[7];
342
343   if (userdb__split(s, v, 7) < 7) {
344     free(pw);
345     return (0);
346   }
347
348   pw->pw_name = xstrdup(v[0]);
349   pw->pw_passwd = xstrdup(v[1]);
350   pw->pw_uid = (uid_t)atol(v[2]);
351   pw->pw_gid = (gid_t)atol(v[3]);
352   pw->pw_gecos = xstrdup(v[4]);
353   pw->pw_dir = xstrdup(v[5]);
354   pw->pw_shell = xstrdup(v[6]);
355   return (pw);
356 }
357
358 /* --- @userdb_freeUser@ --- *
359  *
360  * Arguments:   @void *rec@ = pointer to a user record
361  *
362  * Returns:     ---
363  *
364  * Use:         Frees a user record.
365  */
366
367 void userdb_freeUser(void *rec)
368 {
369   struct passwd *pw;
370
371   if (!rec)
372     return;
373
374   pw = rec;
375   free(pw->pw_name);
376   free(pw->pw_passwd);
377   free(pw->pw_gecos);
378   free(pw->pw_dir);
379   free(pw->pw_shell);
380   free(pw);
381
382
383 /* --- @userdb__dumpGroup@ --- *
384  *
385  * Arguments:   @const struct group *gr@ = pointer to a group block
386  *              @FILE *fp@ = pointer to stream to write on
387  *
388  * Returns:     ---
389  *
390  * Use:         Writes a group's information to a stream.
391  */
392
393 #ifndef NTRACE
394
395 static void userdb__dumpGroup(const struct group *gr)
396 {
397   char *const *p;
398
399   trace(TRACE_DEBUG,
400          "debug: name `%s' passwd `%s' gid %i",
401          gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
402   for (p = gr->gr_mem; *p; p++)
403     trace(TRACE_DEBUG,"debug: ... `%s'", *p);
404 }
405
406 #endif
407
408 /* --- @userdb_copyGroup@ --- *
409  *
410  * Arguments:   @struct group *gr@ = pointer to group block
411  *
412  * Returns:     Pointer to copied block
413  *
414  * Use:         Copies a group block.  The copy is `deep' so all the strings
415  *              are copied too.  Free the copy with @userdb_freeGroup@ when
416  *              you don't want it any more.
417  */
418
419 struct group *userdb_copyGroup(struct group *gr)
420 {
421   struct group *ngr;
422   int i, max;
423
424   if (!gr)
425     return (0);
426
427   ngr = xmalloc(sizeof(*ngr));
428
429   ngr->gr_name = xstrdup(gr->gr_name);
430   ngr->gr_passwd = xstrdup(gr->gr_passwd);
431   ngr->gr_gid = gr->gr_gid;
432
433   for (max = 0; gr->gr_mem[max]; max++)
434     ;
435   ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
436   for (i = 0; i < max; i++)
437     ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
438   ngr->gr_mem[max] = 0;
439
440   return (ngr);
441 }
442
443 /* --- @userdb__buildGroup@ --- *
444  *
445  * Arguments:   @char *s@ = pointer to group line string
446  *
447  * Returns:     Pointer to a group block
448  *
449  * Use:         Parses an entry in the groups file.  The string is garbled
450  *              by @strtok@ as we go.
451  */
452
453 static struct group *userdb__buildGroup(char *s)
454 {
455   struct group *gr = xmalloc(sizeof(*gr));
456   char *v[4];
457   int i;
458
459   /* --- Do the easy bits --- */
460
461   if (userdb__split(s, v, 4) < 3) {
462     free(gr);
463     return (0);
464   }
465   gr->gr_name = xstrdup(v[0]);
466   gr->gr_passwd = xstrdup(v[1]);
467   gr->gr_gid = (gid_t)atol(v[2]);
468
469   /* --- Count the number of members --- */
470
471   s = v[3];
472   i = 0;
473   if (s && s[0]) {
474     for (;;) {
475       i++;
476       if ((s = strpbrk(s, ",")) == 0)
477         break;
478       s++;
479     }
480   }
481
482   /* --- Allocate the block and fill it --- */
483
484   gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
485   i = 0;
486   if (v[3]) {
487     s = strtok(v[3], ",");
488     while (s) {
489       gr->gr_mem[i++] = xstrdup(s);
490       s = strtok(0, ",");
491     }
492   }
493   gr->gr_mem[i] = 0;
494
495   return (gr);
496 }
497
498 /* --- @userdb_freeGroup@ --- *
499  *
500  * Arguments:   @void *rec@ = pointer to a group record
501  *
502  * Returns:     ---
503  *
504  * Use:         Frees a group record.
505  */
506
507 void userdb_freeGroup(void *rec)
508 {
509   struct group *gr;
510   char **p;
511
512   if (!rec)
513     return;
514
515   gr = rec;
516   free(gr->gr_name);
517   free(gr->gr_passwd);
518   for (p = gr->gr_mem; *p; p++)
519     free(*p);
520   free(gr->gr_mem);
521   free(gr);
522
523
524 /*----- Answering queries -------------------------------------------------*/
525
526 /* --- @userdb_userByName@, @userdb_userById@ --- *
527  *
528  * Arguments:   @const char *name@ = pointer to user's name
529  *              @uid_t id@ = user id to find
530  *
531  * Returns:     Pointer to user block, or zero if not found.
532  *
533  * Use:         Looks up a user by name or id.
534  */
535
536 struct passwd *userdb_userByName(const char *name)
537 { return (userdb__byName(&userdb__users, name)); }
538
539 struct passwd *userdb_userById(uid_t id)
540 { return (userdb__byId(&userdb__users, id)); }
541
542 /* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
543  *
544  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
545  *
546  * Returns:     ---
547  *
548  * Use:         Initialises an iteration for the user database.
549  */
550
551 void userdb_iterateUsers(void)
552 { userdb_iterateUsers_r(&userdb__useri); }
553
554 void userdb_iterateUsers_r(userdb_iter *i)
555 { sym_mkiter(i, &userdb__users.nmap); }
556
557 /* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
558  *
559  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
560  *
561  * Returns:     Pointer to the next user block, or null.
562  *
563  * Use:         Returns another user block.
564  */
565
566 struct passwd *userdb_nextUser(void)
567 { return (userdb_nextUser_r(&userdb__useri)); }
568
569 struct passwd *userdb_nextUser_r(userdb_iter *i)
570 {
571   userdb__sym *s = sym_next(i);
572   return (s ? s->rec : 0);
573 }
574
575 /* --- @userdb_groupByName@, @userdb_groupById@ --- *
576  *
577  * Arguments:   @const char *name@ = pointer to group's name
578  *              @gid_t id@ = group id to find
579  *
580  * Returns:     Pointer to group block, or zero if not found.
581  *
582  * Use:         Looks up a group by name or id.
583  */
584
585 struct group *userdb_groupByName(const char *name)
586 { return (userdb__byName(&userdb__groups, name)); }
587
588 struct group *userdb_groupById(gid_t id)
589 { return (userdb__byId(&userdb__groups, id)); }
590
591 /* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
592  *
593  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
594  *
595  * Returns:     ---
596  *
597  * Use:         Initialises an iteration for the group database.
598  */
599
600 void userdb_iterateGroups(void)
601 { userdb_iterateGroups_r(&userdb__groupi); }
602
603 void userdb_iterateGroups_r(userdb_iter *i)
604 { sym_mkiter(i, &userdb__groups.nmap); }
605
606 /* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
607  *
608  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
609  *
610  * Returns:     Pointer to the next group block, or null.
611  *
612  * Use:         Returns another group block.
613  */
614
615 struct group *userdb_nextGroup(void)
616 { return (userdb_nextGroup_r(&userdb__groupi)); }
617
618 struct group *userdb_nextGroup_r(userdb_iter *i)
619 {
620   userdb__sym *s = sym_next(i);
621   return (s ? s->rec : 0);
622 }
623
624 /*----- Yellow pages support ----------------------------------------------*/
625
626 #ifdef HAVE_YP
627
628 /* --- @userdb__foreachUser@ --- *
629  *
630  * Arguments:   @int st@ = YP protocol-level status code
631  *              @char *k@ = address of the key for this record
632  *              @int ksz@ = size of the key
633  *              @char *v@ = address of the value for this record
634  *              @int vsz@ = size of the value
635  *              @char *data@ = pointer to some data passed to me
636  *
637  * Returns:     Zero to be called again, nonzero to end the enumeration.
638  *
639  * Use:         Handles an incoming user record.
640  */
641
642 static int userdb__foreachUser(int st, char *k, int ksz,
643                                char *v, int vsz, char *data)
644 {
645   char *cv;
646   struct passwd *pw;
647
648   if (st != YP_TRUE)
649     return (-1);
650   cv = xmalloc(vsz + 1);
651   memcpy(cv, v, vsz);
652   cv[vsz] = 0;
653   T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
654   pw = userdb__buildUser(cv);
655   if (pw && !userdb__byName(&userdb__users, pw->pw_name)) {
656     IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
657     userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
658   } else
659     userdb_freeUser(pw);
660   free(cv);
661   return (0);
662 }
663
664 /* --- @userdb__foreachGroup@ --- *
665  *
666  * Arguments:   @int st@ = YP protocol-level status code
667  *              @char *k@ = address of the key for this record
668  *              @int ksz@ = size of the key
669  *              @char *v@ = address of the value for this record
670  *              @int vsz@ = size of the value
671  *              @char *data@ = pointer to some data passed to me
672  *
673  * Returns:     Zero to be called again, nonzero to end the enumeration.
674  *
675  * Use:         Handles an incoming user record.
676  */
677
678 static int userdb__foreachGroup(int st, char *k, int ksz,
679                                 char *v, int vsz, char *data)
680 {
681   char *cv;
682   struct group *gr;
683
684   if (st != YP_TRUE)
685     return (-1);
686   cv = xmalloc(vsz + 1);
687   memcpy(cv, v, vsz);
688   cv[vsz] = 0;
689   T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
690   gr = userdb__buildGroup(cv);
691   if (gr && !userdb__byName(&userdb__groups, gr->gr_name)) {
692     IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
693     userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
694   } else
695     userdb_freeGroup(gr);
696   free(cv);
697   return (0);
698 }
699
700 /* --- @userdb_yp@ --- *
701  *
702  * Arguments:   ---
703  *
704  * Returns:     ---
705  *
706  * Use:         Fetches the YP database of users.
707  */
708
709 void userdb_yp(void)
710 {
711   /* --- Bind to a server --- */
712
713   ypstuff_bind();
714   if (!yp_domain)
715     return;
716
717   T( trace(TRACE_DEBUG, "debug: adding NIS users"); )
718
719   /* --- Fetch the users map --- */
720
721   {
722     static struct ypall_callback ucb = { userdb__foreachUser, 0 };
723     yp_all(yp_domain, "passwd.byuid", &ucb);
724   }
725
726   /* --- Fetch the groups map --- */
727
728   {
729     static struct ypall_callback gcb = { userdb__foreachGroup, 0 };
730     yp_all(yp_domain, "group.bygid", &gcb);
731   }
732 }
733
734 #else
735
736 void userdb_yp(void) { ; }
737
738 #endif
739
740 /*----- Building the databases --------------------------------------------*/
741
742 /* --- @userdb_local@ --- *
743  *
744  * Arguments:   ---
745  *
746  * Returns:     ---
747  *
748  * Use:         Reads the local list of users into the maps.
749  */
750
751 void userdb_local(void)
752 {
753   T( trace(TRACE_DEBUG, "debug: adding local users"); )
754
755   /* --- Fetch users first --- */
756
757   {
758     struct passwd *pw;
759
760     setpwent();
761     while ((pw = getpwent()) != 0) {
762       IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
763       if (!userdb__byName(&userdb__users, pw->pw_name))
764         userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
765                          userdb_copyUser(pw));
766     }
767     endpwent();
768   }
769
770   /* --- Then fetch groups --- */
771
772   {
773     struct group *gr;
774
775     setgrent();
776     while ((gr = getgrent()) != 0) {
777       IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
778       if (!userdb__byName(&userdb__groups, gr->gr_name))
779         userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
780                          userdb_copyGroup(gr));
781     }
782     endgrent();
783   }
784 }
785
786 /* --- @userdb_init@ --- *
787  *
788  * Arguments:   ---
789  *
790  * Returns:     ---
791  *
792  * Use:         Initialises the user database.
793  */
794
795 void userdb_init(void)
796 {
797   userdb__createMap(&userdb__users);
798   userdb__createMap(&userdb__groups);
799 }
800
801 /* --- @userdb_end@ --- *
802  *
803  * Arguments:   ---
804  *
805  * Returns:     ---
806  *
807  * Use:         Closes down the user database.
808  */
809
810 void userdb_end(void)
811 {
812   userdb__clearMap(&userdb__users, userdb_freeUser);
813   userdb__clearMap(&userdb__groups, userdb_freeGroup);
814 }
815
816 /*----- Test rig ----------------------------------------------------------*/
817
818 #ifdef TEST_RIG
819
820 void dumpit(const char *msg)
821 {
822   trace(TRACE_DEBUG, "debug: %s", msg);
823
824   {
825     struct passwd *pw;
826     for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
827       userdb__dumpUser(pw);
828   }
829
830   {
831     struct group *gr;
832     for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
833       userdb__dumpGroup(gr);
834   }
835 }
836
837 int main(void)
838 {
839   ego("userdb-test");
840   traceon(stdout, TRACE_ALL);
841   userdb_init();
842   userdb_local();
843   userdb_yp();
844   dumpit("spong");
845 /*  printf("loaded (%lu)\n", track_memused()); */
846   getchar();
847   for (;;) {
848     userdb_end();
849 /*    printf("cleared (%lu)\n", track_memused()); */
850 /*    track_memlist(); */
851     userdb_init();
852     userdb_local();
853     userdb_yp();
854 /*    printf("reloaded (%lu)\n", track_memused()); */
855     getchar();
856   }
857   return (0);
858 }
859
860 #endif
861
862 /*----- That's all, folks -------------------------------------------------*/