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 0, 0 /* insert new services before this spot */
62 * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
63 * our code can tell if there is new info in the key files, the cell server db
64 * files or any of the other files (and reopen the thing) if the date on
68 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
69 static afs_int32 afsconf_FindService(aname)
70 register char *aname; {
71 /* lookup a service name */
73 register struct afsconf_servPair *tsp;
74 #if defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
75 ts = getservbyname(aname, "");
77 ts = getservbyname(aname, (char *) 0);
80 /* we found it in /etc/services, so we use this value */
81 return ts->s_port; /* already in network byte order */
83 /* not found in /etc/services, see if it is one of ours */
84 for(tsp = serviceTable;; tsp++) {
85 if (tsp->name == (char *) 0) return -1;
86 if (!strcmp(tsp->name, aname)) return htons(tsp->port);
90 static int TrimLine(abuffer)
98 if (!isspace(tc)) break;
102 strcpy(abuffer, tbuffer);
108 * IsClientConfigDirectory() -- determine if path matches well-known
109 * client configuration directory.
111 static int IsClientConfigDirectory(const char *path)
113 const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
116 for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
117 int cc = tolower(cdir[i]);
118 int pc = tolower(path[i]);
131 /* hit end of one or both; allow mismatch in existence of trailing slash */
132 if (cdir[i] != '\0') {
133 if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
137 if (path[i] != '\0') {
138 if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
144 #endif /* AFS_NT40_ENV */
147 static int afsconf_Check(adir)
148 register struct afsconf_dir *adir; {
151 register afs_int32 code;
154 /* NT client CellServDB has different file name than NT server or Unix */
155 if (IsClientConfigDirectory(adir->name)) {
156 strcompose(tbuffer, 256,
157 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
159 strcompose(tbuffer, 256,
160 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
163 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
164 #endif /* AFS_NT40_ENV */
166 code = stat(tbuffer, &tstat);
170 /* did file change? */
171 if (tstat.st_mtime == adir->timeRead) {
174 /* otherwise file has changed, so reopen it */
175 return afsconf_Reopen(adir);
178 /* set modtime on file */
179 static afsconf_Touch(adir)
180 register struct afsconf_dir *adir; {
182 struct timeval tvp[2];
185 /* NT client CellServDB has different file name than NT server or Unix */
186 if (IsClientConfigDirectory(adir->name)) {
187 strcompose(tbuffer, 256,
188 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
190 strcompose(tbuffer, 256,
191 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
194 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
195 #endif /* AFS_NT40_ENV */
197 adir->timeRead = 0; /* just in case */
199 return _utime(tbuffer, NULL);
201 gettimeofday(&tvp[0], NULL);
203 return utimes(tbuffer, tvp);
204 #endif /* AFS_NT40_ENV */
207 struct afsconf_dir *afsconf_Open(adir)
208 register char *adir; {
209 register struct afsconf_dir *tdir;
210 register afs_int32 code;
213 /* zero structure and fill in name; rest is done by internal routine */
214 tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
215 bzero(tdir, sizeof(struct afsconf_dir));
216 tdir->name = (char *) malloc(strlen(adir)+1);
217 strcpy(tdir->name, adir);
219 code = afsconf_OpenInternal(tdir);
221 char *afsconf_path, *getenv(), afs_confdir[128];
224 /* Check global place only when local Open failed for whatever reason */
225 if (!(afsconf_path = getenv("AFSCONF"))) {
226 /* 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... */
231 if (!(home_dir = getenv("HOME"))) {
232 /* Our last chance is the "/.AFSCONF" file */
233 fp = fopen("/.AFSCONF", "r");
237 return (struct afsconf_dir *) 0;
239 fgets(afs_confdir, 128, fp);
244 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
245 fp = fopen(pathname, "r");
247 /* Our last chance is the "/.AFSCONF" file */
248 fp = fopen("/.AFSCONF", "r");
252 return (struct afsconf_dir *) 0;
254 fgets(afs_confdir, 128, fp);
257 fgets(afs_confdir, 128, fp);
260 len = strlen(afs_confdir);
264 return (struct afsconf_dir *) 0;
266 if (afs_confdir[len-1] == '\n') {
267 afs_confdir[len-1] = 0;
269 afsconf_path = afs_confdir;
271 tdir->name = (char *) malloc(strlen(afsconf_path)+1);
272 strcpy(tdir->name, afsconf_path);
273 code = afsconf_OpenInternal(tdir);
278 return (struct afsconf_dir *) 0;
286 static int GetCellUnix(struct afsconf_dir *adir)
292 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
293 tf = fopen(tbuffer, "r");
295 rc = fscanf(tf, "%s", tbuffer);
297 adir->cellName = (char *) malloc(strlen(tbuffer)+1);
298 strcpy(adir->cellName, tbuffer);
310 static int GetCellNT(struct afsconf_dir *adir)
312 if (IsClientConfigDirectory(adir->name)) {
313 /* NT client config dir; ThisCell is in registry (no file). */
314 return afssw_GetClientCellName(&adir->cellName);
316 /* NT server config dir; works just like Unix */
317 return GetCellUnix(adir);
320 #endif /* AFS_NT40_ENV */
323 static int afsconf_OpenInternal(adir)
324 register struct afsconf_dir *adir; {
326 register char *tp, *bp;
327 register struct afsconf_entry *curEntry;
328 register afs_int32 code;
330 char tbuffer[256], tbuf1[256];
333 /* figure out the cell name */
337 i = GetCellUnix(adir);
343 /* now parse the individual lines */
347 /* NT client/server have a CellServDB that is the same format as Unix.
348 * However, the NT client uses a different file name
350 if (IsClientConfigDirectory(adir->name)) {
351 /* NT client config dir */
352 strcompose(tbuffer, 256,
353 adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
355 /* NT server config dir */
356 strcompose(tbuffer, 256,
357 adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
360 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
361 #endif /* AFS_NT40_ENV */
363 if (!stat(tbuffer, &tstat)) {
364 adir->timeRead = tstat.st_mtime;
369 strcpy(tbuf1, tbuffer);
370 tf = fopen(tbuffer, "r");
375 tp = fgets(tbuffer, sizeof(tbuffer), tf);
377 TrimLine(tbuffer); /* remove white space */
378 if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue; /* empty line */
379 if (tbuffer[0] == '>') {
380 char linkedcell[MAXCELLCHARS];
381 /* start new cell item */
383 /* thread this guy on the list */
384 curEntry->next = adir->entries;
385 adir->entries = curEntry;
388 curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
389 bzero(curEntry, sizeof(struct afsconf_entry));
390 code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
392 afsconf_CloseInternal(adir);
396 if (linkedcell[0] != '\0') {
397 curEntry->cellInfo.linkedCell =
398 (char *) malloc(strlen(linkedcell) + 1);
399 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
403 /* new host in the current cell */
405 afsconf_CloseInternal(adir);
409 i = curEntry->cellInfo.numServers;
410 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i]);
412 if (code == AFSCONF_SYNTAX) {
413 for (bp=tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
417 fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
421 afsconf_CloseInternal(adir);
424 curEntry->cellInfo.numServers = ++i;
427 fclose(tf); /* close the file now */
429 /* end the last partially-completed cell */
431 curEntry->next = adir->entries;
432 adir->entries = curEntry;
435 /* now read the fs keys, if possible */
436 adir->keystr = (struct afsconf_keys *) 0;
437 afsconf_IntGetKeys(adir);
442 /* parse a line of the form
443 *"128.2.1.3 #hostname"
444 * into the appropriate pieces.
446 static ParseHostLine(aline, addr, aname)
447 register struct sockaddr_in *addr;
448 char *aline, *aname; {
450 register afs_int32 code;
453 code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
454 if (code != 5) return AFSCONF_SYNTAX;
455 addr->sin_family = AF_INET;
457 tp = (char *) &addr->sin_addr;
465 /* parse a line of the form
466 * ">cellname [linkedcellname] [#comments]"
467 * into the appropriate pieces.
469 static ParseCellLine(aline, aname, alname)
470 register char *aline, *aname, *alname; {
472 code = sscanf(aline, ">%s %s", aname, alname);
473 if (code == 1) *alname = '\0';
475 if (*alname == '#') {
479 return (code > 0 ? 0 : AFSCONF_SYNTAX);
482 /* call aproc(entry, arock, adir) for all cells. Proc must return 0, or we'll stop early and return the code it returns */
483 afsconf_CellApply(adir, aproc, arock)
484 struct afsconf_dir *adir;
487 register struct afsconf_entry *tde;
488 register afs_int32 code;
490 for(tde=adir->entries; tde; tde=tde->next) {
491 code = (*aproc)(&tde->cellInfo, arock, adir);
501 afs_int32 afsconf_SawCell = 0;
502 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
503 struct afsconf_dir *adir;
506 struct afsconf_cell *acellInfo; {
507 register struct afsconf_entry *tce;
508 struct afsconf_entry *bestce;
509 register afs_int32 i;
516 if (adir) afsconf_Check(adir);
519 cnLen = strlen(tcell)+1;
520 lcstring (tcell, tcell, cnLen);
521 afsconf_SawCell = 1; /* will ignore the AFSCELL switch on future */
522 /* call to afsconf_GetLocalCell: like klog */
524 i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
531 cnLen = strlen(tcell);
532 bestce = (struct afsconf_entry *) 0;
538 for(tce=adir->entries;tce;tce=tce->next) {
539 if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
545 if (strlen(tce->cellInfo.name) < cnLen) continue; /* clearly wrong */
546 if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
547 if (bestce) ambig = 1; /* ambiguous unless we get exact match */
551 if (!ambig && bestce) {
552 *acellInfo = bestce->cellInfo; /* structure assignment */
554 tservice = afsconf_FindService(aservice);
557 return AFSCONF_NOTFOUND; /* service not found */
559 for(i=0;i<acellInfo->numServers;i++) {
560 acellInfo->hostAddr[i].sin_port = tservice;
568 return AFSCONF_NOTFOUND;
572 afsconf_GetLocalCell(adir, aname, alen)
573 register struct afsconf_dir *adir;
576 static int afsconf_showcell = 0;
583 * If a cell switch was specified in a command, then it should override the
584 * AFSCELL variable. If a cell was specified, then the afsconf_SawCell flag
585 * is set and the cell name in the adir structure is used.
586 * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
588 if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {
589 if ( !afsconf_showcell ) {
590 fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
591 afsconf_showcell = 1;
593 strncpy(aname, afscell_path, alen);
596 if (adir->cellName) {
597 strncpy(aname, adir->cellName, alen);
599 else code = AFSCONF_UNKNOWN;
607 struct afsconf_dir *adir; {
609 afsconf_CloseInternal(adir);
610 if (adir->name) free(adir->name);
616 static int afsconf_CloseInternal(adir)
617 register struct afsconf_dir *adir; {
618 register struct afsconf_entry *td, *nd;
619 register char *tname;
621 tname = adir->name; /* remember name, since that's all we preserve */
623 /* free everything we can find */
624 if (adir->cellName) free(adir->cellName);
625 for(td=adir->entries;td;td=nd) {
627 if (td->cellInfo.linkedCell)
628 free(td->cellInfo.linkedCell);
631 if (adir->keystr) free(adir->keystr);
634 bzero(adir, sizeof(struct afsconf_dir));
635 adir->name = tname; /* restore it */
639 static int afsconf_Reopen(adir)
640 register struct afsconf_dir *adir; {
641 register afs_int32 code;
642 code = afsconf_CloseInternal(adir);
643 if (code) return code;
644 code = afsconf_OpenInternal(adir);
648 /* called during opening of config file */
649 afsconf_IntGetKeys(adir)
650 struct afsconf_dir *adir;
654 struct afsconf_keys *tstr;
655 register afs_int32 code;
658 /* NT client config dir has no KeyFile; don't risk attempting open
659 * because there might be a random file of this name if dir is shared.
661 if (IsClientConfigDirectory(adir->name)) {
662 adir->keystr = ((struct afsconf_keys *)
663 malloc(sizeof(struct afsconf_keys)));
664 adir->keystr->nkeys = 0;
667 #endif /* AFS_NT40_ENV */
670 /* compute the key name and other setup */
672 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
673 tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
677 fd = open(tbuffer, O_RDONLY);
683 code = read(fd, tstr, sizeof(struct afsconf_keys));
685 if (code < sizeof(afs_int32)) {
691 /* convert key structure to host order */
692 tstr->nkeys = ntohl(tstr->nkeys);
693 for(fd=0;fd<tstr->nkeys;fd++)
694 tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
700 /* get keys structure */
701 afsconf_GetKeys(adir, astr)
702 struct afsconf_dir *adir;
703 struct afsconf_keys *astr;
707 bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
713 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
714 IN struct afsconf_dir *adir;
720 register struct afsconf_key *tk;
721 register afs_int32 best;
722 struct afsconf_key *bestk;
726 maxa = adir->keystr->nkeys;
728 best = -1; /* highest kvno we've seen yet */
729 bestk = (struct afsconf_key *) 0; /* ptr to structure providing best */
730 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
731 if (tk->kvno == 999) continue; /* skip bcrypt keys */
732 if (tk->kvno > best) {
737 if (bestk) { /* found any */
738 if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
739 if (avno) *avno = bestk->kvno; /* and kvno to caller */
744 return AFSCONF_NOTFOUND; /* didn't find any keys */
747 /* get a particular key */
748 afsconf_GetKey(adir, avno, akey)
749 struct afsconf_dir *adir;
753 register int i, maxa;
754 register struct afsconf_key *tk;
758 maxa = adir->keystr->nkeys;
760 for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
761 if (tk->kvno == avno) {
762 bcopy(tk->key, akey, 8);
769 return AFSCONF_NOTFOUND;
772 /* save the key structure in the appropriate file */
773 static SaveKeys(adir)
774 struct afsconf_dir *adir;
776 struct afsconf_keys tkeys;
778 register afs_int32 i;
781 bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
783 /* convert it to net byte order */
784 for(i = 0; i<tkeys.nkeys; i++ )
785 tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
786 tkeys.nkeys = htonl(tkeys.nkeys);
788 /* rewrite keys file */
789 strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
790 fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
791 if (fd < 0) return AFSCONF_FAILURE;
792 i = write(fd, &tkeys, sizeof(tkeys));
793 if (i != sizeof(tkeys)) {
795 return AFSCONF_FAILURE;
797 if (close(fd) < 0) return AFSCONF_FAILURE;
801 afsconf_AddKey(adir, akvno, akey, overwrite)
802 struct afsconf_dir *adir;
803 afs_int32 akvno, overwrite;
806 register struct afsconf_keys *tk;
807 register struct afsconf_key *tkey;
808 register afs_int32 i;
815 if (akvno < 0 || akvno > 255) {
821 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
822 if (tkey->kvno == akvno) {
825 return AFSCONF_KEYINUSE;
832 if (tk->nkeys >= AFSCONF_MAXKEYS) {
836 tkey = &tk->key[tk->nkeys++];
839 bcopy(akey, tkey->key, 8);
846 /* this proc works by sliding the other guys down, rather than using a funny
847 kvno value, so that callers can count on getting a good key in key[0].
849 afsconf_DeleteKey(adir, akvno)
850 struct afsconf_dir *adir;
853 register struct afsconf_keys *tk;
854 register struct afsconf_key *tkey;
861 for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
862 if (tkey->kvno == akvno) {
869 return AFSCONF_NOTFOUND;
872 /* otherwise slide the others down. i and tkey point at the guy to delete */
873 for(;i<tk->nkeys-1; i++,tkey++) {
874 tkey->kvno = (tkey+1)->kvno;
875 bcopy((tkey+1)->key, tkey->key, 8);