c036966018007eacbf4694916cf42bd415dfb7cc
[openafs.git] / src / libadmin / cfg / cfgdb.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 /* This file implements configuration functions in the following categories:
11  *   cfg_CellServDb*()   - manage the cell-wide server CellServDb database.
12  */
13
14 #include <afs/param.h>
15 #include <afsconfig.h>
16
17 RCSID("$Header$");
18
19 #include <afs/stds.h>
20
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26
27 #include <pthread.h>
28
29 #include <afs/afs_Admin.h>
30 #include <afs/afs_AdminErrors.h>
31 #include <afs/afs_bosAdmin.h>
32 #include <afs/afs_clientAdmin.h>
33 #include <afs/afs_utilAdmin.h>
34
35 #include <afs/cellconfig.h>
36 #include <afs/bnode.h>
37
38 #include "cfginternal.h"
39 #include "afs_cfgAdmin.h"
40
41
42 /* Local declarations and definitions */
43
44 #define CSDB_OP_ADD  0   /* add a server CellServDB entry */
45 #define CSDB_OP_REM  1   /* remove a server CellServDB entry */
46
47 #define CSDB_WAIT   0  /* wait to begin CellServDB update operations */
48 #define CSDB_GO     1  /* begin CellServDB update operations */
49 #define CSDB_ABORT  2  /* abort CellServDB update operations */
50
51 #define SERVER_NAME_BLOCK_SIZE  20  /* number of server names in a block */
52
53
54 /* server name iterator */
55 typedef struct {
56     int dbOnly;         /* enumerate database servers only */
57     void *iterationId;  /* underlying iteration handle */
58 } cfg_server_iteration_t;
59
60
61 /* CellServDB update control block */
62 typedef struct {
63     /* control block invariants */
64     cfg_host_p cfg_host;        /* host configuration handle */
65     int op;                     /* CellServDB update operation type */
66     char opHostAlias[MAXHOSTCHARS];  /* CellServDB alias for config host */
67     cfg_cellServDbUpdateCallBack_t callBack;  /* CellServDB update callback */
68     void *callBackId;           /* CellServDB update callback cookie */
69
70     /* control block synchronization objects */
71     pthread_cond_t event;       /* disposition change event */
72     pthread_mutex_t mutex;      /* protects disposition and workersActive */
73     int disposition;            /* wait, go, or abort operation */
74     int workersActive;          /* count of active worker threads */
75 } cfg_csdb_update_ctrl_t;
76
77 /* CellServDB update name block */
78 typedef struct {
79     cfg_csdb_update_ctrl_t *ctrl;  /* pointer to common control block */
80     int serverCount;               /* number of entries in serverName array */
81     char serverName[SERVER_NAME_BLOCK_SIZE][AFS_MAX_SERVER_NAME_LEN];
82 } cfg_csdb_update_name_t;
83
84 /* name block iterator */
85 typedef struct {
86     void *serverIter;            /* server name iterator */
87     cfg_csdb_update_ctrl_t *ctrlBlockp;   /* update control block */
88     const char *cfgHost;         /* configuration host name */
89     const char *sysControlHost;  /* system control host name (if any) */
90     short cfgInBlock;            /* configuration host in a name block */
91     short sysInBlock;            /* system control host in a name block */
92     short serverIterDone;        /* server name enumeration complete */
93 } cfg_csdb_nameblock_iteration_t;
94
95
96
97 static int
98 CellServDbUpdate(int updateOp,
99                  void *hostHandle,
100                  const char *sysControlHost,
101                  cfg_cellServDbUpdateCallBack_t callBack,
102                  void *callBackId,
103                  int *maxUpdates,
104                  afs_status_p st);
105
106 static int
107 StartUpdateWorkerThread(cfg_csdb_update_name_t *nameBlockp,
108                         afs_status_p st);
109
110 static void*
111 UpdateWorkerThread(void *argp);
112
113 static int
114 CfgHostGetCellServDbAlias(cfg_host_p cfg_host,
115                           char *cfgHostAlias,
116                           afs_status_p st);
117
118 static int
119 NameBlockGetBegin(cfg_host_p cfg_host,
120                   const char *sysControlHost,
121                   cfg_csdb_update_ctrl_t *ctrlBlockp,
122                   void **iterationIdP,
123                   afs_status_p st);
124
125 static int
126 NameBlockGetNext(void *iterationId,
127                  cfg_csdb_update_name_t *nameBlockp,
128                  afs_status_p st);
129
130 static int
131 NameBlockGetDone(void *iterationId,
132                  afs_status_p st);
133
134 static int
135 ServerNameGetBegin(cfg_host_p cfg_host,
136                    short dbOnly,
137                    void **iterationIdP,
138                    afs_status_p st);
139
140 static int
141 ServerNameGetNext(void *iterationId,
142                   char *serverName,
143                   afs_status_p st);
144
145 static int
146 ServerNameGetDone(void *iterationId,
147                   afs_status_p st);
148
149
150
151 /* ---------------- Exported CellServDB functions ------------------ */
152
153
154 /*
155  * cfg_cellServDbUpdateCallBack_t -- prototype for status callback that is
156  *     invoked by functions that update the CellServDB.
157  *
158  *     Implementation provided by library user.
159  *
160  * ARGUMENTS:
161  *     callBackId - user-supplied context identifier; used by caller to
162  *         distinguish results from different functions (or different
163  *         invocations of the same function) that utilize the same callback.
164  *
165  *     statusItemP - pointer to status information returned by a function
166  *         via this callback; an statusItemP value of NULL indicates
167  *         termination of status callbacks for a given function invocation;
168  *         the termination callback will not occur until all other callbacks
169  *         in the set have completed.
170  *
171  *     status - if statusItemP is NULL then this argument indicates whether
172  *         termination of status callbacks is due to logical completion
173  *         or an error.
174  *
175  * RESTRICTIONS:
176  *    The callback thread is not permitted to reenter the configuration
177  *    library except to deallocate returned storage.
178  */
179
180
181 /*
182  * cfg_CellServDbAddHost() -- Add host being configured to server
183  *     CellServDB on all fileserver and database machines in cell and to
184  *     host's server CellServDB.
185  *
186  *     If a System Control machine has been configured, sysControlHost must
187  *     specify the host name; otherwise, sysControlHost must be NULL.
188  */
189 int ADMINAPI
190 cfg_CellServDbAddHost(void *hostHandle,            /* host config handle */
191                       const char *sysControlHost,  /* sys control host */
192                       cfg_cellServDbUpdateCallBack_t callBack,
193                       void *callBackId,
194                       int *maxUpdates,             /* max servers to update */
195                       afs_status_p st)             /* completion status */
196 {
197     return CellServDbUpdate(CSDB_OP_ADD,
198                             hostHandle,
199                             sysControlHost,
200                             callBack,
201                             callBackId,
202                             maxUpdates,
203                             st);
204 }
205
206
207 /*
208  * cfg_CellServDbRemoveHost() -- Remove host being configured from server
209  *     CellServDB on all fileserver and database machines in cell and from
210  *     host's server CellServDB.
211  *
212  *     If a System Control machine has been configured, sysControlHost must
213  *     specify the host name; otherwise, sysControlHost must be NULL.
214  */
215 int ADMINAPI
216 cfg_CellServDbRemoveHost(void *hostHandle,           /* host config handle */
217                          const char *sysControlHost, /* sys control host */
218                          cfg_cellServDbUpdateCallBack_t callBack,
219                          void *callBackId,
220                          int *maxUpdates,          /* max servers to update */
221                          afs_status_p st)          /* completion status */
222 {
223     return CellServDbUpdate(CSDB_OP_REM,
224                             hostHandle,
225                             sysControlHost,
226                             callBack,
227                             callBackId,
228                             maxUpdates,
229                             st);
230 }
231
232
233 /*
234  * cfg_CellServDbEnumerate() -- Enumerate database machines known to the
235  *     specified database or fileserver machine.  Enumeration is returned
236  *     as a multistring.
237  */
238 int ADMINAPI
239 cfg_CellServDbEnumerate(const char *fsDbHost, /* fileserver or database host */
240                         char **cellName,      /* cell name for cellDbHosts */
241                         char **cellDbHosts,   /* cell database hosts */
242                         afs_status_p st)      /* completion status */
243 {
244     int rc = 1;
245     afs_status_t tst2, tst = 0;
246
247     /* validate parameters */
248
249     if (fsDbHost == NULL || *fsDbHost == '\0') {
250         tst = ADMCFGHOSTNAMENULL;
251     } else if (cellName == NULL) {
252         tst = ADMCFGCELLNAMENULL;
253     } else if (cellDbHosts == NULL) {
254         tst = ADMCFGCELLDBHOSTSNULL;
255     }
256
257     /* enumerate server CellServDB on specified host, along with cell name */
258
259     if (tst == 0) {
260         void *cellHandle;
261         void *bosHandle;
262         char dbhostName[MAXHOSTSPERCELL][BOS_MAX_NAME_LEN];
263         char dbhostCell[BOS_MAX_NAME_LEN];
264         int dbhostCount;
265
266         if (!afsclient_NullCellOpen(&cellHandle, &tst2)) {
267             tst = tst2;
268         } else {
269             if (!bos_ServerOpen(cellHandle, fsDbHost, &bosHandle, &tst2)) {
270                 tst = tst2;
271             } else {
272                 void *dbIter;
273
274                 if (!bos_HostGetBegin(bosHandle, &dbIter, &tst2)) {
275                     tst = tst2;
276                 } else {
277                     for (dbhostCount = 0; ; dbhostCount++) {
278                         char dbhostNameTemp[BOS_MAX_NAME_LEN];
279
280                         if (!bos_HostGetNext(dbIter,
281                                              dbhostNameTemp, &tst2)) {
282                             /* no more entries (or failure) */
283                             if (tst2 != ADMITERATORDONE) {
284                                 tst = tst2;
285                             }
286                             break;
287                         } else if (dbhostCount >= MAXHOSTSPERCELL) {
288                             /* more entries than expected */
289                             tst = ADMCFGCELLSERVDBTOOMANYENTRIES;
290                             break;
291                         } else {
292                             strcpy(dbhostName[dbhostCount], dbhostNameTemp);
293                         }
294                     }
295
296                     if (!bos_HostGetDone(dbIter, &tst2)) {
297                         tst = tst2;
298                     }
299
300                     if (tst == 0) {
301                         /* got database servers; now get cell name */
302                         if (!bos_CellGet(bosHandle, dbhostCell, &tst2)) {
303                             tst = tst2;
304                         }
305                     }
306                 }
307
308                 if (!bos_ServerClose(bosHandle, &tst2)) {
309                     tst = tst2;
310                 }
311             }
312
313             if (!afsclient_CellClose(cellHandle, &tst2)) {
314                 tst = tst2;
315             }
316         }
317
318         if (tst == 0) {
319             /* return database hosts to caller */
320             int i;
321             size_t bufSize = 0;
322
323             for (i = 0; i < dbhostCount; i++) {
324                 bufSize += strlen(dbhostName[i]) + 1;
325             }
326             bufSize++; /* end multistring */
327
328             *cellDbHosts = (char *)malloc(bufSize);
329
330             if (*cellDbHosts == NULL) {
331                 tst = ADMNOMEM;
332             } else {
333                 char *bufp = *cellDbHosts;
334
335                 for (i = 0; i < dbhostCount; i++) {
336                     strcpy(bufp, dbhostName[i]);
337                     bufp += strlen(bufp) + 1;
338                 }
339                 *bufp = '\0';
340             }
341
342             /* return cell name to caller */
343             if (tst == 0) {
344                 *cellName = (char *)malloc(strlen(dbhostCell) + 1);
345
346                 if (*cellName == NULL) {
347                     free(*cellDbHosts);
348                     tst = ADMNOMEM;
349                 } else {
350                     strcpy(*cellName, dbhostCell);
351                 }
352             }
353         }
354     }
355
356     if (tst != 0) {
357         /* indicate failure */
358         rc = 0;
359     }
360     if (st != NULL) {
361         *st = tst;
362     }
363     return rc;
364 }
365
366
367
368 /* ---------------- Exported Utility functions ------------------ */
369
370
371 /*
372  * cfg_CellServDbStatusDeallocate() -- Deallocate CellServDB update status
373  *     record returned by library.
374  */
375 int ADMINAPI
376 cfg_CellServDbStatusDeallocate(cfg_cellServDbStatus_t *statusItempP,
377                                afs_status_p st)
378 {
379     free((void *)statusItempP);
380
381     if (st != NULL) {
382         *st = 0;
383     }
384     return 1;
385 }
386
387
388
389
390
391 /* ---------------- Local functions ------------------ */
392
393
394 /*
395  * CellServDbUpdate() -- add or remove a server CellServDB entry.
396  *
397  *     Common function implementing cfg_CellServDb{Add/Remove}Host().
398  */
399 static int
400 CellServDbUpdate(int updateOp,
401                  void *hostHandle,
402                  const char *sysControlHost,
403                  cfg_cellServDbUpdateCallBack_t callBack,
404                  void *callBackId,
405                  int *maxUpdates,
406                  afs_status_p st)
407 {
408     int rc = 1;
409     afs_status_t tst2, tst = 0;
410     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
411     char fullSysHostName[MAXHOSTCHARS];
412
413     /* validate parameters */
414
415     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
416         tst = tst2;
417     } else if (sysControlHost != NULL && *sysControlHost == '\0') {
418         tst = ADMCFGHOSTNAMENULL;
419     } else if (callBack == NULL) {
420         tst = ADMCFGCALLBACKNULL;
421     } else if (maxUpdates == NULL) {
422         tst = ADMCFGUPDATECOUNTNULL;
423     }
424
425     /* resolve sys ctrl host to fully qualified name (if extant) */
426
427     if (tst == 0) {
428         if (sysControlHost != NULL) {
429             if (!cfgutil_HostNameGetFull(sysControlHost,
430                                          fullSysHostName, &tst2)) {
431                 tst = tst2;
432             } else {
433                 sysControlHost = fullSysHostName;
434             }
435         }
436     }
437
438     /* Update cell-wide server CellServDb as follows:
439      *
440      *   1) If system control machine is in use then update the following:
441      *      system control host + database server hosts + configuration host
442      *
443      *      Updating the system control machine is theoretically sufficient,
444      *      as all server hosts should be getting configuration information
445      *      from there.  However, we don't want to have to delay further
446      *      configuration until this update occurs (which could be set for
447      *      any time interval).  Therefore, we compromise by manually
448      *      updating the database server hosts and the host being configured.
449      *
450      *   2) If no system control machine is in use then update the following:
451      *      fileserver hosts + database server hosts + configuration host
452      *
453      *   General algorithm:
454      *     We create a set of server name blocks, with one thread per name
455      *     block that is responsible for updating the servers in that block.
456      *     All server name blocks share a single control block that stores
457      *     common data and coordinates start/abort and cleanup activities.
458      *     All threads wait for the start/abort signal before performing
459      *     update operations so that this function is atomic.
460      */
461
462     if (tst == 0) {
463         cfg_csdb_update_ctrl_t *ctrlBlockp;
464
465         *maxUpdates = 0;
466
467         /* create control block */
468
469         ctrlBlockp = (cfg_csdb_update_ctrl_t *)malloc(sizeof(*ctrlBlockp));
470
471         if (ctrlBlockp == NULL) {
472             tst = ADMNOMEM;
473         } else {
474             ctrlBlockp->cfg_host = cfg_host;
475             ctrlBlockp->op = updateOp;
476             ctrlBlockp->callBack = callBack;
477             ctrlBlockp->callBackId = callBackId;
478             ctrlBlockp->disposition = CSDB_WAIT;
479             ctrlBlockp->workersActive = 0;
480
481             if (pthread_mutex_init(&ctrlBlockp->mutex, NULL)) {
482                 tst = ADMMUTEXINIT;
483             } else if (pthread_cond_init(&ctrlBlockp->event, NULL)) {
484                 tst = ADMCONDINIT;
485             } else {
486                 /* Unfortunately the bosserver adds/removes entries from
487                  * the server CellServDB based on a case-sensitive string
488                  * comparison, rather than using an address comparison
489                  * to handle aliasing.  So we must use the name for the
490                  * configuration host exactly as listed in the CellServDB.
491                  *
492                  * Of course the 3.5 bosserver can and should be modified to
493                  * handle aliases, but we still have to deal with down-level
494                  * servers in this library.
495                  *
496                  * To get reasonable performance, the presumption is made
497                  * that all server CellServDB are identical.  This way we
498                  * can look up the configuration host alias once and use
499                  * it everywhere.  If this proves to be insufficient then
500                  * this lookup will have to be done for every server to be
501                  * updated which will be very costly; such individual lookups
502                  * would naturally be handled by the update worker threads.
503                  *
504                  * A final presumption is that we can just look at the
505                  * server CellServDB on the current database servers to
506                  * get the configuration host alias.  The only time this
507                  * might get us into trouble is in a re-do scenario.
508                  */
509                 if (!CfgHostGetCellServDbAlias(cfg_host,
510                                                ctrlBlockp->opHostAlias,
511                                                &tst2)) {
512                     tst = tst2;
513                 } else if (*ctrlBlockp->opHostAlias == '\0') {
514                     /* no alias found; go with config host working name */
515                     strcpy(ctrlBlockp->opHostAlias, cfg_host->hostName);
516                 }
517             }
518
519             if (tst != 0) {
520                 free(ctrlBlockp);
521             } else {
522                 /* fill name blocks, handing each to a worker thread */
523                 void *nameBlockIter;
524                 short workersStarted = 0;
525
526                 if (!NameBlockGetBegin(cfg_host,
527                                        sysControlHost,
528                                        ctrlBlockp,
529                                        &nameBlockIter,
530                                        &tst2)) {
531                     tst = tst2;
532                 } else {
533                     cfg_csdb_update_name_t *nameBlockp;
534                     short nameBlockDone = 0;
535
536                     while (!nameBlockDone) {
537                         nameBlockp = ((cfg_csdb_update_name_t *)
538                                       malloc(sizeof(*nameBlockp)));
539
540                         if (nameBlockp == NULL) {
541                             tst = ADMNOMEM;
542                             nameBlockDone = 1;
543
544                         } else if (!NameBlockGetNext(nameBlockIter,
545                                                      nameBlockp,
546                                                      &tst2)) {
547                             /* no more entries (or failure) */
548                             if (tst2 != ADMITERATORDONE) {
549                                 tst = tst2;
550                             }
551                             free(nameBlockp);
552                             nameBlockDone = 1;
553
554                         } else {
555                             *maxUpdates += nameBlockp->serverCount;
556
557                             if (StartUpdateWorkerThread(nameBlockp, &tst2)) {
558                                 /* increment worker count; lock not required
559                                  * until workers given start/abort signal.
560                                  */
561                                 ctrlBlockp->workersActive++;
562                                 workersStarted = 1;
563                             } else {
564                                 tst = tst2;
565                                 free(nameBlockp);
566                                 nameBlockDone = 1;
567                             }
568                         }
569                     }
570
571                     if (!NameBlockGetDone(nameBlockIter, &tst2)) {
572                         tst = tst2;
573                     }
574                 }
575
576                 if (workersStarted) {
577                     /* worker threads started; set disposition and signal */
578                     if (pthread_mutex_lock(&ctrlBlockp->mutex)) {
579                         tst = ADMMUTEXLOCK;
580                     } else {
581                         if (tst == 0) {
582                             /* tell workers to proceed with updates */
583                             ctrlBlockp->disposition = CSDB_GO;
584                         } else {
585                             /* tell workers to abort */
586                             ctrlBlockp->disposition = CSDB_ABORT;
587                         }
588
589                         if (pthread_mutex_unlock(&ctrlBlockp->mutex)) {
590                             tst = ADMMUTEXUNLOCK;
591                         }
592                         if (pthread_cond_broadcast(&ctrlBlockp->event)) {
593                             tst = ADMCONDSIGNAL;
594                         }
595                     }
596                 } else {
597                     /* no worker threads started */
598                     free(ctrlBlockp);
599                 }
600             }
601         }
602     }
603
604     if (tst != 0) {
605         /* indicate failure */
606         rc = 0;
607     }
608     if (st != NULL) {
609         *st = tst;
610     }
611     return rc;
612 }
613
614
615 /*
616  * StartUpdateWorkerThread() -- start an update worker thread for the
617  *     given server name block
618  *
619  * RETURN CODES: 1 success, 0 failure  (st indicates why)
620  */
621 static int
622 StartUpdateWorkerThread(cfg_csdb_update_name_t *nameBlockp,
623                         afs_status_p st)
624 {
625     int rc = 1;
626     afs_status_t tst = 0;
627     pthread_attr_t tattr;
628     pthread_t tid;
629
630     if (pthread_attr_init(&tattr)) {
631         tst = ADMTHREADATTRINIT;
632
633     } else if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED)) {
634         tst = ADMTHREADATTRSETDETACHSTATE;
635
636     } else if (pthread_create(&tid,
637                               &tattr,
638                               UpdateWorkerThread, (void *) nameBlockp)) {
639         tst = ADMTHREADCREATE;
640     }
641
642     if (tst != 0) {
643         /* indicate failure */
644         rc = 0;
645     }
646     if (st != NULL) {
647         *st = tst;
648     }
649     return rc;
650 }
651
652
653 /*
654  * UpdateWorkerThread() -- thread for updating CellServDB of servers in
655  *     a single name block.
656  */
657 static void*
658 UpdateWorkerThread(void *argp)
659 {
660     afs_status_t sync_tst = 0;
661     cfg_csdb_update_name_t *nameBlockp = (cfg_csdb_update_name_t *)argp;
662     int opDisposition;
663
664     /* Pthread mutex and condition variable functions should never fail,
665      * but if they do make a best effort attempt to report the problem;
666      * will not be able to avoid race conditions in the face of such failures.
667      */
668
669     if (pthread_mutex_lock(&nameBlockp->ctrl->mutex)) {
670         sync_tst = ADMMUTEXLOCK;
671     }
672
673     while ((opDisposition = nameBlockp->ctrl->disposition) == CSDB_WAIT) {
674         /* wait for start/abort signal */
675         if (pthread_cond_wait(&nameBlockp->ctrl->event,
676                               &nameBlockp->ctrl->mutex)) {
677             /* avoid tight loop if condition variable wait fails */
678             cfgutil_Sleep(1);
679         }
680     }
681
682     if (pthread_mutex_unlock(&nameBlockp->ctrl->mutex)) {
683         sync_tst = ADMMUTEXUNLOCK;
684     }
685
686     if (opDisposition == CSDB_GO) {
687         /* proceed with CellServDB update */
688         int i;
689
690         for (i = 0; i < nameBlockp->serverCount; i++) {
691             cfg_cellServDbStatus_t *statusp;
692
693             /* alloc memory for status information (including host name) */
694
695             while ((statusp = (cfg_cellServDbStatus_t *)
696                     malloc(sizeof(*statusp) +
697                            AFS_MAX_SERVER_NAME_LEN)) == NULL) {
698                 /* avoid tight loop while waiting for status storage */
699                 cfgutil_Sleep(1);
700             }
701
702             statusp->fsDbHost = ((char *)statusp + sizeof(*statusp));
703
704             /* make update and set status information */
705
706             strcpy(statusp->fsDbHost, nameBlockp->serverName[i]);
707
708             if (sync_tst != 0) {
709                 /* report pthread problem */
710                 statusp->status = sync_tst;
711             } else {
712                 /* attempt update and report update status */
713                 void *bosHandle;
714                 afs_status_t tst2, tst = 0;
715
716                 if (!bos_ServerOpen(nameBlockp->ctrl->cfg_host->cellHandle,
717                                     nameBlockp->serverName[i],
718                                     &bosHandle,
719                                     &tst2)) {
720                     tst = tst2;
721
722                 } else {
723                     char *opHost = nameBlockp->ctrl->opHostAlias;
724
725                     if (nameBlockp->ctrl->op == CSDB_OP_ADD) {
726                         if (!bos_HostCreate(bosHandle, opHost, &tst2)) {
727                             tst = tst2;
728                         }
729                     } else {
730                         if (!bos_HostDelete(bosHandle, opHost, &tst2)) {
731                             if (tst2 != BZNOENT) {
732                                 tst = tst2;
733                             }
734                         }
735                     }
736
737                     if (!bos_ServerClose(bosHandle, &tst2)) {
738                         tst = tst2;
739                     }
740                 }
741                 statusp->status = tst;
742             }
743
744             /* make call back to return update status */
745
746             (*nameBlockp->ctrl->callBack)(nameBlockp->ctrl->callBackId,
747                                           statusp,
748                                           0);
749         }
750     }
751
752     /* last worker makes termination call back and deallocates control block */
753
754     if (pthread_mutex_lock(&nameBlockp->ctrl->mutex)) {
755         sync_tst = ADMMUTEXLOCK;
756     }
757
758     nameBlockp->ctrl->workersActive--;
759
760     if (nameBlockp->ctrl->workersActive == 0) {
761         if (opDisposition == CSDB_GO) {
762             (*nameBlockp->ctrl->callBack)(nameBlockp->ctrl->callBackId,
763                                           NULL,
764                                           sync_tst);
765         }
766         free(nameBlockp->ctrl);
767     }
768
769     if (pthread_mutex_unlock(&nameBlockp->ctrl->mutex)) {
770         sync_tst = ADMMUTEXUNLOCK;
771     }
772
773     /* all workers deallocate their own name block */
774     free (nameBlockp);
775
776     return NULL;
777 }
778
779
780 /*
781  * CfgHostGetCellServDbAlias() -- Get alias for configuration host name
782  *     as listed in the server CellServDB.  If no alias is found then
783  *     cfgHostAlias is set to the empty string.
784  * 
785  *     Note: cfgHostAlias is presumed to be a buffer of size MAXHOSTCHARS.
786  *           Presumes all server CellServDB are identical.
787  *           Only checks server CellServDB of database servers.
788  *
789  * RETURN CODES: 1 success, 0 failure  (st indicates why)
790  */
791 static int
792 CfgHostGetCellServDbAlias(cfg_host_p cfg_host,
793                           char *cfgHostAlias,
794                           afs_status_p st)
795 {
796     int rc = 1;
797     afs_status_t tst2, tst = 0;
798     void *dbIter;
799
800     if (!util_DatabaseServerGetBegin(cfg_host->cellName, &dbIter, &tst2)) {
801         tst = tst2;
802     } else {
803         util_databaseServerEntry_t dbhostEntry;
804         afs_status_t dbhostSt = 0;
805         short dbhostDone = 0;
806         short dbhostFound = 0;
807
808         while (!dbhostDone) {
809             if (!util_DatabaseServerGetNext(dbIter, &dbhostEntry, &tst2)) {
810                 /* no more entries (or failure) */
811                 if (tst2 != ADMITERATORDONE) {
812                     tst = tst2;
813                 }
814                 dbhostDone = 1;
815
816             } else if (!cfgutil_HostNameGetCellServDbAlias(dbhostEntry.serverName,
817                                                            cfg_host->hostName,
818                                                            cfgHostAlias,
819                                                            &tst2)) {
820                 /* save failure status but keep trying */
821                 dbhostSt = tst2;
822
823             } else if (*cfgHostAlias != '\0') {
824                 dbhostFound = 1;
825                 dbhostDone = 1;
826             }
827         }
828
829         if (!dbhostFound) {
830             *cfgHostAlias = '\0';
831
832             if (tst == 0) {
833                 tst = dbhostSt;
834             }
835         }
836
837         if (!util_DatabaseServerGetDone(dbIter, &tst2)) {
838             tst = tst2;
839         }
840     }
841
842     if (tst != 0) {
843         /* indicate failure */
844         rc = 0;
845     }
846     if (st != NULL) {
847         *st = tst;
848     }
849     return rc;
850 }
851
852
853 /*
854  * NameBlockGetBegin() -- initialize name block iteration
855  *
856  * RETURN CODES: 1 success, 0 failure  (st indicates why)
857  */
858 static int
859 NameBlockGetBegin(cfg_host_p cfg_host,
860                   const char *sysControlHost,
861                   cfg_csdb_update_ctrl_t *ctrlBlockp,
862                   void **iterationIdP,
863                   afs_status_p st)
864 {
865     int rc = 1;
866     afs_status_t tst2, tst = 0;
867     cfg_csdb_nameblock_iteration_t *nbIterp;
868
869     nbIterp = (cfg_csdb_nameblock_iteration_t *)malloc(sizeof(*nbIterp));
870
871     if (nbIterp == NULL) {
872         tst = ADMNOMEM;
873     } else {
874         short dbOnly = (sysControlHost != NULL);
875
876         nbIterp->ctrlBlockp = ctrlBlockp;
877         nbIterp->cfgHost = cfg_host->hostName;
878         nbIterp->sysControlHost = sysControlHost;
879         nbIterp->cfgInBlock = 0;
880         nbIterp->sysInBlock = 0;
881         nbIterp->serverIterDone = 0;
882
883         if (!ServerNameGetBegin(cfg_host,
884                                 dbOnly, &nbIterp->serverIter, &tst2)) {
885             tst = tst2;
886         }
887
888         if (tst == 0) {
889             *iterationIdP = nbIterp;
890         } else {
891             free(nbIterp);
892         }
893     }
894
895     if (tst != 0) {
896         /* indicate failure */
897         rc = 0;
898     }
899     if (st != NULL) {
900         *st = tst;
901     }
902     return rc;
903 }
904
905
906 /*
907  * NameBlockGetNext() -- fill next name block
908  * 
909  * RETURN CODES: 1 success, 0 failure  (st indicates why)
910  */
911 static int
912 NameBlockGetNext(void *iterationId,
913                  cfg_csdb_update_name_t *nameBlockp,
914                  afs_status_p st)
915 {
916     int rc = 1;
917     afs_status_t tst2, tst = 0;
918     cfg_csdb_nameblock_iteration_t *nbIterp;
919     int i;
920
921     nbIterp = (cfg_csdb_nameblock_iteration_t *)iterationId;
922
923     nameBlockp->ctrl = nbIterp->ctrlBlockp;
924     nameBlockp->serverCount = 0;
925
926     for (i = 0; i < SERVER_NAME_BLOCK_SIZE; i++) {
927         short nameEntered = 0;
928
929         if (!nbIterp->serverIterDone) {
930             if (ServerNameGetNext(nbIterp->serverIter,
931                                   nameBlockp->serverName[i],
932                                   &tst2)) {
933                 /* Got server name; check if matches cfg or sys control host.
934                  * Do a simple string compare, rather than making an expensive
935                  * cfgutil_HostNameIsAlias() call because it will not cause
936                  * any problems to have a duplicate in the list.
937                  */
938                 nameEntered = 1;
939
940                 if (!nbIterp->cfgInBlock) {
941                     if (!strcasecmp(nbIterp->cfgHost,
942                                     nameBlockp->serverName[i])) {
943                         nbIterp->cfgInBlock = 1;
944                     }
945                 }
946
947                 if (!nbIterp->sysInBlock && nbIterp->sysControlHost != NULL) {
948                     if (!strcasecmp(nbIterp->sysControlHost,
949                                     nameBlockp->serverName[i])) {
950                         nbIterp->sysInBlock = 1;
951                     }
952                 }
953
954             } else {
955                 /* no more entries (or failure) */
956                 if (tst2 == ADMITERATORDONE) {
957                     nbIterp->serverIterDone = 1;
958                 } else {
959                     tst = tst2;
960                     break;
961                 }
962             }
963         }
964
965         if (!nameEntered) {
966             /* include config host and (possibly) sys control host */
967             if (!nbIterp->cfgInBlock) {
968                 /* shouldn't be duplicate, but OK if is */
969                 strcpy(nameBlockp->serverName[i], nbIterp->cfgHost);
970                 nbIterp->cfgInBlock = 1;
971                 nameEntered = 1;
972
973             } else if (!nbIterp->sysInBlock &&
974                        nbIterp->sysControlHost != NULL) {
975                 /* shouldn't be duplicate, but OK if is */
976                 strcpy(nameBlockp->serverName[i], nbIterp->sysControlHost);
977                 nbIterp->sysInBlock = 1;
978                 nameEntered = 1;
979             }
980         }
981
982         if (nameEntered) {
983             nameBlockp->serverCount++;
984         } else {
985             /* no more server names */
986             if (nameBlockp->serverCount == 0) {
987                 tst = ADMITERATORDONE;
988             }
989             break;
990         }
991     }
992
993     if (tst != 0) {
994         /* indicate failure */
995         rc = 0;
996     }
997     if (st != NULL) {
998         *st = tst;
999     }
1000     return rc;
1001 }
1002
1003
1004 /*
1005  * NameBlockGetDone() -- finalize name block iteration
1006  *
1007  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1008  */
1009 static int
1010 NameBlockGetDone(void *iterationId,
1011                  afs_status_p st)
1012 {
1013     int rc = 1;
1014     afs_status_t tst2, tst = 0;
1015     cfg_csdb_nameblock_iteration_t *nbIterp;
1016
1017     nbIterp = (cfg_csdb_nameblock_iteration_t *)iterationId;
1018
1019     if (!ServerNameGetDone(nbIterp->serverIter, &tst2)) {
1020         tst = tst2;
1021     }
1022
1023     free(nbIterp);
1024
1025     if (tst != 0) {
1026         /* indicate failure */
1027         rc = 0;
1028     }
1029     if (st != NULL) {
1030         *st = tst;
1031     }
1032     return rc;
1033 }
1034
1035
1036 /*
1037  * ServerNameGetBegin() -- begin database server and (optionally) fileserver
1038  *     name enumeration
1039  *
1040  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1041  */
1042 static int
1043 ServerNameGetBegin(cfg_host_p cfg_host,
1044                    short dbOnly,
1045                    void **iterationIdP,
1046                    afs_status_p st)
1047 {
1048     int rc = 1;
1049     afs_status_t tst2, tst = 0;
1050     cfg_server_iteration_t *serverIterp;
1051
1052     serverIterp = (cfg_server_iteration_t *)malloc(sizeof(*serverIterp));
1053
1054     if (serverIterp == NULL) {
1055         tst = ADMNOMEM;
1056     } else {
1057         serverIterp->dbOnly = dbOnly;
1058
1059         if (dbOnly) {
1060             if (!util_DatabaseServerGetBegin(cfg_host->cellName,
1061                                              &serverIterp->iterationId,
1062                                              &tst2)) {
1063                 tst = tst2;
1064             }
1065         } else {
1066             if (!afsclient_AFSServerGetBegin(cfg_host->cellHandle,
1067                                              &serverIterp->iterationId,
1068                                              &tst2)) {
1069                 tst = tst2;
1070             }
1071         }
1072
1073         if (tst == 0) {
1074             *iterationIdP = serverIterp;
1075         } else {
1076             free(serverIterp);
1077         }
1078     }
1079
1080     if (tst != 0) {
1081         /* indicate failure */
1082         rc = 0;
1083     }
1084     if (st != NULL) {
1085         *st = tst;
1086     }
1087     return rc;
1088 }
1089
1090
1091 /*
1092  * ServerNameGetNext() -- get next server name
1093  *
1094  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1095  */
1096 static int
1097 ServerNameGetNext(void *iterationId,
1098                   char *serverName,
1099                   afs_status_p st)
1100 {
1101     int rc = 1;
1102     afs_status_t tst2, tst = 0;
1103     cfg_server_iteration_t *serverIterp;
1104
1105     serverIterp = (cfg_server_iteration_t *)iterationId;
1106
1107     if (serverIterp->dbOnly) {
1108         util_databaseServerEntry_t serverEntry;
1109
1110         if (!util_DatabaseServerGetNext(serverIterp->iterationId,
1111                                         &serverEntry,
1112                                         &tst2)) {
1113             tst = tst2;
1114         } else {
1115             strcpy(serverName, serverEntry.serverName);
1116         }
1117     } else {
1118         afs_serverEntry_t serverEntry;
1119
1120         if (!afsclient_AFSServerGetNext(serverIterp->iterationId,
1121                                         &serverEntry,
1122                                         &tst2)) {
1123             tst = tst2;
1124         } else {
1125             strcpy(serverName, serverEntry.serverName);
1126         }
1127     }
1128
1129     if (tst != 0) {
1130         /* indicate failure */
1131         rc = 0;
1132     }
1133     if (st != NULL) {
1134         *st = tst;
1135     }
1136     return rc;
1137 }
1138
1139
1140 /*
1141  * ServerNameGetDone() -- terminate server enumeration
1142  *
1143  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1144  */
1145 static int
1146 ServerNameGetDone(void *iterationId,
1147                   afs_status_p st)
1148 {
1149     int rc = 1;
1150     afs_status_t tst2, tst = 0;
1151     cfg_server_iteration_t *serverIterp;
1152
1153     serverIterp = (cfg_server_iteration_t *)iterationId;
1154
1155     if (serverIterp->dbOnly) {
1156         if (!util_DatabaseServerGetDone(serverIterp->iterationId, &tst2)) {
1157             tst = tst2;
1158         }
1159     } else {
1160         if (!afsclient_AFSServerGetDone(serverIterp->iterationId, &tst2)) {
1161             tst = tst2;
1162         }
1163     }
1164
1165     free(serverIterp);
1166
1167     if (tst != 0) {
1168         /* indicate failure */
1169         rc = 0;
1170     }
1171     if (st != NULL) {
1172         *st = tst;
1173     }
1174     return rc;
1175 }