auth: accept a NULL afsconf_dir in afsconf_SetCellInfo again
[openafs.git] / src / auth / cellconfig.c
index 2b99c0b..d570afd 100644 (file)
@@ -73,13 +73,12 @@ static int TrimLine(char *abuffer, int abufsize);
 static int GetCellNT(struct afsconf_dir *adir);
 #endif
 static int GetCellUnix(struct afsconf_dir *adir);
-static int afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
-                               char clones[]);
+static int LoadConfig(struct afsconf_dir *adir);
 static int ParseHostLine(char *aline, struct sockaddr_in *addr,
-                        char *aname, char *aclone);
+                        char *aname, char *aclone /* boolean */);
 static int ParseCellLine(char *aline, char *aname,
                         char *alname);
-static int afsconf_CloseInternal(struct afsconf_dir *adir);
+static int UnloadConfig(struct afsconf_dir *adir);
 static int afsconf_Reopen(struct afsconf_dir *adir);
 
 #ifndef T_AFSDB
@@ -214,11 +213,7 @@ afsconf_FindService(const char *aname)
     if (aname == NULL || aname[0] == '\0')
        return -1;
 
-#if     defined(AFS_OSF_ENV)
-    ts = getservbyname(aname, "");
-#else
     ts = (struct servent *) getservbyname(aname, NULL);
