2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afs/param.h>
11 #include <afsconfig.h>
13 #include <afs/pthread_glock.h>
15 #include "../afs/sysincludes.h"
16 #include "../afs/afsincludes.h"
18 #include <sys/types.h>
21 #include <sys/utime.h>
23 #include <WINNT/afssw.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
31 #include <arpa/nameser.h>
33 #endif /* AFS_AFSDB_ENV */
34 #endif /* AFS_NT40_ENV */
42 #include <afs/afsutil.h>
43 #include "cellconfig.h"
46 static ParseHostLine();
47 static ParseCellLine();
48 static afsconf_OpenInternal();
49 static afsconf_CloseInternal();
50 static afsconf_Reopen();
52 static struct afsconf_servPair serviceTable [] = {
63 "afsres", 7010, /* residency database for MR-AFS */
64 "afsremio", 7011, /* remote I/O interface for MR-AFS */
65 0, 0 /* insert new services before this spot */
69 * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
70 * our code can tell if there is new info in the key files, the cell server db
71 * files or any of the other files (and reopen the thing) if the date on
75 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
76 static afs_int32 afsconf_FindService(aname)
77 register char *aname; {
78 /* lookup a service name */
80 register struct afsconf_servPair *tsp;
82 #if defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
83 ts = getservbyname(aname, "");
85 ts = getservbyname(aname, (char *) 0);
88 /* we found it in /etc/services, so we use this value */
89 return ts->s_port; /* already in network byte order */
92 /* not found in /etc/services, see if it is one of ours */
93 for(tsp = serviceTable;; tsp++) {
94 if (tsp->name == (char *) 0) return -1;
95 if (!strcmp(tsp->name, aname)) return htons(tsp->port);
99 static int TrimLine(abuffer)
107 if (!isspace(tc)) break;
111 strcpy(abuffer, tbuffer);
117 * IsClientConfigDirectory() -- determine if path matches well-known
118 * client configuration directory.
120 static int IsClientConfigDirectory(const char *path)
122 const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
125 for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
126 int cc = tolower(cdir[i]);
127 int pc = tolower(path[i]);
140 /* hit end of one or both; allow mismatch in existence of trailing slash */
141 if (cdir[i] != '\0') {
142 if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
146 if (path[i] != '\0') {
147 if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
153 #endif /* AFS_NT40_ENV */
156 static int afsconf_Check(adir)
157 register struct afsconf_dir *adir; {
160 register afs_int32 code;
163 /* NT client CellServDB has different file name than NT server or Unix */
164 if (IsClientConfigDirectory(adir->name)) {
165 strcompose(tbuffer, 256,
166 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
168 strcompose(tbuffer, 256,
169 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
172 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
173 #endif /* AFS_NT40_ENV */
175 code = stat(tbuffer, &tstat);
179 /* did file change? */
180 if (tstat.st_mtime == adir->timeRead) {
183 /* otherwise file has changed, so reopen it */
184 return afsconf_Reopen(adir);
187 /* set modtime on file */
188 static afsconf_Touch(adir)
189 register struct afsconf_dir *adir; {
191 struct timeval tvp[2];
194 /* NT client CellServDB has different file name than NT server or Unix */
195 if (IsClientConfigDirectory(adir->name)) {
196 strcompose(tbuffer, 256,
197 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
199 strcompose(tbuffer, 256,
200 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
203 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
204 #endif /* AFS_NT40_ENV */
206 adir->timeRead = 0; /* just in case */
208 return _utime(tbuffer, NULL);
210 gettimeofday(&tvp[0], NULL);
212 return utimes(tbuffer, tvp);
213 #endif /* AFS_NT40_ENV */
216 struct afsconf_dir *afsconf_Open(adir)
217 register char *adir; {
218 register struct afsconf_dir *tdir;
219 register afs_int32 code;
222 /* zero structure and fill in name; rest is done by internal routine */
223 tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
224 bzero(tdir, sizeof(struct afsconf_dir));
225 tdir->name = (char *) malloc(strlen(adir)+1);
226 strcpy(tdir->name, adir);
228 code = afsconf_OpenInternal(tdir, 0, 0);
230 char *afsconf_path, *getenv(), afs_confdir[128];
233 /* Check global place only when local Open failed for whatever reason */
234 if (!(afsconf_path = getenv("AFSCONF"))) {
235 /* 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... */
240 if (!(home_dir = getenv("HOME"))) {
241 /* Our last chance is the "/.AFSCONF" file */
242 fp = fopen("/.AFSCONF", "r");
246 return (struct afsconf_dir *) 0;
248 fgets(afs_confdir, 128, fp);
253 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
254 fp = fopen(pathname, "r");
256 /* Our last chance is the "/.AFSCONF" file */
257 fp = fopen("/.AFSCONF", "r");
261 return (struct afsconf_dir *) 0;
263 fgets(afs_confdir, 128, fp);
266 fgets(afs_confdir, 128, fp);
269 len = strlen(afs_confdir);
273 return (struct afsconf_dir *) 0;
275 if (afs_confdir[len-1] == '\n') {
276 afs_confdir[len-1] = 0;
278 afsconf_path = afs_confdir;
280 tdir->name = (char *) malloc(strlen(afsconf_path)+1);
281 strcpy(tdir->name, afsconf_path);
282 code = afsconf_OpenInternal(tdir, 0, 0);
287 return (struct afsconf_dir *) 0;
295 static int GetCellUnix(struct afsconf_dir *adir)
301 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
302 tf = fopen(tbuffer, "r");
304 rc = fscanf(tf, "%s", tbuffer);
306 adir->cellName = (char *) malloc(strlen(tbuffer)+1);
307 strcpy(adir->cellName, tbuffer);
319 static int GetCellNT(struct afsconf_dir *adir)
321 if (IsClientConfigDirectory(adir->name)) {
322 /* NT client config dir; ThisCell is in registry (no file). */
323 return afssw_GetClientCellName(&adir->cellName);
325 /* NT server config dir; works just like Unix */
326 return GetCellUnix(adir);
329 #endif /* AFS_NT40_ENV */
332 static int afsconf_OpenInternal(adir, cell, clones)
333 register struct afsconf_dir *adir;
338 register char *tp, *bp;
339 register struct afsconf_entry *curEntry;
340 register afs_int32 code;
342 char tbuffer[256], tbuf1[256];
345 /* figure out the cell name */
349 i = GetCellUnix(adir);
355 /* now parse the individual lines */
359 /* NT client/server have a CellServDB that is the same format as Unix.
360 * However, the NT client uses a different file name
362 if (IsClientConfigDirectory(adir->name)) {
363 /* NT client config dir */
364 strcompose(tbuffer, 256,
365 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
367 /* NT server config dir */
368 strcompose(tbuffer, 256,
369 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
372 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
373 #endif /* AFS_NT40_ENV */
375 if (!stat(tbuffer, &tstat)) {
376 adir->timeRead = tstat.st_mtime;
381 strcpy(tbuf1, tbuffer);
382 tf = fopen(tbuffer, "r");
387 tp = fgets(tbuffer, sizeof(tbuffer), tf);
389 TrimLine(tbuffer); /* remove white space */
390 if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue; /* empty line */
391 if (tbuffer[0] == '>') {
392 char linkedcell[MAXCELLCHARS];
393 /* start new cell item */
395 /* thread this guy on the list */
396 curEntry->next = adir->entries;
397 adir->entries = curEntry;
400 curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
401 bzero(curEntry, sizeof(struct afsconf_entry));
402 code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
404 afsconf_CloseInternal(adir);
408 if (linkedcell[0] != '\0') {
409 curEntry->cellInfo.linkedCell =
410 (char *) malloc(strlen(linkedcell) + 1);
411 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
415 /* new host in the current cell */
417 afsconf_CloseInternal(adir);
421 i = curEntry->cellInfo.numServers;
422 if (cell && !strcmp(cell, curEntry->cellInfo.name))
423 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], &clones[i]);
425 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], 0);
427 if (code == AFSCONF_SYNTAX) {
428 for (bp=tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
432 fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
436 afsconf_CloseInternal(adir);
439 curEntry->cellInfo.numServers = ++i;
442 fclose(tf); /* close the file now */
444 /* end the last partially-completed cell */
446 curEntry->next = adir->entries;
447 adir->entries = curEntry;
450 /* now read the fs keys, if possible */
451 adir->keystr = (struct afsconf_keys *) 0;
452 afsconf_IntGetKeys(adir);
457 /* parse a line of the form
458 *"128.2.1.3 #hostname" or
459 *"[128.2.1.3] #hostname" for clones
460 * into the appropriate pieces.
462 static ParseHostLine(aline, addr, aname, aclone)
464 register struct sockaddr_in *addr;
468 register afs_int32 code;
472 if (aclone) *aclone = 1;
473 code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
475 if (aclone) *aclone = 0;
476 code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
478 if (code != 5) return AFSCONF_SYNTAX;
479 addr->sin_family = AF_INET;
481 tp = (char *) &addr->sin_addr;
489 /* parse a line of the form
490 * ">cellname [linkedcellname] [#comments]"
491 * into the appropriate pieces.
493 static ParseCellLine(aline, aname, alname)
494 register char *aline, *aname, *alname; {
496 code = sscanf(aline, ">%s %s", aname, alname);
497 if (code == 1) *alname = '\0';
499 if (*alname == '#') {
503 return (code > 0 ? 0 : AFSCONF_SYNTAX);
506 /* call aproc(entry, arock, adir) for all cells. Proc must return 0, or we'll stop early and return the code it returns */
507 afsconf_CellApply(adir, aproc, arock)
508 struct afsconf_dir *adir;
511 register struct afsconf_entry *tde;
512 register afs_int32 code;
514 for(tde=adir->entries; tde; tde=tde->next) {
515 code = (*aproc)(&tde->cellInfo, arock, adir);
525 afs_int32 afsconf_SawCell = 0;
527 afsconf_GetExtendedCellInfo(adir, acellName, aservice, acellInfo, clones)
528 struct afsconf_dir *adir;
531 struct afsconf_cell *acellInfo;
537 code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
544 cell = (char *) &acellInfo->name;
546 code = afsconf_OpenInternal(adir, cell, clones);
551 afsconf_GetAfsdbInfo(acellName, aservice, acellInfo)
554 struct afsconf_cell *acellInfo;
557 int tservice, len, i;
558 unsigned char answer[1024];
564 /* The resolver isn't always MT-safe.. Perhaps this ought to be
565 * replaced with a more fine-grained lock just for the resolver
569 len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
573 return AFSCONF_NOTFOUND;
575 p = answer + sizeof(HEADER); /* Skip header */
576 code = dn_expand(answer, answer + len, p, host, sizeof(host));
578 return AFSCONF_NOTFOUND;
579 strncpy(acellInfo->name, host, sizeof(acellInfo->name));
581 p += code + QFIXEDSZ; /* Skip name */
583 while (p < answer + len) {
586 code = dn_expand(answer, answer + len, p, host, sizeof(host));
588 return AFSCONF_NOTFOUND;
590 p += code; /* Skip the name */
591 type = (p[0] << 8) | p[1];
592 p += 4; /* Skip type and class */
593 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
594 p += 4; /* Skip the TTL */
595 size = (p[0] << 8) | p[1];
596 p += 2; /* Skip the size */
598 if (type == T_AFSDB) {
601 code = dn_expand(answer, answer+len, p+2, host, sizeof(host));
603 return AFSCONF_NOTFOUND;
605 /* Do we want to get TTL data for the A record as well? */
606 he = gethostbyname(host);
607 if (he && server_num < MAXHOSTSPERCELL) {
609 memcpy(&ipaddr, he->h_addr, he->h_length);
610 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
611 strncpy(acellInfo->hostName[server_num], host,
612 sizeof(acellInfo->hostName[server_num]));
615 if (!minttl || ttl < minttl) minttl = ttl;
622 if (server_num == 0) /* No AFSDB records */
623 return AFSCONF_NOTFOUND;
624 acellInfo->numServers = server_num;
627 tservice = afsconf_FindService(aservice);
629 return AFSCONF_NOTFOUND; /* service not found */
630 for (i=0; i<acellInfo->numServers; i++) {
631 acellInfo->hostAddr[i].sin_port = tservice;
635 acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
639 #endif /* AFS_AFSDB_ENV */
641 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
642 struct afsconf_dir *adir;
645 struct afsconf_cell *acellInfo; {
646 register struct afsconf_entry *tce;
647 struct afsconf_entry *bestce;
648 register afs_int32 i;
655 if (adir) afsconf_Check(adir);
658 cnLen = strlen(tcell)+1;
659 lcstring (tcell, tcell, cnLen);
660 afsconf_SawCell = 1; /* will ignore the AFSCELL switch on future */
661 /* call to afsconf_GetLocalCell: like klog */
663 i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
670 cnLen = strlen(tcell);
671 bestce = (struct afsconf_entry *) 0;
677 for(tce=adir->entries;tce;tce=tce->next) {
678 if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
684 if (strlen(tce->cellInfo.name) < cnLen) continue; /* clearly wrong */
685 if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
686 if (bestce) ambig = 1; /* ambiguous unless we get exact match */
690 if (!ambig && bestce) {
691 *acellInfo = bestce->cellInfo; /* structure assignment */
693 tservice = afsconf_FindService(aservice);
696 return AFSCONF_NOTFOUND; /* service not found */
698 for(i=0;i<acellInfo->numServers;i++) {
699 acellInfo->hostAddr[i].sin_port = tservice;
702 acellInfo->timeout = 0;
709 return afsconf_GetAfsdbInfo(acellName, aservice, acellInfo);
711 return AFSCONF_NOTFOUND;
712 #endif /* AFS_AFSDB_ENV */
716 afsconf_GetLocalCell(adir, aname, alen)
717 register struct afsconf_dir *adir;
720 static int afsconf_showcell = 0;
727 * If a cell switch was specified in a command, then it should override the
728 * AFSCELL variable. If a cell was specified, then the afsconf_SawCell flag
729 * is set and the cell name in the adir structure is used.
730 * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
732 if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {
733 if ( !afsconf_showcell ) {
734 fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
735 afsconf_showcell = 1;
737 strncpy(aname, afscell_path, alen);
740 if (adir->cellName) {
741 strncpy(aname, adir->cellName, alen);
743 else code = AFSCONF_UNKNOWN;
751 struct afsconf_dir *adir; {
753 afsconf_CloseInternal(adir);
754 if (adir->name) free(adir->name);
760 static int afsconf_CloseInternal(adir)
761 register struct afsconf_dir *adir; {
762 register struct afsconf_entry *td, *nd;
763 register char *tname;
765 tname = adir->name; /* remember name, since that's all we preserve */
767 /* free everything we can find */
768 if (adir->cellName) free(adir->cellName);
769 for(td=adir->entries;td;td=nd) {
771 if (td->cellInfo.linkedCell)
772 free(td->cellInfo.linkedCell);
775 if (adir->keystr) free(adir->keystr);
778 bzero(adir, sizeof(struct afsconf_dir));
779 adir->name = tname; /* restore it */
783 static int afsconf_Reopen(adir)
784 register struct afsconf_dir *adir; {
785 register afs_int32 code;
786 code = afsconf_CloseInternal(adir);
787 if (code) return code;
788 code = afsconf_OpenInternal(adir, 0, 0);
792 /* called during opening of config file */
793 afsconf_IntGetKeys(adir)
794 struct afsconf_dir *adir;
798 struct afsconf_keys *tstr;
799 register afs_int32 code;
802 /* NT client config dir has no KeyFile; don't risk attempting open
803 * because there might be a random file of this name if dir is shared.
805 if (IsClientConfigDirectory(adir->name)) {
806 adir->keystr = ((struct afsconf_keys *)
807 malloc(sizeof(struct afsconf_keys)));
808 adir->keystr->nkeys = 0;
811 #endif /* AFS_NT40_ENV */
814 /* compute the key name and other setup */
816 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
817 tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
821 fd = open(tbuffer, O_RDONLY);
827 code = read(fd, tstr, sizeof(struct afsconf_keys));
829 if (code < sizeof(afs_int32)) {
835 /* convert key structure to host order */
836 tstr->nkeys = ntohl(tstr->nkeys);
837 for(fd=0;fd<tstr->nkeys;fd++)
838 tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
844 /* get keys structure */
845 afsconf_GetKeys(adir, astr)
846 struct afsconf_dir *adir;
847 struct afsconf_keys *astr;
849 register afs_int32 code;
852 code = afsconf_Check(adir);
854 return AFSCONF_FAILURE;
855 bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
861 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
862 IN struct afsconf_dir *adir;
868 register struct afsconf_key *tk;
869 register afs_int32 best;
870 struct afsconf_key *bestk;
871 register afs_int32 code;
874 code = afsconf_Check(adir);
876 return AFSCONF_FAILURE;
877 maxa = adir->keystr->nkeys;
879 best = -1; /* highest kvno we've seen yet */
880 bestk = (struct afsconf_key *) 0; /* ptr to structure providing best */
881 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
882 if (tk->kvno == 999) continue; /* skip bcrypt keys */
883 if (tk->kvno > best) {
888 if (bestk) { /* found any */
889 if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
890 if (avno) *avno = bestk->kvno; /* and kvno to caller */
895 return AFSCONF_NOTFOUND; /* didn't find any keys */
898 /* get a particular key */
899 afsconf_GetKey(adir, avno, akey)
900 struct afsconf_dir *adir;
904 register int i, maxa;
905 register struct afsconf_key *tk;
906 register afs_int32 code;
909 code = afsconf_Check(adir);
911 return AFSCONF_FAILURE;
912 maxa = adir->keystr->nkeys;
914 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
915 if (tk->kvno == avno) {
916 bcopy(tk->key, akey, 8);
923 return AFSCONF_NOTFOUND;
926 /* save the key structure in the appropriate file */
927 static SaveKeys(adir)
928 struct afsconf_dir *adir;
930 struct afsconf_keys tkeys;
932 register afs_int32 i;
935 bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
937 /* convert it to net byte order */
938 for(i = 0; i<tkeys.nkeys; i++ )
939 tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
940 tkeys.nkeys = htonl(tkeys.nkeys);
942 /* rewrite keys file */
943 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
944 fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
945 if (fd < 0) return AFSCONF_FAILURE;
946 i = write(fd, &tkeys, sizeof(tkeys));
947 if (i != sizeof(tkeys)) {
949 return AFSCONF_FAILURE;
951 if (close(fd) < 0) return AFSCONF_FAILURE;
955 afsconf_AddKey(adir, akvno, akey, overwrite)
956 struct afsconf_dir *adir;
957 afs_int32 akvno, overwrite;
960 register struct afsconf_keys *tk;
961 register struct afsconf_key *tkey;
962 register afs_int32 i;
969 if (akvno < 0 || akvno > 255) {
975 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
976 if (tkey->kvno == akvno) {
979 return AFSCONF_KEYINUSE;
986 if (tk->nkeys >= AFSCONF_MAXKEYS) {
990 tkey = &tk->key[tk->nkeys++];
993 bcopy(akey, tkey->key, 8);
1000 /* this proc works by sliding the other guys down, rather than using a funny
1001 kvno value, so that callers can count on getting a good key in key[0].
1003 afsconf_DeleteKey(adir, akvno)
1004 struct afsconf_dir *adir;
1007 register struct afsconf_keys *tk;
1008 register struct afsconf_key *tkey;
1015 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
1016 if (tkey->kvno == akvno) {
1023 return AFSCONF_NOTFOUND;
1026 /* otherwise slide the others down. i and tkey point at the guy to delete */
1027 for(;i<tk->nkeys-1; i++,tkey++) {
1028 tkey->kvno = (tkey+1)->kvno;
1029 bcopy((tkey+1)->key, tkey->key, 8);
1033 afsconf_Touch(adir);