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