9f946b953dc74ed1f9333385ae6a89623234a4aa
[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 = (char *)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((void *)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 = (cfg_csdb_update_ctrl_t *) 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 = ((cfg_csdb_update_name_t *)
497                                       malloc(sizeof(*nameBlockp)));
498
499                         if (nameBlockp == NULL) {
500                             tst = ADMNOMEM;
501                             nameBlockDone = 1;
502
503                         } else
504                             if (!NameBlockGetNext
505                                 (nameBlockIter, nameBlockp, &tst2)) {
506                             /* no more entries (or failure) */
507                             if (tst2 != ADMITERATORDONE) {
508                                 tst = tst2;
509                             }
510                             free(nameBlockp);
511                             nameBlockDone = 1;
512
513                         } else {
514                             *maxUpdates += nameBlockp->serverCount;
515
516                             if (StartUpdateWorkerThread(nameBlockp, &tst2)) {
517                                 /* increment worker count; lock not required
518                                  * until workers given start/abort signal.
519                                  */
520                                 ctrlBlockp->workersActive++;
521                                 workersStarted = 1;
522                             } else {
523                                 tst = tst2;
524                                 free(nameBlockp);
525                                 nameBlockDone = 1;
526                             }
527                         }
528                     }
529
530                     if (!NameBlockGetDone(nameBlockIter, &tst2)) {
531                         tst = tst2;
532                     }
533                 }
534
535                 if (workersStarted) {
536                     /* worker threads started; set disposition and signal */
537                     if (pthread_mutex_lock(&ctrlBlockp->mutex)) {
538                         tst = ADMMUTEXLOCK;
539                     } else {
540                         if (tst == 0) {
541                             /* tell workers to proceed with updates */
542                             ctrlBlockp->disposition = CSDB_GO;
543                         } else {
544                             /* tell workers to abort */
545                             ctrlBlockp->disposition = CSDB_ABORT;
546                         }
547
548                         if (pthread_mutex_unlock(&ctrlBlockp->mutex)) {
549                             tst = ADMMUTEXUNLOCK;
550                         }
551                         if (pthread_cond_broadcast(&ctrlBlockp->event)) {
552                             tst = ADMCONDSIGNAL;
553                         }
554                     }
555                 } else {
556                     /* no worker threads started */
557                     free(ctrlBlockp);
558                 }
559             }
560         }
561     }
562
563     if (tst != 0) {
564         /* indicate failure */
565         rc = 0;
566     }
567     if (st != NULL) {
568         *st = tst;
569     }
570     return rc;
571 }
572
573
574 /*
575  * StartUpdateWorkerThread() -- start an update worker thread for the
576  *     given server name block
577  *
578  * RETURN CODES: 1 success, 0 failure  (st indicates why)
579  */
580 static int
581 StartUpdateWorkerThread(cfg_csdb_update_name_t * nameBlockp, afs_status_p st)
582 {
583     int rc = 1;
584     afs_status_t tst = 0;
585     pthread_attr_t tattr;
586     pthread_t tid;
587
588     if (pthread_attr_init(&tattr)) {
589         tst = ADMTHREADATTRINIT;
590
591     } else if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED)) {
592         tst = ADMTHREADATTRSETDETACHSTATE;
593
594     } else
595         if (pthread_create
596             (&tid, &tattr, UpdateWorkerThread, (void *)nameBlockp)) {
597         tst = ADMTHREADCREATE;
598     }
599
600     if (tst != 0) {
601         /* indicate failure */
602         rc = 0;
603     }
604     if (st != NULL) {
605         *st = tst;
606     }
607     return rc;
608 }
609
610
611 /*
612  * UpdateWorkerThread() -- thread for updating CellServDB of servers in
613  *     a single name block.
614  */
615 static void *
616 UpdateWorkerThread(void *argp)
617 {
618     afs_status_t sync_tst = 0;
619     cfg_csdb_update_name_t *nameBlockp = (cfg_csdb_update_name_t *) argp;
620     int opDisposition;
621
622     /* Pthread mutex and condition variable functions should never fail,
623      * but if they do make a best effort attempt to report the problem;
624      * will not be able to avoid race conditions in the face of such failures.
625      */
626
627     if (pthread_mutex_lock(&nameBlockp->ctrl->mutex)) {
628         sync_tst = ADMMUTEXLOCK;
629     }
630
631     while ((opDisposition = nameBlockp->ctrl->disposition) == CSDB_WAIT) {
632         /* wait for start/abort signal */
633         if (pthread_cond_wait
634             (&nameBlockp->ctrl->event, &nameBlockp->ctrl->mutex)) {
635             /* avoid tight loop if condition variable wait fails */
636             cfgutil_Sleep(1);
637         }
638     }
639
640     if (pthread_mutex_unlock(&nameBlockp->ctrl->mutex)) {
641         sync_tst = ADMMUTEXUNLOCK;
642     }
643
644     if (opDisposition == CSDB_GO) {
645         /* proceed with CellServDB update */
646         int i;
647
648         for (i = 0; i < nameBlockp->serverCount; i++) {
649             cfg_cellServDbStatus_t *statusp;
650
651             /* alloc memory for status information (including host name) */
652
653             while ((statusp = (cfg_cellServDbStatus_t *)
654                     malloc(sizeof(*statusp) + AFS_MAX_SERVER_NAME_LEN)) ==
655                    NULL) {
656                 /* avoid tight loop while waiting for status storage */
657                 cfgutil_Sleep(1);
658             }
659
660             statusp->fsDbHost = ((char *)statusp + sizeof(*statusp));
661
662             /* make update and set status information */
663
664             strcpy(statusp->fsDbHost, nameBlockp->serverName[i]);
665
666             if (sync_tst != 0) {
667                 /* report pthread problem */
668                 statusp->status = sync_tst;
669             } else {
670                 /* attempt update and report update status */
671                 void *bosHandle;
672                 afs_status_t tst2, tst = 0;
673
674                 if (!bos_ServerOpen
675                     (nameBlockp->ctrl->cfg_host->cellHandle,
676                      nameBlockp->serverName[i], &bosHandle, &tst2)) {
677                     tst = tst2;
678
679                 } else {
680                     char *opHost = nameBlockp->ctrl->opHostAlias;
681
682                     if (nameBlockp->ctrl->op == CSDB_OP_ADD) {
683                         if (!bos_HostCreate(bosHandle, opHost, &tst2)) {
684                             tst = tst2;
685                         }
686                     } else {
687                         if (!bos_HostDelete(bosHandle, opHost, &tst2)) {
688                             if (tst2 != BZNOENT) {
689                                 tst = tst2;
690                             }
691                         }
692                     }
693
694                     if (!bos_ServerClose(bosHandle, &tst2)) {
695                         tst = tst2;
696                     }
697                 }
698                 statusp->status = tst;
699             }
700
701             /* make call back to return update status */
702
703             (*nameBlockp->ctrl->callBack) (nameBlockp->ctrl->callBackId,
704                                            statusp, 0);
705         }
706     }
707
708     /* last worker makes termination call back and deallocates control block */
709
710     if (pthread_mutex_lock(&nameBlockp->ctrl->mutex)) {
711         sync_tst = ADMMUTEXLOCK;
712     }
713
714     nameBlockp->ctrl->workersActive--;
715
716     if (nameBlockp->ctrl->workersActive == 0) {
717         if (opDisposition == CSDB_GO) {
718             (*nameBlockp->ctrl->callBack) (nameBlockp->ctrl->callBackId, NULL,
719                                            sync_tst);
720         }
721         free(nameBlockp->ctrl);
722     }
723
724     if (pthread_mutex_unlock(&nameBlockp->ctrl->mutex)) {
725         sync_tst = ADMMUTEXUNLOCK;
726     }
727
728     /* all workers deallocate their own name block */
729     free(nameBlockp);
730
731     return NULL;
732 }
733
734
735 /*
736  * CfgHostGetCellServDbAlias() -- Get alias for configuration host name
737  *     as listed in the server CellServDB.  If no alias is found then
738  *     cfgHostAlias is set to the empty string.
739  * 
740  *     Note: cfgHostAlias is presumed to be a buffer of size MAXHOSTCHARS.
741  *           Presumes all server CellServDB are identical.
742  *           Only checks server CellServDB of database servers.
743  *
744  * RETURN CODES: 1 success, 0 failure  (st indicates why)
745  */
746 static int
747 CfgHostGetCellServDbAlias(cfg_host_p cfg_host, char *cfgHostAlias,
748                           afs_status_p st)
749 {
750     int rc = 1;
751     afs_status_t tst2, tst = 0;
752     void *dbIter;
753
754     if (!util_DatabaseServerGetBegin(cfg_host->cellName, &dbIter, &tst2)) {
755         tst = tst2;
756     } else {
757         util_databaseServerEntry_t dbhostEntry;
758         afs_status_t dbhostSt = 0;
759         short dbhostDone = 0;
760         short dbhostFound = 0;
761
762         while (!dbhostDone) {
763             if (!util_DatabaseServerGetNext(dbIter, &dbhostEntry, &tst2)) {
764                 /* no more entries (or failure) */
765                 if (tst2 != ADMITERATORDONE) {
766                     tst = tst2;
767                 }
768                 dbhostDone = 1;
769
770             } else
771                 if (!cfgutil_HostNameGetCellServDbAlias
772                     (dbhostEntry.serverName, cfg_host->hostName, cfgHostAlias,
773                      &tst2)) {
774                 /* save failure status but keep trying */
775                 dbhostSt = tst2;
776
777             } else if (*cfgHostAlias != '\0') {
778                 dbhostFound = 1;
779                 dbhostDone = 1;
780             }
781         }
782
783         if (!dbhostFound) {
784             *cfgHostAlias = '\0';
785
786             if (tst == 0) {
787                 tst = dbhostSt;
788             }
789         }
790
791         if (!util_DatabaseServerGetDone(dbIter, &tst2)) {
792             tst = tst2;
793         }
794     }
795
796     if (tst != 0) {
797         /* indicate failure */
798         rc = 0;
799     }
800     if (st != NULL) {
801         *st = tst;
802     }
803     return rc;
804 }
805
806
807 /*
808  * NameBlockGetBegin() -- initialize name block iteration
809  *
810  * RETURN CODES: 1 success, 0 failure  (st indicates why)
811  */
812 static int
813 NameBlockGetBegin(cfg_host_p cfg_host, const char *sysControlHost,
814                   cfg_csdb_update_ctrl_t * ctrlBlockp, void **iterationIdP,
815                   afs_status_p st)
816 {
817     int rc = 1;
818     afs_status_t tst2, tst = 0;
819     cfg_csdb_nameblock_iteration_t *nbIterp;
820
821     nbIterp = (cfg_csdb_nameblock_iteration_t *) malloc(sizeof(*nbIterp));
822
823     if (nbIterp == NULL) {
824         tst = ADMNOMEM;
825     } else {
826         short dbOnly = (sysControlHost != NULL);
827
828         nbIterp->ctrlBlockp = ctrlBlockp;
829         nbIterp->cfgHost = cfg_host->hostName;
830         nbIterp->sysControlHost = sysControlHost;
831         nbIterp->cfgInBlock = 0;
832         nbIterp->sysInBlock = 0;
833         nbIterp->serverIterDone = 0;
834
835         if (!ServerNameGetBegin
836             (cfg_host, dbOnly, &nbIterp->serverIter, &tst2)) {
837             tst = tst2;
838         }
839
840         if (tst == 0) {
841             *iterationIdP = nbIterp;
842         } else {
843             free(nbIterp);
844         }
845     }
846
847     if (tst != 0) {
848         /* indicate failure */
849         rc = 0;
850     }
851     if (st != NULL) {
852         *st = tst;
853     }
854     return rc;
855 }
856
857
858 /*
859  * NameBlockGetNext() -- fill next name block
860  * 
861  * RETURN CODES: 1 success, 0 failure  (st indicates why)
862  */
863 static int
864 NameBlockGetNext(void *iterationId, cfg_csdb_update_name_t * nameBlockp,
865                  afs_status_p st)
866 {
867     int rc = 1;
868     afs_status_t tst2, tst = 0;
869     cfg_csdb_nameblock_iteration_t *nbIterp;
870     int i;
871
872     nbIterp = (cfg_csdb_nameblock_iteration_t *) iterationId;
873
874     nameBlockp->ctrl = nbIterp->ctrlBlockp;
875     nameBlockp->serverCount = 0;
876
877     for (i = 0; i < SERVER_NAME_BLOCK_SIZE; i++) {
878         short nameEntered = 0;
879
880         if (!nbIterp->serverIterDone) {
881             if (ServerNameGetNext
882                 (nbIterp->serverIter, nameBlockp->serverName[i], &tst2)) {
883                 /* Got server name; check if matches cfg or sys control host.
884                  * Do a simple string compare, rather than making an expensive
885                  * cfgutil_HostNameIsAlias() call because it will not cause
886                  * any problems to have a duplicate in the list.
887                  */
888                 nameEntered = 1;
889
890                 if (!nbIterp->cfgInBlock) {
891                     if (!strcasecmp
892                         (nbIterp->cfgHost, nameBlockp->serverName[i])) {
893                         nbIterp->cfgInBlock = 1;
894                     }
895                 }
896
897                 if (!nbIterp->sysInBlock && nbIterp->sysControlHost != NULL) {
898                     if (!strcasecmp
899                         (nbIterp->sysControlHost,
900                          nameBlockp->serverName[i])) {
901                         nbIterp->sysInBlock = 1;
902                     }
903                 }
904
905             } else {
906                 /* no more entries (or failure) */
907                 if (tst2 == ADMITERATORDONE) {
908                     nbIterp->serverIterDone = 1;
909                 } else {
910                     tst = tst2;
911                     break;
912                 }
913             }
914         }
915
916         if (!nameEntered) {
917             /* include config host and (possibly) sys control host */
918             if (!nbIterp->cfgInBlock) {
919                 /* shouldn't be duplicate, but OK if is */
920                 strcpy(nameBlockp->serverName[i], nbIterp->cfgHost);
921                 nbIterp->cfgInBlock = 1;
922                 nameEntered = 1;
923
924             } else if (!nbIterp->sysInBlock
925                        && nbIterp->sysControlHost != NULL) {
926                 /* shouldn't be duplicate, but OK if is */
927                 strcpy(nameBlockp->serverName[i], nbIterp->sysControlHost);
928                 nbIterp->sysInBlock = 1;
929                 nameEntered = 1;
930             }
931         }
932
933         if (nameEntered) {
934             nameBlockp->serverCount++;
935         } else {
936             /* no more server names */
937             if (nameBlockp->serverCount == 0) {
938                 tst = ADMITERATORDONE;
939             }
940             break;
941         }
942     }
943
944     if (tst != 0) {
945         /* indicate failure */
946         rc = 0;
947     }
948     if (st != NULL) {
949         *st = tst;
950     }
951     return rc;
952 }
953
954
955 /*
956  * NameBlockGetDone() -- finalize name block iteration
957  *
958  * RETURN CODES: 1 success, 0 failure  (st indicates why)
959  */
960 static int
961 NameBlockGetDone(void *iterationId, afs_status_p st)
962 {
963     int rc = 1;
964     afs_status_t tst2, tst = 0;
965     cfg_csdb_nameblock_iteration_t *nbIterp;
966
967     nbIterp = (cfg_csdb_nameblock_iteration_t *) iterationId;
968
969     if (!ServerNameGetDone(nbIterp->serverIter, &tst2)) {
970         tst = tst2;
971     }
972
973     free(nbIterp);
974
975     if (tst != 0) {
976         /* indicate failure */
977         rc = 0;
978     }
979     if (st != NULL) {
980         *st = tst;
981     }
982     return rc;
983 }
984
985
986 /*
987  * ServerNameGetBegin() -- begin database server and (optionally) fileserver
988  *     name enumeration
989  *
990  * RETURN CODES: 1 success, 0 failure  (st indicates why)
991  */
992 static int
993 ServerNameGetBegin(cfg_host_p cfg_host, short dbOnly, void **iterationIdP,
994                    afs_status_p st)
995 {
996     int rc = 1;
997     afs_status_t tst2, tst = 0;
998     cfg_server_iteration_t *serverIterp;
999
1000     serverIterp = (cfg_server_iteration_t *) malloc(sizeof(*serverIterp));
1001
1002     if (serverIterp == NULL) {
1003         tst = ADMNOMEM;
1004     } else {
1005         serverIterp->dbOnly = dbOnly;
1006
1007         if (dbOnly) {
1008             if (!util_DatabaseServerGetBegin
1009                 (cfg_host->cellName, &serverIterp->iterationId, &tst2)) {
1010                 tst = tst2;
1011             }
1012         } else {
1013             if (!afsclient_AFSServerGetBegin
1014                 (cfg_host->cellHandle, &serverIterp->iterationId, &tst2)) {
1015                 tst = tst2;
1016             }
1017         }
1018
1019         if (tst == 0) {
1020             *iterationIdP = serverIterp;
1021         } else {
1022             free(serverIterp);
1023         }
1024     }
1025
1026     if (tst != 0) {
1027         /* indicate failure */
1028         rc = 0;
1029     }
1030     if (st != NULL) {
1031         *st = tst;
1032     }
1033     return rc;
1034 }
1035
1036
1037 /*
1038  * ServerNameGetNext() -- get next server name
1039  *
1040  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1041  */
1042 static int
1043 ServerNameGetNext(void *iterationId, char *serverName, afs_status_p st)
1044 {
1045     int rc = 1;
1046     afs_status_t tst2, tst = 0;
1047     cfg_server_iteration_t *serverIterp;
1048
1049     serverIterp = (cfg_server_iteration_t *) iterationId;
1050
1051     if (serverIterp->dbOnly) {
1052         util_databaseServerEntry_t serverEntry;
1053
1054         if (!util_DatabaseServerGetNext
1055             (serverIterp->iterationId, &serverEntry, &tst2)) {
1056             tst = tst2;
1057         } else {
1058             strcpy(serverName, serverEntry.serverName);
1059         }
1060     } else {
1061         afs_serverEntry_t serverEntry;
1062
1063         if (!afsclient_AFSServerGetNext
1064             (serverIterp->iterationId, &serverEntry, &tst2)) {
1065             tst = tst2;
1066         } else {
1067             strcpy(serverName, serverEntry.serverName);
1068         }
1069     }
1070
1071     if (tst != 0) {
1072         /* indicate failure */
1073         rc = 0;
1074     }
1075     if (st != NULL) {
1076         *st = tst;
1077     }
1078     return rc;
1079 }
1080
1081
1082 /*
1083  * ServerNameGetDone() -- terminate server enumeration
1084  *
1085  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1086  */
1087 static int
1088 ServerNameGetDone(void *iterationId, afs_status_p st)
1089 {
1090     int rc = 1;
1091     afs_status_t tst2, tst = 0;
1092     cfg_server_iteration_t *serverIterp;
1093
1094     serverIterp = (cfg_server_iteration_t *) iterationId;
1095
1096     if (serverIterp->dbOnly) {
1097         if (!util_DatabaseServerGetDone(serverIterp->iterationId, &tst2)) {
1098             tst = tst2;
1099         }
1100     } else {
1101         if (!afsclient_AFSServerGetDone(serverIterp->iterationId, &tst2)) {
1102             tst = tst2;
1103         }
1104     }
1105
1106     free(serverIterp);
1107
1108     if (tst != 0) {
1109         /* indicate failure */
1110         rc = 0;
1111     }
1112     if (st != NULL) {
1113         *st = tst;
1114     }
1115     return rc;
1116 }