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