-#endif
     if (ts) {
        /* we found it in /etc/services, so we use this value */
        return ts->s_port;      /* already in network byte order */
@@ -320,32 +315,32 @@ _afsconf_IsClientConfigDirectory(const char *path)
 
 #ifdef AFS_NT40_ENV
 static void
-_afsconf_CellServDBPath(struct afsconf_dir *adir, char **path)
+_afsconf_CellServDBPath(const char *dirname, char **path)
 {
     char *p;
 
     /* NT client CellServDB has different file name than NT server or Unix */
-    if (_afsconf_IsClientConfigDirectory(adir->name)) {
+    if (_afsconf_IsClientConfigDirectory(dirname)) {
        if (!afssw_GetClientCellServDBDir(&p)) {
            if (asprintf(path, "%s/%s", p, AFSDIR_CELLSERVDB_FILE_NTCLIENT) < 0)
                *path = NULL;
            free(p);
        } else {
-           if (asprintf(path, "%s/%s", adir->name,
+           if (asprintf(path, "%s/%s", dirname,
                         AFSDIR_CELLSERVDB_FILE_NTCLIENT) < 0)
                *path = NULL;
        }
     } else {
-       if (asprintf(path, "%s/%s", adir->name, AFSDIR_CELLSERVDB_FILE) < 0)
+       if (asprintf(path, "%s/%s", dirname, AFSDIR_CELLSERVDB_FILE) < 0)
            *path = NULL;
     }
     return;
 }
 #else
 static void
-_afsconf_CellServDBPath(struct afsconf_dir *adir, char **path)
+_afsconf_CellServDBPath(const char *dirname, char **path)
 {
-    if (asprintf(path, "%s/%s", adir->name, AFSDIR_CELLSERVDB_FILE) < 0)
+    if (asprintf(path, "%s/%s", dirname, AFSDIR_CELLSERVDB_FILE) < 0)
        *path = NULL;
 }
 #endif /* AFS_NT40_ENV */
@@ -353,7 +348,6 @@ _afsconf_CellServDBPath(struct afsconf_dir *adir, char **path)
 int
 _afsconf_UpToDate(struct afsconf_dir *adir)
 {
-    char *cellservDB;
     struct stat tstat;
     int code;
     time_t now = time(0);
@@ -363,12 +357,10 @@ _afsconf_UpToDate(struct afsconf_dir *adir)
     }
     adir->timeCheck = now;
 
-    _afsconf_CellServDBPath(adir, &cellservDB);
-    if (cellservDB == NULL)
+    if (adir->cellservDB == NULL)
        return 0;
 
-    code = stat(cellservDB, &tstat);
-    free(cellservDB);
+    code = stat(adir->cellservDB, &tstat);
     if (code < 0)
        return 0; /* Can't throw the error, so just say we're not up to date */
 
@@ -407,7 +399,6 @@ _afsconf_Check(struct afsconf_dir *adir)
 int
 _afsconf_Touch(struct afsconf_dir *adir)
 {
-    char *cellservDB;
     int code;
 #ifndef AFS_NT40_ENV
     struct timeval tvp[2];
@@ -416,22 +407,129 @@ _afsconf_Touch(struct afsconf_dir *adir)
     adir->timeRead = 0;                /* just in case */
     adir->timeCheck = 0;
 
-    _afsconf_CellServDBPath(adir, &cellservDB);
-    if (cellservDB == NULL)
+    if (adir->cellservDB == NULL)
        return ENOMEM;
 
 #ifdef AFS_NT40_ENV
-    code = _utime(cellservDB, NULL);
+    code = _utime(adir->cellservDB, NULL);
 #else
     gettimeofday(&tvp[0], NULL);
     tvp[1] = tvp[0];
-    code = utimes(cellservDB, tvp);
+    code = utimes(adir->cellservDB, tvp);
 #endif /* AFS_NT40_ENV */
-    free(cellservDB);
 
     return code;
 }
 
+/**
+ * Read a single line from a file.
+ *
+ * Retreive the first line of a file if present. The trailing new
+ * line is stripped.
+ *
+ * @note The returned string must be freed by the caller.
+ *
+ * @param[in]  pathname    pathname to the file
+ * @param[out] aline       address of a string
+ *
+ * @return 0            success
+ *         ENOENT       unable to open file
+ *         EIO          empty file or read error
+ *         ENOMEM       unable to allocate output
+ *         ENAMETOOLONG first line of file exceeds MAXPATHLEN
+ */
+static int
+ReadFirstLine(const char *pathname, char **aline)
+{
+    char *line = NULL;
+    char buffer[MAXPATHLEN + 2]; /* Extra char for truncation check. */
+    afsconf_FILE *fp;
+    size_t len = 0;
+
+    fp = fopen(pathname, "r");
+    if (!fp)
+       return ENOENT;
+    if (fgets(buffer, sizeof(buffer), fp) != NULL)
+       len = strlen(buffer);
+    fclose(fp);
+    if (len == 0)
+       return EIO;
+    /* Trim the trailing newline, if one. */
+    if (buffer[len - 1] == '\n') {
+       buffer[len - 1] = '\0';
+       len--;
+    }
+    /* Truncation check. */
+    if (len > MAXPATHLEN)
+        return ENAMETOOLONG;
+    line = strdup(buffer);
+    if (line == NULL)
+       return ENOMEM;
+    *aline = line;
+    return 0;
+}
+
+/**
+ * Find an alternative path to the configuration directory.
+ *
+ * Attempt to find an alternative configuration directory pathname.  First
+ * check for the presence of the AFSCONF environment variable, then check for
+ * the contents of the $HOME/.AFSCONF file, then check for the contents of the
+ * /.AFSCONF file.
+ *
+ * The AFSCONF environment, or contents of $HOME/.AFSCONF, or /.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.
+ *
+ * @param[out] aconfdir  pointer to an allocated string if a path was found
+ *
+ * @return 0 on success
+ *
+ * @note The returned string in pathp must be freed by the caller.
+ */
+static int
+GetAlternatePath(char **aconfdir)
+{
+    int code;
+    char *confdir = NULL;
+    char *afsconf_path = getenv("AFSCONF");
+
+    if (afsconf_path) {
+       confdir = strdup(afsconf_path);
+       if (!confdir)
+           return ENOMEM;
+    }
+
+    if (!confdir) {
+       char *home_dir = getenv("HOME");
+       if (home_dir) {
+           char *pathname = NULL;
+           int r = asprintf(&pathname, "%s/%s", home_dir, ".AFSCONF");
+           if (r < 0 || !pathname)
+               return ENOMEM;
+           code = ReadFirstLine(pathname, &confdir);
+           free(pathname);
+           pathname = NULL;
+           if (code && code != ENOENT)
+               return code;
+       }
+    }
+
+    if (!confdir) {
+       code = ReadFirstLine("/.AFSCONF", &confdir);
+       if (code && code != ENOENT)
+           return code;
+    }
+
+    if (!confdir)
+       return ENOENT;
+
+    *aconfdir = confdir;
+    return 0;
+}
+
 struct afsconf_dir *
 afsconf_Open(const char *adir)
 {
@@ -439,61 +537,23 @@ afsconf_Open(const char *adir)
     afs_int32 code;
 
     LOCK_GLOBAL_MUTEX;
-    /* zero structure and fill in name; rest is done by internal routine */
-    tdir = calloc(1, sizeof(struct afsconf_dir));
+    /* Zero structure and fill in name, the rest is done by LoadConfig. */
+    tdir = calloc(1, sizeof(*tdir));
+    if (!tdir)
+       goto fail;
     tdir->name = strdup(adir);
+    if (!tdir->name)
+       goto fail;
 
-    code = afsconf_OpenInternal(tdir, 0, 0);
+    code = LoadConfig(tdir);
     if (code) {
-       char *afsconf_path, afs_confdir[128];
-
        free(tdir->name);
-       /* Check global place only when local Open failed for whatever reason */
-       if (!(afsconf_path = getenv("AFSCONF"))) {
-           /* 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... */
-           char *home_dir;
-           afsconf_FILE *fp;
-           size_t len = 0;
-           int r;
-
-           if (!(home_dir = getenv("HOME"))) {
-               /* Our last chance is the "/.AFSCONF" file */
-               fp = fopen("/.AFSCONF", "r");
-               if (fp == 0)
-                   goto fail;
-
-           } else {
-               char *pathname = NULL;
-
-               r = asprintf(&pathname, "%s/%s", home_dir, ".AFSCONF");
-               if (r < 0 || pathname == NULL)
-                   goto fail;
-
-               fp = fopen(pathname, "r");
-               free(pathname);
-
-               if (fp == 0) {
-                   /* Our last chance is the "/.AFSCONF" file */
-                   fp = fopen("/.AFSCONF", "r");
-                   if (fp == 0)
-                       goto fail;
-               }
-           }
-           if (fgets(afs_confdir, 128, fp) != NULL)
-               len = strlen(afs_confdir);
-           fclose(fp);
-           if (len == 0)
-               goto fail;
-
-           if (afs_confdir[len - 1] == '\n') {
-               afs_confdir[len - 1] = 0;
-           }
-           afsconf_path = afs_confdir;
-       }
-       tdir->name = strdup(afsconf_path);
-       code = afsconf_OpenInternal(tdir, 0, 0);
+       tdir->name = NULL;
+       code = GetAlternatePath(&tdir->name);
+       if (code)
+           goto fail;
+       code = LoadConfig(tdir);
        if (code) {
-           free(tdir->name);
            goto fail;
        }
     }
@@ -501,6 +561,8 @@ afsconf_Open(const char *adir)
     return tdir;
 
 fail:
+    if (tdir)
+       free(tdir->name);
     free(tdir);
     UNLOCK_GLOBAL_MUTEX;
     return NULL;
@@ -617,10 +679,25 @@ cm_enumCellRegistryProc(void *rockp, char * cellNamep)
 }
 #endif /* AFS_NT40_ENV */
 
-
+/**
+ * Load the cell configuration into memory.
+ *
+ * Read the cell configuration into a newly allocated or cleared afsconf_dir
+ * structure. Reads the CellServDB file, and if present, the cell alias file,
+ * the key files, and kerberos related files.
+ *
+ * If the configuration cannot be loaded for any reason, any partial changes
+ * are freed. The name member is preserved.
+ *
+ * @param[in,out] adir  pointer to the cell configuration
+ *                      the name member must be set to the pathname of the
+ *                      cell configuration to be loaded. All other members
+ *                      must be unassigned.
+ *
+ * @returns 0 on success
+ */
 static int
-afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
-                    char clones[])
+LoadConfig(struct afsconf_dir *adir)
 {
     afsconf_FILE *tf;
     char *tp, *bp;
@@ -630,13 +707,12 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
     afs_int32 i;
     char tbuffer[256];
     struct stat tstat;
-    char *cellservDB;
 
 #ifdef AFS_NT40_ENV
     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
 #endif /* AFS_NT40_ENV */
 
-    /* init the keys queue before any call to afsconf_CloseInternal() */
+    /* init the keys queue before any call to UnloadConfig() */
     _afsconf_InitKeys(adir);
 
     /* figure out the local cell name */
@@ -656,20 +732,20 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
     /* now parse the individual lines */
     curEntry = 0;
 
-    _afsconf_CellServDBPath(adir, &cellservDB);
+    _afsconf_CellServDBPath(adir->name, &adir->cellservDB);
 
 #ifdef AFS_NT40_ENV
     if (_afsconf_IsClientConfigDirectory(adir->name))
         enumCellRegistry.client = 1;
 #endif /* AFS_NT40_ENV */
 
-    if (!stat(cellservDB, &tstat)) {
+    if (!stat(adir->cellservDB, &tstat)) {
        adir->timeRead = tstat.st_mtime;
     } else {
        adir->timeRead = 0;
     }
 
-    tf = fopen(cellservDB, "r");
+    tf = fopen(adir->cellservDB, "r");
     if (!tf) {
        return -1;
     }
@@ -706,7 +782,7 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
            code =
                ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
            if (code) {
-               afsconf_CloseInternal(adir);
+               UnloadConfig(adir);
                fclose(tf);
                free(curEntry);
                return -1;
@@ -716,24 +792,16 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
        } else {
            /* new host in the current cell */
            if (!curEntry) {
-               afsconf_CloseInternal(adir);
+               UnloadConfig(adir);
                fclose(tf);
                return -1;
            }
            i = curEntry->cellInfo.numServers;
            if (i < MAXHOSTSPERCELL) {
-               if (cell && !strcmp(cell, curEntry->cellInfo.name))
-                   code =
-                       ParseHostLine(tbuffer,
-                                     &curEntry->cellInfo.hostAddr[i],
-                                     curEntry->cellInfo.hostName[i],
-                                     &clones[i]);
-               else
-                   code =
-                       ParseHostLine(tbuffer,
-                                     &curEntry->cellInfo.hostAddr[i],
-                                     curEntry->cellInfo.hostName[i], 0);
-
+               code = ParseHostLine(tbuffer,
+                                    &curEntry->cellInfo.hostAddr[i],
+                                    curEntry->cellInfo.hostName[i],
+                                    &curEntry->cellInfo.clone[i]);
                if (code) {
                    if (code == AFSCONF_SYNTAX) {
                        for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
@@ -743,23 +811,22 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
                        *bp = '\0';
                        fprintf(stderr,
                                "Can't properly parse host line \"%s\" in configuration file %s\n",
-                               tbuffer, cellservDB);
+                               tbuffer, adir->cellservDB);
                    }
                    free(curEntry);
                    fclose(tf);
-                   afsconf_CloseInternal(adir);
+                   UnloadConfig(adir);
                    return -1;
                }
                curEntry->cellInfo.numServers = ++i;
            } else {
                fprintf(stderr,
                        "Too many hosts for cell %s in configuration file %s\n",
-                       curEntry->cellInfo.name, cellservDB);
+                       curEntry->cellInfo.name, adir->cellservDB);
            }
        }
     }
     fclose(tf);                        /* close the file now */
-    free(cellservDB);
 
     /* end the last partially-completed cell */
     if (curEntry) {
@@ -841,9 +908,10 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
  */
 static int
 ParseHostLine(char *aline, struct sockaddr_in *addr, char *aname,
-             char *aclone)
+             char *aclone /* boolean */)
 {
-    int c1, c2, c3, c4;
+    int i;
+    int c[4];
     afs_int32 code;
     char *tp;
 
@@ -851,25 +919,34 @@ ParseHostLine(char *aline, struct sockaddr_in *addr, char *aname,
        if (aclone)
            *aclone = 1;
        /* FIXME: length of aname unknown here */
-       code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
+       code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c[0], &c[1], &c[2], &c[3],
+                     aname);
     } else {
        if (aclone)
            *aclone = 0;
        /* FIXME: length of aname unknown here */
-       code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
+       code = sscanf(aline, "%d.%d.%d.%d #%s", &c[0], &c[1], &c[2], &c[3],
+                     aname);
     }
     if (code != 5)
        return AFSCONF_SYNTAX;
+    for(i = 0; i < 4; ++i) {
+       if (c[i] < 0 || c[i] > 255) {
+           fprintf(stderr, "Illegal IP address %d.%d.%d.%d\n", c[0], c[1],
+                   c[2], c[3]);
+           return AFSCONF_SYNTAX;
+       }
+    }
     addr->sin_family = AF_INET;
     addr->sin_port = 0;
 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
     addr->sin_len = sizeof(struct sockaddr_in);
 #endif
     tp = (char *)&addr->sin_addr;
-    *tp++ = c1;
-    *tp++ = c2;
-    *tp++ = c3;
-    *tp++ = c4;
+    *tp++ = c[0];
+    *tp++ = c[1];
+    *tp++ = c[2];
+    *tp++ = c[3];
     return 0;
 }
 
@@ -945,18 +1022,15 @@ afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
                            char clones[])
 {
     afs_int32 code;
-    char *cell;
+    int i;
 
     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
     if (code)
        return code;
 
-    if (acellName)
-       cell = acellName;
-    else
-       cell = (char *)&acellInfo->name;
-
-    code = afsconf_OpenInternal(adir, cell, clones);
+    for (i = 0; i < acellInfo->numServers; i++) {
+       clones[i] = acellInfo->clone[i];
+    }
     return code;
 }
 
