venus: Remove dedebug
[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 rcnt, wcnt = -1, 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     do {
541         rcnt = read(fd2, buf, BUFSIZ);
542         if (rcnt == -1)
543             break;
544         wcnt = write(fd1, buf, rcnt);
545     } while (rcnt == BUFSIZ && rcnt == wcnt);
546     if (rcnt == -1 || wcnt != rcnt) {
547         uss_procs_PrintErr(line, "read/write error to %s\n", a_to);
548         close(fd1);
549         close(fd2);
550         return (1);
551     }
552     rc = close(fd1);
553     if (rc) {
554         uss_procs_PrintErr(line, "Failed to close '%s' %s\n", a_to,
555                            strerror(errno));
556         return (1);
557     }
558     if ((rc = close(fd2)))
559         uss_procs_PrintErr(line, "Warning: Failed to close '%s': %s\n",
560                            a_from, strerror(errno));
561     return (0);
562
563 }                               /*Copy */
564
565 /*-----------------------------------------------------------------------
566  * static Echo
567  *
568  * Description:
569  *       Writes a string into a file and sets its mode.
570  *
571  * Arguments:
572  *      a_s    : String to write.
573  *      a_f    : Filename to write to.
574  *      a_mode : New Unix mode to set.
575  *
576  * Returns:
577  *      0 if everything went well,
578  *      if there was a problem.
579  *
580  * Environment:
581  *      Nothing interesting.
582  *
583  * Side Effects:
584  *      As advertised.
585  *------------------------------------------------------------------------*/
586
587 static int
588 Echo(char *a_s, char *a_f, int a_mode)
589 {                               /*Echo */
590
591     int fd;
592
593     umask(0);
594     fd = open(a_f, O_EXCL | O_CREAT | O_WRONLY, a_mode);
595     if (fd < 0) {
596         if (errno == EEXIST) {
597             /*
598              * The file exists.  Since we can only be called when
599              * overwriting has been enabled, we back off and open the
600              * file normally (not supplying the O_EXCL), then continue
601              * normally.  Notice that we must truncate the file, since
602              * if the original file is longer than the stuff we're about
603              * to put in, all the old data past the current write will
604              * still be there.
605              */
606             fd = open(a_f, O_TRUNC | O_WRONLY, a_mode);
607             if (fd < 0) {
608                 uss_procs_PrintErr(line,
609                                    "%s: Failed to open '%s' for overwrite: %s.\n",
610                                    uss_whoami, a_f, strerror(errno));
611                 return (1);
612             }
613         } else {
614             uss_procs_PrintErr(line, "%s: Failed to open '%s': %s. \n",
615                                uss_whoami, a_f, strerror(errno));
616             return (1);
617         }
618     }
619     if (write(fd, a_s, strlen(a_s)) != strlen(a_s) ||
620         write(fd, "\n", 1) != 1) {
621         uss_procs_PrintErr(line, "Short write to '%s'\n", a_f);
622         close(fd);
623         return (1);
624     }
625     if (close(fd)) {
626         uss_procs_PrintErr(line, "Failed to close '%s': %s\n", a_f,
627                            strerror(errno));
628         return (1);
629     }
630     return (0);
631
632 }                               /*Echo */
633
634 /*-----------------------------------------------------------------------
635  * static uss_procs_PickADir
636  *
637  * Description:
638  *      Tries to replace $AUTO by a subdir.  Subdirs are given by means
639  *      of "G" in the configuration file.  Each of the directories is
640  *      examined, and the one with the inimum number of entries is
641  *      picked.
642  *
643  * Arguments:
644  *      a_path : ???
645  *      a_cp   : ???
646  *
647  * Returns:
648  *      0 if everything went well,
649  *      -1 if there was a problem.
650  *
651  * Environment:
652  *      Called with THREE parameters from lex.c!
653  *
654  * Side Effects:
655  *      As advertised.
656  *------------------------------------------------------------------------*/
657
658 afs_int32
659 uss_procs_PickADir(char *path, char *cp)
660 {                               /*uss_procs_PickADir */
661
662     char cd[uss_DirPoolLen];            /*Current  directory for search */
663
664     int i, count, MinIndex = 0, mina = 10000;
665     struct dirent *dp;
666     DIR *dirp;
667     char dirname[uss_DirPoolLen*2];
668
669     if (uss_NumGroups == 0) {
670         fprintf(stderr, "%s: No choice yet given to replace $AUTO\n",
671                 uss_whoami);
672         fprintf(stderr, "%s: Use the G command before $AUTO in config file\n",
673                 uss_whoami);
674         return (-1);
675     }
676
677     if (uss_Auto[0] != '\0')
678         return (0);             /* we have already done this for this user */
679
680     if (*(cp - 1) == '/') {     /*it's ..../$AUTO */
681         for (i = 0; &path[i] != cp; i++)
682             cd[i] = path[i];
683         cd[i] = '\0';
684     } else {
685         if (path != cp) {
686             fprintf(stderr,
687                     "%s: $AUTO must be used to replace the whole path or the whole name of a subdirectory. Found: %s$AUTO\n",
688                     uss_whoami, path);
689             return (-1);
690         }
691         cd[0] = '/';
692         cd[1] = '\0';
693     }
694
695     /*
696      * We now have the current dir (cd).  Search all of the given
697      * subdirs (by G in template), count the number of entries in
698      * each and pick the minimum.
699      */
700     for (i = 0; i < uss_NumGroups; i++) {
701         snprintf(dirname, sizeof(dirname), "%s/%s", cd, uss_DirPool[i]);
702         if ((dirp = opendir(dirname)) == NULL) {
703             if (errno != ENOTDIR)
704                 fprintf(stderr,
705                         "%s: Warning: Can't open dir '%s' (errno=%d). Skipped.\n",
706                         uss_whoami, dirname, errno);
707             continue;           /*Skip and continue anyway */
708         }
709         count = 0;
710         for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
711             if (dp->d_name[0] != '.')
712                 count++;        /* forget about files starting with . */
713 #ifdef USS_PROCS_DB
714         printf("debug: Dir '%s' has %d entries\n", dirname, count);
715 #endif /* USS_PROCS_DB */
716         if (count < mina) {
717             mina = count;
718             MinIndex = i;
719         }
720         closedir(dirp);
721     }
722     if (mina == 10000) {        /* We found nothing */
723         fprintf(stderr, "%s: Warning: No valid choice to replace $AUTO\n",
724                 uss_whoami);
725         uss_Auto[0] = '\0';
726     } else {
727         strcpy(uss_Auto, uss_DirPool[MinIndex]);
728         if (uss_verbose)
729             printf("Picking dir w/minimum number of entries: '%s'\n",
730                    uss_Auto);
731     }
732     return (0);
733
734 }                               /*uss_procs_PickADir */
735
736 /*-----------------------------------------------------------------------
737  * EXPORTED uss_procs_AddToDirPool
738  *
739  * Environment:
740  *      Called from the code generated by the uss grammar.
741  *
742  * Side Effects:
743  *      As advertised.
744  *------------------------------------------------------------------------*/
745
746 int
747 uss_procs_AddToDirPool(char *a_dirToAdd)
748 {
749     if (uss_NumGroups > 99) {
750         return (-1);
751     }
752     strcpy(uss_DirPool[uss_NumGroups++], a_dirToAdd);
753     return 0;
754 }
755
756 /*-----------------------------------------------------------------------
757  * EXPORTED uss_procs_FindAndOpen
758  *
759  * Environment:
760  *      Nothing interesting.
761  *
762  * Side Effects:
763  *      As advertised.
764  *------------------------------------------------------------------------*/
765
766 FILE *
767 uss_procs_FindAndOpen(char *a_fileToOpen)
768 {                               /*uss_procs_FindAndOpen */
769
770 #define NUM_TPL_PATHS 3
771
772     FILE *rv;                   /*Template file descriptor */
773     int i;                      /*Loop counter */
774     char *tmp_str;              /*Points to the name of the file */
775                                 /* -> a_fileToOpen or -> buffer @tmp_str_free */
776     char *tmp_str_free = NULL;  /*Dynamically built filename */
777     static char
778       TemplatePath[NUM_TPL_PATHS][1024];        /*Template directories */
779     int cant_read;              /*Can't read the file? */
780
781     /*
782      * If a full pathname was given, just take it as is.
783      */
784     if (strchr(a_fileToOpen, '/')) {
785         tmp_str = a_fileToOpen;
786         rv = fopen(a_fileToOpen, "r");
787     } else {
788         /*
789          * A relative pathname was given.  Try to find the file in each of
790          * the default template directories.
791          */
792         int mem_error = 0;
793         cant_read = 0;
794
795         sprintf(TemplatePath[0], "%s", ".");
796         sprintf(TemplatePath[1], "/afs/%s/common/uss", uss_Cell);
797         sprintf(TemplatePath[2], "%s", "/etc");
798
799         for (i = 0; i < NUM_TPL_PATHS; i++) {
800             int code;
801             free(tmp_str_free);
802             tmp_str_free = NULL;
803             code = asprintf(&tmp_str_free, "%s/%s",
804                             TemplatePath[i], a_fileToOpen);
805             if (code == -1) {
806                 tmp_str_free = NULL;
807                 mem_error = 1;
808                 rv = NULL;
809                 break;
810             }
811             tmp_str = tmp_str_free;
812
813             if ((rv = fopen(tmp_str, "r")) != NULL)
814                 break;
815
816             /*
817              * If the file was there but couldn't be opened, we have
818              * to report this.
819              */
820             if (errno != ENOENT) {
821                 cant_read = 1;
822                 break;
823             }
824         }                       /*Look in template directories */
825
826         /*
827          * If we found and opened the file, we're happy.  Otherwise,
828          * print out what went wrong.
829          */
830         if (rv != NULL) {
831             if (uss_verbose)
832                 fprintf(stderr, "Using template '%s'\n", tmp_str);
833         } /*Got it */
834         else {
835             /*
836              * Check to see if we specifically found the file but
837              * couldn't read it.
838              */
839             if (mem_error) {
840                 fprintf(stderr, "%s: Error allocating memory\n",
841                         uss_whoami);
842             }
843             else if (cant_read)
844                 fprintf(stderr, "%s: Can't open template '%s': %s\n",
845                         uss_whoami, tmp_str, strerror(errno));
846             else {
847                 fprintf(stderr, "%s: Can't find template '%s' in searchlist",
848                         uss_whoami, a_fileToOpen);
849                 for (i = 0; i < NUM_TPL_PATHS; i++)
850                     fprintf(stderr, " '%s'", TemplatePath[i]);
851                 fprintf(stderr, "\n");
852             }                   /*Can't find template */
853         }                       /*Didn't get it */
854     }                           /*Relative pathname given */
855
856     /*
857      * Whatever happened, return what we got.
858      */
859     free(tmp_str_free);
860     return (rv);
861
862 }                               /*uss_procs_FindAndOpen */
863
864
865 /*-----------------------------------------------------------------------
866  * EXPORTED uss_procs_PrintErr
867  *
868  * Environment:
869  *      Nothing interesting.
870  *
871  * Side Effects:
872  *      As advertised.
873  *------------------------------------------------------------------------*/
874
875 void
876 uss_procs_PrintErr(int a_lineNum, char *a_fmt, ... )
877 {                               /*uss_procs_PrintErr */
878     va_list ap;
879
880     va_start(ap, a_fmt);
881     uss_syntax_err++;
882     fprintf(stderr, "%s: Template file, line %d: ", uss_whoami, a_lineNum);
883     vfprintf(stderr, a_fmt, ap);
884     va_end(ap);
885 }                               /*uss_procs_PrintErr */