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