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>
12 #include <afs/pthread_glock.h>
14 #include "../afs/sysincludes.h"
15 #include "../afs/afsincludes.h"
17 #include <sys/types.h>
20 #include <sys/utime.h>
22 #include <WINNT/afssw.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
37 #include <afs/afsutil.h>
38 #include "cellconfig.h"
41 static ParseHostLine();
42 static ParseCellLine();
43 static afsconf_OpenInternal();
44 static afsconf_CloseInternal();
45 static afsconf_Reopen();
47 static struct afsconf_servPair serviceTable [] = {
58 "afsres", 7010, /* residency database for MR-AFS */
59 "afsremio", 7011, /* remote I/O interface for MR-AFS */
60 0, 0 /* insert new services before this spot */
64 * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
65 * our code can tell if there is new info in the key files, the cell server db
66 * files or any of the other files (and reopen the thing) if the date on
70 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
71 static afs_int32 afsconf_FindService(aname)
72 register char *aname; {
73 /* lookup a service name */
75 register struct afsconf_servPair *tsp;
76 #if defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
77 ts = getservbyname(aname, "");
79 ts = getservbyname(aname, (char *) 0);
82 /* we found it in /etc/services, so we use this value */
83 return ts->s_port; /* already in network byte order */
85 /* not found in /etc/services, see if it is one of ours */
86 for(tsp = serviceTable;; tsp++) {
87 if (tsp->name == (char *) 0) return -1;
88 if (!strcmp(tsp->name, aname)) return htons(tsp->port);
92 static int TrimLine(abuffer)
100 if (!isspace(tc)) break;
104 strcpy(abuffer, tbuffer);
110 * IsClientConfigDirectory() -- determine if path matches well-known
111 * client configuration directory.
113 static int IsClientConfigDirectory(const char *path)
115 const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
118 for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
119 int cc = tolower(cdir[i]);
120 int pc = tolower(path[i]);
133 /* hit end of one or both; allow mismatch in existence of trailing slash */
134 if (cdir[i] != '\0') {
135 if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
139 if (path[i] != '\0') {
140 if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
146 #endif /* AFS_NT40_ENV */
149 static int afsconf_Check(adir)
150 register struct afsconf_dir *adir; {
153 register afs_int32 code;
156 /* NT client CellServDB has different file name than NT server or Unix */
157 if (IsClientConfigDirectory(adir->name)) {
158 strcompose(tbuffer, 256,
159 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
161 strcompose(tbuffer, 256,
162 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
165 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
166 #endif /* AFS_NT40_ENV */
168 code = stat(tbuffer, &tstat);
172 /* did file change? */
173 if (tstat.st_mtime == adir->timeRead) {
176 /* otherwise file has changed, so reopen it */
177 return afsconf_Reopen(adir);
180 /* set modtime on file */
181 static afsconf_Touch(adir)
182 register struct afsconf_dir *adir; {
184 struct timeval tvp[2];
187 /* NT client CellServDB has different file name than NT server or Unix */
188 if (IsClientConfigDirectory(adir->name)) {
189 strcompose(tbuffer, 256,
190 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
192 strcompose(tbuffer, 256,
193 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
196 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
197 #endif /* AFS_NT40_ENV */
199 adir->timeRead = 0; /* just in case */
201 return _utime(tbuffer, NULL);
203 gettimeofday(&tvp[0], NULL);
205 return utimes(tbuffer, tvp);
206 #endif /* AFS_NT40_ENV */
209 struct afsconf_dir *afsconf_Open(adir)
210 register char *adir; {
211 register struct afsconf_dir *tdir;
212 register afs_int32 code;
215 /* zero structure and fill in name; rest is done by internal routine */
216 tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
217 bzero(tdir, sizeof(struct afsconf_dir));
218 tdir->name = (char *) malloc(strlen(adir)+1);
219 strcpy(tdir->name, adir);
221 code = afsconf_OpenInternal(tdir, 0, 0);
223 char *afsconf_path, *getenv(), afs_confdir[128];
226 /* Check global place only when local Open failed for whatever reason */
227 if (!(afsconf_path = getenv("AFSCONF"))) {
228 /* 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... */
233 if (!(home_dir = getenv("HOME"))) {
234 /* Our last chance is the "/.AFSCONF" file */
235 fp = fopen("/.AFSCONF", "r");
239 return (struct afsconf_dir *) 0;
241 fgets(afs_confdir, 128, fp);
246 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
247 fp = fopen(pathname, "r");
249 /* Our last chance is the "/.AFSCONF" file */
250 fp = fopen("/.AFSCONF", "r");
254 return (struct afsconf_dir *) 0;
256 fgets(afs_confdir, 128, fp);
259 fgets(afs_confdir, 128, fp);
262 len = strlen(afs_confdir);
266 return (struct afsconf_dir *) 0;
268 if (afs_confdir[len-1] == '\n') {
269 afs_confdir[len-1] = 0;
271 afsconf_path = afs_confdir;
273 tdir->name = (char *) malloc(strlen(afsconf_path)+1);
274 strcpy(tdir->name, afsconf_path);
275 code = afsconf_OpenInternal(tdir, 0, 0);
280 return (struct afsconf_dir *) 0;
288 static int GetCellUnix(struct afsconf_dir *adir)
294 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
295 tf = fopen(tbuffer, "r");
297 rc = fscanf(tf, "%s", tbuffer);
299 adir->cellName = (char *) malloc(strlen(tbuffer)+1);
300 strcpy(adir->cellName, tbuffer);
312 static int GetCellNT(struct afsconf_dir *adir)
314 if (IsClientConfigDirectory(adir->name)) {
315 /* NT client config dir; ThisCell is in registry (no file). */
316 return afssw_GetClientCellName(&adir->cellName);
318 /* NT server config dir; works just like Unix */
319 return GetCellUnix(adir);
322 #endif /* AFS_NT40_ENV */
325 static int afsconf_OpenInternal(adir, cell, clones)
326 register struct afsconf_dir *adir;
331 register char *tp, *bp;
332 register struct afsconf_entry *curEntry;
333 register afs_int32 code;
335 char tbuffer[256], tbuf1[256];
338 /* figure out the cell name */
342 i = GetCellUnix(adir);
348 /* now parse the individual lines */
352 /* NT client/server have a CellServDB that is the same format as Unix.
353 * However, the NT client uses a different file name
355 if (IsClientConfigDirectory(adir->name)) {
356 /* NT client config dir */
357 strcompose(tbuffer, 256,
358 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
360 /* NT server config dir */
361 strcompose(tbuffer, 256,
362 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
365 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
366 #endif /* AFS_NT40_ENV */
368 if (!stat(tbuffer, &tstat)) {
369 adir->timeRead = tstat.st_mtime;
374 strcpy(tbuf1, tbuffer);
375 tf = fopen(tbuffer, "r");
380 tp = fgets(tbuffer, sizeof(tbuffer), tf);
382 TrimLine(tbuffer); /* remove white space */
383 if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue; /* empty line */
384 if (tbuffer[0] == '>') {
385 char linkedcell[MAXCELLCHARS];
386 /* start new cell item */
388 /* thread this guy on the list */
389 curEntry->next = adir->entries;
390 adir->entries = curEntry;
393 curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
394 bzero(curEntry, sizeof(struct afsconf_entry));
395 code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
397 afsconf_CloseInternal(adir);
401 if (linkedcell[0] != '\0') {
402 curEntry->cellInfo.linkedCell =
403 (char *) malloc(strlen(linkedcell) + 1);
404 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
408 /* new host in the current cell */
410 afsconf_CloseInternal(adir);
414 i = curEntry->cellInfo.numServers;
415 if (cell && !strcmp(cell, curEntry->cellInfo.name))
416 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], &clones[i]);
418 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], 0);
420 if (code == AFSCONF_SYNTAX) {
421 for (bp=tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
425 fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
429 afsconf_CloseInternal(adir);
432 curEntry->cellInfo.numServers = ++i;
435 fclose(tf); /* close the file now */
437 /* end the last partially-completed cell */
439 curEntry->next = adir->entries;
440 adir->entries = curEntry;
443 /* now read the fs keys, if possible */
444 adir->keystr = (struct afsconf_keys *) 0;
445 afsconf_IntGetKeys(adir);
450 /* parse a line of the form
451 *"128.2.1.3 #hostname" or
452 *"[128.2.1.3] #hostname" for clones
453 * into the appropriate pieces.
455 static ParseHostLine(aline, addr, aname, aclone)
457 register struct sockaddr_in *addr;
461 register afs_int32 code;
465 if (aclone) *aclone = 1;
466 code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
468 if (aclone) *aclone = 0;
469 code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
471 if (code != 5) return AFSCONF_SYNTAX;
472 addr->sin_family = AF_INET;
474 tp = (char *) &addr->sin_addr;
482 /* parse a line of the form
483 * ">cellname [linkedcellname] [#comments]"
484 * into the appropriate pieces.
486 static ParseCellLine(aline, aname, alname)
487 register char *aline, *aname, *alname; {
489 code = sscanf(aline, ">%s %s", aname, alname);
490 if (code == 1) *alname = '\0';
492 if (*alname == '#') {
496 return (code > 0 ? 0 : AFSCONF_SYNTAX);
499 /* call aproc(entry, arock, adir) for all cells. Proc must return 0, or we'll stop early and return the code it returns */
500 afsconf_CellApply(adir, aproc, arock)
501 struct afsconf_dir *adir;
504 register struct afsconf_entry *tde;
505 register afs_int32 code;
507 for(tde=adir->entries; tde; tde=tde->next) {
508 code = (*aproc)(&tde->cellInfo, arock, adir);
518 afs_int32 afsconf_SawCell = 0;
520 afsconf_GetExtendedCellInfo(adir, acellName, aservice, acellInfo, clones)
521 struct afsconf_dir *adir;
524 struct afsconf_cell *acellInfo;
530 code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
537 cell = (char *) &acellInfo->name;
539 code = afsconf_OpenInternal(adir, cell, clones);
543 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
544 struct afsconf_dir *adir;
547 struct afsconf_cell *acellInfo; {
548 register struct afsconf_entry *tce;
549 struct afsconf_entry *bestce;
550 register afs_int32 i;
557 if (adir) afsconf_Check(adir);
560 cnLen = strlen(tcell)+1;
561 lcstring (tcell, tcell, cnLen);
562 afsconf_SawCell = 1; /* will ignore the AFSCELL switch on future */
563 /* call to afsconf_GetLocalCell: like klog */
565 i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
572 cnLen = strlen(tcell);
573 bestce = (struct afsconf_entry *) 0;
579 for(tce=adir->entries;tce;tce=tce->next) {
580 if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
586 if (strlen(tce->cellInfo.name) < cnLen) continue; /* clearly wrong */
587 if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
588 if (bestce) ambig = 1; /* ambiguous unless we get exact match */
592 if (!ambig && bestce) {
593 *acellInfo = bestce->cellInfo; /* structure assignment */
595 tservice = afsconf_FindService(aservice);
598 return AFSCONF_NOTFOUND; /* service not found */
600 for(i=0;i<acellInfo->numServers;i++) {
601 acellInfo->hostAddr[i].sin_port = tservice;
609 return AFSCONF_NOTFOUND;
613 afsconf_GetLocalCell(adir, aname, alen)
614 register struct afsconf_dir *adir;
617 static int afsconf_showcell = 0;
624 * If a cell switch was specified in a command, then it should override the
625 * AFSCELL variable. If a cell was specified, then the afsconf_SawCell flag
626 * is set and the cell name in the adir structure is used.
627 * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
629 if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {
630 if ( !afsconf_showcell ) {
631 fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
632 afsconf_showcell = 1;
634 strncpy(aname, afscell_path, alen);
637 if (adir->cellName) {
638 strncpy(aname, adir->cellName, alen);
640 else code = AFSCONF_UNKNOWN;
648 struct afsconf_dir *adir; {
650 afsconf_CloseInternal(adir);
651 if (adir->name) free(adir->name);
657 static int afsconf_CloseInternal(adir)
658 register struct afsconf_dir *adir; {
659 register struct afsconf_entry *td, *nd;
660 register char *tname;
662 tname = adir->name; /* remember name, since that's all we preserve */
664 /* free everything we can find */
665 if (adir->cellName) free(adir->cellName);
666 for(td=adir->entries;td;td=nd) {
668 if (td->cellInfo.linkedCell)
669 free(td->cellInfo.linkedCell);
672 if (adir->keystr) free(adir->keystr);
675 bzero(adir, sizeof(struct afsconf_dir));
676 adir->name = tname; /* restore it */
680 static int afsconf_Reopen(adir)
681 register struct afsconf_dir *adir; {
682 register afs_int32 code;
683 code = afsconf_CloseInternal(adir);
684 if (code) return code;
685 code = afsconf_OpenInternal(adir, 0, 0);
689 /* called during opening of config file */
690 afsconf_IntGetKeys(adir)
691 struct afsconf_dir *adir;
695 struct afsconf_keys *tstr;
696 register afs_int32 code;
699 /* NT client config dir has no KeyFile; don't risk attempting open
700 * because there might be a random file of this name if dir is shared.
702 if (IsClientConfigDirectory(adir->name)) {
703 adir->keystr = ((struct afsconf_keys *)
704 malloc(sizeof(struct afsconf_keys)));
705 adir->keystr->nkeys = 0;
708 #endif /* AFS_NT40_ENV */
711 /* compute the key name and other setup */
713 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
714 tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
718 fd = open(tbuffer, O_RDONLY);
724 code = read(fd, tstr, sizeof(struct afsconf_keys));
726 if (code < sizeof(afs_int32)) {
732 /* convert key structure to host order */
733 tstr->nkeys = ntohl(tstr->nkeys);
734 for(fd=0;fd<tstr->nkeys;fd++)
735 tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
741 /* get keys structure */
742 afsconf_GetKeys(adir, astr)
743 struct afsconf_dir *adir;
744 struct afsconf_keys *astr;
748 bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
754 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
755 IN struct afsconf_dir *adir;
761 register struct afsconf_key *tk;
762 register afs_int32 best;
763 struct afsconf_key *bestk;
767 maxa = adir->keystr->nkeys;
769 best = -1; /* highest kvno we've seen yet */
770 bestk = (struct afsconf_key *) 0; /* ptr to structure providing best */
771 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
772 if (tk->kvno == 999) continue; /* skip bcrypt keys */
773 if (tk->kvno > best) {
778 if (bestk) { /* found any */
779 if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
780 if (avno) *avno = bestk->kvno; /* and kvno to caller */
785 return AFSCONF_NOTFOUND; /* didn't find any keys */
788 /* get a particular key */
789 afsconf_GetKey(adir, avno, akey)
790 struct afsconf_dir *adir;
794 register int i, maxa;
795 register struct afsconf_key *tk;
799 maxa = adir->keystr->nkeys;
801 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
802 if (tk->kvno == avno) {
803 bcopy(tk->key, akey, 8);
810 return AFSCONF_NOTFOUND;
813 /* save the key structure in the appropriate file */
814 static SaveKeys(adir)
815 struct afsconf_dir *adir;
817 struct afsconf_keys tkeys;
819 register afs_int32 i;
822 bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
824 /* convert it to net byte order */
825 for(i = 0; i<tkeys.nkeys; i++ )
826 tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
827 tkeys.nkeys = htonl(tkeys.nkeys);
829 /* rewrite keys file */
830 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
831 fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
832 if (fd < 0) return AFSCONF_FAILURE;
833 i = write(fd, &tkeys, sizeof(tkeys));
834 if (i != sizeof(tkeys)) {
836 return AFSCONF_FAILURE;
838 if (close(fd) < 0) return AFSCONF_FAILURE;
842 afsconf_AddKey(adir, akvno, akey, overwrite)
843 struct afsconf_dir *adir;
844 afs_int32 akvno, overwrite;
847 register struct afsconf_keys *tk;
848 register struct afsconf_key *tkey;
849 register afs_int32 i;
856 if (akvno < 0 || akvno > 255) {
862 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
863 if (tkey->kvno == akvno) {
866 return AFSCONF_KEYINUSE;
873 if (tk->nkeys >= AFSCONF_MAXKEYS) {
877 tkey = &tk->key[tk->nkeys++];
880 bcopy(akey, tkey->key, 8);
887 /* this proc works by sliding the other guys down, rather than using a funny
888 kvno value, so that callers can count on getting a good key in key[0].
890 afsconf_DeleteKey(adir, akvno)
891 struct afsconf_dir *adir;
894 register struct afsconf_keys *tk;
895 register struct afsconf_key *tkey;
902 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
903 if (tkey->kvno == akvno) {
910 return AFSCONF_NOTFOUND;
913 /* otherwise slide the others down. i and tkey point at the guy to delete */
914 for(;i<tk->nkeys-1; i++,tkey++) {
915 tkey->kvno = (tkey+1)->kvno;
916 bcopy((tkey+1)->key, tkey->key, 8);