71eb5ea992007886352239a9d70958d27a0b618b
[openafs.git] / src / uss / uss_procs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  *      Implementation of basic procedures for the AFS user account
12  *      facility.
13  */
14
15 /*
16  * --------------------- Required definitions ---------------------
17  */
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 RCSID("$Header$");
22
23 #include "uss_procs.h"          /*Module interface*/
24 #include "uss_common.h"         /*Common defs & operations*/
25 #include "uss_acl.h"            /*ACL-related operations*/
26 #include <errno.h>              /*Unix error codes*/
27 #include <pwd.h>                /*Password info*/
28 #include <sys/stat.h>           /*Stat defs*/
29 #include <dirent.h>             /*Directory package*/
30 #include <sys/file.h>           /*O_EXCL, O_CREAT, etc*/
31 #ifdef  AFS_SUN5_ENV
32 #include <fcntl.h>
33 #endif
34
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #else
38 #ifdef HAVE_STRINGS_H
39 #include <strings.h>
40 #endif
41 #endif
42
43 #include <afs/kautils.h>        /*MAXKTCREALMLEN*/
44
45 extern int errno;
46
47 #undef USS_PROCS_DB
48 #undef USS_PROCS_DB_INSTANCE
49 #undef USS_PROCS_DB_BUILDDIR
50
51 #define uss_procs_MAX_SIZE      2048
52
53 char temp[1000];
54 extern int line;
55
56 static int Copy();
57 static int Echo();
58
59 /*-----------------------------------------------------------------------
60  * EXPORTED uss_procs_BuildDir
61  *
62  * Environment:
63  *      Called from the code generated by the uss grammar.
64  *
65  * Side Effects:
66  *      As advertised.
67  *------------------------------------------------------------------------*/
68
69 afs_int32 uss_procs_BuildDir(a_path, a_mode, a_owner, a_access)
70     char *a_path;
71     char *a_mode;
72     char *a_owner;
73     char *a_access;
74
75 { /*uss_procs_BuildDir*/
76
77     int m, o;
78     char buf[1000];
79     struct uss_subdir *new_dir;
80     struct stat stbuf;
81
82     /*
83      * Don't do anything if there's already a problem.
84      */
85     if (uss_syntax_err)
86         return(1);
87     
88     if (uss_verbose)
89         printf("Building directory '%s'; owner: '%s', ACL: '%s'\n",
90                a_path, a_owner, a_access);
91
92     /*
93      * If we've not been given permission to overwrite things, make sure
94      * the target doesn't exist before doing anything.
95      */
96     if (!uss_OverwriteThisOne) {
97         strcpy(temp, a_path);
98         if (!stat(temp, &stbuf)) {
99             if (uss_verbose)
100                 printf("\t[Directory exists, NOT overwriting it]\n");
101             return(0);
102         }
103     }
104
105     sscanf(a_mode, "%o", &m);
106     o = uss_procs_GetOwner(a_owner);
107 #ifdef USS_PROCS_DB_BUILDDIR
108     printf("DEBUGGING: Owner '%s' is uid %d\n", a_owner, o);
109 #endif /* USS_PROCS_DB_BUILDDIR */
110
111     if (!uss_DryRun) {
112         if (mkdir(a_path, m)) {
113             /*
114              * Can't make the directory.  Complain if the directory doesn't
115              * already exist.
116              */
117             if (errno != EEXIST) {
118                 uss_procs_PrintErr(line,
119                                    "Failed to create directory '%s': %s\n",
120                                    a_path, sys_errlist[errno]);
121                 return(1);
122             } /*Directory didn't exist*/
123         } /*Create the directory*/
124     } /*Not a dry run*/
125     else {
126         if (uss_OverwriteThisOne)
127             fprintf(stderr, "\t[Dry run: mkdir %s, mode %o]\n",
128                     a_path, m);
129     } /*Dry run*/
130
131     if (!uss_DryRun) {
132         if (chmod(a_path, m)) {
133             uss_procs_PrintErr(line,
134                 "Can't chmod() directory '%s' to be '%s' : %s\n",
135                 a_path, a_mode, sys_errlist[errno]);
136                 return(1);
137         } /* chmod the directory */
138         if (chown(a_path, o, -1)) {
139             uss_procs_PrintErr(line,
140                                "Can't chown() directory '%s' to be owned by '%s' (uid %d): %s\n",
141                                a_path, a_owner, o, sys_errlist[errno]);
142             return(1);
143         } /*Couldn't chown*/
144     } /*Not a dry run*/
145     else {
146         fprintf(stderr,
147                 "\t[Dry run: chown() directory '%s' to be owned by user]\n",
148                 a_path);
149     } /*Dry run*/
150
151     /*
152       * Set the ACL for this new directory so that the uss_AccountCreator
153       * is the only party that has rights.  This will be corrected as the
154       * final action performed on the account.
155       */
156     sprintf(buf, "%s %s all", a_path, uss_AccountCreator);
157     if (!uss_DryRun) {
158         if (uss_verbose)
159             fprintf(stderr,
160                     "Setting ACL: '%s'\n", buf);
161         if (uss_acl_SetAccess(buf, 1, 0))
162             return(1);
163     } /*Not a dry run*/
164     else {
165         fprintf(stderr, "\t[Dry run: uss_acl_SetAccess(%s) on '%s']\n",
166                 buf, a_path);
167     } /*Dry run*/
168
169     /*
170      * Use our linked list to remember this directory's true ACL setting so
171      * we may set it correctly at the tail end of the account creation.
172      */
173     new_dir = (struct uss_subdir *) malloc(sizeof(struct uss_subdir));
174     new_dir->previous = uss_currentDir;
175     new_dir->path = (char *) malloc(strlen(a_path)+1);
176     strcpy(new_dir->path, a_path);
177     new_dir->finalACL = (char *) malloc(strlen(a_access)+1);
178     strcpy(new_dir->finalACL, a_access);
179     uss_currentDir = new_dir;
180
181     /*
182      * Return the happy news.
183      */
184     return(0);
185
186 } /*uss_procs_BuildDir*/
187
188
189 /*-----------------------------------------------------------------------
190  * EXPORTED uss_procs_CpFile
191  *
192  * Environment:
193  *      Called from the code generated by the uss grammar.
194  *
195  * Side Effects:
196  *      As advertised.
197  *------------------------------------------------------------------------*/
198
199 afs_int32 uss_procs_CpFile(a_path, a_mode, a_owner, a_proto)
200     char *a_path;
201     char *a_mode;
202     char *a_owner;
203     char *a_proto;
204     
205 { /*uss_procs_CpFile*/
206     
207     int m, o;
208     struct stat stbuf;
209     char *cp;
210
211     /*
212      * Don't do anything if something has already gone wrong.
213      */
214     if (uss_syntax_err)
215         return(1);
216
217     if (uss_verbose)
218         printf("Installing '%s'\n", a_path);
219
220     /*
221      * If we've not been given permission to overwrite things, make sure
222      * the target doesn't exist before doing anything.
223      */
224     if (!uss_OverwriteThisOne) {
225         strcpy(temp, a_path);
226         if (!stat(temp, &stbuf)) {
227             if (uss_verbose)
228                 printf("\t[Entry exists, NOT overwriting it]\n");
229             return(0);
230         }
231     }
232
233     sscanf(a_mode, "%o", &m);
234     o = uss_procs_GetOwner(a_owner);
235
236     strcpy(temp, a_proto);
237
238     if (stat(temp, &stbuf)) {
239         uss_procs_PrintErr(line, "Failed to stat '%s': %s\n",
240                            a_proto, sys_errlist[errno]);
241         return(1);
242     }
243
244     if (stbuf.st_mode & S_IFDIR) {
245         if ((cp = strrchr(a_path, '/')) == NULL) {
246             strcat(a_proto, "/");
247             strcat(a_proto, a_path);
248         }
249         else {
250             /*
251              * Append the last part (file name).
252              */
253             strcat(a_proto, cp);
254         }
255     } /*Target is a directory*/
256
257     if (!uss_DryRun) {
258         if (Copy(a_proto, a_path, m)) {
259             uss_procs_PrintErr(line,
260                                "Failed to copy '%s' to '%s'\n",
261                                a_proto, a_path);
262             return(1);
263         } /*Copy failed*/
264     } /*Not a dry run*/
265     else {
266         fprintf(stderr, "\t[Dry run: Copying '%s' to '%s', mode %o]\n",
267                 a_proto, a_path, m);
268     }
269
270     if (!uss_DryRun) {
271         if (chown(a_path, o, -1)) {
272             uss_procs_PrintErr(line,
273                                "Can't chown() file '%s' to be owned by '%s' (uid %d): %s\n",
274                                a_path, a_owner, o, sys_errlist[errno]);
275             return(1);
276         } /*chown failed*/
277     } /*Not a dry run*/
278     else {
279         fprintf(stderr, "\t[Dry run: chown() file '%s' to be owned by user]\n",
280                 a_path);
281     } /*Dry run*/
282
283     /*
284      * Return the happy news.
285      */
286     return(0);
287     
288 } /*uss_procs_CpFile*/
289
290
291 /*-----------------------------------------------------------------------
292  * EXPORTED uss_procs_EchoToFile
293  *
294  * Environment:
295  *      Called from the code generated by the uss grammar.
296  *
297  * Side Effects:
298  *      As advertised.
299  *------------------------------------------------------------------------*/
300
301 afs_int32 uss_procs_EchoToFile(a_path, a_mode, a_owner, a_content)
302     char *a_path;
303     char *a_mode;
304     char *a_owner;
305     char *a_content;
306     
307 { /*uss_procs_EchoToFile*/
308     
309     int m, o;
310     struct stat stbuf;
311     
312     /*
313      * Don't do anything if something has already gone wrong.
314      */
315     if (uss_syntax_err)
316         return(1);
317     
318     if (uss_verbose)
319         printf("Echoing to '%s'\n", a_path);
320     
321     /*
322      * If we've not been given permission to overwrite things, make sure
323      * the target doesn't exist before doing anything.
324      */
325     if (!uss_OverwriteThisOne) {
326         strcpy(temp, a_path);
327         if (!stat(temp, &stbuf)) {
328             if (uss_verbose)
329                 printf("\t[Entry exists, NOT overwriting it]\n");
330             return(0);
331         }
332     }
333     
334     sscanf(a_mode, "%o", &m);
335     o = uss_procs_GetOwner(a_owner);
336     
337     if (!uss_DryRun) {
338         if (Echo(a_content, a_path, m)){
339             uss_procs_PrintErr(line,
340                                "Failed to echo string '%s' to file '%s'\n",
341                                a_content, a_path);
342             return(1);
343         }
344     } /*Not a dry run*/
345     else {
346         fprintf(stderr, "\t[Dry run: Echoing '%s' into file '%s']\n",
347                 a_content, a_path);
348     } /*Dry run*/
349     
350     if (!uss_DryRun) {
351         if (chown(a_path, o, -1)){
352             uss_procs_PrintErr(line,
353                                "Can't chown() file '%s' to be owned by '%s' (uid %d): %s\n",
354                                a_path, a_owner, o, sys_errlist[errno]);
355             return(1);
356         }
357     } /*Not a dry run*/
358     else {
359         fprintf(stderr,
360                 "\t[Dry run: chown() file '%s' to be owned by user]\n",
361                 a_path);
362     } /*Dry run*/
363     
364     /*
365      * Return the happy news.
366      */
367     return(0);
368     
369 } /*uss_procs_EchoToFile*/
370
371
372 /*-----------------------------------------------------------------------
373  * EXPORTED uss_procs_Exec
374  *
375  * Environment:
376  *      Called from the code generated by the uss grammar, as well as
377  *      from uss.c itself.
378  *
379  * Side Effects:
380  *      As advertised.
381  *------------------------------------------------------------------------*/
382
383 afs_int32 uss_procs_Exec(a_command)
384     char *a_command;
385
386 { /*uss_procs_Exec*/
387
388     if (uss_verbose)
389       printf("Running '%s'\n", a_command);
390
391     if (!uss_DryRun) {
392       if (system(a_command)) {
393         uss_procs_PrintErr(line,
394                            "Failed to run the '%s' command: %s\n",
395                            a_command, sys_errlist[errno]);
396         return(1);
397       }
398     } /*Not a dry run*/
399     else {
400       fprintf(stderr, "\t[Dry run: executing '%s']\n", a_command);
401     } /*Dry run*/
402
403     /*
404      * Return the happy news.
405      */
406     return(0);
407
408 } /*uss_procs_Exec*/
409
410
411 /*-----------------------------------------------------------------------
412  * EXPORTED uss_procs_SetLink
413  *
414  * Environment:
415  *      Called from the code generated by the uss grammar.
416  *
417  * Side Effects:
418  *      As advertised.
419  *------------------------------------------------------------------------*/
420
421 afs_int32 uss_procs_SetLink(a_path1, a_path2, a_type)
422     char *a_path1;
423     char *a_path2;
424     char a_type;
425
426 { /*uss_procs_SetLink*/
427
428     struct stat stbuf;
429
430     if (uss_verbose)
431       printf("Setting link '%s' to '%s'\n", a_path1, a_path2);
432
433     /*
434      * If we've not been given permission to overwrite things, make sure
435      * the target doesn't exist before doing anything.
436      */
437     if (!uss_OverwriteThisOne) {
438       strcpy(temp, a_path2);
439       if (!stat(temp, &stbuf)) {
440           if (uss_verbose)
441               printf("\t[Entry exists, NOT overwriting it]\n");
442           return(0);
443       }
444     }
445
446     if (a_type == 's') {
447       /*
448        * Symbolic link.
449        */
450       if (!uss_DryRun) {
451         if (symlink(a_path1, a_path2)) {
452           uss_procs_PrintErr(line,
453                              "Failed to make symlink '%s' to '%s': %s\n",
454                              a_path1, a_path2, sys_errlist[errno]);
455           return(1);
456         }
457       } /*Dry run*/
458       else {
459         fprintf(stderr, "\t[Dry run: Making symlink '%s' to '%s']\n",
460                 a_path1, a_path2);
461       } /*Not a dry run*/
462     } /*Symbolic link*/
463     else {
464       /*
465        * Hard link.
466        */
467       if (!uss_DryRun) {
468         if (link(a_path1, a_path2)) {
469           uss_procs_PrintErr(line,
470                              "Failed to make hard link '%s' to '%s': %s\n",
471                              a_path1, a_path2, sys_errlist[errno]);
472           return(1);
473         }
474       } /*Dry run*/
475       else {
476         fprintf(stderr, "\t[Dry run: Making hard link '%s' to '%s']\n",
477                 a_path1, a_path2);
478       } /*Not a dry run*/
479     } /*Hard link*/
480
481     /*
482       * Return the happy news.
483       */
484     return(0);
485
486 } /*uss_procs_SetLink*/
487
488
489 /*-----------------------------------------------------------------------
490  * EXPORTED uss_procs_GetOwner
491  *
492  * Environment:
493  *      Nothing interesting.
494  *
495  * Side Effects:
496  *      As advertised.
497  *------------------------------------------------------------------------*/
498
499 int uss_procs_GetOwner(a_ownerStr)
500     char *a_ownerStr;
501
502 { /*uss_procs_GetOwner*/
503
504     struct passwd *pw;  /*Ptr to password file entry*/
505     int ownerID;        /*Numerical owner*/
506
507     ownerID = atoi(a_ownerStr);
508     if ((ownerID == 0) && (a_ownerStr[0] != '0')) {
509         /*
510          * The owner is not in numerical format
511          */
512         if ((pw = getpwnam(a_ownerStr)) == NULL) {
513             uss_procs_PrintErr(line,
514                                "Owner '%s' is an unknown user\n",
515                                a_ownerStr);
516             return(1);
517         }
518         return(pw->pw_uid);
519     }
520     else
521         return(ownerID);
522
523 } /*uss_procs_GetOwner*/
524
525 /*-----------------------------------------------------------------------
526  * static Copy
527  *
528  * Description:
529  *       Copies the "from" file to the "to" file and sets the mode. 
530  *
531  * Arguments:
532  *      a_from : File to copy from.
533  *      a_to   : File to copy to.
534  *      a_mode : New Unix mode to set.
535  *
536  * Returns:
537  *      0 if everything went well,
538  *      if there was a problem.
539  *
540  * Environment:
541  *      Nothing interesting.
542  *
543  * Side Effects:
544  *      As advertised.
545  *------------------------------------------------------------------------*/
546
547 static int Copy(a_from, a_to, a_mode)
548     char *a_from;
549     char *a_to;
550     int a_mode;
551
552 { /*Copy*/
553
554     register int fd1, fd2;
555     char buf[BUFSIZ];
556     int cnt, rc;
557
558     umask(0);
559     fd1 = open(a_to, O_EXCL | O_CREAT | O_WRONLY, a_mode);
560     if (fd1 < 0) {
561         if (errno == EEXIST) {
562             /*
563              * The file exists.  Since we can only be called when overwriting
564              * has been enabled, we back off and open the file normally (not
565              * supplying O_EXCL), then continue normally.
566              */
567             fd1 = open(a_to, O_CREAT | O_WRONLY, a_mode);
568             if (fd1 < 0) {
569                 uss_procs_PrintErr(line,
570                                    "%s: Failed to open '%s' for overwrite: %s.\n",
571                                    uss_whoami, a_to, sys_errlist[errno]);
572                 return(1);
573             }
574         } else {
575             uss_procs_PrintErr(line, "%s: Failed to open '%s': %s.\n",
576                                uss_whoami, a_to, sys_errlist[errno]);
577             return(1);
578         }
579     }
580
581     if ((fd2 = open(a_from, O_RDONLY, 0)) < 0) {
582         uss_procs_PrintErr(line, "%s: Error reading '%s': %s\n",
583                            uss_whoami, a_from, sys_errlist[errno]);
584         close(fd1);
585         return(1);
586     }
587     while ((cnt = read(fd2, buf, BUFSIZ)) == BUFSIZ)
588         write(fd1, buf, cnt);
589
590     write(fd1, buf, cnt);
591     rc = close(fd1);
592     if (rc) {
593         uss_procs_PrintErr(line,
594                            "Failed to close '%s' %s\n",
595                            a_to, sys_errlist[errno]);
596         return(1);
597     }
598     if(rc = close(fd2))
599         uss_procs_PrintErr(line, "Warning: Failed to close '%s': %s\n",
600                            a_from, sys_errlist[errno]);
601     return(0);
602
603 } /*Copy*/
604
605 /*-----------------------------------------------------------------------
606  * static Echo
607  *
608  * Description:
609  *       Writes a string into a file and sets its mode.
610  *
611  * Arguments:
612  *      a_s    : String to write.
613  *      a_f    : Filename to write to.
614  *      a_mode : New Unix mode to set.
615  *
616  * Returns:
617  *      0 if everything went well,
618  *      if there was a problem.
619  *
620  * Environment:
621  *      Nothing interesting.
622  *
623  * Side Effects:
624  *      As advertised.
625  *------------------------------------------------------------------------*/
626
627 static int Echo(a_s, a_f, a_mode)
628     char *a_s;
629     char *a_f;
630     int a_mode;
631
632 { /*Echo*/
633
634     register int fd;
635
636     umask(0);
637     fd = open(a_f, O_EXCL | O_CREAT | O_WRONLY, a_mode);
638     if (fd < 0) {
639         if (errno == EEXIST) {
640             /*
641              * The file exists.  Since we can only be called when
642              * overwriting has been enabled, we back off and open the
643              * file normally (not supplying the O_EXCL), then continue
644              * normally.  Notice that we must truncate the file, since
645              * if the original file is longer than the stuff we're about
646              * to put in, all the old data past the current write will
647              * still be there.
648              */
649             fd = open(a_f, O_TRUNC | O_WRONLY, a_mode);
650             if (fd < 0) {
651                 uss_procs_PrintErr(line,
652                                    "%s: Failed to open '%s' for overwrite: %s.\n",
653                                    uss_whoami, a_f, sys_errlist[errno]);
654                 return(1);
655             }
656         } else {
657             uss_procs_PrintErr(line, "%s: Failed to open '%s': %s. \n",
658                                uss_whoami, a_f, sys_errlist[errno]);
659             return(1);
660         }
661     }
662     write(fd, a_s, strlen(a_s));
663     write(fd, "\n", 1);
664     if (close(fd)){
665         uss_procs_PrintErr(line, "Failed to close '%s': %s\n",
666                            a_f, sys_errlist[errno]);
667         return(1);
668     }
669     return(0);
670
671 } /*Echo*/
672
673 /*-----------------------------------------------------------------------
674  * static uss_procs_PickADir
675  *
676  * Description:
677  *      Tries to replace $AUTO by a subdir.  Subdirs are given by means
678  *      of "G" in the configuration file.  Each of the directories is
679  *      examined, and the one with the inimum number of entries is
680  *      picked.
681  *
682  * Arguments:
683  *      a_path : ???
684  *      a_cp   : ???
685  *
686  * Returns:
687  *      0 if everything went well,
688  *      -1 if there was a problem.
689  *
690  * Environment:
691  *      Called with THREE parameters from lex.c!
692  *
693  * Side Effects:
694  *      As advertised.
695  *------------------------------------------------------------------------*/
696
697 afs_int32 uss_procs_PickADir(path, cp)
698     char *path;
699     char *cp;
700
701 { /*uss_procs_PickADir*/
702
703     char cd[300]; /*Current  directory for search*/
704
705     int i, count, MinIndex, mina = 10000;
706     struct dirent *dp;
707     DIR *dirp;
708     char dirname[300];
709
710     if (uss_NumGroups == 0){
711         fprintf(stderr,"%s: No choice yet given to replace $AUTO\n",
712                 uss_whoami);
713         fprintf(stderr,"%s: Use the G command before $AUTO in config file\n",
714                 uss_whoami);
715         return(-1);
716     }
717
718     if (uss_Auto[0] != '\0')
719         return(0); /* we have already done this for this user */
720
721     if (*(cp-1) == '/') { /*it's ..../$AUTO*/
722         for (i = 0; &path[i] != cp; i++)
723             cd[i] = path[i];
724         cd[i] = '\0';
725     }
726     else {
727         if (path != cp){ 
728             fprintf(stderr,"%s: $AUTO must be used to replace the whole path or the whole name of a subdirectory. Found: %s$AUTO\n", uss_whoami, path);
729             return(-1);
730         }
731         cd[0] = '/'; cd[1] = '\0';
732     }
733
734     /* 
735      * We now have the current dir (cd).  Search all of the given
736      * subdirs (by G in template), count the number of entries in
737      * each and pick the minimum.
738      */
739     for (i=0; i < uss_NumGroups; i++) {
740         sprintf(dirname, "%s/%s", cd, uss_DirPool[i]);
741         if ((dirp = opendir(dirname)) == NULL) {
742             if (errno != ENOTDIR)
743                 fprintf(stderr,
744                         "%s: Warning: Can't open dir '%s' (errno=%d). Skipped.\n",
745                         uss_whoami, dirname, errno);
746             continue;   /*Skip and continue anyway*/
747         }
748         count = 0;
749         for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
750             if (dp->d_name[0] != '.')
751                 count++; /* forget about files starting with .*/
752 #ifdef USS_PROCS_DB
753         printf("debug: Dir '%s' has %d entries\n", dirname, count);
754 #endif /* USS_PROCS_DB */
755         if (count < mina) {
756             mina = count;
757             MinIndex = i;
758         }
759         closedir(dirp);
760     }
761     if (mina == 10000) { /* We found nothing */
762         fprintf(stderr,"%s: Warning: No valid choice to replace $AUTO\n",
763                 uss_whoami);
764         uss_Auto[0]= '\0';
765     }
766     else {
767         strcpy(uss_Auto, uss_DirPool[MinIndex]);
768         if (uss_verbose)
769             printf("Picking dir w/minimum number of entries: '%s'\n",
770                    uss_Auto);
771     }
772     return(0);
773
774 } /*uss_procs_PickADir*/
775
776 /*-----------------------------------------------------------------------
777  * EXPORTED uss_procs_AddToDirPool
778  *
779  * Environment:
780  *      Called from the code generated by the uss grammar.
781  *
782  * Side Effects:
783  *      As advertised.
784  *------------------------------------------------------------------------*/
785
786 int uss_procs_AddToDirPool(a_dirToAdd)
787     char *a_dirToAdd;
788 {
789     if (uss_NumGroups > 99) {
790        return(-1);
791     }
792     strcpy(uss_DirPool[uss_NumGroups++], a_dirToAdd);
793     return 0;
794 }
795
796 /*-----------------------------------------------------------------------
797  * EXPORTED uss_procs_FindAndOpen
798  *
799  * Environment:
800  *      Nothing interesting.
801  *
802  * Side Effects:
803  *      As advertised.
804  *------------------------------------------------------------------------*/
805
806 FILE *uss_procs_FindAndOpen(a_fileToOpen)
807     char *a_fileToOpen;
808
809 { /*uss_procs_FindAndOpen*/
810
811 #define NUM_TPL_PATHS 3
812
813     FILE *rv;                            /*Template file descriptor*/
814     int i;                               /*Loop counter*/
815     char tmp_str[uss_MAX_SIZE];          /*Tmp string*/
816     static char
817       TemplatePath[NUM_TPL_PATHS][1024]; /*Template directories*/
818     int cant_read;                       /*Can't read the file?*/
819
820     /*
821      * If a full pathname was given, just take it as is.
822      */
823     if (strchr(a_fileToOpen, '/')) {
824         strcpy(tmp_str, a_fileToOpen);
825         rv = fopen(a_fileToOpen, "r");
826     }
827     else {
828         /*
829          * A relative pathname was given.  Try to find the file in each of
830          * the default template directories.
831          */
832         cant_read = 0;
833         
834         sprintf(TemplatePath[0], "%s", ".");
835         sprintf(TemplatePath[1], "/afs/%s/common/uss", uss_Cell);
836         sprintf(TemplatePath[2], "%s", "/etc");
837         
838         for (i = 0; i < NUM_TPL_PATHS; i++) {
839             sprintf(tmp_str, "%s/%s", TemplatePath[i], a_fileToOpen);
840             if ((rv = fopen(tmp_str, "r")) != NULL)
841                 break;
842
843             /*
844              * If the file was there but couldn't be opened, we have
845              * to report this.
846              */
847             if (errno != ENOENT) {
848                 cant_read = 1;
849                 break;
850             }
851         } /*Look in template directories*/
852         
853         /*
854          * If we found and opened the file, we're happy.  Otherwise,
855          * print out what went wrong.
856          */
857         if (rv != NULL) {
858             if (uss_verbose)
859                 fprintf(stderr, "Using template '%s'\n", tmp_str);
860         } /*Got it*/
861         else {
862             /*
863              * Check to see if we specifically found the file but
864              * couldn't read it.
865              */
866             if (cant_read)
867                 fprintf(stderr,
868                         "%s: Can't open template '%s': %s\n",
869                         uss_whoami, tmp_str, sys_errlist[errno]);
870             else {
871                 fprintf(stderr,
872                         "%s: Can't find template '%s' in searchlist",
873                         uss_whoami, a_fileToOpen);
874                 for (i = 0; i < NUM_TPL_PATHS; i++)
875                     fprintf(stderr, " '%s'", TemplatePath[i]);
876                 fprintf(stderr, "\n");
877             } /*Can't find template*/
878         } /*Didn't get it*/
879     } /*Relative pathname given*/
880     
881     /*
882      * Whatever happened, return what we got.
883      */
884     return(rv);
885
886 } /*uss_procs_FindAndOpen*/
887
888
889 /*-----------------------------------------------------------------------
890  * EXPORTED uss_procs_PrintErr
891  *
892  * Environment:
893  *      Nothing interesting.
894  *
895  * Side Effects:
896  *      As advertised.
897  *------------------------------------------------------------------------*/
898
899 void uss_procs_PrintErr(a_lineNum, a_fmt, a_1, a_2, a_3, a_4, a_5)
900     int a_lineNum;
901     char *a_fmt;
902     char *a_1;
903     char *a_2;
904     char *a_3;
905     char *a_4;
906     char *a_5;
907
908 { /*uss_procs_PrintErr*/
909
910     uss_syntax_err++;
911     fprintf(stderr,"%s: Template file, line %d: ", uss_whoami, a_lineNum);
912     fprintf(stderr, a_fmt, a_1, a_2, a_3, a_4, a_5);
913
914 } /*uss_procs_PrintErr*/