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