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