DEVEL15-getcellunix-use-stdio-again-20060925
[openafs.git] / src / auth / cellconfig.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 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <afs/pthread_glock.h>
18 #ifdef UKERNEL
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #else /* UKERNEL */
22 #include <sys/types.h>
23 #ifdef AFS_NT40_ENV
24 #include <winsock2.h>
25 #include <sys/utime.h>
26 #include <io.h>
27 #include <WINNT/afssw.h>
28 #else
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <sys/file.h>
33 #include <sys/time.h>
34 #ifdef AFS_AFSDB_ENV
35 #include <arpa/nameser.h>
36 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
37 #include <arpa/nameser_compat.h>
38 #endif
39 #include <resolv.h>
40 #endif /* AFS_AFSDB_ENV */
41 #endif /* AFS_NT40_ENV */
42 #include <afs/afsint.h>
43 #include <errno.h>
44 #include <ctype.h>
45 #include <time.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #ifdef HAVE_STRING_H
51 #include <string.h>
52 #else
53 #ifdef HAVE_STRINGS_H
54 #include <strings.h>
55 #endif
56 #endif
57 #ifdef HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #endif /* UKERNEL */
61 #include <afs/afsutil.h>
62 #include "cellconfig.h"
63 #include "keys.h"
64 #ifdef AFS_NT40_ENV
65 #ifdef AFS_AFSDB_ENV
66 /* cm_dns.h depends on cellconfig.h */
67 #include <cm_dns.h>
68 #endif /* AFS_AFSDB_ENV */
69 #endif
70 static struct afsconf_servPair serviceTable[] = {
71     {"afs", 7000,},
72     {"afscb", 7001,},
73     {"afsprot", 7002,},
74     {"afsvldb", 7003,},
75     {"afskauth", 7004,},
76     {"afsvol", 7005,},
77     {"afserror", 7006,},
78     {"afsnanny", 7007,},
79     {"afsupdate", 7008,},
80     {"afsrmtsys", 7009,},
81     {"afsres", 7010,},          /* residency database for MR-AFS */
82     {"afsremio", 7011,},        /* remote I/O interface for MR-AFS */
83     {0, 0}                      /* insert new services before this spot */
84 };
85
86 /* Prototypes */
87 static afs_int32 afsconf_FindService(register const char *aname);
88 static int TrimLine(char *abuffer);
89 #ifdef AFS_NT40_ENV
90 static int IsClientConfigDirectory(const char *path);
91 static int GetCellNT(struct afsconf_dir *adir);
92 #endif
93 static int afsconf_Check(register struct afsconf_dir *adir);
94 static int afsconf_Touch(register struct afsconf_dir *adir);
95 static int GetCellUnix(struct afsconf_dir *adir);
96 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
97                                 char clones[]);
98 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
99                          char *aname, char *aclone);
100 static int ParseCellLine(register char *aline, register char *aname,
101                          register char *alname);
102 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
103 static int afsconf_Reopen(register struct afsconf_dir *adir);
104 static int SaveKeys(struct afsconf_dir *adir);
105
106 #ifndef T_AFSDB
107 #define T_AFSDB 18              /* per RFC1183 section 1 */
108 #endif
109
110 /*
111  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
112  * our code can tell if there is new info in the key files, the cell server db
113  * files or any of the other files (and reopen the thing) if the date on
114  * CellServDB changes.
115  */
116
117 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
118 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
119    get an fd <= 255. We allow the fileserver to claim more fds than that.
120    This has always been a problem since pr_Initialize would have the same
121    issue, but hpr_Initialize makes it more likely that we would see this. 
122    Work around it. This is not generic. It's coded with the needs of
123    afsconf_* in mind only.
124
125    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
126 */
127
128 #define BUFFER 4096
129
130 struct afsconf_iobuffer {
131     int _file;
132     char *buffer;
133     char *ptr;
134     char *endptr;
135 };
136
137 typedef struct afsconf_iobuffer afsconf_FILE;
138
139 static afsconf_FILE *
140 afsconf_fopen(const char *fname, const char *fmode)
141 {
142     int fd;
143     afsconf_FILE *iop;
144     
145     if ((fd = open(fname, O_RDONLY)) == -1) {
146         return NULL;
147     }
148     
149     iop = malloc(sizeof(struct afsconf_iobuffer));
150     if (iop == NULL) {
151         (void) close(fd);
152         errno = ENOMEM;
153         return NULL;
154     }
155     iop->_file = fd;
156     iop->buffer = malloc(BUFFER);
157     if (iop->buffer == NULL) {
158         (void) close(fd);
159         free((void *) iop);
160         errno = ENOMEM;
161         return NULL;
162     }
163     iop->ptr = iop->buffer;
164     iop->endptr = iop->buffer;
165     return iop;
166 }
167
168 static int
169 afsconf_fclose(afsconf_FILE *iop)
170 {
171     if (iop == NULL) {
172         return 0;
173     }
174     close(iop->_file);
175     free((void *)iop->buffer);
176     free((void *)iop);
177     return 0;
178 }
179
180 static char *
181 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
182 {
183     char *p;
184     
185     p = s;
186     for (;;) {
187         char c;
188         
189         if (iop->ptr == iop->endptr) {
190             ssize_t len;
191             
192             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
193                 return NULL;
194             }
195             if (len == 0) {
196                 *p = 0;
197                 if (s == p) {
198                     return NULL;
199                 }
200                 return s;
201             }
202             iop->ptr = iop->buffer;
203             iop->endptr = iop->buffer + len;
204         }
205         c = *iop->ptr++;
206         *p++ = c;
207         if ((p - s) == (n - 1)) {
208             *p = 0;
209             return s;
210         }
211         if (c == '\n') {
212             *p = 0;
213             return s;
214         }
215     }
216 }
217 #define fopen afsconf_fopen
218 #define fclose afsconf_fclose
219 #define fgets afsconf_fgets
220 #else
221 #define afsconf_FILE FILE
222 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
223
224 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
225 static afs_int32
226 afsconf_FindService(register const char *aname)
227 {
228     /* lookup a service name */
229     struct servent *ts;
230     register struct afsconf_servPair *tsp;
231
232 #if     defined(AFS_OSF_ENV) 
233     ts = getservbyname(aname, "");
234 #else
235     ts = getservbyname(aname, NULL);
236 #endif
237     if (ts) {
238         /* we found it in /etc/services, so we use this value */
239         return ts->s_port;      /* already in network byte order */
240     }
241
242     /* not found in /etc/services, see if it is one of ours */
243     for (tsp = serviceTable;; tsp++) {
244         if (tsp->name == NULL)
245             return -1;
246         if (!strcmp(tsp->name, aname))
247             return htons(tsp->port);
248     }
249 }
250
251 static int
252 TrimLine(char *abuffer)
253 {
254     char tbuffer[256];
255     register char *tp;
256     register int tc;
257
258     tp = abuffer;
259     while ((tc = *tp)) {
260         if (!isspace(tc))
261             break;
262         tp++;
263     }
264     strcpy(tbuffer, tp);
265     strcpy(abuffer, tbuffer);
266     return 0;
267 }
268
269 #ifdef AFS_NT40_ENV
270 /*
271  * IsClientConfigDirectory() -- determine if path matches well-known
272  *     client configuration directory.
273  */
274 static int
275 IsClientConfigDirectory(const char *path)
276 {
277     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
278     int i;
279
280     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
281         int cc = tolower(cdir[i]);
282         int pc = tolower(path[i]);
283
284         if (cc == '\\') {
285             cc = '/';
286         }
287         if (pc == '\\') {
288             pc = '/';
289         }
290         if (cc != pc) {
291             return 0;
292         }
293     }
294
295     /* hit end of one or both; allow mismatch in existence of trailing slash */
296     if (cdir[i] != '\0') {
297         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
298             return 0;
299         }
300     }
301     if (path[i] != '\0') {
302         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
303             return 0;
304         }
305     }
306     return 1;
307 }
308 #endif /* AFS_NT40_ENV */
309
310
311 static int
312 afsconf_Check(register struct afsconf_dir *adir)
313 {
314     char tbuffer[256], *p;
315     struct stat tstat;
316     register afs_int32 code;
317
318 #ifdef AFS_NT40_ENV
319     /* NT client CellServDB has different file name than NT server or Unix */
320     if (IsClientConfigDirectory(adir->name)) {
321         if (!afssw_GetClientCellServDBDir(&p)) {
322             strcompose(tbuffer, sizeof(tbuffer), p, "/",
323                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
324             free(p);
325         } else {
326             int len;
327             strncpy(tbuffer, adir->name, sizeof(tbuffer));
328             len = (int)strlen(tbuffer);
329             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
330                 strncat(tbuffer, "\\", sizeof(tbuffer));
331             }
332             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
333                     sizeof(tbuffer));
334             tbuffer[sizeof(tbuffer) - 1] = '\0';
335         }
336     } else {
337         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
338                    NULL);
339     }
340 #else
341     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
342 #endif /* AFS_NT40_ENV */
343
344     code = stat(tbuffer, &tstat);
345     if (code < 0) {
346         return code;
347     }
348     /* did file change? */
349     if (tstat.st_mtime == adir->timeRead) {
350         return 0;
351     }
352     /* otherwise file has changed, so reopen it */
353     return afsconf_Reopen(adir);
354 }
355
356 /* set modtime on file */
357 static int
358 afsconf_Touch(register struct afsconf_dir *adir)
359 {
360     char tbuffer[256], *p;
361 #ifndef AFS_NT40_ENV
362     struct timeval tvp[2];
363 #endif
364
365     adir->timeRead = 0;         /* just in case */
366
367 #ifdef AFS_NT40_ENV
368     /* NT client CellServDB has different file name than NT server or Unix */
369
370     if (IsClientConfigDirectory(adir->name)) {
371         if (!afssw_GetClientCellServDBDir(&p)) {
372             strcompose(tbuffer, sizeof(tbuffer), p, "/",
373                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
374             free(p);
375         } else {
376             int len = (int)strlen(tbuffer);
377             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
378                 strncat(tbuffer, "\\", sizeof(tbuffer));
379             }
380             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
381                     sizeof(tbuffer));
382             tbuffer[sizeof(tbuffer) - 1] = '\0';
383         }
384     } else {
385         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
386                    NULL);
387     }
388
389     return _utime(tbuffer, NULL);
390
391 #else
392     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
393     gettimeofday(&tvp[0], NULL);
394     tvp[1] = tvp[0];
395     return utimes(tbuffer, tvp);
396 #endif /* AFS_NT40_ENV */
397 }
398
399 struct afsconf_dir *
400 afsconf_Open(register const char *adir)
401 {
402     register struct afsconf_dir *tdir;
403     register afs_int32 code;
404
405     LOCK_GLOBAL_MUTEX;
406     /* zero structure and fill in name; rest is done by internal routine */
407     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
408     memset(tdir, 0, sizeof(struct afsconf_dir));
409     tdir->name = (char *)malloc(strlen(adir) + 1);
410     strcpy(tdir->name, adir);
411
412     code = afsconf_OpenInternal(tdir, 0, 0);
413     if (code) {
414         char *afsconf_path, afs_confdir[128];
415
416         free(tdir->name);
417         /* Check global place only when local Open failed for whatever reason */
418         if (!(afsconf_path = getenv("AFSCONF"))) {
419             /* The "AFSCONF" environment (or contents of "/.AFSCONF") will be typically set to something like "/afs/<cell>/common/etc" where, by convention, the default files for "ThisCell" and "CellServDB" will reside; note that a major drawback is that a given afs client on that cell may NOT contain the same contents... */
420             char *home_dir;
421             afsconf_FILE *fp;
422             size_t len;
423
424             if (!(home_dir = getenv("HOME"))) {
425                 /* Our last chance is the "/.AFSCONF" file */
426                 fp = fopen("/.AFSCONF", "r");
427                 if (fp == 0) {
428                     free(tdir);
429                     UNLOCK_GLOBAL_MUTEX;
430                     return (struct afsconf_dir *)0;
431                 }
432                 fgets(afs_confdir, 128, fp);
433                 fclose(fp);
434             } else {
435                 char pathname[256];
436
437                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
438                 fp = fopen(pathname, "r");
439                 if (fp == 0) {
440                     /* Our last chance is the "/.AFSCONF" file */
441                     fp = fopen("/.AFSCONF", "r");
442                     if (fp == 0) {
443                         free(tdir);
444                         UNLOCK_GLOBAL_MUTEX;
445                         return (struct afsconf_dir *)0;
446                     }
447                 }
448                 fgets(afs_confdir, 128, fp);
449                 fclose(fp);
450             }
451             len = strlen(afs_confdir);
452             if (len == 0) {
453                 free(tdir);
454                 UNLOCK_GLOBAL_MUTEX;
455                 return (struct afsconf_dir *)0;
456             }
457             if (afs_confdir[len - 1] == '\n') {
458                 afs_confdir[len - 1] = 0;
459             }
460             afsconf_path = afs_confdir;
461         }
462         tdir->name = (char *)malloc(strlen(afsconf_path) + 1);
463         strcpy(tdir->name, afsconf_path);
464         code = afsconf_OpenInternal(tdir, 0, 0);
465         if (code) {
466             free(tdir->name);
467             free(tdir);
468             UNLOCK_GLOBAL_MUTEX;
469             return (struct afsconf_dir *)0;
470         }
471     }
472     UNLOCK_GLOBAL_MUTEX;
473     return tdir;
474 }
475
476 static int
477 GetCellUnix(struct afsconf_dir *adir)
478 {
479     int rc;
480     char tbuffer[256];
481     char *p;
482     afsconf_FILE *fp;
483     
484     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
485     fp = fopen(tbuffer, "r");
486     if (fp == 0) {
487         return -1;
488     }
489     rc = fgets(tbuffer, 256, fp);
490     fclose(fp);
491
492     p = strchr(tbuffer, '\n');
493     if (p)
494         *p = '\0';
495
496     adir->cellName = strdup(tbuffer);
497     return 0;
498 }
499
500
501 #ifdef AFS_NT40_ENV
502 static int
503 GetCellNT(struct afsconf_dir *adir)
504 {
505     if (IsClientConfigDirectory(adir->name)) {
506         /* NT client config dir; ThisCell is in registry (no file). */
507         return afssw_GetClientCellName(&adir->cellName);
508     } else {
509         /* NT server config dir; works just like Unix */
510         return GetCellUnix(adir);
511     }
512 }
513 #endif /* AFS_NT40_ENV */
514
515
516 static int
517 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
518                      char clones[])
519 {
520     afsconf_FILE *tf;
521     register char *tp, *bp;
522     register struct afsconf_entry *curEntry;
523     struct afsconf_aliasentry *curAlias;
524     register afs_int32 code;
525     afs_int32 i;
526     char tbuffer[256], tbuf1[256];
527     struct stat tstat;
528
529     /* figure out the cell name */
530 #ifdef AFS_NT40_ENV
531     i = GetCellNT(adir);
532 #else
533     i = GetCellUnix(adir);
534 #endif
535
536 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
537     if (i) {
538         return i;
539     }
540 #endif
541
542     /* now parse the individual lines */
543     curEntry = 0;
544
545 #ifdef AFS_NT40_ENV
546     /* NT client/server have a CellServDB that is the same format as Unix.
547      * However, the NT client uses a different file name
548      */
549     if (IsClientConfigDirectory(adir->name)) {
550         /* NT client config dir */
551         char *p;
552         if (!afssw_GetClientCellServDBDir(&p)) {
553             strcompose(tbuffer, sizeof(tbuffer), p, "/",
554                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
555             free(p);
556         } else {
557             int len;
558             strncpy(tbuffer, adir->name, sizeof(tbuffer));
559             len = (int)strlen(tbuffer);
560             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
561                 strncat(tbuffer, "\\", sizeof(tbuffer));
562             }
563             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
564                     sizeof(tbuffer));
565             tbuffer[sizeof(tbuffer) - 1] = '\0';
566         }
567     } else {
568         /* NT server config dir */
569         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
570                    NULL);
571     }
572 #else
573     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
574 #endif /* AFS_NT40_ENV */
575
576     if (!stat(tbuffer, &tstat)) {
577         adir->timeRead = tstat.st_mtime;
578     } else {
579         adir->timeRead = 0;
580     }
581
582     strcpy(tbuf1, tbuffer);
583     tf = fopen(tbuffer, "r");
584     if (!tf) {
585         return -1;
586     }
587     while (1) {
588         tp = fgets(tbuffer, sizeof(tbuffer), tf);
589         if (!tp)
590             break;
591         TrimLine(tbuffer);      /* remove white space */
592         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
593             continue;           /* empty line */
594         if (tbuffer[0] == '>') {
595             char linkedcell[MAXCELLCHARS];
596             /* start new cell item */
597             if (curEntry) {
598                 /* thread this guy on the list */
599                 curEntry->next = adir->entries;
600                 adir->entries = curEntry;
601                 curEntry = 0;
602             }
603             curEntry =
604                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
605             memset(curEntry, 0, sizeof(struct afsconf_entry));
606             code =
607                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
608             if (code) {
609                 afsconf_CloseInternal(adir);
610                 fclose(tf);
611                 free(curEntry);
612                 return -1;
613             }
614             if (linkedcell[0] != '\0') {
615                 curEntry->cellInfo.linkedCell =
616                     (char *)malloc(strlen(linkedcell) + 1);
617                 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
618             }
619         } else {
620             /* new host in the current cell */
621             if (!curEntry) {
622                 afsconf_CloseInternal(adir);
623                 fclose(tf);
624                 return -1;
625             }
626             i = curEntry->cellInfo.numServers;
627             if (cell && !strcmp(cell, curEntry->cellInfo.name))
628                 code =
629                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
630                                   curEntry->cellInfo.hostName[i], &clones[i]);
631             else
632                 code =
633                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
634                                   curEntry->cellInfo.hostName[i], 0);
635             if (code) {
636                 if (code == AFSCONF_SYNTAX) {
637                     for (bp = tbuffer; *bp != '\n'; bp++) {     /* Take out the <cr> from the buffer */
638                         if (!*bp)
639                             break;
640                     }
641                     *bp = '\0';
642                     fprintf(stderr,
643                             "Can't properly parse host line \"%s\" in configuration file %s\n",
644                             tbuffer, tbuf1);
645                 }
646                 free(curEntry);
647                 fclose(tf);
648                 afsconf_CloseInternal(adir);
649                 return -1;
650             }
651             curEntry->cellInfo.numServers = ++i;
652         }
653     }
654     fclose(tf);                 /* close the file now */
655
656     /* end the last partially-completed cell */
657     if (curEntry) {
658         curEntry->next = adir->entries;
659         adir->entries = curEntry;
660     }
661
662     /* Read in the alias list */
663     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
664
665     tf = fopen(tbuffer, "r");
666     while (tf) {
667         char *aliasPtr;
668
669         tp = fgets(tbuffer, sizeof(tbuffer), tf);
670         if (!tp)
671             break;
672         TrimLine(tbuffer);      /* remove white space */
673
674         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
675             continue;           /* empty line */
676
677         tp = tbuffer;
678         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
679             tp++;
680         if (tp[0] == '\0')
681             continue;           /* invalid line */
682
683         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
684             0[tp++] = '\0';
685         if (tp[0] == '\0')
686             continue;           /* invalid line */
687
688         aliasPtr = tp;
689         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
690                && tp[0] != '\n')
691             tp++;
692         tp[0] = '\0';
693
694         curAlias = malloc(sizeof(*curAlias));
695         memset(curAlias, 0, sizeof(*curAlias));
696
697         strcpy(curAlias->aliasInfo.aliasName, aliasPtr);
698         strcpy(curAlias->aliasInfo.realName, tbuffer);
699
700         curAlias->next = adir->alias_entries;
701         adir->alias_entries = curAlias;
702     }
703
704     if (tf != NULL)
705         fclose(tf);
706     /* now read the fs keys, if possible */
707     adir->keystr = (struct afsconf_keys *)0;
708     afsconf_IntGetKeys(adir);
709
710     return 0;
711 }
712
713 /* parse a line of the form
714  *"128.2.1.3   #hostname" or
715  *"[128.2.1.3]  #hostname" for clones
716  * into the appropriate pieces.  
717  */
718 static int
719 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
720               char *aclone)
721 {
722     int c1, c2, c3, c4;
723     register afs_int32 code;
724     register char *tp;
725
726     if (*aline == '[') {
727         if (aclone)
728             *aclone = 1;
729         /* FIXME: length of aname unknown here */
730         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
731     } else {
732         if (aclone)
733             *aclone = 0;
734         /* FIXME: length of aname unknown here */
735         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
736     }
737     if (code != 5)
738         return AFSCONF_SYNTAX;
739     addr->sin_family = AF_INET;
740     addr->sin_port = 0;
741 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
742     addr->sin_len = sizeof(struct sockaddr_in);
743 #endif
744     tp = (char *)&addr->sin_addr;
745     *tp++ = c1;
746     *tp++ = c2;
747     *tp++ = c3;
748     *tp++ = c4;
749     return 0;
750 }
751
752 /* parse a line of the form
753  * ">cellname [linkedcellname] [#comments]"
754  * into the appropriate pieces.
755  */
756 static int
757 ParseCellLine(register char *aline, register char *aname,
758               register char *alname)
759 {
760     register int code;
761     /* FIXME: length of aname, alname unknown here */
762     code = sscanf(aline, ">%s %s", aname, alname);
763     if (code == 1)
764         *alname = '\0';
765     if (code == 2) {
766         if (*alname == '#') {
767             *alname = '\0';
768         }
769     }
770     return (code > 0 ? 0 : AFSCONF_SYNTAX);
771 }
772
773 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
774 int
775 afsconf_CellApply(struct afsconf_dir *adir,
776                   int (*aproc) (struct afsconf_cell * cell, char *arock,
777                                 struct afsconf_dir * dir), char *arock)
778 {
779     register struct afsconf_entry *tde;
780     register afs_int32 code;
781     LOCK_GLOBAL_MUTEX;
782     for (tde = adir->entries; tde; tde = tde->next) {
783         code = (*aproc) (&tde->cellInfo, arock, adir);
784         if (code) {
785             UNLOCK_GLOBAL_MUTEX;
786             return code;
787         }
788     }
789     UNLOCK_GLOBAL_MUTEX;
790     return 0;
791 }
792
793 /* call aproc(entry, arock, adir) for all cell aliases.
794  * Proc must return 0, or we'll stop early and return the code it returns
795  */
796 int
797 afsconf_CellAliasApply(struct afsconf_dir *adir,
798                        int (*aproc) (struct afsconf_cellalias * alias,
799                                      char *arock, struct afsconf_dir * dir),
800                        char *arock)
801 {
802     register struct afsconf_aliasentry *tde;
803     register afs_int32 code;
804     LOCK_GLOBAL_MUTEX;
805     for (tde = adir->alias_entries; tde; tde = tde->next) {
806         code = (*aproc) (&tde->aliasInfo, arock, adir);
807         if (code) {
808             UNLOCK_GLOBAL_MUTEX;
809             return code;
810         }
811     }
812     UNLOCK_GLOBAL_MUTEX;
813     return 0;
814 }
815
816 afs_int32 afsconf_SawCell = 0;
817
818 int
819 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
820                             char *aservice, struct afsconf_cell *acellInfo,
821                             char clones[])
822 {
823     afs_int32 code;
824     char *cell;
825
826     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
827     if (code)
828         return code;
829
830     if (acellName)
831         cell = acellName;
832     else
833         cell = (char *)&acellInfo->name;
834
835     code = afsconf_OpenInternal(adir, cell, clones);
836     return code;
837 }
838
839 #ifdef AFS_AFSDB_ENV
840 #if !defined(AFS_NT40_ENV)
841 int
842 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
843                      struct afsconf_cell *acellInfo)
844 {
845     afs_int32 code;
846     int tservice, i, len;
847     unsigned char answer[1024];
848     unsigned char *p;
849     char *dotcellname;
850     int cellnamelength;
851     char realCellName[256];
852     char host[256];
853     int server_num = 0;
854     int minttl = 0;
855
856     /* The resolver isn't always MT-safe.. Perhaps this ought to be
857      * replaced with a more fine-grained lock just for the resolver
858      * operations.
859      */
860
861     if ( ! strchr(acellName,'.') ) {
862        cellnamelength=strlen(acellName);
863        dotcellname=malloc(cellnamelength+2);
864        memcpy(dotcellname,acellName,cellnamelength);
865        dotcellname[cellnamelength]='.';
866        dotcellname[cellnamelength+1]=0;
867        LOCK_GLOBAL_MUTEX;
868             len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
869        if ( len < 0 ) {
870           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
871        }
872        UNLOCK_GLOBAL_MUTEX;
873        free(dotcellname);
874     } else {
875        LOCK_GLOBAL_MUTEX;
876             len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
877        UNLOCK_GLOBAL_MUTEX;
878     }
879     if (len < 0)
880         return AFSCONF_NOTFOUND;
881
882     p = answer + sizeof(HEADER);        /* Skip header */
883     code = dn_expand(answer, answer + len, p, host, sizeof(host));
884     if (code < 0)
885         return AFSCONF_NOTFOUND;
886
887     p += code + QFIXEDSZ;       /* Skip name */
888
889     while (p < answer + len) {
890         int type, ttl, size;
891
892         code = dn_expand(answer, answer + len, p, host, sizeof(host));
893         if (code < 0)
894             return AFSCONF_NOTFOUND;
895
896         p += code;              /* Skip the name */
897         type = (p[0] << 8) | p[1];
898         p += 4;                 /* Skip type and class */
899         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
900         p += 4;                 /* Skip the TTL */
901         size = (p[0] << 8) | p[1];
902         p += 2;                 /* Skip the size */
903
904         if (type == T_AFSDB) {
905             struct hostent *he;
906             short afsdb_type;
907
908             afsdb_type = (p[0] << 8) | p[1];
909             if (afsdb_type == 1) {
910                 /*
911                  * We know this is an AFSDB record for our cell, of the
912                  * right AFSDB type.  Write down the true cell name that
913                  * the resolver gave us above.
914                  */
915                 strcpy(realCellName, host);
916             }
917
918             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
919             if (code < 0)
920                 return AFSCONF_NOTFOUND;
921
922             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
923                 /* Do we want to get TTL data for the A record as well? */
924                 (he = gethostbyname(host))) {
925                 afs_int32 ipaddr;
926                 memcpy(&ipaddr, he->h_addr, he->h_length);
927                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
928                 strncpy(acellInfo->hostName[server_num], host,
929                         sizeof(acellInfo->hostName[server_num]));
930                 server_num++;
931
932                 if (!minttl || ttl < minttl)
933                     minttl = ttl;
934             }
935         }
936
937         p += size;
938     }
939
940     if (server_num == 0)        /* No AFSDB records */
941         return AFSCONF_NOTFOUND;
942
943     /* Convert the real cell name to lowercase */
944     for (p = (unsigned char *)realCellName; *p; p++)
945         *p = tolower(*p);
946
947     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
948     acellInfo->numServers = server_num;
949
950     if (aservice) {
951         tservice = afsconf_FindService(aservice);
952         if (tservice < 0)
953             return AFSCONF_NOTFOUND;    /* service not found */
954         for (i = 0; i < acellInfo->numServers; i++) {
955             acellInfo->hostAddr[i].sin_port = tservice;
956         }
957     }
958
959     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
960
961     return 0;
962 }
963 #else /* windows */
964 int
965 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
966                      struct afsconf_cell *acellInfo)
967 {
968     register afs_int32 i;
969     int tservice;
970     struct afsconf_entry DNSce;
971     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
972     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
973     int numServers;
974     int rc;
975     int ttl;
976
977     DNSce.cellInfo.numServers = 0;
978     DNSce.next = NULL;
979     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, &numServers,
980                       &ttl);
981     /* ignore the ttl here since this code is only called by transitory programs
982      * like klog, etc. */
983     if (rc < 0)
984         return -1;
985     if (numServers == 0)
986         return -1;
987
988     for (i = 0; i < numServers; i++) {
989         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
990                sizeof(long));
991         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
992         acellInfo->hostAddr[i].sin_family = AF_INET;
993
994         /* sin_port supplied by connection code */
995     }
996
997     acellInfo->numServers = numServers;
998     strcpy(acellInfo->name, acellName);
999     if (aservice) {
1000         LOCK_GLOBAL_MUTEX;
1001         tservice = afsconf_FindService(aservice);
1002         UNLOCK_GLOBAL_MUTEX;
1003         if (tservice < 0) {
1004             return AFSCONF_NOTFOUND;    /* service not found */
1005         }
1006         for (i = 0; i < acellInfo->numServers; i++) {
1007             acellInfo->hostAddr[i].sin_port = tservice;
1008         }
1009     }
1010     acellInfo->linkedCell = NULL;       /* no linked cell */
1011     acellInfo->flags = 0;
1012     return 0;
1013 }
1014 #endif /* windows */
1015 #endif /* AFS_AFSDB_ENV */
1016
1017 int
1018 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1019                     struct afsconf_cell *acellInfo)
1020 {
1021     register struct afsconf_entry *tce;
1022     struct afsconf_aliasentry *tcae;
1023     struct afsconf_entry *bestce;
1024     register afs_int32 i;
1025     int tservice;
1026     char *tcell;
1027     int cnLen;
1028     int ambig;
1029     char tbuffer[64];
1030
1031     LOCK_GLOBAL_MUTEX;
1032     if (adir)
1033         afsconf_Check(adir);
1034     if (acellName) {
1035         tcell = acellName;
1036         cnLen = (int)(strlen(tcell) + 1);
1037         lcstring(tcell, tcell, cnLen);
1038         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1039         /* call to afsconf_GetLocalCell: like klog  */
1040     } else {
1041         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1042         if (i) {
1043             UNLOCK_GLOBAL_MUTEX;
1044             return i;
1045         }
1046         tcell = tbuffer;
1047     }
1048     cnLen = strlen(tcell);
1049     bestce = (struct afsconf_entry *)0;
1050     ambig = 0;
1051     if (!adir) {
1052         UNLOCK_GLOBAL_MUTEX;
1053         return 0;
1054     }
1055
1056     /* Look through the list of aliases */
1057     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1058         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1059             tcell = tcae->aliasInfo.realName;
1060             break;
1061         }
1062     }
1063
1064     for (tce = adir->entries; tce; tce = tce->next) {
1065         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1066             /* found our cell */
1067             bestce = tce;
1068             ambig = 0;
1069             break;
1070         }
1071         if (strlen(tce->cellInfo.name) < cnLen)
1072             continue;           /* clearly wrong */
1073         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1074             if (bestce)
1075                 ambig = 1;      /* ambiguous unless we get exact match */
1076             bestce = tce;
1077         }
1078     }
1079     if (!ambig && bestce && bestce->cellInfo.numServers) {
1080         *acellInfo = bestce->cellInfo;  /* structure assignment */
1081         if (aservice) {
1082             tservice = afsconf_FindService(aservice);
1083             if (tservice < 0) {
1084                 UNLOCK_GLOBAL_MUTEX;
1085                 return AFSCONF_NOTFOUND;        /* service not found */
1086             }
1087             for (i = 0; i < acellInfo->numServers; i++) {
1088                 acellInfo->hostAddr[i].sin_port = tservice;
1089             }
1090         }
1091         acellInfo->timeout = 0;
1092         UNLOCK_GLOBAL_MUTEX;
1093         return 0;
1094     } else {
1095         UNLOCK_GLOBAL_MUTEX;
1096 #ifdef AFS_AFSDB_ENV
1097         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1098 #else
1099         return AFSCONF_NOTFOUND;
1100 #endif /* AFS_AFSDB_ENV */
1101     }
1102 }
1103
1104 int
1105 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1106                      afs_int32 alen)
1107 {
1108     static int afsconf_showcell = 0;
1109     char *afscell_path;
1110     afs_int32 code = 0;
1111
1112     LOCK_GLOBAL_MUTEX;
1113     /*
1114      * If a cell switch was specified in a command, then it should override the 
1115      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1116      * is set and the cell name in the adir structure is used.
1117      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1118      */
1119     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1120         if (!afsconf_showcell) {
1121             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1122                     afscell_path);
1123             afsconf_showcell = 1;
1124         }
1125         strncpy(aname, afscell_path, alen);
1126     } else {
1127         afsconf_Check(adir);
1128         if (adir->cellName) {
1129             strncpy(aname, adir->cellName, alen);
1130         } else
1131             code = AFSCONF_UNKNOWN;
1132     }
1133
1134     UNLOCK_GLOBAL_MUTEX;
1135     return (code);
1136 }
1137
1138 int
1139 afsconf_Close(struct afsconf_dir *adir)
1140 {
1141     LOCK_GLOBAL_MUTEX;
1142     afsconf_CloseInternal(adir);
1143     if (adir->name)
1144         free(adir->name);
1145     free(adir);
1146     UNLOCK_GLOBAL_MUTEX;
1147     return 0;
1148 }
1149
1150 static int
1151 afsconf_CloseInternal(register struct afsconf_dir *adir)
1152 {
1153     register struct afsconf_entry *td, *nd;
1154     struct afsconf_aliasentry *ta, *na;
1155     register char *tname;
1156
1157     tname = adir->name;         /* remember name, since that's all we preserve */
1158
1159     /* free everything we can find */
1160     if (adir->cellName)
1161         free(adir->cellName);
1162     for (td = adir->entries; td; td = nd) {
1163         nd = td->next;
1164         if (td->cellInfo.linkedCell)
1165             free(td->cellInfo.linkedCell);
1166         free(td);
1167     }
1168     for (ta = adir->alias_entries; ta; ta = na) {
1169         na = ta->next;
1170         free(ta);
1171     }
1172     if (adir->keystr)
1173         free(adir->keystr);
1174
1175     /* reinit */
1176     memset(adir, 0, sizeof(struct afsconf_dir));
1177     adir->name = tname;         /* restore it */
1178     return 0;
1179 }
1180
1181 static int
1182 afsconf_Reopen(register struct afsconf_dir *adir)
1183 {
1184     register afs_int32 code;
1185     code = afsconf_CloseInternal(adir);
1186     if (code)
1187         return code;
1188     code = afsconf_OpenInternal(adir, 0, 0);
1189     return code;
1190 }
1191
1192 /* called during opening of config file */
1193 int
1194 afsconf_IntGetKeys(struct afsconf_dir *adir)
1195 {
1196     char tbuffer[256];
1197     register int fd;
1198     struct afsconf_keys *tstr;
1199     register afs_int32 code;
1200
1201 #ifdef AFS_NT40_ENV
1202     /* NT client config dir has no KeyFile; don't risk attempting open
1203      * because there might be a random file of this name if dir is shared.
1204      */
1205     if (IsClientConfigDirectory(adir->name)) {
1206         adir->keystr = ((struct afsconf_keys *)
1207                         malloc(sizeof(struct afsconf_keys)));
1208         adir->keystr->nkeys = 0;
1209         return 0;
1210     }
1211 #endif /* AFS_NT40_ENV */
1212
1213     LOCK_GLOBAL_MUTEX;
1214     /* compute the key name and other setup */
1215     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1216     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1217     adir->keystr = tstr;
1218
1219     /* read key file */
1220     fd = open(tbuffer, O_RDONLY);
1221     if (fd < 0) {
1222         tstr->nkeys = 0;
1223         UNLOCK_GLOBAL_MUTEX;
1224         return 0;
1225     }
1226     code = read(fd, tstr, sizeof(struct afsconf_keys));
1227     close(fd);
1228     if (code < sizeof(afs_int32)) {
1229         tstr->nkeys = 0;
1230         UNLOCK_GLOBAL_MUTEX;
1231         return 0;
1232     }
1233
1234     /* convert key structure to host order */
1235     tstr->nkeys = ntohl(tstr->nkeys);
1236
1237     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1238         tstr->nkeys = 0;
1239         UNLOCK_GLOBAL_MUTEX;
1240         return 0;
1241     }
1242
1243     for (fd = 0; fd < tstr->nkeys; fd++)
1244         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1245
1246     UNLOCK_GLOBAL_MUTEX;
1247     return 0;
1248 }
1249
1250 /* get keys structure */
1251 int
1252 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1253 {
1254     register afs_int32 code;
1255
1256     LOCK_GLOBAL_MUTEX;
1257     code = afsconf_Check(adir);
1258     if (code) {
1259         UNLOCK_GLOBAL_MUTEX;
1260         return AFSCONF_FAILURE;
1261     }
1262     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1263     UNLOCK_GLOBAL_MUTEX;
1264     return 0;
1265 }
1266
1267 /* get latest key */
1268 afs_int32
1269 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, char *akey)
1270 {
1271     register int i;
1272     int maxa;
1273     register struct afsconf_key *tk;
1274     register afs_int32 best;
1275     struct afsconf_key *bestk;
1276     register afs_int32 code;
1277
1278     LOCK_GLOBAL_MUTEX;
1279     code = afsconf_Check(adir);
1280     if (code) {
1281         UNLOCK_GLOBAL_MUTEX;
1282         return AFSCONF_FAILURE;
1283     }
1284     maxa = adir->keystr->nkeys;
1285
1286     best = -1;                  /* highest kvno we've seen yet */
1287     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1288     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1289         if (tk->kvno == 999)
1290             continue;           /* skip bcrypt keys */
1291         if (tk->kvno > best) {
1292             best = tk->kvno;
1293             bestk = tk;
1294         }
1295     }
1296     if (bestk) {                /* found any  */
1297         if (akey)
1298             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1299         if (avno)
1300             *avno = bestk->kvno;        /* and kvno to caller */
1301         UNLOCK_GLOBAL_MUTEX;
1302         return 0;
1303     }
1304     UNLOCK_GLOBAL_MUTEX;
1305     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1306 }
1307
1308 /* get a particular key */
1309 int
1310 afsconf_GetKey(struct afsconf_dir *adir, afs_int32 avno, char *akey)
1311 {
1312     register int i, maxa;
1313     register struct afsconf_key *tk;
1314     register afs_int32 code;
1315
1316     LOCK_GLOBAL_MUTEX;
1317     code = afsconf_Check(adir);
1318     if (code) {
1319         UNLOCK_GLOBAL_MUTEX;
1320         return AFSCONF_FAILURE;
1321     }
1322     maxa = adir->keystr->nkeys;
1323
1324     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1325         if (tk->kvno == avno) {
1326             memcpy(akey, tk->key, 8);
1327             UNLOCK_GLOBAL_MUTEX;
1328             return 0;
1329         }
1330     }
1331
1332     UNLOCK_GLOBAL_MUTEX;
1333     return AFSCONF_NOTFOUND;
1334 }
1335
1336 /* save the key structure in the appropriate file */
1337 static int
1338 SaveKeys(struct afsconf_dir *adir)
1339 {
1340     struct afsconf_keys tkeys;
1341     register int fd;
1342     register afs_int32 i;
1343     char tbuffer[256];
1344
1345     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1346
1347     /* convert it to net byte order */
1348     for (i = 0; i < tkeys.nkeys; i++)
1349         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1350     tkeys.nkeys = htonl(tkeys.nkeys);
1351
1352     /* rewrite keys file */
1353     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1354     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1355     if (fd < 0)
1356         return AFSCONF_FAILURE;
1357     i = write(fd, &tkeys, sizeof(tkeys));
1358     if (i != sizeof(tkeys)) {
1359         close(fd);
1360         return AFSCONF_FAILURE;
1361     }
1362     if (close(fd) < 0)
1363         return AFSCONF_FAILURE;
1364     return 0;
1365 }
1366
1367 int
1368 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1369                afs_int32 overwrite)
1370 {
1371     register struct afsconf_keys *tk;
1372     register struct afsconf_key *tkey;
1373     register afs_int32 i;
1374     int foundSlot;
1375
1376     LOCK_GLOBAL_MUTEX;
1377     tk = adir->keystr;
1378
1379     if (akvno != 999) {
1380         if (akvno < 0 || akvno > 255) {
1381             UNLOCK_GLOBAL_MUTEX;
1382             return ERANGE;
1383         }
1384     }
1385     foundSlot = 0;
1386     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1387         if (tkey->kvno == akvno) {
1388             if (!overwrite) {
1389                 UNLOCK_GLOBAL_MUTEX;
1390                 return AFSCONF_KEYINUSE;
1391             }
1392             foundSlot = 1;
1393             break;
1394         }
1395     }
1396     if (!foundSlot) {
1397         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1398             UNLOCK_GLOBAL_MUTEX;
1399             return AFSCONF_FULL;
1400         }
1401         tkey = &tk->key[tk->nkeys++];
1402     }
1403     tkey->kvno = akvno;
1404     memcpy(tkey->key, akey, 8);
1405     i = SaveKeys(adir);
1406     afsconf_Touch(adir);
1407     UNLOCK_GLOBAL_MUTEX;
1408     return i;
1409 }
1410
1411 /* this proc works by sliding the other guys down, rather than using a funny
1412     kvno value, so that callers can count on getting a good key in key[0].
1413 */
1414 int
1415 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1416 {
1417     register struct afsconf_keys *tk;
1418     register struct afsconf_key *tkey;
1419     register int i;
1420     int foundFlag = 0;
1421
1422     LOCK_GLOBAL_MUTEX;
1423     tk = adir->keystr;
1424
1425     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1426         if (tkey->kvno == akvno) {
1427             foundFlag = 1;
1428             break;
1429         }
1430     }
1431     if (!foundFlag) {
1432         UNLOCK_GLOBAL_MUTEX;
1433         return AFSCONF_NOTFOUND;
1434     }
1435
1436     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1437     for (; i < tk->nkeys - 1; i++, tkey++) {
1438         tkey->kvno = (tkey + 1)->kvno;
1439         memcpy(tkey->key, (tkey + 1)->key, 8);
1440     }
1441     tk->nkeys--;
1442     i = SaveKeys(adir);
1443     afsconf_Touch(adir);
1444     UNLOCK_GLOBAL_MUTEX;
1445     return i;
1446 }