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