afsconfig-and-rcsid-all-around-20010705
[openafs.git] / src / package / conftree.c
1 /*
2  * (C) Copyright Transarc Corporation 1989
3  * Licensed Materials - Property of Transarc
4  * All Rights Reserved.
5  */
6
7 /*------------------------------------------------------------------------
8  * conftree.c
9  *
10  * Description:
11  *      Configuration tree module for package, the AFS workstation
12  *      configuration tool.
13  *
14  * Author:
15  *      Transarc Corporation & Carnegie Mellon University
16  *------------------------------------------------------------------------*/
17
18 #include <afs/param.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/param.h>
23 #include <pwd.h>
24 #include <grp.h>
25
26 #include "globals.h"
27 #include "package.h"
28
29 #include "validupdates.h"
30
31 char *emalloc();
32 FILE *efopen();
33 char *strcpy();
34 extern FILE *yyin;  /*Input file for the YACC parser*/
35
36 /*------------------------------------------------------------------------
37  * [static] namehash
38  *
39  * Description:
40  *      Returns a hash value for the given name.
41  *
42  * Arguments:
43  *      char *name : Ptr to name to hash.
44  *
45  * Returns:
46  *      Hash value associated with the given name.
47  *
48  * Environment:
49  *      This routine is private to the module.
50  *
51  * Side Effects:
52  *      As advertised.
53  *------------------------------------------------------------------------*/
54
55 static int namehash(name)
56     register char *name;
57
58 { /*namehash*/
59
60     register int hash;
61
62     hash = 0;
63     while (*name != '\0')
64         hash += (hash << 6) + *name++;
65     return(hash);
66
67 } /*namehash*/
68
69 /*------------------------------------------------------------------------
70  * [static] AllocConfigNode
71  *
72  * Description:
73  *       Allocate storage for a configuration tree node.
74  *
75  * Arguments:
76  *      None.
77  *
78  * Returns:
79  *      Ptr to the freshly-allocated node if successful,
80  *      Otherwise we'll exit the program.
81  *
82  * Environment:
83  *      This routine is private to the module.
84  *
85  * Side Effects:
86  *      May exit from package entirely.
87  *------------------------------------------------------------------------*/
88
89 static CTREEPTR AllocConfigNode()
90
91 { /*AllocConfigNode*/
92
93     register CTREEPTR np;
94
95     np = (CTREEPTR) emalloc(sizeof(CTREE));
96     bzero((char *)np, sizeof(CTREE));
97     return(np);
98
99 } /*AllocConfigNode*/
100
101 /*------------------------------------------------------------------------
102  * [static] ValidUpdtSpec
103  *
104  * Description:
105  *      Checks whether the given update specification is valid for a file
106  *      of the given filetype.
107  *
108  * Arguments:
109  *      u_short ftype : The type of file to be updated.
110  *      u_short uspec : The update spec to check.
111  *
112  * Returns:
113  *      TRUE if the check succeeds,
114  *      FALSE otherwise.
115  *
116  * Environment:
117  *      This routine is private to the module.
118  *
119  * Side Effects:
120  *      As advertised.
121  *------------------------------------------------------------------------*/
122
123 static int ValidUpdtSpec(ftype, uspec)
124     u_short ftype;
125     u_short uspec;
126
127 { /*ValidUpdtSpec*/
128
129     register struct updatetype *u;
130
131     /*
132       * Scan the list of valid update specs, succeed if you find an
133       * exact match.
134       */
135     for (u = validupdates; u->filetype != 0; u++)
136         if ((u->filetype == ftype) && (u->updtflags == uspec))
137             return(TRUE);
138     return(FALSE);
139
140 } /*ValidUpdtSpec*/
141
142 /*------------------------------------------------------------------------
143  * [static] ValidateUserName
144  *
145  * Description:
146  *      Given a pointer to a user name, see if that name is a valid one
147  *      by possibly looking in the password file.  If it's valid, return
148  *      the associated uid via the uidp parameter.
149  *
150  * Arguments:
151  *      char *name  : Ptr to user name to validate.
152  *      short *uidp : Matching uid is placed here upon success.
153  *
154  * Returns:
155  *      TRUE on success,
156  *      FALSE otherwise.
157  *
158  * Environment:
159  *      This routine is private to the module.
160  *
161  * Side Effects:
162  *      The passwd structure pointer, pw, is a static.  Thus, we
163  *      recall the last search into the password file.  Before
164  *      doing a lookup, we check to see if we've lucked out and
165  *      the results are already cached.
166  *------------------------------------------------------------------------*/
167
168 static int ValidateUserName(name, uidp)
169     register char *name;
170     register short *uidp;
171
172 { /*ValidateUserName*/
173
174     register afs_int32 uid;
175     char *nptr;
176     static struct passwd *pw = NULL;    /*Ptr to passwd file entry*/
177     
178     /*
179       * We always know what root is; don't bother with a passwd lookup
180       * in this case.
181       */
182     if (strcmp(name, "root") == 0) {
183         *uidp = 0;
184         return (TRUE);
185     }
186
187     /*
188       * If we have the results from a previous search, don't do it
189       * again.  Otherwise, take the plunge.
190       */
191     if (pw == NULL || strcmp(pw->pw_name, name))
192       pw = getpwnam(name);
193
194     if (pw == NULL) {
195         uid = strtol(name, &nptr, 10);
196         if ((int)(nptr-name) == strlen(name))
197             pw = getpwuid((uid_t)uid);
198     }
199
200     if (pw != NULL) {
201       /*
202         * Found the given name.  Return the matching pid.
203         */
204         *uidp = pw->pw_uid;
205         return(TRUE);
206       }
207     else
208       /*
209         * Abject failure.
210         */
211       return(FALSE);
212
213 } /*ValidateUserName*/
214
215 /*------------------------------------------------------------------------
216  * [static] ValidateGroupName
217  *
218  * Description:
219  *      Given a pointer to a group name, see if that name is a valid one
220  *      by possibly looking in the group file.  If it's valid, return
221  *      the associated gid via the gidp parameter.
222  *
223  * Arguments:
224  *      char *name  : Ptr to group name to validate.
225  *      short *gidp : Matching gid is placed here upon success.
226  *
227  * Returns:
228  *      TRUE on success,
229  *      FALSE otherwise.
230  *
231  * Environment:
232  *      This routine is private to the module.
233  *
234  * Side Effects:
235  *      The group structure pointer, gr, is a static.  Thus, we
236  *      recall the last search into the group file.  Before
237  *      doing a lookup, we check to see if we've lucked out and
238  *      the results are already cached.
239  *------------------------------------------------------------------------*/
240
241 static int ValidateGroupName(name, gidp)
242     register char *name;
243     register short *gidp;
244
245 { /*ValidateGroupName*/
246
247     register afs_int32 gid;
248     char *nptr;
249     static struct group *gr = NULL;     /*Ptr to group structure*/
250
251     /*
252       * We always know the group number for wheel, so don't bother doing
253       * any lookups.
254       */
255     if (strcmp(name, "wheel") == 0) {
256         *gidp = 0;
257         return (TRUE);
258     }
259
260     /*
261       * If we have the results from a previous search, don't do it
262       * again.  Otherwise, take the plunge.
263       */
264     if (gr == NULL || strcmp(gr->gr_name, name))
265       gr = getgrnam(name);
266
267     if (gr == NULL) {
268         gid = strtol(name, &nptr, 10);
269         if ((int)(nptr-name) == strlen(name))
270             gr = getgrgid((gid_t)gid);
271     }
272
273     if (gr != NULL) {
274       /*
275         * Found the given group.  Return the matching gid.
276         */
277       *gidp = gr->gr_gid;
278       return(TRUE);
279     }
280     else
281       return(FALSE);
282
283 } /*ValidateGroupName*/
284
285 /*------------------------------------------------------------------------
286  * InitializeConfigTree
287  *
288  * Description:
289  *      Allocates storage for the root of the configuration tree.
290  *
291  * Arguments:
292  *      None.
293  *
294  * Returns:
295  *      0 if successful.  On failure, package exits.
296  *
297  * Environment:
298  *      The newly-allocated space is recorded in the global variable
299  *      config_root.
300  *
301  * Side Effects:
302  *      As described; may exit from package.
303  *------------------------------------------------------------------------*/
304
305 int InitializeConfigTree()
306
307 { /*InitializeConfigTree*/
308
309     config_root = AllocConfigNode();
310     return(0);
311
312 } /*InitializeConfigTree*/
313
314 /*------------------------------------------------------------------------
315  * LocateChildNode
316  *
317  * Description:
318  *      Locate the node corresponding to the given name in the entries
319  *      corresponding to the specified directory node.
320  *
321  * Arguments:
322  *      CTREEPTR dp : Config tree node whose entries are to be searched
323  *                      for the given name.
324  *      char *name  : Ptr to the string name to search for.
325  *      int lmode   : Lookup mode, either C_LOCATE or C_CREATE.
326  *
327  * Returns:
328  *      Ptr to located node if it existed, else
329  *      Ptr to newly-created node if no match found and C_CREATE mode used,
330  *      Otherwise a null pointer.  Null will also be returned if the given
331  *              directory node isn't really for a directory.
332  *
333  * Environment:
334  *      Nothing interesting.
335  *
336  * Side Effects:
337  *      May create entries and nodes; may exit package if any of these
338  *      creations fail.
339  *------------------------------------------------------------------------*/
340
341 CTREEPTR LocateChildNode(dp, name, lmode)
342     register CTREEPTR dp;
343     register char *name;
344     register int lmode;
345
346 { /*LocateChildNode*/
347
348     register int hash;          /*Hash value for given name*/
349     register ENTRYPTR ep;       /*Ptr to entry being examined*/
350     register int found_entry;   /*Found entry we want?*/
351
352     /*
353       * First, make sure dp corresponds to a directory
354       */
355     if (dp->type != S_IFDIR)
356         return(NULL);
357
358     /*
359       * Set up to search the entries hanging off the directory node.
360       * Precompute the hash value for the name.
361       */
362     hash = namehash(name);
363     ep = dp->entryp;
364     found_entry = FALSE;
365
366     /*
367       * Sweep through the list of entries until we find our match or
368       * fall off the end.
369       */
370     while ((ep != NULL) && !found_entry) {
371       /*
372         * We compare the hash value first, and only if that succeeds
373         * do we do the string compare.
374         */
375       if ((ep->hash == hash) && (strcmp(ep->name, name) == 0))
376         found_entry = TRUE;
377       else
378         /*
379           * No match.  Move on to the next entry, if any.
380           */
381         ep = ep->nextp;
382
383     } /*Search list of entries*/
384
385     /*
386       * If we found it, return the node hanging off the entry.
387       */
388     if (found_entry)
389       return(ep->nodep);
390
391     /*
392       * We didn't find the given name.  If we aren't supposed to create
393       * a node for the name, we return failure.
394       */
395     if (!(lmode & C_CREATE))
396       return(NULL);
397
398     /*
399       * Create a new entry and node to stand for the given name, link it
400       * in, and return it to our caller.
401       */
402     ep = (ENTRYPTR)emalloc(sizeof(ENTRY));
403     ep->nodep = AllocConfigNode();
404     ep->name = (char *) emalloc((unsigned)(strlen(name)+1));
405     ep->hash = hash;
406     (void)strcpy(ep->name, name);
407     ep->nextp = dp->entryp;
408     dp->entryp = ep;
409     return(ep->nodep);
410
411 } /*LocateChildNode*/
412
413 /*------------------------------------------------------------------------
414  * LocatePathNode
415  *
416  * Description:
417  *      Locates the configuration tree node corresponding to the given
418  *      file or directory.
419  *
420  * Arguments:
421  *      CTREEPTR dp : Config tree node from which search is to begin.
422  *                      (Actually, config_root!!)
423  *      char *path  : Path to be searched for.
424  *      int lmode   : Search mode to use (C_LOCATE, C_CREATE).
425  *
426  * Returns:
427  *      Ptr to located node if it existed, else
428  *      Ptr to newly-created node if no match found and C_CREATE mode used,
429  *      Otherwise a null pointer.  Null will also be returned if the given
430  *              directory node isn't really for a directory.
431  *
432  * Environment:
433  *      The search is breadth-first, plucking out each piece of the path
434  *      from left to right.
435  *
436  * Side Effects:
437  *      Exits from package.
438  *------------------------------------------------------------------------*/
439
440 CTREEPTR LocatePathNode(dp, path, lmode)
441     register CTREEPTR dp;
442     register char *path;
443     register int lmode;
444
445 { /*LocatePathNode*/
446
447     register char *name;        /*Points to start of new subdir/file in path*/
448     register char savech;       /*Saves chars being murdered during search*/
449
450     /*
451       * Skip over leading slashes.
452       */
453     while (*path == '/')
454         path++;
455
456     while (dp != NULL && *path != '\0') {
457         /*
458           * Pull off the leftmost portion of the (remaining) pathname,
459           * then search for it through all the entries for the current
460           * directory node serving as the root of the search.
461           */
462         name = path;
463         while (*path != '\0' && *path != '/')
464             path++;
465         savech = *path; *path = '\0';
466         if ((lmode & C_CREATE) && (dp->type == 0))
467             /*
468               * This is an unfilled non-leaf node.  Mark it as being
469               * a directory node
470               */
471             dp->type = S_IFDIR;
472
473         /*
474           * Look for the name fragment among all entries corresponding
475           * to the root node.
476           */
477         dp = LocateChildNode(dp, name, lmode);
478
479         /*
480           * Restore the char we overwrote with a null, then bump the
481           * path to the start of the next component.
482           */
483         *path = savech;
484         while (*path == '/')
485             path++;
486
487       } /*while loop*/
488
489     /*
490       * dp now contains the path associated with the given path, so
491       * just return it.
492       */
493     return(dp);
494
495 } /*LocatePathNode*/
496
497 /*------------------------------------------------------------------------
498  * BuildConfigTree
499  *
500  * Description:
501  *      Builds a configuration tree from its YACC specification.
502  *
503  * Arguments:
504  *      FILE *f : File containing the configuration info.
505  *
506  * Returns:
507  *      Value returned by the YACC parser (0 for success, else 1), or
508  *      Exits if there is insufficient memory in which to build the tree.
509  *
510  * Environment:
511  *      Nothing interesting.
512  *
513  * Side Effects:
514  *      May exit from package.
515  *------------------------------------------------------------------------*/
516
517 int BuildConfigTree(f)
518     FILE *f;
519
520 { /*BuildConfigTree*/
521
522     int ret;
523
524     yyin = f;
525     ret = yyparse();
526     return(ret);
527
528 }  /*BuildConfigTree*/
529
530 /*------------------------------------------------------------------------
531  * [static] AddEntry
532  *
533  * Description:
534  *      Adds an entry to the configuration tree.
535  *
536  * Arguments:
537  *      u_short filetype    : Specifies type of file to be updated (regular
538  *                              file, directory, device, etc.)
539  *      u_short updtspec    : Specifies actions during update, if necessary.
540  *      char *filename      : Name of file to be updated.
541  *      PROTOTYPE prototype : Prototype for filename (e.g., dev numbers,
542  *                              directory prefix, etc.)
543  *      OWNER ownershipinfo : Ownership info for filename.
544  *      MODE mode           : Protection (mode) bits for filename.
545  *
546  * Returns:
547  *      0 on success,
548  *      Error value otherwise.
549  *      If there is insufficient memory to add the entry, or if invalid
550  *              parameters are encountered, the entire program exits.
551  *
552  * Environment:
553  *      Searches always start from the root of the configuration tree.
554  *
555  * Side Effects:
556  *      As advertised; may exit from package.
557  *------------------------------------------------------------------------*/
558
559 int AddEntry(filetype, updtspec, filename, prototype, ownershipinfo, mode)
560     u_short filetype;
561     u_short updtspec;
562     char *filename;
563     PROTOTYPE prototype;
564     OWNER ownershipinfo;
565     MODE mode;
566
567 { /*AddEntry*/
568
569     CTREEPTR np;        /*Ptr to config tree node holding info on filename*/
570     short uid, gid;     /*Uid, gid returned from validation functions*/
571     
572     debug_message("[AddEntry] Called for filename %s", filename);
573     
574     /*
575      * Check that the given update specification is a legal one.
576      */
577     if (ValidUpdtSpec(filetype, updtspec) == FALSE)
578         fatal("** Invalid update specification for file %s", filename);
579     
580     /*
581      * Find the node corresponding to the given filename, creating one if
582      * necessary.
583      */
584     if ((np = LocatePathNode(config_root, filename, C_LOCATE|C_CREATE)) == NULL)
585         fatal("** Invalid path encountered: %s", filename);
586     
587     /*
588      * Start adding entries to np after checking for potential conflicts.
589      *
590      * Should we print out a warning if the same file appears twice even
591      * in the absence of a type confict?
592      */
593     if ((np->flag & F_TYPE) && (np->type != filetype))
594         fatal("** Type conflict for file %s", filename);
595     
596     np->flag |= F_TYPE;
597     np->type = filetype;
598     
599 #if 0
600     if ((np->flag & F_UPDT) && (np->updtspec != updtspec))
601         fatal("** Update specification conflict for file %s", filename);
602     else
603 #endif /* 0 */
604     {
605         np->flag |= F_UPDT;
606         np->updtspec |= updtspec;
607     }
608     
609     if ((filetype == S_IFCHR)
610         || (filetype == S_IFBLK)
611 #ifdef S_IFIFO
612         || (filetype == S_IFIFO)
613 #endif /* S_IFIFO */
614         ) {
615         /*
616          * Device prototype
617          */
618         if ((np->flag & F_PROTO) && (prototype.flag != P_NONE)) {
619             if ((prototype.flag != P_DEV)
620                 || ((np->proto.info.rdev != prototype.info.rdev)))
621                 fatal("** Device number conflict for device %s", filename);
622         }
623         else
624             if (prototype.flag == P_FILE)
625                 fatal("** Prototype conflict for device %s", filename);
626             else
627                 if (prototype.flag == P_DEV) {
628                     np->flag |= F_PROTO;
629                     np->proto.flag = P_DEV;
630                     np->proto.info.rdev = prototype.info.rdev;
631                 }
632     }
633     else {
634         /*
635          * File prototype, if any
636          */
637         if ((np->flag & F_PROTO) && (prototype.flag != P_NONE)) {
638             if ((prototype.flag != P_FILE)
639                 || (strcmp(np->proto.info.path, prototype.info.path)))
640                 fatal("** Prototype conflict for file %s", filename);
641         }
642         else
643             if (prototype.flag == P_DEV)
644                 fatal("** Prototype conflict for file %s", filename);
645             else
646                 if (prototype.flag == P_FILE) {
647                     np->flag |= F_PROTO;
648                     np->proto.flag = P_FILE;
649                     np->proto.info.path =
650                         emalloc((unsigned)(strlen(prototype.info.path)+1));
651                     (void) strcpy(np->proto.info.path, prototype.info.path);
652                 }
653     }
654     
655     if (ownershipinfo.username != NULL) {
656         /*
657          * Ownership info, if any
658          */
659         if (ValidateUserName(ownershipinfo.username, &uid) == FALSE)
660             fatal("** Unknown user %s for file %s",
661                   ownershipinfo.username, filename);
662         else
663             if ((np->flag & F_UID) && (np->uid != uid))
664                 fatal("** Uid conflict for file %s (new val: %d, old val: %d)",
665                       filename, np->uid, uid);
666             else {
667                 np->flag |= F_UID;
668                 np->uid = uid;
669             }
670     } /*Process user ownership info*/
671     
672     if (ownershipinfo.groupname != NULL) {
673         if (ValidateGroupName(ownershipinfo.groupname, &gid) == FALSE)
674             fatal("** Unknown group %s for file %s",
675                   ownershipinfo.groupname, filename);
676         else
677             if ((np->flag & F_GID) && (np->gid != gid))
678                 fatal("** Gid conflict for file %s (new val: %d, old val: %d)",
679                       filename, np->gid, gid);
680             else {
681                 np->flag |= F_GID; np->gid = gid;
682             }
683     } /*Process group ownership info*/
684     
685     if (mode.inherit_flag != TRUE) {
686         if (mode.modeval > (u_short)~S_IFMT)
687             fatal("** Bad mode %d for file %s", mode.modeval, filename);
688         if ((np->flag & F_MODE) && ((np->mode & ~S_IFMT) != mode.modeval))
689             fatal("** Mode conflict for file %s", filename);
690         else {
691             np->flag |= F_MODE;
692             np->mode |= mode.modeval;
693         }
694     } /*Mode inherit flag turned off*/
695     
696     /*
697      * If we reached this point, everything must have been OK
698      */
699     return(0);
700
701 }  /*AddEntry*/
702
703 /*------------------------------------------------------------------------
704  * ApplyConfigTree
705  *
706  * Description:
707  *      Apply the given function to each node of the configuration tree
708  *      in pre-order fashion.
709  *
710  * Arguments:
711  *      int (*func)() : Function to apply.
712  *
713  * Returns:
714  *      Void.
715  *
716  * Environment:
717  *      Nothing interesting.
718  *
719  * Side Effects:
720  *      Whatever the given function does.
721  *------------------------------------------------------------------------*/
722
723 void ApplyConfigTree(func)
724     int (*func)();
725
726 { /*ApplyConfigTree*/
727
728     char *path; /*Path to pass on down*/
729
730     /*
731       * Create room for the largest path possible, and set it to the
732       * null string.  This forces the application to be started at
733       * ``/''.
734       */
735     path = (char *)emalloc(MAXPATHLEN+1);
736     path[0] = '\0';
737
738     TraverseConfigTree(config_root, path, func);
739
740 } /*ApplyConfigTree*/
741
742 /*------------------------------------------------------------------------
743  * TraverseConfigTree
744  *
745  * Description:
746  *      Traverses the subtree of the configuration tree rooted at np
747  *      in pre-order fashion, applying function func to each node.
748  *
749  * Arguments:
750  *      CTREEPTR np   : Root of config tree to traverse.
751  *      char *path    : Path on which to start traversal.
752  *      int (*func)() : Function to apply to each node.
753  *
754  * Returns:
755  *      Void.
756  *
757  * Environment:
758  *      Nothing interesting.
759  *
760  * Side Effects:
761  *      Whatever func might do.
762  *------------------------------------------------------------------------*/
763
764 TraverseConfigTree(np, path, func)
765     register CTREEPTR np;
766     char *path;
767     int (*func)();
768
769 { /*TraverseConfigTree*/
770
771     register char *endp;        /*Marks the end of a string*/
772     register ENTRYPTR ep;       /*Current entry pointer*/
773     register int len;           /*Length of the pathname*/
774
775     /*
776       * If the path is empty, start it off with "/".
777       */
778     len = strlen(path);
779     if (len == 0)
780         (void) strcpy(path, "/");
781
782     /*
783       * Apply the function to the current node.
784       */
785     (void)(*func)(np, path);
786
787     if (len == 0)
788         (void) strcpy(path, "");
789
790     /*
791       * If we've reached a leaf node (a non-directory), start heading
792       * back up.
793       */
794     if ((np->type) != S_IFDIR)
795       return;
796
797     /*
798       * We're currently at a directory node.  For each entry in the entry
799       * list for this node, conjure up the name associated with the entry's
800       * node and call ourselves recursively.  This calling sequence gives
801       * us the preorder traversal.
802       */
803     endp = path + len;
804     *endp++ = '/';
805     *endp = 0;
806     for (ep = np->entryp; ep; ep = ep->nextp) {
807       /*
808         * Tack on the node's name component to the end of the path and
809         * descend.
810         */
811       (void) strcpy(endp, ep->name);
812       TraverseConfigTree(ep->nodep, path, func);
813     }
814
815     /*
816       * We've finished the preorder walk under this node.  Terminate
817       * the path properly before returning.
818       */
819     *--endp = 0;
820
821 }  /*TraverseConfigTree*/