@@ -964,7 +1038,7 @@ afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
 int
 afsconf_LookupServer(const char *service, const char *protocol,
                     const char *cellName, unsigned short afsdbPort,
-                    int *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS],
+                    afs_uint32 *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS],
                     unsigned short ports[], unsigned short ipRanks[],
                     int *numServers, int *ttl, char **arealCellName)
 {
@@ -1104,7 +1178,7 @@ afsconf_LookupServer(const char *service, const char *protocol,
                /* Do we want to get TTL data for the A record as well? */
                (he = gethostbyname(host))) {
                if (he->h_addrtype == AF_INET) {
-                   afs_int32 ipaddr;
+                   afs_uint32 ipaddr;
                    memcpy(&ipaddr, he->h_addr, sizeof(ipaddr));
                    cellHostAddrs[server_num] = ipaddr;
                    ports[server_num] = afsdbPort;
@@ -1138,7 +1212,7 @@ afsconf_LookupServer(const char *service, const char *protocol,
                /* Do we want to get TTL data for the A record as well? */
                (he = gethostbyname(host))) {
                if (he->h_addrtype == AF_INET) {
-                   afs_int32 ipaddr;
+                   afs_uint32 ipaddr;
 
                    memcpy(&ipaddr, he->h_addr, sizeof(ipaddr));
                    cellHostAddrs[server_num] = ipaddr;
@@ -1188,7 +1262,7 @@ int
 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
                     struct afsconf_cell *acellInfo)
 {
-    afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
+    afs_uint32 cellHostAddrs[AFSMAXCELLHOSTS];
     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
     unsigned short ipRanks[AFSMAXCELLHOSTS];
     unsigned short ports[AFSMAXCELLHOSTS];
@@ -1256,7 +1330,7 @@ afsconf_GetAfsdbInfo(char *acellName, char *aservice,
     int tservice = afsconf_FindService(aservice);   /* network byte order */
     const char *ianaName = afsconf_FindIANAName(aservice);
     struct afsconf_entry DNSce;
-    afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
+    afs_uint32 cellHostAddrs[AFSMAXCELLHOSTS];
     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
     unsigned short ipRanks[AFSMAXCELLHOSTS];
     unsigned short ports[AFSMAXCELLHOSTS];          /* network byte order */
@@ -1406,9 +1480,11 @@ afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
             short numServers=0;                                        /*Num active servers for the cell */
             struct sockaddr_in hostAddr[MAXHOSTSPERCELL];      /*IP addresses for cell's servers */
             char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];      /*Names for cell's servers */
+            char clone[MAXHOSTSPERCELL];                       /*Indicates which ones are clones. */
 
             memset(&hostAddr, 0, sizeof(hostAddr));
             memset(&hostName, 0, sizeof(hostName));
+            memset(&clone, 0, sizeof(clone));
 
             for ( j=0; j<acellInfo->numServers && numServers < MAXHOSTSPERCELL; j++ ) {
                 struct hostent *he = gethostbyname(acellInfo->hostName[j]);
@@ -1437,6 +1513,7 @@ afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
 #endif
                         memcpy(&hostAddr[numServers].sin_addr.s_addr, he->h_addr_list[i], sizeof(afs_uint32));
                         strcpy(hostName[numServers], acellInfo->hostName[j]);
+                        clone[numServers] = acellInfo->clone[j];
                         foundAddr = 1;
                         numServers++;
                     }
@@ -1444,6 +1521,7 @@ afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
                 if (!foundAddr) {
                     hostAddr[numServers] = acellInfo->hostAddr[j];
                     strcpy(hostName[numServers], acellInfo->hostName[j]);
+                    clone[numServers] = acellInfo->clone[j];
                     numServers++;
                 }
             }
@@ -1451,6 +1529,7 @@ afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
             for (i=0; i<numServers; i++) {
                 acellInfo->hostAddr[i] = hostAddr[i];
                 strcpy(acellInfo->hostName[i], hostName[i]);
+                acellInfo->clone[i] = clone[i];
             }
             acellInfo->numServers = numServers;
             acellInfo->flags |= AFSCONF_CELL_FLAG_DNS_QUERIED;
@@ -1476,7 +1555,7 @@ afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
  *
  * @return status
  *    @retval 0 success
- *    @retval AFSCONF_UNKNOWN failed to get cellname
+ *    @retval AFSCONF_NOCELLNAME cannot determine local cell name
  *
  * @internal
  */
@@ -1509,7 +1588,7 @@ _afsconf_GetLocalCell(struct afsconf_dir *adir, char **pname, int check)
        if (adir->cellName) {
            *pname = adir->cellName;
        } else
-           code = AFSCONF_UNKNOWN;
+           code = AFSCONF_NOCELLNAME;
     }
     return code;
 }
@@ -1532,8 +1611,12 @@ afsconf_GetLocalCell(struct afsconf_dir *adir, char *aname, afs_int32 alen)
 int
 afsconf_Close(struct afsconf_dir *adir)
 {
+    if (adir == NULL) {
+       return 0;
+    }
+
     LOCK_GLOBAL_MUTEX;
-    afsconf_CloseInternal(adir);
+    UnloadConfig(adir);
     if (adir->name)
        free(adir->name);
     free(adir);
@@ -1541,18 +1624,36 @@ afsconf_Close(struct afsconf_dir *adir)
     return 0;
 }
 
+/**
+ * Free members of a cell configuration, except the name.
+ *
+ * Free all of the memory allocated by the LoadConfig function and
+ * reset the afsconf_dir to zeros.  The pathname to the configuration
+ * is preserved to allow for a subquent call the LoadConfig to load
+ * a new configuration.
+ *
+ * @param[in,out] adir  pointer to the cell configuration
+ *
+ * @returns 0 on success
+ */
 static int
-afsconf_CloseInternal(struct afsconf_dir *adir)
+UnloadConfig(struct afsconf_dir *adir)
 {
     struct afsconf_entry *td, *nd;
     struct afsconf_aliasentry *ta, *na;
     char *tname;
 
+    if (adir == NULL) {
+       return 0;
+    }
+
     tname = adir->name;                /* remember name, since that's all we preserve */
 
     /* free everything we can find */
     if (adir->cellName)
        free(adir->cellName);
+    if (adir->cellservDB)
+       free(adir->cellservDB);
     for (td = adir->entries; td; td = nd) {
        nd = td->next;
        if (td->cellInfo.linkedCell)
@@ -1577,9 +1678,205 @@ static int
 afsconf_Reopen(struct afsconf_dir *adir)
 {
     afs_int32 code;
-    code = afsconf_CloseInternal(adir);
+    code = UnloadConfig(adir);
     if (code)
        return code;
-    code = afsconf_OpenInternal(adir, 0, 0);
+    code = LoadConfig(adir);
+    return code;
+}
+
+static int
+VerifyEntries(struct afsconf_cell *aci)
+{
+    int i;
+    struct hostent *th;
+
+    for (i = 0; i < aci->numServers; i++) {
+       if (aci->hostAddr[i].sin_addr.s_addr == 0) {
+           /* no address spec'd */
+           if (*(aci->hostName[i]) != 0) {
+               int code;
+               struct addrinfo hints;
+               struct addrinfo *result;
+               struct addrinfo *rp;
+
+               memset(&hints, 0, sizeof(struct addrinfo));
+               hints.ai_family = AF_INET;
+               hints.ai_socktype = SOCK_DGRAM;
+
+               code = getaddrinfo(aci->hostName[i], NULL, &hints, &result);
+               if (code) {
+                   printf("Host %s not found in host database...\n",
+                          aci->hostName[i]);
+                   return AFSCONF_FAILURE;
+               }
+               for (rp = result; rp != NULL; rp = rp->ai_next) {
+                   struct sockaddr_in *sa = (struct sockaddr_in *)rp->ai_addr;
+                   if (!rx_IsLoopbackAddr(ntohl(sa->sin_addr.s_addr))) {
+                       aci->hostAddr[i].sin_addr.s_addr = sa->sin_addr.s_addr;
+                       break;
+                   }
+               }
+               freeaddrinfo(result);
+               if (aci->hostAddr[i].sin_addr.s_addr == 0) {
+                   printf("No non-loopback addresses found for host %s\n",
+                          aci->hostName[i]);
+                   return AFSCONF_FAILURE;
+               }
+           }
+           /* otherwise we're deleting this entry */
+       } else {
+           /* address spec'd, perhaps no name known */
+           if (aci->hostName[i][0] != 0)
+               continue;       /* name known too */
+           /* figure out name, if possible */
+           th = gethostbyaddr((char *)(&aci->hostAddr[i].sin_addr), 4,
+                              AF_INET);
+           if (!th) {
+               strcpy(aci->hostName[i], "UNKNOWNHOST");
+           } else {
+               if (strlcpy(aci->hostName[i],
+                           th->h_name,
+                           sizeof(aci->hostName[i]))
+                       >= sizeof(aci->hostName[i])) {
+                  strcpy(aci->hostName[i], "UNKNOWNHOST");
+               }
+           }
+       }
+    }
+    return 0;
+}
+
+/**
+ * Set cell information (deprecated)
+ *
+ * Write ThisCell and CellServDB containing exactly one cell's info specified
+ * by acellInfo param.  Useful only on the server (which describes only one
+ * cell).
+ *
+ * @param adir       cell configuation data; may be NULL
+ * @param apath      cell configuration path
+ * @param acellInfo  cell information
+ *
+ * @note This interface was changed at some point to optionally accept the
+ *       afsconf_dir data structure.  This is a handle to the internal cache
+ *       that is maintained by the bosserver.
+ *
+ * @return 0 on success
+ */
+int
+afsconf_SetCellInfo(struct afsconf_dir *adir, const char *apath,
+                   struct afsconf_cell *acellInfo)
+{
+    afs_int32 code;
+
+    code = afsconf_SetExtendedCellInfo(adir, apath, acellInfo, NULL);
     return code;
 }
+
+/**
+ * Set cell information
+ *
+ * Write ThisCell and CellServDB containing exactly one cell's info specified
+ * by acellInfo param.  Useful only on the server (which describes only one
+ * cell).
+ *
+ * @param adir       cell configuation data; may be NULL
+ * @param apath      cell configuration path
+ * @param acellInfo  cell information
+ * @param clones     array of booleans to indicate which hosts are clones
+ *
+ * @return 0 on success
+ */
+int
+afsconf_SetExtendedCellInfo(struct afsconf_dir *adir,
+                           const char *apath,
+                           struct afsconf_cell *acellInfo, char clones[])
+{
+    afs_int32 code;
+    int fd;
+    char tbuffer[1024];
+    FILE *tf;
+    afs_int32 i;
+
+    opr_Assert(apath);
+    opr_Assert(acellInfo);
+
+    LOCK_GLOBAL_MUTEX;
+    /* write ThisCell file */
+    strcompose(tbuffer, 1024, apath, "/", AFSDIR_THISCELL_FILE, (char *)NULL);
+
+    fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0666);
+    if (fd < 0) {
+       UNLOCK_GLOBAL_MUTEX;
+       return errno;
+    }
+    i = (int)strlen(acellInfo->name);
+    code = write(fd, acellInfo->name, i);
+    if (code != i) {
+       UNLOCK_GLOBAL_MUTEX;
+       return AFSCONF_FAILURE;
+    }
+    if (close(fd) < 0) {
+       UNLOCK_GLOBAL_MUTEX;
+       return errno;
+    }
+
+    /* make sure we have both name and address for each host, looking up other
+     * if need be */
+    code = VerifyEntries(acellInfo);
+    if (code) {
+       UNLOCK_GLOBAL_MUTEX;
+       return code;
+    }
+
+    /* write CellServDB */
+    if (adir) {
+       tf = fopen(adir->cellservDB, "w");
+    } else {
+       char *cellservDB = NULL;
+       _afsconf_CellServDBPath(apath, &cellservDB);
+       if (cellservDB)
+           tf = fopen(cellservDB, "w");
+       else
+           tf = NULL;
+       free(cellservDB);
+    }
+    if (!tf) {
+       UNLOCK_GLOBAL_MUTEX;
+       return AFSCONF_NOTFOUND;
+    }
+    fprintf(tf, ">%s   #Cell name\n", acellInfo->name);
+    for (i = 0; i < acellInfo->numServers; i++) {
+       code = acellInfo->hostAddr[i].sin_addr.s_addr;  /* net order */
+       if (code == 0)
+           continue;           /* delete request */
+       code = ntohl(code);     /* convert to host order */
+       if (clones && clones[i])
+           fprintf(tf, "[%d.%d.%d.%d]  #%s\n", (code >> 24) & 0xff,
+                   (code >> 16) & 0xff, (code >> 8) & 0xff, code & 0xff,
+                   acellInfo->hostName[i]);
+       else
+           fprintf(tf, "%d.%d.%d.%d    #%s\n", (code >> 24) & 0xff,
+                   (code >> 16) & 0xff, (code >> 8) & 0xff, code & 0xff,
+                   acellInfo->hostName[i]);
+    }
+    if (ferror(tf)) {
+       fclose(tf);
+       UNLOCK_GLOBAL_MUTEX;
+       return AFSCONF_FAILURE;
+    }
+    code = fclose(tf);
+
+    /* Reset the timestamp in the cache, so that
+     * the CellServDB is read into the cache next time.
+     * Resolves the lost update problem due to an inconsistent cache
+     */
+    if (adir)
+       adir->timeRead = 0;
+
+    UNLOCK_GLOBAL_MUTEX;
+    if (code == EOF)
+       return AFSCONF_FAILURE;
+    return 0;
+}