thiscell-whitespace-20061120
[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, int abufsize);
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, int abufsize)
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     strlcpy(tbuffer, tp, sizeof tbuffer);
265     strlcpy(abuffer, tbuffer, abufsize);
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 = strdup(adir);
410
411     code = afsconf_OpenInternal(tdir, 0, 0);
412     if (code) {
413         char *afsconf_path, afs_confdir[128];
414
415         free(tdir->name);
416         /* Check global place only when local Open failed for whatever reason */
417         if (!(afsconf_path = getenv("AFSCONF"))) {
418             /* 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... */
419             char *home_dir;
420             afsconf_FILE *fp;
421             size_t len;
422
423             if (!(home_dir = getenv("HOME"))) {
424                 /* Our last chance is the "/.AFSCONF" file */
425                 fp = fopen("/.AFSCONF", "r");
426                 if (fp == 0) {
427                     free(tdir);
428                     UNLOCK_GLOBAL_MUTEX;
429                     return (struct afsconf_dir *)0;
430                 }
431                 fgets(afs_confdir, 128, fp);
432                 fclose(fp);
433             } else {
434                 char pathname[256];
435
436                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
437                 fp = fopen(pathname, "r");
438                 if (fp == 0) {
439                     /* Our last chance is the "/.AFSCONF" file */
440                     fp = fopen("/.AFSCONF", "r");
441                     if (fp == 0) {
442                         free(tdir);
443                         UNLOCK_GLOBAL_MUTEX;
444                         return (struct afsconf_dir *)0;
445                     }
446                 }
447                 fgets(afs_confdir, 128, fp);
448                 fclose(fp);
449             }
450             len = strlen(afs_confdir);
451             if (len == 0) {
452                 free(tdir);
453                 UNLOCK_GLOBAL_MUTEX;
454                 return (struct afsconf_dir *)0;
455             }
456             if (afs_confdir[len - 1] == '\n') {
457                 afs_confdir[len - 1] = 0;
458             }
459             afsconf_path = afs_confdir;
460         }
461         tdir->name = strdup(afsconf_path);
462         code = afsconf_OpenInternal(tdir, 0, 0);
463         if (code) {
464             free(tdir->name);
465             free(tdir);
466             UNLOCK_GLOBAL_MUTEX;
467             return (struct afsconf_dir *)0;
468         }
469     }
470     UNLOCK_GLOBAL_MUTEX;
471     return tdir;
472 }
473
474 static int
475 GetCellUnix(struct afsconf_dir *adir)
476 {
477     char *rc;
478     char tbuffer[256];
479     char *start, *p;
480     afsconf_FILE *fp;
481     
482     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
483     fp = fopen(tbuffer, "r");
484     if (fp == 0) {
485         return -1;
486     }
487     rc = fgets(tbuffer, 256, fp);
488     fclose(fp);
489     if (rc == NULL)
490         return -1;
491
492     start = tbuffer;
493     while (*start != '\0' && isspace(*start))
494         start++;
495     p = start;
496     while (*p != '\0' && !isspace(*p))
497         p++;
498     *p = '\0';
499     if (*start == '\0')
500         return -1;
501
502     adir->cellName = strdup(start);
503     return 0;
504 }
505
506
507 #ifdef AFS_NT40_ENV
508 static int
509 GetCellNT(struct afsconf_dir *adir)
510 {
511     if (IsClientConfigDirectory(adir->name)) {
512         /* NT client config dir; ThisCell is in registry (no file). */
513         return afssw_GetClientCellName(&adir->cellName);
514     } else {
515         /* NT server config dir; works just like Unix */
516         return GetCellUnix(adir);
517     }
518 }
519 #endif /* AFS_NT40_ENV */
520
521
522 static int
523 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
524                      char clones[])
525 {
526     afsconf_FILE *tf;
527     register char *tp, *bp;
528     register struct afsconf_entry *curEntry;
529     struct afsconf_aliasentry *curAlias;
530     register afs_int32 code;
531     afs_int32 i;
532     char tbuffer[256], tbuf1[256];
533     struct stat tstat;
534
535     /* figure out the cell name */
536 #ifdef AFS_NT40_ENV
537     i = GetCellNT(adir);
538 #else
539     i = GetCellUnix(adir);
540 #endif
541
542 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
543     if (i) {
544         return i;
545     }
546 #endif
547
548     /* now parse the individual lines */
549     curEntry = 0;
550
551 #ifdef AFS_NT40_ENV
552     /* NT client/server have a CellServDB that is the same format as Unix.
553      * However, the NT client uses a different file name
554      */
555     if (IsClientConfigDirectory(adir->name)) {
556         /* NT client config dir */
557         char *p;
558         if (!afssw_GetClientCellServDBDir(&p)) {
559             strcompose(tbuffer, sizeof(tbuffer), p, "/",
560                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
561             free(p);
562         } else {
563             int len;
564             strncpy(tbuffer, adir->name, sizeof(tbuffer));
565             len = (int)strlen(tbuffer);
566             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
567                 strncat(tbuffer, "\\", sizeof(tbuffer));
568             }
569             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
570                     sizeof(tbuffer));
571             tbuffer[sizeof(tbuffer) - 1] = '\0';
572         }
573     } else {
574         /* NT server config dir */
575         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
576                    NULL);
577     }
578 #else
579     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
580 #endif /* AFS_NT40_ENV */
581
582     if (!stat(tbuffer, &tstat)) {
583         adir->timeRead = tstat.st_mtime;
584     } else {
585         adir->timeRead = 0;
586     }
587
588     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
589     tf = fopen(tbuffer, "r");
590     if (!tf) {
591         return -1;
592     }
593     while (1) {
594         tp = fgets(tbuffer, sizeof(tbuffer), tf);
595         if (!tp)
596             break;
597         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
598         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
599             continue;           /* empty line */
600         if (tbuffer[0] == '>') {
601             char linkedcell[MAXCELLCHARS];
602             /* start new cell item */
603             if (curEntry) {
604                 /* thread this guy on the list */
605                 curEntry->next = adir->entries;
606                 adir->entries = curEntry;
607                 curEntry = 0;
608             }
609             curEntry =
610                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
611             memset(curEntry, 0, sizeof(struct afsconf_entry));
612             code =
613                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
614             if (code) {
615                 afsconf_CloseInternal(adir);
616                 fclose(tf);
617                 free(curEntry);
618                 return -1;
619             }
620             if (linkedcell[0] != '\0')
621                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
622         } else {
623             /* new host in the current cell */
624             if (!curEntry) {
625                 afsconf_CloseInternal(adir);
626                 fclose(tf);
627                 return -1;
628             }
629             i = curEntry->cellInfo.numServers;
630             if (cell && !strcmp(cell, curEntry->cellInfo.name))
631                 code =
632                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
633                                   curEntry->cellInfo.hostName[i], &clones[i]);
634             else
635                 code =
636                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
637                                   curEntry->cellInfo.hostName[i], 0);
638             if (code) {
639                 if (code == AFSCONF_SYNTAX) {
640                     for (bp = tbuffer; *bp != '\n'; bp++) {     /* Take out the <cr> from the buffer */
641                         if (!*bp)
642                             break;
643                     }
644                     *bp = '\0';
645                     fprintf(stderr,
646                             "Can't properly parse host line \"%s\" in configuration file %s\n",
647                             tbuffer, tbuf1);
648                 }
649                 free(curEntry);
650                 fclose(tf);
651                 afsconf_CloseInternal(adir);
652                 return -1;
653             }
654             curEntry->cellInfo.numServers = ++i;
655         }
656     }
657     fclose(tf);                 /* close the file now */
658
659     /* end the last partially-completed cell */
660     if (curEntry) {
661         curEntry->next = adir->entries;
662         adir->entries = curEntry;
663     }
664
665     /* Read in the alias list */
666     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
667
668     tf = fopen(tbuffer, "r");
669     while (tf) {
670         char *aliasPtr;
671
672         tp = fgets(tbuffer, sizeof(tbuffer), tf);
673         if (!tp)
674             break;
675         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
676
677         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
678             continue;           /* empty line */
679
680         tp = tbuffer;
681         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
682             tp++;
683         if (tp[0] == '\0')
684             continue;           /* invalid line */
685
686         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
687             0[tp++] = '\0';
688         if (tp[0] == '\0')
689             continue;           /* invalid line */
690
691         aliasPtr = tp;
692         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
693                && tp[0] != '\n')
694             tp++;
695         tp[0] = '\0';
696
697         curAlias = malloc(sizeof(*curAlias));
698         memset(curAlias, 0, sizeof(*curAlias));
699
700         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
701         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
702
703         curAlias->next = adir->alias_entries;
704         adir->alias_entries = curAlias;
705     }
706
707     if (tf != NULL)
708         fclose(tf);
709     /* now read the fs keys, if possible */
710     adir->keystr = (struct afsconf_keys *)0;
711     afsconf_IntGetKeys(adir);
712
713     return 0;
714 }
715
716 /* parse a line of the form
717  *"128.2.1.3   #hostname" or
718  *"[128.2.1.3]  #hostname" for clones
719  * into the appropriate pieces.  
720  */
721 static int
722 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
723               char *aclone)
724 {
725     int c1, c2, c3, c4;
726     register afs_int32 code;
727     register char *tp;
728
729     if (*aline == '[') {
730         if (aclone)
731             *aclone = 1;
732         /* FIXME: length of aname unknown here */
733         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
734     } else {
735         if (aclone)
736             *aclone = 0;
737         /* FIXME: length of aname unknown here */
738         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
739     }
740     if (code != 5)
741         return AFSCONF_SYNTAX;
742     addr->sin_family = AF_INET;
743     addr->sin_port = 0;
744 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
745     addr->sin_len = sizeof(struct sockaddr_in);
746 #endif
747     tp = (char *)&addr->sin_addr;
748     *tp++ = c1;
749     *tp++ = c2;
750     *tp++ = c3;
751     *tp++ = c4;
752     return 0;
753 }
754
755 /* parse a line of the form
756  * ">cellname [linkedcellname] [#comments]"
757  * into the appropriate pieces.
758  */
759 static int
760 ParseCellLine(register char *aline, register char *aname,
761               register char *alname)
762 {
763     register int code;
764     /* FIXME: length of aname, alname unknown here */
765     code = sscanf(aline, ">%s %s", aname, alname);
766     if (code == 1)
767         *alname = '\0';
768     if (code == 2) {
769         if (*alname == '#') {
770             *alname = '\0';
771         }
772     }
773     return (code > 0 ? 0 : AFSCONF_SYNTAX);
774 }
775
776 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
777 int
778 afsconf_CellApply(struct afsconf_dir *adir,
779                   int (*aproc) (struct afsconf_cell * cell, char *arock,
780                                 struct afsconf_dir * dir), char *arock)
781 {
782     register struct afsconf_entry *tde;
783     register afs_int32 code;
784     LOCK_GLOBAL_MUTEX;
785     for (tde = adir->entries; tde; tde = tde->next) {
786         code = (*aproc) (&tde->cellInfo, arock, adir);
787         if (code) {
788             UNLOCK_GLOBAL_MUTEX;
789             return code;
790         }
791     }
792     UNLOCK_GLOBAL_MUTEX;
793     return 0;
794 }
795
796 /* call aproc(entry, arock, adir) for all cell aliases.
797  * Proc must return 0, or we'll stop early and return the code it returns
798  */
799 int
800 afsconf_CellAliasApply(struct afsconf_dir *adir,
801                        int (*aproc) (struct afsconf_cellalias * alias,
802                                      char *arock, struct afsconf_dir * dir),
803                        char *arock)
804 {
805     register struct afsconf_aliasentry *tde;
806     register afs_int32 code;
807     LOCK_GLOBAL_MUTEX;
808     for (tde = adir->alias_entries; tde; tde = tde->next) {
809         code = (*aproc) (&tde->aliasInfo, arock, adir);
810         if (code) {
811             UNLOCK_GLOBAL_MUTEX;
812             return code;
813         }
814     }
815     UNLOCK_GLOBAL_MUTEX;
816     return 0;
817 }
818
819 afs_int32 afsconf_SawCell = 0;
820
821 int
822 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
823                             char *aservice, struct afsconf_cell *acellInfo,
824                             char clones[])
825 {
826     afs_int32 code;
827     char *cell;
828
829     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
830     if (code)
831         return code;
832
833     if (acellName)
834         cell = acellName;
835     else
836         cell = (char *)&acellInfo->name;
837
838     code = afsconf_OpenInternal(adir, cell, clones);
839     return code;
840 }
841
842 #ifdef AFS_AFSDB_ENV
843 #if !defined(AFS_NT40_ENV)
844 int
845 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
846                      struct afsconf_cell *acellInfo)
847 {
848     afs_int32 code;
849     int tservice, i, len;
850     unsigned char answer[1024];
851     unsigned char *p;
852     char *dotcellname;
853     int cellnamelength;
854     char realCellName[256];
855     char host[256];
856     int server_num = 0;
857     int minttl = 0;
858
859     /* The resolver isn't always MT-safe.. Perhaps this ought to be
860      * replaced with a more fine-grained lock just for the resolver
861      * operations.
862      */
863
864     if ( ! strchr(acellName,'.') ) {
865        cellnamelength=strlen(acellName);
866        dotcellname=malloc(cellnamelength+2);
867        memcpy(dotcellname,acellName,cellnamelength);
868        dotcellname[cellnamelength]='.';
869        dotcellname[cellnamelength+1]=0;
870        LOCK_GLOBAL_MUTEX;
871             len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
872        if ( len < 0 ) {
873           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
874        }
875        UNLOCK_GLOBAL_MUTEX;
876        free(dotcellname);
877     } else {
878        LOCK_GLOBAL_MUTEX;
879             len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
880        UNLOCK_GLOBAL_MUTEX;
881     }
882     if (len < 0)
883         return AFSCONF_NOTFOUND;
884
885     p = answer + sizeof(HEADER);        /* Skip header */
886     code = dn_expand(answer, answer + len, p, host, sizeof(host));
887     if (code < 0)
888         return AFSCONF_NOTFOUND;
889
890     p += code + QFIXEDSZ;       /* Skip name */
891
892     while (p < answer + len) {
893         int type, ttl, size;
894
895         code = dn_expand(answer, answer + len, p, host, sizeof(host));
896         if (code < 0)
897             return AFSCONF_NOTFOUND;
898
899         p += code;              /* Skip the name */
900         type = (p[0] << 8) | p[1];
901         p += 4;                 /* Skip type and class */
902         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
903         p += 4;                 /* Skip the TTL */
904         size = (p[0] << 8) | p[1];
905         p += 2;                 /* Skip the size */
906
907         if (type == T_AFSDB) {
908             struct hostent *he;
909             short afsdb_type;
910
911             afsdb_type = (p[0] << 8) | p[1];
912             if (afsdb_type == 1) {
913                 /*
914                  * We know this is an AFSDB record for our cell, of the
915                  * right AFSDB type.  Write down the true cell name that
916                  * the resolver gave us above.
917                  */
918                 strlcpy(realCellName, host, sizeof realCellName);
919             }
920
921             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
922             if (code < 0)
923                 return AFSCONF_NOTFOUND;
924
925             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
926                 /* Do we want to get TTL data for the A record as well? */
927                 (he = gethostbyname(host))) {
928                 afs_int32 ipaddr;
929                 memcpy(&ipaddr, he->h_addr, he->h_length);
930                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
931                 strncpy(acellInfo->hostName[server_num], host,
932                         sizeof(acellInfo->hostName[server_num]));
933                 server_num++;
934
935                 if (!minttl || ttl < minttl)
936                     minttl = ttl;
937             }
938         }
939
940         p += size;
941     }
942
943     if (server_num == 0)        /* No AFSDB records */
944         return AFSCONF_NOTFOUND;
945
946     /* Convert the real cell name to lowercase */
947     for (p = (unsigned char *)realCellName; *p; p++)
948         *p = tolower(*p);
949
950     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
951     acellInfo->numServers = server_num;
952
953     if (aservice) {
954         tservice = afsconf_FindService(aservice);
955         if (tservice < 0)
956             return AFSCONF_NOTFOUND;    /* service not found */
957         for (i = 0; i < acellInfo->numServers; i++) {
958             acellInfo->hostAddr[i].sin_port = tservice;
959         }
960     }
961
962     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
963
964     return 0;
965 }
966 #else /* windows */
967 int
968 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
969                      struct afsconf_cell *acellInfo)
970 {
971     register afs_int32 i;
972     int tservice;
973     struct afsconf_entry DNSce;
974     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
975     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
976     int numServers;
977     int rc;
978     int ttl;
979
980     DNSce.cellInfo.numServers = 0;
981     DNSce.next = NULL;
982     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, &numServers,
983                       &ttl);
984     /* ignore the ttl here since this code is only called by transitory programs
985      * like klog, etc. */
986     if (rc < 0)
987         return -1;
988     if (numServers == 0)
989         return -1;
990
991     for (i = 0; i < numServers; i++) {
992         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
993                sizeof(long));
994         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
995         acellInfo->hostAddr[i].sin_family = AF_INET;
996
997         /* sin_port supplied by connection code */
998     }
999
1000     acellInfo->numServers = numServers;
1001     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1002     if (aservice) {
1003         LOCK_GLOBAL_MUTEX;
1004         tservice = afsconf_FindService(aservice);
1005         UNLOCK_GLOBAL_MUTEX;
1006         if (tservice < 0) {
1007             return AFSCONF_NOTFOUND;    /* service not found */
1008         }
1009         for (i = 0; i < acellInfo->numServers; i++) {
1010             acellInfo->hostAddr[i].sin_port = tservice;
1011         }
1012     }
1013     acellInfo->linkedCell = NULL;       /* no linked cell */
1014     acellInfo->flags = 0;
1015     return 0;
1016 }
1017 #endif /* windows */
1018 #endif /* AFS_AFSDB_ENV */
1019
1020 int
1021 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1022                     struct afsconf_cell *acellInfo)
1023 {
1024     register struct afsconf_entry *tce;
1025     struct afsconf_aliasentry *tcae;
1026     struct afsconf_entry *bestce;
1027     register afs_int32 i;
1028     int tservice;
1029     char *tcell;
1030     int cnLen;
1031     int ambig;
1032     char tbuffer[64];
1033
1034     LOCK_GLOBAL_MUTEX;
1035     if (adir)
1036         afsconf_Check(adir);
1037     if (acellName) {
1038         tcell = acellName;
1039         cnLen = (int)(strlen(tcell) + 1);
1040         lcstring(tcell, tcell, cnLen);
1041         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1042         /* call to afsconf_GetLocalCell: like klog  */
1043     } else {
1044         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1045         if (i) {
1046             UNLOCK_GLOBAL_MUTEX;
1047             return i;
1048         }
1049         tcell = tbuffer;
1050     }
1051     cnLen = strlen(tcell);
1052     bestce = (struct afsconf_entry *)0;
1053     ambig = 0;
1054     if (!adir) {
1055         UNLOCK_GLOBAL_MUTEX;
1056         return 0;
1057     }
1058
1059     /* Look through the list of aliases */
1060     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1061         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1062             tcell = tcae->aliasInfo.realName;
1063             break;
1064         }
1065     }
1066
1067     for (tce = adir->entries; tce; tce = tce->next) {
1068         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1069             /* found our cell */
1070             bestce = tce;
1071             ambig = 0;
1072             break;
1073         }
1074         if (strlen(tce->cellInfo.name) < cnLen)
1075             continue;           /* clearly wrong */
1076         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1077             if (bestce)
1078                 ambig = 1;      /* ambiguous unless we get exact match */
1079             bestce = tce;
1080         }
1081     }
1082     if (!ambig && bestce && bestce->cellInfo.numServers) {
1083         *acellInfo = bestce->cellInfo;  /* structure assignment */
1084         if (aservice) {
1085             tservice = afsconf_FindService(aservice);
1086             if (tservice < 0) {
1087                 UNLOCK_GLOBAL_MUTEX;
1088                 return AFSCONF_NOTFOUND;        /* service not found */
1089             }
1090             for (i = 0; i < acellInfo->numServers; i++) {
1091                 acellInfo->hostAddr[i].sin_port = tservice;
1092             }
1093         }
1094         acellInfo->timeout = 0;
1095         UNLOCK_GLOBAL_MUTEX;
1096         return 0;
1097     } else {
1098         UNLOCK_GLOBAL_MUTEX;
1099 #ifdef AFS_AFSDB_ENV
1100         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1101 #else
1102         return AFSCONF_NOTFOUND;
1103 #endif /* AFS_AFSDB_ENV */
1104     }
1105 }
1106
1107 int
1108 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1109                      afs_int32 alen)
1110 {
1111     static int afsconf_showcell = 0;
1112     char *afscell_path;
1113     afs_int32 code = 0;
1114
1115     LOCK_GLOBAL_MUTEX;
1116     /*
1117      * If a cell switch was specified in a command, then it should override the 
1118      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1119      * is set and the cell name in the adir structure is used.
1120      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1121      */
1122     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1123         if (!afsconf_showcell) {
1124             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1125                     afscell_path);
1126             afsconf_showcell = 1;
1127         }
1128         strncpy(aname, afscell_path, alen);
1129     } else {
1130         afsconf_Check(adir);
1131         if (adir->cellName) {
1132             strncpy(aname, adir->cellName, alen);
1133         } else
1134             code = AFSCONF_UNKNOWN;
1135     }
1136
1137     UNLOCK_GLOBAL_MUTEX;
1138     return (code);
1139 }
1140
1141 int
1142 afsconf_Close(struct afsconf_dir *adir)
1143 {
1144     LOCK_GLOBAL_MUTEX;
1145     afsconf_CloseInternal(adir);
1146     if (adir->name)
1147         free(adir->name);
1148     free(adir);
1149     UNLOCK_GLOBAL_MUTEX;
1150     return 0;
1151 }
1152
1153 static int
1154 afsconf_CloseInternal(register struct afsconf_dir *adir)
1155 {
1156     register struct afsconf_entry *td, *nd;
1157     struct afsconf_aliasentry *ta, *na;
1158     register char *tname;
1159
1160     tname = adir->name;         /* remember name, since that's all we preserve */
1161
1162     /* free everything we can find */
1163     if (adir->cellName)
1164         free(adir->cellName);
1165     for (td = adir->entries; td; td = nd) {
1166         nd = td->next;
1167         if (td->cellInfo.linkedCell)
1168             free(td->cellInfo.linkedCell);
1169         free(td);
1170     }
1171     for (ta = adir->alias_entries; ta; ta = na) {
1172         na = ta->next;
1173         free(ta);
1174     }
1175     if (adir->keystr)
1176         free(adir->keystr);
1177
1178     /* reinit */
1179     memset(adir, 0, sizeof(struct afsconf_dir));
1180     adir->name = tname;         /* restore it */
1181     return 0;
1182 }
1183
1184 static int
1185 afsconf_Reopen(register struct afsconf_dir *adir)
1186 {
1187     register afs_int32 code;
1188     code = afsconf_CloseInternal(adir);
1189     if (code)
1190         return code;
1191     code = afsconf_OpenInternal(adir, 0, 0);
1192     return code;
1193 }
1194
1195 /* called during opening of config file */
1196 int
1197 afsconf_IntGetKeys(struct afsconf_dir *adir)
1198 {
1199     char tbuffer[256];
1200     register int fd;
1201     struct afsconf_keys *tstr;
1202     register afs_int32 code;
1203
1204 #ifdef AFS_NT40_ENV
1205     /* NT client config dir has no KeyFile; don't risk attempting open
1206      * because there might be a random file of this name if dir is shared.
1207      */
1208     if (IsClientConfigDirectory(adir->name)) {
1209         adir->keystr = ((struct afsconf_keys *)
1210                         malloc(sizeof(struct afsconf_keys)));
1211         adir->keystr->nkeys = 0;
1212         return 0;
1213     }
1214 #endif /* AFS_NT40_ENV */
1215
1216     LOCK_GLOBAL_MUTEX;
1217     /* compute the key name and other setup */
1218     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1219     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1220     adir->keystr = tstr;
1221
1222     /* read key file */
1223     fd = open(tbuffer, O_RDONLY);
1224     if (fd < 0) {
1225         tstr->nkeys = 0;
1226         UNLOCK_GLOBAL_MUTEX;
1227         return 0;
1228     }
1229     code = read(fd, tstr, sizeof(struct afsconf_keys));
1230     close(fd);
1231     if (code < sizeof(afs_int32)) {
1232         tstr->nkeys = 0;
1233         UNLOCK_GLOBAL_MUTEX;
1234         return 0;
1235     }
1236
1237     /* convert key structure to host order */
1238     tstr->nkeys = ntohl(tstr->nkeys);
1239
1240     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1241         tstr->nkeys = 0;
1242         UNLOCK_GLOBAL_MUTEX;
1243         return 0;
1244     }
1245
1246     for (fd = 0; fd < tstr->nkeys; fd++)
1247         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1248
1249     UNLOCK_GLOBAL_MUTEX;
1250     return 0;
1251 }
1252
1253 /* get keys structure */
1254 int
1255 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1256 {
1257     register afs_int32 code;
1258
1259     LOCK_GLOBAL_MUTEX;
1260     code = afsconf_Check(adir);
1261     if (code) {
1262         UNLOCK_GLOBAL_MUTEX;
1263         return AFSCONF_FAILURE;
1264     }
1265     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1266     UNLOCK_GLOBAL_MUTEX;
1267     return 0;
1268 }
1269
1270 /* get latest key */
1271 afs_int32
1272 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, char *akey)
1273 {
1274     register int i;
1275     int maxa;
1276     register struct afsconf_key *tk;
1277     register afs_int32 best;
1278     struct afsconf_key *bestk;
1279     register afs_int32 code;
1280
1281     LOCK_GLOBAL_MUTEX;
1282     code = afsconf_Check(adir);
1283     if (code) {
1284         UNLOCK_GLOBAL_MUTEX;
1285         return AFSCONF_FAILURE;
1286     }
1287     maxa = adir->keystr->nkeys;
1288
1289     best = -1;                  /* highest kvno we've seen yet */
1290     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1291     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1292         if (tk->kvno == 999)
1293             continue;           /* skip bcrypt keys */
1294         if (tk->kvno > best) {
1295             best = tk->kvno;
1296             bestk = tk;
1297         }
1298     }
1299     if (bestk) {                /* found any  */
1300         if (akey)
1301             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1302         if (avno)
1303             *avno = bestk->kvno;        /* and kvno to caller */
1304         UNLOCK_GLOBAL_MUTEX;
1305         return 0;
1306     }
1307     UNLOCK_GLOBAL_MUTEX;
1308     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1309 }
1310
1311 /* get a particular key */
1312 int
1313 afsconf_GetKey(struct afsconf_dir *adir, afs_int32 avno, char *akey)
1314 {
1315     register int i, maxa;
1316     register struct afsconf_key *tk;
1317     register afs_int32 code;
1318
1319     LOCK_GLOBAL_MUTEX;
1320     code = afsconf_Check(adir);
1321     if (code) {
1322         UNLOCK_GLOBAL_MUTEX;
1323         return AFSCONF_FAILURE;
1324     }
1325     maxa = adir->keystr->nkeys;
1326
1327     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1328         if (tk->kvno == avno) {
1329             memcpy(akey, tk->key, 8);
1330             UNLOCK_GLOBAL_MUTEX;
1331             return 0;
1332         }
1333     }
1334
1335     UNLOCK_GLOBAL_MUTEX;
1336     return AFSCONF_NOTFOUND;
1337 }
1338
1339 /* save the key structure in the appropriate file */
1340 static int
1341 SaveKeys(struct afsconf_dir *adir)
1342 {
1343     struct afsconf_keys tkeys;
1344     register int fd;
1345     register afs_int32 i;
1346     char tbuffer[256];
1347
1348     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1349
1350     /* convert it to net byte order */
1351     for (i = 0; i < tkeys.nkeys; i++)
1352         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1353     tkeys.nkeys = htonl(tkeys.nkeys);
1354
1355     /* rewrite keys file */
1356     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1357     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1358     if (fd < 0)
1359         return AFSCONF_FAILURE;
1360     i = write(fd, &tkeys, sizeof(tkeys));
1361     if (i != sizeof(tkeys)) {
1362         close(fd);
1363         return AFSCONF_FAILURE;
1364     }
1365     if (close(fd) < 0)
1366         return AFSCONF_FAILURE;
1367     return 0;
1368 }
1369
1370 int
1371 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1372                afs_int32 overwrite)
1373 {
1374     register struct afsconf_keys *tk;
1375     register struct afsconf_key *tkey;
1376     register afs_int32 i;
1377     int foundSlot;
1378
1379     LOCK_GLOBAL_MUTEX;
1380     tk = adir->keystr;
1381
1382     if (akvno != 999) {
1383         if (akvno < 0 || akvno > 255) {
1384             UNLOCK_GLOBAL_MUTEX;
1385             return ERANGE;
1386         }
1387     }
1388     foundSlot = 0;
1389     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1390         if (tkey->kvno == akvno) {
1391             if (!overwrite) {
1392                 UNLOCK_GLOBAL_MUTEX;
1393                 return AFSCONF_KEYINUSE;
1394             }
1395             foundSlot = 1;
1396             break;
1397         }
1398     }
1399     if (!foundSlot) {
1400         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1401             UNLOCK_GLOBAL_MUTEX;
1402             return AFSCONF_FULL;
1403         }
1404         tkey = &tk->key[tk->nkeys++];
1405     }
1406     tkey->kvno = akvno;
1407     memcpy(tkey->key, akey, 8);
1408     i = SaveKeys(adir);
1409     afsconf_Touch(adir);
1410     UNLOCK_GLOBAL_MUTEX;
1411     return i;
1412 }
1413
1414 /* this proc works by sliding the other guys down, rather than using a funny
1415     kvno value, so that callers can count on getting a good key in key[0].
1416 */
1417 int
1418 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1419 {
1420     register struct afsconf_keys *tk;
1421     register struct afsconf_key *tkey;
1422     register int i;
1423     int foundFlag = 0;
1424
1425     LOCK_GLOBAL_MUTEX;
1426     tk = adir->keystr;
1427
1428     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1429         if (tkey->kvno == akvno) {
1430             foundFlag = 1;
1431             break;
1432         }
1433     }
1434     if (!foundFlag) {
1435         UNLOCK_GLOBAL_MUTEX;
1436         return AFSCONF_NOTFOUND;
1437     }
1438
1439     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1440     for (; i < tk->nkeys - 1; i++, tkey++) {
1441         tkey->kvno = (tkey + 1)->kvno;
1442         memcpy(tkey->key, (tkey + 1)->key, 8);
1443     }
1444     tk->nkeys--;
1445     i = SaveKeys(adir);
1446     afsconf_Touch(adir);
1447     UNLOCK_GLOBAL_MUTEX;
1448     return i;
1449 }