Sources placed under CVS control.
[become] / src / userdb.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
3 * $Id: userdb.c,v 1.1 1997/07/21 13:47:43 mdw Exp $
4 *
5 * User database management
6 *
7 * (c) 1997 EBI
8 */
9
10/*----- Licencing 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
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: userdb.c,v $
32 * Revision 1.1 1997/07/21 13:47:43 mdw
33 * Initial revision
34 *
35 */
36
37/*----- Header files ------------------------------------------------------*/
38
39/* --- ANSI headers --- */
40
41#include <ctype.h>
42#include <errno.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47/* --- Unix headers --- */
48
49#include <sys/types.h>
50
51#include <grp.h>
52#include <pwd.h>
53#include <unistd.h>
54
55/* --- Local headers --- */
56
57#include "config.h"
58#include "sym.h"
59#include "userdb.h"
60#include "utils.h"
61
62/*----- Type definitions --------------------------------------------------*/
63
64/* --- A map link --- */
65
66typedef struct userdb__node {
67 struct userdb__node *next;
68 void *rec;
69} userdb__node;
70
71/* --- A reference to a real record --- */
72
73typedef struct userdb__sym {
74 sym_base _base;
75 void *rec;
76} userdb__sym;
77
78/* --- A name- and number-mapping --- */
79
80typedef struct userdb__map {
81 sym_table nmap;
82 sym_table idmap;
83 userdb__node *list;
84} userdb__map;
85
86/*----- Static variables --------------------------------------------------*/
87
88static userdb__map userdb__users; /* Map of user info blocks */
89static sym_iter userdb__useri; /* Iterator for users */
90static userdb__map userdb__groups; /* Map of group info blocks */
91static sym_iter userdb__groupi; /* Iterator for groups */
92
93/*----- Map management functions ------------------------------------------*/
94
95/* --- @userdb__createMap@ --- *
96 *
97 * Arguments: @userdb__map *m@ = pointer to a map block
98 *
99 * Returns: ---
100 *
101 * Use: Initialises a map table.
102 */
103
104static void userdb__createMap(userdb__map *m)
105{
106 sym_createTable(&m->nmap);
107 sym_createTable(&m->idmap);
108 m->list = 0;
109}
110
111/* --- @userdb__addToMap@ --- *
112 *
113 * Arguments: @userdb__map *m@ = pointer to the map block
114 * @const char *name@ = pointer to the item's name
115 * @int id@ = the item's id number
116 * @void *rec@ = pointer to the actual record
117 *
118 * Returns: ---
119 *
120 * Use: Adds an item to the given map.
121 */
122
123static void userdb__addToMap(userdb__map *m,
124 const char *name,
125 int id, void *rec)
126{
127 unsigned f;
128 userdb__sym *s;
129 userdb__node *n;
130
131 s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
132 if (!f)
133 s->rec = rec;
134
135 s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
136 if (!f)
137 s->rec = rec;
138
139 n = xmalloc(sizeof(*n));
140 n->rec = rec;
141 n->next = m->list;
142 m->list = n;
143}
144
145/* --- @userdb__byName@ --- *
146 *
147 * Arguments: @userdb__map *m@ = pointer to a map block
148 * @const char *name@ = name to look up
149 *
150 * Returns: A pointer to the appropriate block, or zero if not found.
151 *
152 * Use: Looks up a name in a mapping and returns the result.
153 */
154
155static void *userdb__byName(userdb__map *m, const char *name)
156{
157 userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
158 return (s ? s->rec : 0);
159}
160
161/* --- @userdb__byId@ --- *
162 *
163 * Arguments: @userdb__map *m@ = pointer to a map block
164 * @int id@ = id number to find
165 *
166 * Returns: A pointer to the appropriate block, or zero if not found.
167 *
168 * Use: Looks up an ID in a mapping, and returns the result.
169 */
170
171static void *userdb__byId(userdb__map *m, int id)
172{
173 userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
174 return (s ? s->rec : 0);
175}
176
177/* --- @userdb__clearMap@ --- *
178 *
179 * Arguments: @userdb__map *m@ = pointer to a map block
180 * @void (*freerec)(void *rec)@ = pointer to a free-record proc
181 *
182 * Returns: ---
183 *
184 * Use: Clears a map, emptying it and releasing the memory it
185 * occupied.
186 */
187
188static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
189{
190 userdb__node *n, *t;
191
192 sym_destroyTable(&m->nmap);
193 sym_destroyTable(&m->idmap);
194
195 for (n = m->list; n; n = t) {
196 t = n->next;
197 freerec(n->rec);
198 free(n);
199 }
200}
201
202/*----- User and group block management -----------------------------------*/
203
204/* --- @userdb__dumpUser@ --- *
205 *
206 * Arguments: @const struct passwd *pw@ = pointer to a user block
207 * @FILE *fp@ = pointer to stream to write on
208 *
209 * Returns: ---
210 *
211 * Use: Writes a user's informationt to a stream.
212 */
213
214#ifndef NDEBUG
215
216static void userdb__dumpUser(const struct passwd *pw, FILE *fp)
217{
218 printf("\n"
219 "*** name == %s\n"
220 "*** passwd == %s\n"
221 "*** uid == %i\n"
222 "*** gid == %i\n"
223 "*** gecos == %s\n"
224 "*** home == %s\n"
225 "*** shell == %s\n",
226 pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid,
227 pw->pw_gecos, pw->pw_dir, pw->pw_shell);
228}
229
230#else
231
232#define userdb__dumpUser(pw, fp) ((void)0)
233
234#endif
235
236/* --- @userdb_copyUser@ --- *
237 *
238 * Arguments: @struct passwd *pw@ = pointer to block to copy
239 *
240 * Returns: Pointer to the copy.
241 *
242 * Use: Copies a user block. The copy is `deep' so all the strings
243 * are copied too. Free the copy with @userdb_freeUser@ when
244 * you don't want it any more.
245 */
246
247struct passwd *userdb_copyUser(struct passwd *pw)
248{
249 struct passwd *npw;
250
251 if (!pw)
252 return (0);
253
254 npw = xmalloc(sizeof(*npw));
255
256 npw->pw_name = xstrdup(pw->pw_name);
257 npw->pw_passwd = xstrdup(pw->pw_passwd);
258 npw->pw_uid = pw->pw_uid;
259 npw->pw_gid = pw->pw_gid;
260 npw->pw_gecos = xstrdup(pw->pw_gecos);
261 npw->pw_dir = xstrdup(pw->pw_dir);
262 npw->pw_shell = xstrdup(pw->pw_shell);
263
264 return (npw);
265}
266
267/* --- @userdb__buildUser@ --- *
268 *
269 * Arguments: @char *s@ = pointer to user string
270 *
271 * Returns: Pointer to a user block.
272 *
273 * Use: Converts a line from a user file into a password entry.
274 * Note that the string is corrupted by @strtok@ while it gets
275 * parsed.
276 */
277
278static struct passwd *userdb__buildUser(char *s)
279{
280 struct passwd *pw = xmalloc(sizeof(*pw));
281
282 s = strtok(s, ":"); if (!s) goto tidy_0; pw->pw_name = xstrdup(s);
283 s = strtok(0, ":"); if (!s) goto tidy_1; pw->pw_passwd = xstrdup(s);
284 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_uid = atoi(s);
285 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gid = atoi(s);
286 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gecos = xstrdup(s);
287 s = strtok(0, ":"); if (!s) goto tidy_3; pw->pw_dir = xstrdup(s);
288 s = strtok(0, ":"); if (!s) goto tidy_4; pw->pw_shell = xstrdup(s);
289 return (pw);
290
291 /* --- Error handling --- */
292
293tidy_4:
294 free(pw->pw_dir);
295tidy_3:
296 free(pw->pw_gecos);
297tidy_2:
298 free(pw->pw_passwd);
299tidy_1:
300 free(pw->pw_name);
301tidy_0:
302 free(pw);
303
304 return (0);
305}
306
307/* --- @userdb_freeUser@ --- *
308 *
309 * Arguments: @void *rec@ = pointer to a user record
310 *
311 * Returns: ---
312 *
313 * Use: Frees a user record.
314 */
315
316void userdb_freeUser(void *rec)
317{
318 struct passwd *pw;
319
320 if (!rec)
321 return;
322
323 pw = rec;
324 free(pw->pw_name);
325 free(pw->pw_passwd);
326 free(pw->pw_gecos);
327 free(pw->pw_dir);
328 free(pw->pw_shell);
329 free(pw);
330}
331
332/* --- @userdb__dumpGroup@ --- *
333 *
334 * Arguments: @const struct group *gr@ = pointer to a group block
335 * @FILE *fp@ = pointer to stream to write on
336 *
337 * Returns: ---
338 *
339 * Use: Writes a group's information to a stream.
340 */
341
342#ifndef NDEBUG
343
344static void userdb__dumpGroup(const struct group *gr, FILE *fp)
345{
346 char *const *p;
347
348 printf("\n"
349 "*** name == %s\n"
350 "*** passwd == %s\n"
351 "*** gid == %i\n"
352 "*** members...\n",
353 gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
354 for (p = gr->gr_mem; *p; p++)
355 printf("*** %s\n", *p);
356}
357
358#else
359
360#define userdb__dumpUser(pw, fp) ((void)0)
361
362#endif
363
364/* --- @userdb_copyGroup@ --- *
365 *
366 * Arguments: @struct group *gr@ = pointer to group block
367 *
368 * Returns: Pointer to copied block
369 *
370 * Use: Copies a group block. The copy is `deep' so all the strings
371 * are copied too. Free the copy with @userdb_freeGroup@ when
372 * you don't want it any more.
373 */
374
375struct group *userdb_copyGroup(struct group *gr)
376{
377 struct group *ngr;
378 int i, max;
379
380 if (!gr)
381 return (0);
382
383 ngr = xmalloc(sizeof(*ngr));
384
385 ngr->gr_name = xstrdup(gr->gr_name);
386 ngr->gr_passwd = xstrdup(gr->gr_passwd);
387 ngr->gr_gid = gr->gr_gid;
388
389 for (max = 0; gr->gr_mem[max]; max++)
390 ;
391 ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
392 for (i = 0; i < max; i++)
393 ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
394 ngr->gr_mem[max] = 0;
395
396 return (ngr);
397}
398
399/* --- @userdb__buildGroup@ --- *
400 *
401 * Arguments: @char *s@ = pointer to group line string
402 *
403 * Returns: Pointer to a group block
404 *
405 * Use: Parses an entry in the groups file. The string is garbled
406 * by @strtok@ as we go.
407 */
408
409static struct group *userdb__buildGroup(char *s)
410{
411 struct group *gr = xmalloc(sizeof(*gr));
412 char *p;
413 int i;
414
415 /* --- Do the easy bits --- */
416
417 s = strtok(s, ":"); if (!s) goto tidy_0; gr->gr_name = xstrdup(s);
418 s = strtok(0, ":"); if (!s) goto tidy_1; gr->gr_passwd = xstrdup(s);
419 s = strtok(0, ":"); if (!s) goto tidy_2; gr->gr_gid = atoi(s);
420
421 /* --- Find the start of the member list --- */
422
423 s = strtok(0, "");
424 if (!s)
425 goto tidy_2;
426
427 /* --- Count the number of members --- */
428
429 p = s;
430 i = 0;
431 for (;;) {
432 i++;
433 if ((p = strpbrk(p, ",")) == 0)
434 break;
435 p++;
436 }
437
438 /* --- Allocate the block and fill it --- */
439
440 gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
441 i = 0;
442 s = strtok(s, ",");
443 do {
444 gr->gr_mem[i++] = xstrdup(s);
445 s = strtok(0, ",");
446 } while (s);
447 gr->gr_mem[i] = 0;
448
449 return (gr);
450
451 /* --- Various tidying-up things --- */
452
453tidy_2:
454 free(gr->gr_passwd);
455tidy_1:
456 free(gr->gr_name);
457tidy_0:
458 free(gr);
459
460 return (0);
461}
462
463/* --- @userdb_freeGroup@ --- *
464 *
465 * Arguments: @void *rec@ = pointer to a group record
466 *
467 * Returns: ---
468 *
469 * Use: Frees a group record.
470 */
471
472void userdb_freeGroup(void *rec)
473{
474 struct group *gr;
475 char **p;
476
477 if (!rec)
478 return;
479
480 gr = rec;
481 free(gr->gr_name);
482 free(gr->gr_passwd);
483 for (p = gr->gr_mem; *p; p++)
484 free(*p);
485 free(gr->gr_mem);
486 free(gr);
487}
488
489/*----- Higher-level functions --------------------------------------------*/
490
491/* --- @userdb_local@ --- *
492 *
493 * Arguments: ---
494 *
495 * Returns: ---
496 *
497 * Use: Reads the local list of users into the maps.
498 */
499
500void userdb_local(void)
501{
502 D( printf("adding local users...\n"); )
503
504 /* --- Fetch users first --- */
505
506 {
507 struct passwd *pw;
508
509 setpwent();
510 while ((pw = getpwent()) != 0) {
511 D( userdb__dumpUser(pw, stdout); )
512 if (!userdb__byName(&userdb__users, pw->pw_name))
513 userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
514 userdb_copyUser(pw));
515 }
516 endpwent();
517 }
518
519 /* --- Then fetch groups --- */
520
521 {
522 struct group *gr;
523
524 setgrent();
525 while ((gr = getgrent()) != 0) {
526 D( userdb__dumpGroup(gr, stdout); )
527 if (!userdb__byName(&userdb__groups, gr->gr_name))
528 userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
529 userdb_copyGroup(gr));
530 }
531 endgrent();
532 }
533}
534
535/* --- @userdb__getLine@ --- *
536 *
537 * Arguments: @char *buff@ = pointer to buffer to read into
538 * @size_t sz@ = size of the buffer
539 * @FILE *fp@ = pointer to stream to read on
540 *
541 * Returns: Zero if something didn't work.
542 *
543 * Use: Reads a line into the buffer. If the line didn't fit, bits
544 * of it are thrown away. The newline character is not
545 * included.
546 */
547
548static char *userdb__getLine(char *buff, size_t sz, FILE *fp)
549{
550 if ((buff = fgets(buff, sz, fp)) == 0)
551 return (0);
552 sz = strlen(buff) - 1;
553 if (buff[sz] == '\n')
554 buff[sz] = 0;
555 else for (;;) {
556 int ch = getc(fp);
557 if (ch == '\n' || ch == EOF)
558 break;
559 }
560 return (buff);
561}
562
563/* --- @userdb_yp@ --- *
564 *
565 * Arguments: ---
566 *
567 * Returns: ---
568 *
569 * Use: Fetches the YP database of users.
570 */
571
572void userdb_yp(void)
573{
574
575#ifdef HAVE_YP
576
577 char line[1024];
578 FILE *fp;
579
580 D( printf("adding nis users\n"); )
581
582 /* --- First, users --- */
583
584 if ((fp = popen("ypcat passwd", "r")) != 0) {
585 while (userdb__getLine(line, sizeof(line), fp)) {
586 struct passwd *pw;
587
588 if ((pw = userdb__buildUser(line)) != 0) {
589 D( userdb__dumpUser(pw, stdout); )
590 if (userdb__byName(&userdb__users, pw->pw_name))
591 userdb_freeUser(pw);
592 else
593 userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
594 }
595 }
596 pclose(fp);
597 }
598
599 /* --- Next, groups --- */
600
601
602 if ((fp = popen("ypcat group", "r")) != 0) {
603 while (userdb__getLine(line, sizeof(line), fp)) {
604 struct group *gr;
605
606 if ((gr = userdb__buildGroup(line)) != 0) {
607 D( userdb__dumpGroup(gr, stdout); )
608 if (userdb__byName(&userdb__groups, gr->gr_name))
609 userdb_freeGroup(gr);
610 else
611 userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
612 }
613 }
614 pclose(fp);
615 }
616
617#endif
618
619}
620
621/* --- @userdb_userByName@, @userdb_userById@ --- *
622 *
623 * Arguments: @const char *name@ = pointer to user's name
624 * @uid_t id@ = user id to find
625 *
626 * Returns: Pointer to user block, or zero if not found.
627 *
628 * Use: Looks up a user by name or id.
629 */
630
631struct passwd *userdb_userByName(const char *name)
632{ return (userdb__byName(&userdb__users, name)); }
633
634struct passwd *userdb_userById(uid_t id)
635{ return (userdb__byId(&userdb__users, id)); }
636
637/* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
638 *
639 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
640 *
641 * Returns: ---
642 *
643 * Use: Initialises an iteration for the user database.
644 */
645
646void userdb_iterateUsers(void)
647{ userdb_iterateUsers_r(&userdb__useri); }
648
649void userdb_iterateUsers_r(userdb_iter *i)
650{ sym_createIter(i, &userdb__users.nmap); }
651
652/* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
653 *
654 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
655 *
656 * Returns: Pointer to the next user block, or null.
657 *
658 * Use: Returns another user block.
659 */
660
661struct passwd *userdb_nextUser(void)
662{ return (userdb_nextUser_r(&userdb__useri)); }
663
664struct passwd *userdb_nextUser_r(userdb_iter *i)
665{
666 userdb__sym *s = sym_next(i);
667 return (s ? s->rec : 0);
668}
669
670/* --- @userdb_groupByName@, @userdb_groupById@ --- *
671 *
672 * Arguments: @const char *name@ = pointer to group's name
673 * @gid_t id@ = group id to find
674 *
675 * Returns: Pointer to group block, or zero if not found.
676 *
677 * Use: Looks up a group by name or id.
678 */
679
680struct group *userdb_groupByName(const char *name)
681{ return (userdb__byName(&userdb__groups, name)); }
682
683struct group *userdb_groupById(gid_t id)
684{ return (userdb__byId(&userdb__groups, id)); }
685
686/* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
687 *
688 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
689 *
690 * Returns: ---
691 *
692 * Use: Initialises an iteration for the group database.
693 */
694
695void userdb_iterateGroups(void)
696{ userdb_iterateGroups_r(&userdb__groupi); }
697
698void userdb_iterateGroups_r(userdb_iter *i)
699{ sym_createIter(i, &userdb__groups.nmap); }
700
701/* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
702 *
703 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
704 *
705 * Returns: Pointer to the next group block, or null.
706 *
707 * Use: Returns another group block.
708 */
709
710struct group *userdb_nextGroup(void)
711{ return (userdb_nextGroup_r(&userdb__groupi)); }
712
713struct group *userdb_nextGroup_r(userdb_iter *i)
714{
715 userdb__sym *s = sym_next(i);
716 return (s ? s->rec : 0);
717}
718
719/* --- @userdb_init@ --- *
720 *
721 * Arguments: ---
722 *
723 * Returns: ---
724 *
725 * Use: Initialises the user database.
726 */
727
728void userdb_init(void)
729{
730 userdb__createMap(&userdb__users);
731 userdb__createMap(&userdb__groups);
732}
733
734/* --- @userdb_reinit@ --- *
735 *
736 * Arguments: ---
737 *
738 * Returns: ---
739 *
740 * Use: Reinitialises the user database.
741 */
742
743void userdb_reinit(void)
744{
745 userdb__clearMap(&userdb__users, userdb_freeUser);
746 userdb__clearMap(&userdb__groups, userdb_freeGroup);
747 userdb_init();
748}
749
750/*----- Test rig ----------------------------------------------------------*/
751
752#ifdef TEST_RIG
753
754void dumpit(const char *msg)
755{
756 printf("\n\n$$$ %s\n", msg);
757
758 {
759 struct passwd *pw;
760 for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
761 userdb__dumpUser(pw, stdout);
762 }
763
764 {
765 struct group *gr;
766 for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
767 userdb__dumpGroup(gr, stdout);
768 }
769}
770
771int main(void)
772{
773 userdb_init();
774 dumpit("cleared");
775 userdb_local();
776 dumpit("local");
777 userdb_yp();
778 dumpit("yp");
779 return (0);
780}
781
782#endif
783
784/*----- That's all, folks -------------------------------------------------*/