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