include-afsconfig-before-param-h-20010712
[openafs.git] / src / libadmin / cfg / cfgservers.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_BosServer*()    - configure the BOS server.
12  *   cfg_DbServers*()    - configure the database servers.
13  *   cfg_FileServer*()   - configure the fileserver.
14  *   cfg_UpdateServer*() - configure the update server.
15  *   cfg_UpdateClient*() - configure update clients.
16  */
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 RCSID("$Header$");
22
23 #include <afs/stds.h>
24
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <ctype.h>
31
32 #ifdef AFS_NT40_ENV
33 #include <windows.h>
34 #include <WINNT/afsreg.h>
35 #endif /* AFS_NT40_ENV */
36
37 #include <pthread.h>
38
39 #include <afs/afs_Admin.h>
40 #include <afs/afs_AdminErrors.h>
41 #include <afs/afs_bosAdmin.h>
42 #include <afs/afs_clientAdmin.h>
43 #include <afs/afs_utilAdmin.h>
44 #include <afs/afs_vosAdmin.h>
45
46 #include <afs/dirpath.h>
47 #include <afs/bnode.h>
48 #include <afs/cellconfig.h>
49 #include <afs/bubasics.h>
50 #include <rx/rx_null.h>
51
52 #define UBIK_INTERNALS  /* need "internal" symbols from ubik.h */
53 #include <ubik.h>
54
55 #include "cfginternal.h"
56 #include "afs_cfgAdmin.h"
57
58
59 /* Local declarations and definitions */
60
61 #define KASERVER_BOSNAME "kaserver"
62 #define KASERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/kaserver"
63
64 #define PTSERVER_BOSNAME "ptserver"
65 #define PTSERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/ptserver"
66
67 #define VLSERVER_BOSNAME "vlserver"
68 #define VLSERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/vlserver"
69
70 #define BUSERVER_BOSNAME "buserver"
71 #define BUSERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/buserver"
72
73 #define UPSERVER_BOSNAME "upserver"
74 #define UPSERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/upserver"
75
76 #define UPCLIENT_BOSNAME "upclient"
77 #define UPCLIENT_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/upclient"
78
79 #define FILESERVER_BOSNAME "fs"
80 #define FILESERVER_EXEPATH  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/fileserver"
81 #define VOLSERVER_EXEPATH   AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/volserver"
82 #define SALVAGER_EXEPATH    AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/salvager"
83
84
85 static int
86 SimpleProcessStart(void *bosHandle,
87                    const char *instance,
88                    const char *executable,
89                    const char *args,
90                    afs_status_p st);
91
92 static int
93 FsProcessStart(void *bosHandle,
94                const char *instance,
95                const char *fileserverExe,
96                const char *volserverExe,
97                const char *salvagerExe,
98                afs_status_p st);
99
100 static int
101 BosProcessDelete(void *bosHandle,
102                  const char *instance,
103                  afs_status_p st);
104
105 static void
106 UpdateCommandParse(char *cmdString,
107                    short *hasSysPathP,
108                    short *hasBinPathP);
109
110 static int
111 UbikQuorumCheck(cfg_host_p cfg_host,
112                 const char *dbInstance,
113                 short *hasQuorum,
114                 afs_status_p st);
115
116 static int
117 UbikVoteStatusFetch(int serverAddr,
118                     unsigned short serverPort,
119                     short *isSyncSite,
120                     short *isWriteReady,
121                     afs_status_p st);
122
123
124
125
126
127
128 /* ---------------- Exported constants ---------------- */
129
130 const char *cfg_kaserverBosName = KASERVER_BOSNAME;
131 const char *cfg_ptserverBosName = PTSERVER_BOSNAME;
132 const char *cfg_vlserverBosName = VLSERVER_BOSNAME;
133 const char *cfg_buserverBosName = BUSERVER_BOSNAME;
134 const char *cfg_fileserverBosName = FILESERVER_BOSNAME;
135 const char *cfg_upserverBosName = UPSERVER_BOSNAME;
136
137 const char *cfg_upclientBosNamePrefix = UPCLIENT_BOSNAME;
138 const char *cfg_upclientSysBosSuffix = "etc";
139 const char *cfg_upclientBinBosSuffix = "bin";
140
141
142
143 /* ---------------- Exported BOS Server functions ------------------ */
144
145
146 /*
147  * cfg_BosServerStart() -- Start the BOS server on host.
148  *
149  *     Timeout is the maximum time, in seconds, to wait for BOS to start.
150  */
151 int ADMINAPI
152 cfg_BosServerStart(void *hostHandle,      /* host config handle */
153                    short noAuth,          /* start in NoAuth mode */
154                    unsigned int timeout,  /* timeout (in seconds) */
155                    afs_status_p st)       /* completion status */
156 {
157     int rc = 1;
158     afs_status_t tst2, tst = 0;
159     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
160     short wasRunning = 0;
161
162     /* validate parameters */
163
164     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
165         tst = tst2;
166     }
167
168     /* remote configuration not yet supported in this function */
169
170     if (tst == 0) {
171         if (!cfg_host->is_local) {
172             tst = ADMCFGNOTSUPPORTED;
173         }
174     }
175
176     /* start bosserver (if not already running) */
177
178 #ifdef AFS_NT40_ENV
179     /* Windows - bosserver is controlled via the BOS control service */
180     if (tst == 0) {
181         DWORD auxArgc;
182         LPCTSTR auxArgBuf[1], *auxArgv;
183
184         if (noAuth) {
185             auxArgc = 1;
186             auxArgv = auxArgBuf;
187             auxArgBuf[0] = "-noauth";
188         } else {
189             auxArgc = 0;
190             auxArgv = NULL;
191         }
192
193         if (!cfgutil_WindowsServiceStart(AFSREG_SVR_SVC_NAME,
194                                          auxArgc,
195                                          auxArgv,
196                                          timeout,
197                                          &wasRunning,
198                                          &tst2)) {
199             /* failed to start BOS control service */
200             tst = tst2;
201         }
202     }
203
204 #else
205     if (tst == 0) {
206         /* function not yet implemented for Unix */
207         tst = ADMCFGNOTSUPPORTED;
208     }
209 #endif /* AFS_NT40_ENV */
210
211     /* put bosserver into requested auth mode if was already running */
212
213     if (tst == 0 && wasRunning) {
214         if (!cfgutil_HostSetNoAuthFlag(cfg_host, noAuth, &tst2)) {
215             tst = tst2;
216         }
217     }
218
219     if (tst != 0) {
220         /* indicate failure */
221         rc = 0;
222     }
223     if (st != NULL) {
224         *st = tst;
225     }
226     return rc;
227 }
228
229
230
231 /*
232  * cfg_BosServerStop() -- Stop the BOS server on host.
233  *
234  *     Timeout is the maximum time, in seconds, to wait for BOS to stop.
235  */
236 int ADMINAPI
237 cfg_BosServerStop(void *hostHandle,      /* host config handle */
238                   unsigned int timeout,  /* timeout (in seconds) */
239                   afs_status_p st)       /* completion status */
240 {
241     int rc = 1;
242     afs_status_t tst2, tst = 0;
243     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
244
245     /* validate parameters */
246
247     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
248         tst = tst2;
249     }
250
251     /* remote configuration not yet supported in this function */
252
253     if (tst == 0) {
254         if (!cfg_host->is_local) {
255             tst = ADMCFGNOTSUPPORTED;
256         }
257     }
258
259     /* stop the bosserver (if running) */
260
261 #ifdef AFS_NT40_ENV
262     /* Windows - bosserver is controlled via the BOS control service */
263     if (tst == 0) {
264         short wasStopped;
265
266         if (!cfgutil_WindowsServiceStop(AFSREG_SVR_SVC_NAME,
267                                         timeout,
268                                         &wasStopped,
269                                         &tst2)) {
270             /* failed to stop BOS control service */
271             tst = tst2;
272         }
273     }
274 #else
275     if (tst == 0) {
276         /* function not yet implemented for Unix */
277         tst = ADMCFGNOTSUPPORTED;
278     }
279 #endif /* AFS_NT40_ENV */
280
281     if (tst != 0) {
282         /* indicate failure */
283         rc = 0;
284     }
285     if (st != NULL) {
286         *st = tst;
287     }
288     return rc;
289 }
290
291
292 /*
293  * cfg_BosServerQueryStatus() -- Query status of BOS server on host.
294  *
295  *     The argument *isBosProcP is set to TRUE only if BOS processes
296  *     are currently executing (as opposed to configured but stopped).
297  */
298 int ADMINAPI
299 cfg_BosServerQueryStatus(void *hostHandle,    /* host config handle */
300                          short *isStartedP,   /* BOS server is started */
301                          short *isBosProcP,   /* BOS processes running */
302                          afs_status_p st)     /* completion status */
303 {
304     int rc = 1;
305     afs_status_t tst2, tst = 0;
306     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
307
308     /* validate parameters and prepare host handle for bos functions */
309
310     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
311         tst = tst2;
312     } else if (isStartedP == NULL) {
313         tst = ADMCFGSTARTEDFLAGPNULL;
314     } else if (isBosProcP == NULL) {
315         tst = ADMCFGBOSSERVERPROCSFLAGPNULL;
316     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
317         tst = tst2;
318     }
319
320     /* remote configuration not yet supported in this function */
321
322     if (tst == 0) {
323         if (!cfg_host->is_local) {
324             tst = ADMCFGNOTSUPPORTED;
325         }
326     }
327
328     /* determine if bosserver is running */
329
330 #ifdef AFS_NT40_ENV
331     /* Windows - bosserver is controlled via the BOS control service */
332     if (tst == 0) {
333         DWORD svcState;
334
335         *isStartedP = *isBosProcP = 0;
336
337         if (!cfgutil_WindowsServiceQuery(AFSREG_SVR_SVC_NAME,
338                                          &svcState,
339                                          &tst2)) {
340             tst = tst2;
341         } else if (svcState == SERVICE_RUNNING) {
342             *isStartedP = 1;
343         }
344     }
345 #else
346     if (tst == 0) {
347         /* function not yet implemented for Unix */
348         tst = ADMCFGNOTSUPPORTED;
349     }
350 #endif /* AFS_NT40_ENV */
351
352
353     /* query status of bos processes */
354
355     if (tst == 0 && *isStartedP) {
356         void *procIter;
357
358         *isBosProcP = 0;
359
360         if (!bos_ProcessNameGetBegin(cfg_host->bosHandle,
361                                      &procIter,
362                                      &tst2)) {
363             tst = tst2;
364         } else {
365             /* iterate over process names, checking status of each */
366             char procName[BOS_MAX_NAME_LEN];
367             bos_ProcessExecutionState_t procState;
368             char procAuxState[BOS_MAX_NAME_LEN];
369             int procDone = 0;
370
371             while (!procDone) {
372                 if (!bos_ProcessNameGetNext(procIter,
373                                             procName, &tst2)) {
374                     /* no more processes (or failure) */
375                     if (tst2 != ADMITERATORDONE) {
376                         tst = tst2;
377                     }
378                     procDone = 1;
379
380                 } else if (!bos_ProcessExecutionStateGet(cfg_host->bosHandle,
381                                                          procName,
382                                                          &procState,
383                                                          procAuxState,
384                                                          &tst2)) {
385                     /* process removed (or failure) */
386                     if (tst2 != BZNOENT) {
387                         tst = tst2;
388                         procDone = 1;
389                     }
390
391                 } else {
392                     if (procState != BOS_PROCESS_STOPPED) {
393                         *isBosProcP = 1;
394                         procDone = 1;
395                     }
396                 }
397             }
398
399             if (!bos_ProcessNameGetDone(procIter, &tst2)) {
400                 tst = tst2;
401             }
402         }
403     }
404
405     if (tst != 0) {
406         /* indicate failure */
407         rc = 0;
408     }
409     if (st != NULL) {
410         *st = tst;
411     }
412     return rc;
413 }
414
415
416
417
418 /* ---------------- Exported Database Server functions ------------------ */
419
420
421
422 /*
423  * cfg_AuthServerStart() -- Start authentication server on host and wait
424  *     for server to be ready to accept requests.
425  *
426  *     This function is intended to be used when configuring the first
427  *     machine in a cell; it enables the AFS server principal to be created
428  *     and configured before starting the remaining database servers.
429  */
430 int ADMINAPI
431 cfg_AuthServerStart(void *hostHandle,  /* host config handle */
432                     afs_status_p st)   /* completion status */
433 {
434     int rc = 1;
435     afs_status_t tst2, tst = 0;
436     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
437
438     /* validate parameters and prepare host handle for bos functions */
439
440     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
441         tst = tst2;
442     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
443         tst = tst2;
444     }
445
446     /* create and start authentication server instance */
447
448     if (tst == 0) {
449         if (!SimpleProcessStart(cfg_host->bosHandle,
450                                 KASERVER_BOSNAME,
451                                 KASERVER_EXEPATH, NULL, &tst2)) {
452             tst = tst2;
453         }
454     }
455
456     /* wait for authentication server to achieve quorum */
457
458     if (tst == 0) {
459         time_t timeStart = time(NULL);
460         short kaHasQuorum;
461
462         while (1) {
463             if (!UbikQuorumCheck(cfg_host,
464                                  KASERVER_BOSNAME, &kaHasQuorum, &tst2)) {
465                 tst = tst2;
466                 break;
467
468             } else if (kaHasQuorum) {
469                 /* quorum on authentication server */
470                 break;
471
472             } else {
473                 /* quorum not yet achieved on authentication server */
474                 if (difftime(time(NULL), timeStart) > 180) {
475                     tst = ADMCFGQUORUMWAITTIMEOUT;
476                     break;
477                 } else {
478                     cfgutil_Sleep(2);
479                 }
480             }
481         }
482     }
483
484     if (tst != 0) {
485         /* indicate failure */
486         rc = 0;
487     }
488     if (st != NULL) {
489         *st = tst;
490     }
491     return rc;
492 }
493
494
495 /*
496  * cfg_DbServersStart() -- Start the standard (required) database servers,
497  *     and optionally the backup database server, on host.
498  *
499  *     The BOS instance names used are the string constants:
500  *         cfg_kaserverBosName - authentication server
501  *         cfg_ptserverBosName - protection server
502  *         cfg_vlserverBosName - volume location server
503  *         cfg_buserverBosName - backup server
504  */
505 int ADMINAPI
506 cfg_DbServersStart(void *hostHandle,  /* host config handle */
507                    short startBkDb,   /* start backup server */
508                    afs_status_p st)   /* completion status */
509 {
510     int rc = 1;
511     afs_status_t tst2, tst = 0;
512     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
513
514     /* validate parameters and prepare host handle for bos functions */
515
516     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
517         tst = tst2;
518     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
519         tst = tst2;
520     }
521
522     /* create and start database server instances */
523
524     if (tst == 0) {
525         /* try all regardless of failures; last error code wins */
526         if (!SimpleProcessStart(cfg_host->bosHandle,
527                                 KASERVER_BOSNAME,
528                                 KASERVER_EXEPATH, NULL, &tst2)) {
529             tst = tst2;
530         }
531         if (!SimpleProcessStart(cfg_host->bosHandle,
532                                 PTSERVER_BOSNAME,
533                                 PTSERVER_EXEPATH, NULL, &tst2)) {
534             tst = tst2;
535         }
536         if (!SimpleProcessStart(cfg_host->bosHandle,
537                                 VLSERVER_BOSNAME,
538                                 VLSERVER_EXEPATH, NULL, &tst2)) {
539             tst = tst2;
540         }
541         if (startBkDb &&
542             !SimpleProcessStart(cfg_host->bosHandle,
543                                 BUSERVER_BOSNAME,
544                                 BUSERVER_EXEPATH, NULL, &tst2)) {
545             tst = tst2;
546         }
547     }
548
549     if (tst != 0) {
550         /* indicate failure */
551         rc = 0;
552     }
553     if (st != NULL) {
554         *st = tst;
555     }
556     return rc;
557 }
558
559
560 /*
561  * cfg_DbServersStop() -- Stop, and unconfigure, the database servers on host.
562  */
563 int ADMINAPI
564 cfg_DbServersStop(void *hostHandle,  /* host config handle */
565                   afs_status_p st)   /* completion status */
566 {
567     int rc = 1;
568     afs_status_t tst2, tst = 0;
569     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
570
571     /* validate parameters and prepare host handle for bos functions */
572
573     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
574         tst = tst2;
575     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
576         tst = tst2;
577     }
578
579     /* stop and delete database server instances */
580
581     if (tst == 0) {
582         /* try all regardless of failures; last error code wins */
583         if (!BosProcessDelete(cfg_host->bosHandle, KASERVER_BOSNAME, &tst2)) {
584             tst = tst2;
585         }
586         if (!BosProcessDelete(cfg_host->bosHandle, PTSERVER_BOSNAME, &tst2)) {
587             tst = tst2;
588         }
589         if (!BosProcessDelete(cfg_host->bosHandle, VLSERVER_BOSNAME, &tst2)) {
590             tst = tst2;
591         }
592         if (!BosProcessDelete(cfg_host->bosHandle, BUSERVER_BOSNAME, &tst2)) {
593             tst = tst2;
594         }
595     }
596
597     if (tst != 0) {
598         /* indicate failure */
599         rc = 0;
600     }
601     if (st != NULL) {
602         *st = tst;
603     }
604     return rc;
605 }
606
607
608 /*
609  * cfg_DbServersQueryStatus() -- Query status of database servers on host.
610  *
611  *     If detailed status information is not required, detailsP can be NULL.
612  *
613  *     If *isStdDbP is FALSE and *isBkDbP is TRUE then the host is in an
614  *     inconsistent state; the remaining database servers should be configured.
615  */
616 int ADMINAPI
617 cfg_DbServersQueryStatus(void *hostHandle,    /* host config handle */
618                          short *isStdDbP,     /* std DB servers configured */
619                          short *isBkDbP,      /* backup DB server configured */
620                          cfg_dbServersStatus_t *detailsP,  /* config details */
621                          afs_status_p st)     /* completion status */
622 {
623     int rc = 1;
624     afs_status_t tst2, tst = 0;
625     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
626     short inCellServDb, isKaserver, isPtserver, isVlserver, isBuserver;
627
628     inCellServDb = isKaserver = isPtserver = isVlserver = isBuserver = 0;
629
630     /* validate parameters and prepare host handle for bos functions */
631
632     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
633         tst = tst2;
634     } else if (isStdDbP == NULL || isBkDbP == NULL) {
635         tst = ADMCFGDBSERVERCONFIGFLAGPNULL;
636     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
637         tst = tst2;
638     }
639
640     /* query host's server CellServDb to see if it lists itself */
641
642     if (tst == 0) {
643         char hostNameAlias[MAXHOSTCHARS];
644
645         if (!cfgutil_HostNameGetCellServDbAlias(cfg_host->hostName,
646                                                 cfg_host->hostName,
647                                                 hostNameAlias,
648                                                 &tst2)) {
649             tst = tst2;
650         } else if (*hostNameAlias != '\0') {
651             /* host in its own CellServDb */
652             inCellServDb = 1;
653         }
654     }
655
656     /* query bosserver to determine what database servers are configured */
657
658     if (tst == 0) {
659         bos_ProcessType_t procType;
660         bos_ProcessInfo_t procInfo;
661
662         if (bos_ProcessInfoGet(cfg_host->bosHandle,
663                                KASERVER_BOSNAME,
664                                &procType, &procInfo, &tst2)) {
665             isKaserver = 1;
666         } else if (tst2 != BZNOENT) {
667             tst = tst2;
668         }
669
670         if (tst == 0) {
671             if (bos_ProcessInfoGet(cfg_host->bosHandle,
672                                    PTSERVER_BOSNAME,
673                                    &procType, &procInfo, &tst2)) {
674                 isPtserver = 1;
675             } else if (tst2 != BZNOENT) {
676                 tst = tst2;
677             }
678         }
679
680         if (tst == 0) {
681             if (bos_ProcessInfoGet(cfg_host->bosHandle,
682                                    VLSERVER_BOSNAME,
683                                    &procType, &procInfo, &tst2)) {
684                 isVlserver = 1;
685             } else if (tst2 != BZNOENT) {
686                 tst = tst2;
687             }
688         }
689
690         if (tst == 0) {
691             if (bos_ProcessInfoGet(cfg_host->bosHandle,
692                                    BUSERVER_BOSNAME,
693                                    &procType, &procInfo, &tst2)) {
694                 isBuserver = 1;
695             } else if (tst2 != BZNOENT) {
696                 tst = tst2;
697             }
698         }
699     }
700
701     if (tst == 0) {
702         /* success; return results */
703         *isStdDbP = (inCellServDb && isKaserver && isPtserver && isVlserver);
704         *isBkDbP  = (inCellServDb && isBuserver);
705
706         if (detailsP) {
707             detailsP->inCellServDb = inCellServDb;
708             detailsP->isKaserver = isKaserver;
709             detailsP->isPtserver = isPtserver;
710             detailsP->isVlserver = isVlserver;
711             detailsP->isBuserver = isBuserver;
712         }
713     } else {
714         /* indicate failure */
715         rc = 0;
716     }
717     if (st != NULL) {
718         *st = tst;
719     }
720     return rc;
721 }
722
723
724 /*
725  * cfg_DbServersRestartAll() -- Restart all database servers in host's cell.
726  */
727 int ADMINAPI
728 cfg_DbServersRestartAll(void *hostHandle,   /* host config handle */
729                         afs_status_p st)    /* completion status */
730 {
731     int rc = 1;
732     afs_status_t tst2, tst = 0;
733     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
734
735     /* validate parameters and prepare host handle for bos functions */
736
737     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
738         tst = tst2;
739     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
740         tst = tst2;
741     }
742
743     /* restart all database servers in host's cell */
744
745     if (tst == 0) {
746         void *dbIter;
747
748         if (!bos_HostGetBegin(cfg_host->bosHandle,
749                               &dbIter,
750                               &tst2)) {
751             tst = tst2;
752         } else {
753             /* iterate over server CellServDb, restarting db servers */
754             char dbhostName[BOS_MAX_NAME_LEN];
755             void *dbhostHandle;
756             int dbhostDone = 0;
757
758             while (!dbhostDone) {
759                 if (!bos_HostGetNext(dbIter,
760                                      dbhostName, &tst2)) {
761                     /* no more entries (or failure) */
762                     if (tst2 != ADMITERATORDONE) {
763                         tst = tst2;
764                     }
765                     dbhostDone = 1;
766
767                 } else if (!bos_ServerOpen(cfg_host->cellHandle,
768                                            dbhostName,
769                                            &dbhostHandle,
770                                            &tst2)) {
771                     /* failed to get bos handle; note error but keep going */
772                     tst = tst2;
773
774                 } else {
775                     /* restart db servers; note errors, but keep going */
776                     if (!bos_ProcessRestart(dbhostHandle,
777                                             KASERVER_BOSNAME, &tst2)) {
778                         tst = tst2;
779                     }
780                     if (!bos_ProcessRestart(dbhostHandle,
781                                             PTSERVER_BOSNAME, &tst2)) {
782                         tst = tst2;
783                     }
784                     if (!bos_ProcessRestart(dbhostHandle,
785                                             VLSERVER_BOSNAME, &tst2)) {
786                         tst = tst2;
787                     }
788                     if (!bos_ProcessRestart(dbhostHandle,
789                                             BUSERVER_BOSNAME, &tst2)) {
790                         /* may not be running a backup server */
791                         if (tst2 != BZNOENT) {
792                             tst = tst2;
793                         }
794                     }
795
796                     if (!bos_ServerClose(dbhostHandle, &tst2)) {
797                         tst = tst2;
798                     }
799                 }
800             }
801
802             if (!bos_HostGetDone(dbIter, &tst2)) {
803                 tst = tst2;
804             }
805         }
806     }
807
808     if (tst != 0) {
809         /* indicate failure */
810         rc = 0;
811
812         /* should really utilize a callback (or some other mechanism) to
813          * indicate which restarts failed and why.
814          */
815     }
816     if (st != NULL) {
817         *st = tst;
818     }
819     return rc;
820 }
821
822
823 /*
824  * cfg_DbServersWaitForQuorum() -- Wait for database servers in host's cell
825  *     to achieve quorum.
826  *
827  *     Timeout is the maximum time, in seconds, to wait for quorum.
828  *
829  *     NOTE: Function does not check for backup server quorum since
830  *           configuration does not require modifying the backup database.
831  */
832 int ADMINAPI
833 cfg_DbServersWaitForQuorum(void *hostHandle,     /* host config handle */
834                            unsigned int timeout, /* timeout in sec. */
835                            afs_status_p st)      /* completion status */
836 {
837     int rc = 1;
838     afs_status_t tst2, tst = 0;
839     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
840
841     /* validate parameters and prepare host handle for bos functions */
842
843     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
844         tst = tst2;
845     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
846         tst = tst2;
847     }
848
849     /* wait for the database servers in host's cell to achieve quorum */
850
851     if (tst == 0) {
852         time_t timeStart = time(NULL);
853         short kaHasQuorum, ptHasQuorum, vlHasQuorum;
854
855         kaHasQuorum = ptHasQuorum = vlHasQuorum = 0;
856
857         while (1) {
858             if (!kaHasQuorum) {
859                 if (!UbikQuorumCheck(cfg_host,
860                                      KASERVER_BOSNAME, &kaHasQuorum, &tst2)) {
861                     tst = tst2;
862                     break;
863                 }
864             }
865
866             if (!ptHasQuorum) {
867                 if (!UbikQuorumCheck(cfg_host,
868                                      PTSERVER_BOSNAME, &ptHasQuorum, &tst2)) {
869                     tst = tst2;
870                     break;
871                 }
872             }
873
874             if (!vlHasQuorum) {
875                 if (!UbikQuorumCheck(cfg_host,
876                                      VLSERVER_BOSNAME, &vlHasQuorum, &tst2)) {
877                     tst = tst2;
878                     break;
879                 }
880             }
881
882             if (kaHasQuorum && ptHasQuorum && vlHasQuorum) {
883                 /* quorum on all dbservers of interest */
884                 break;
885             } else {
886                 /* quorum not yet achieved for one or more dbservers */
887                 if ((timeout == 0) ||
888                     (difftime(time(NULL), timeStart) > timeout)) {
889                     tst = ADMCFGQUORUMWAITTIMEOUT;
890                     break;
891                 } else {
892                     cfgutil_Sleep(10);
893                 }
894             }
895         }
896     }
897
898     if (tst != 0) {
899         /* indicate failure */
900         rc = 0;
901     }
902     if (st != NULL) {
903         *st = tst;
904     }
905     return rc;
906 }
907
908
909
910 /*
911  * cfg_DbServersStopAllBackup() -- Stop, and unconfigure, all backup servers
912  *     in host's cell.
913  */
914 int ADMINAPI
915 cfg_DbServersStopAllBackup(void *hostHandle,  /* host config handle */
916                            afs_status_p st)   /* completion status */
917 {
918     int rc = 1;
919     afs_status_t tst2, tst = 0;
920     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
921
922     /* validate parameters and prepare host handle for bos functions */
923
924     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
925         tst = tst2;
926     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
927         tst = tst2;
928     }
929
930     /* stop and delete all backup servers in host's cell */
931
932     if (tst == 0) {
933         void *dbIter;
934
935         if (!bos_HostGetBegin(cfg_host->bosHandle,
936                               &dbIter,
937                               &tst2)) {
938             tst = tst2;
939         } else {
940             /* iterate over server CellServDb, unconfiguring backup servers */
941             char dbhostName[BOS_MAX_NAME_LEN];
942             void *dbhostHandle;
943             int dbhostDone = 0;
944
945             while (!dbhostDone) {
946                 if (!bos_HostGetNext(dbIter,
947                                      dbhostName, &tst2)) {
948                     /* no more entries (or failure) */
949                     if (tst2 != ADMITERATORDONE) {
950                         tst = tst2;
951                     }
952                     dbhostDone = 1;
953
954                 } else if (!bos_ServerOpen(cfg_host->cellHandle,
955                                            dbhostName,
956                                            &dbhostHandle,
957                                            &tst2)) {
958                     /* failed to get bos handle; note error but keep going */
959                     tst = tst2;
960
961                 } else {
962                     /* unconfig backup server; note errors, but keep going */
963                     if (!BosProcessDelete(dbhostHandle,
964                                           BUSERVER_BOSNAME, &tst2)) {
965                         tst = tst2;
966                     }
967
968                     if (!bos_ServerClose(dbhostHandle, &tst2)) {
969                         tst = tst2;
970                     }
971                 }
972             }
973
974             if (!bos_HostGetDone(dbIter, &tst2)) {
975                 tst = tst2;
976             }
977         }
978     }
979
980     if (tst != 0) {
981         /* indicate failure */
982         rc = 0;
983
984         /* should really utilize a callback (or some other mechanism) to
985          * indicate which stops failed and why.
986          */
987     }
988     if (st != NULL) {
989         *st = tst;
990     }
991     return rc;
992 }
993
994
995
996
997
998 /* ---------------- Exported File Server functions ------------------ */
999
1000
1001 /*
1002  * cfg_FileServerStart() -- Start the file server on host.
1003  *
1004  *     The BOS instance name used is the string constant cfg_fileserverBosName.
1005  */
1006 int ADMINAPI
1007 cfg_FileServerStart(void *hostHandle,  /* host config handle */
1008                     afs_status_p st)   /* completion status */
1009 {
1010     int rc = 1;
1011     afs_status_t tst2, tst = 0;
1012     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1013
1014     /* validate parameters and prepare host handle for bos functions */
1015
1016     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1017         tst = tst2;
1018     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1019         tst = tst2;
1020     }
1021
1022     /* create and start file server instance */
1023
1024     if (tst == 0) {
1025         if (!FsProcessStart(cfg_host->bosHandle,
1026                             FILESERVER_BOSNAME,
1027                             FILESERVER_EXEPATH,
1028                             VOLSERVER_EXEPATH,
1029                             SALVAGER_EXEPATH,
1030                             &tst2)) {
1031             tst = tst2;
1032         } else {
1033             /* TO BE DONE: need a reliable "is started and ready" check */
1034             cfgutil_Sleep(5);
1035         }
1036     }
1037
1038     if (tst != 0) {
1039         /* indicate failure */
1040         rc = 0;
1041     }
1042     if (st != NULL) {
1043         *st = tst;
1044     }
1045     return rc;
1046 }
1047
1048
1049 /*
1050  * cfg_FileServerStop() -- Stop, and unconfigure, the file server on host.
1051  */
1052 int ADMINAPI
1053 cfg_FileServerStop(void *hostHandle,  /* host config handle */
1054                    afs_status_p st)   /* completion status */
1055 {
1056     int rc = 1;
1057     afs_status_t tst2, tst = 0;
1058     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1059
1060     /* validate parameters and prepare host handle for bos functions */
1061
1062     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1063         tst = tst2;
1064     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1065         tst = tst2;
1066     }
1067
1068     /* stop and delete file server instance */
1069
1070     if (tst == 0) {
1071         if (!BosProcessDelete(cfg_host->bosHandle,
1072                               FILESERVER_BOSNAME, &tst2)) {
1073             tst = tst2;
1074         } else {
1075             /* file server instance deleted; remove its addresses from VLDB */
1076             int addrCount, i;
1077             afs_int32 *addrList;
1078
1079             /* note: ignore any errors since address removal is optional;
1080              * e.g., a common source of errors will be attempting to remove
1081              * an address while volumes tied to that address are still listed
1082              * in the VLDB (in which case the address is not removed).
1083              */
1084             if (cfgutil_HostAddressFetchAll(cfg_host->hostName,
1085                                             &addrCount, &addrList, &tst2)) {
1086                 for (i = 0; i < addrCount; i++) {
1087                     (void)vos_FileServerAddressRemove(cfg_host->cellHandle,
1088                                                       NULL,
1089                                                       addrList[i], &tst2);
1090                 }
1091                 free(addrList);
1092             }
1093         }
1094     }
1095
1096     if (tst != 0) {
1097         /* indicate failure */
1098         rc = 0;
1099     }
1100     if (st != NULL) {
1101         *st = tst;
1102     }
1103     return rc;
1104 }
1105
1106
1107 /*
1108  * cfg_FileServerQueryStatus() -- Query status of file server on host.
1109  */
1110 int ADMINAPI
1111 cfg_FileServerQueryStatus(void *hostHandle,   /* host config handle */
1112                           short *isFsP,       /* file server configured */
1113                           afs_status_p st)    /* completion status */
1114 {
1115     int rc = 1;
1116     afs_status_t tst2, tst = 0;
1117     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1118
1119     /* validate parameters and prepare host handle for bos functions */
1120
1121     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1122         tst = tst2;
1123     } else if (isFsP == NULL) {
1124         tst = ADMCFGFILESERVERCONFIGFLAGPNULL;
1125     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1126         tst = tst2;
1127     }
1128
1129     /* query bosserver to determine if fileserver is configured */
1130
1131     if (tst == 0) {
1132         bos_ProcessType_t procType;
1133         bos_ProcessInfo_t procInfo;
1134
1135         *isFsP = 0;
1136
1137         if (bos_ProcessInfoGet(cfg_host->bosHandle,
1138                                FILESERVER_BOSNAME,
1139                                &procType, &procInfo, &tst2)) {
1140             /* instance exists; check type for good measure */
1141             if (procType == BOS_PROCESS_FS) {
1142                 *isFsP = 1;
1143             }
1144         } else if (tst2 != BZNOENT) {
1145             tst = tst2;
1146         }
1147     }
1148
1149     if (tst != 0) {
1150         /* indicate failure */
1151         rc = 0;
1152     }
1153     if (st != NULL) {
1154         *st = tst;
1155     }
1156     return rc;
1157 }
1158
1159
1160
1161
1162 /* ---------------- Exported Update Server functions ------------------ */
1163
1164
1165
1166 /*
1167  * cfg_UpdateServerStart() -- Start the Update server on host.
1168  *
1169  *     Argument strings exportClear and exportCrypt each specify a set of
1170  *     space-separated directories to export or are NULL.
1171  *
1172  *     The BOS instance name used is the string constant cfg_upserverBosName.
1173  */
1174 int ADMINAPI
1175 cfg_UpdateServerStart(void *hostHandle,         /* host config handle */
1176                       const char *exportClear,  /* dirs to export in clear */
1177                       const char *exportCrypt,  /* dirs to export encrypted */
1178                       afs_status_p st)          /* completion status */
1179 {
1180     int rc = 1;
1181     afs_status_t tst2, tst = 0;
1182     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1183
1184     /* validate parameters and prepare host handle for bos functions */
1185
1186     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1187         tst = tst2;
1188     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1189         tst = tst2;
1190     }
1191
1192     /* stop and delete existing update server instance, if any.
1193      * we do this because the set of exported directores might be changing.
1194      */
1195
1196     if (tst == 0) {
1197         if (!BosProcessDelete(cfg_host->bosHandle, UPSERVER_BOSNAME, &tst2)) {
1198             tst = tst2;
1199         }
1200     }
1201
1202     /* create and start update server instance */
1203
1204     if (tst == 0) {
1205         char argsBuf[AFSDIR_PATH_MAX], *args;
1206         size_t argsLen = 0;
1207
1208         if (exportClear != NULL && *exportClear != '\0') {
1209             argsLen += strlen("-clear ") + strlen(exportClear) + 1;
1210         }
1211         if (exportCrypt != NULL && *exportCrypt != '\0') {
1212             argsLen += strlen("-crypt ") + strlen(exportCrypt) + 1;
1213         }
1214
1215         if (argsLen == 0) {
1216             args = NULL;
1217         } else {
1218             if (argsLen <= AFSDIR_PATH_MAX) {
1219                 args = argsBuf;
1220             } else {
1221                 args = (char *)malloc(argsLen);
1222             }
1223
1224             if (args == NULL) {
1225                 tst = ADMNOMEM;
1226             } else {
1227                 if (exportClear == NULL) {
1228                     sprintf(args, "-crypt %s", exportCrypt);
1229                 } else if (exportCrypt == NULL) {
1230                     sprintf(args, "-clear %s", exportClear);
1231                 } else {
1232                     sprintf(args,
1233                             "-clear %s -crypt %s", exportClear, exportCrypt);
1234                 }
1235             }
1236         }
1237
1238         if (tst == 0) {
1239             if (!SimpleProcessStart(cfg_host->bosHandle,
1240                                     UPSERVER_BOSNAME,
1241                                     UPSERVER_EXEPATH, args, &tst2)) {
1242                 tst = tst2;
1243             }
1244
1245             if (args != NULL && args != argsBuf) {
1246                 free(args);
1247             }
1248         }
1249     }
1250
1251     if (tst != 0) {
1252         /* indicate failure */
1253         rc = 0;
1254     }
1255     if (st != NULL) {
1256         *st = tst;
1257     }
1258     return rc;
1259 }
1260
1261
1262 /*
1263  * cfg_UpdateServerStop() -- Stop, and unconfigure, the Update server on host.
1264  */
1265 int ADMINAPI
1266 cfg_UpdateServerStop(void *hostHandle,   /* host config handle */
1267                      afs_status_p st)    /* completion status */
1268 {
1269     int rc = 1;
1270     afs_status_t tst2, tst = 0;
1271     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1272
1273     /* validate parameters and prepare host handle for bos functions */
1274
1275     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1276         tst = tst2;
1277     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1278         tst = tst2;
1279     }
1280
1281     /* stop and delete upserver instance */
1282
1283     if (tst == 0) {
1284         if (!BosProcessDelete(cfg_host->bosHandle, UPSERVER_BOSNAME, &tst2)) {
1285             tst = tst2;
1286         }
1287     }
1288
1289     if (tst != 0) {
1290         /* indicate failure */
1291         rc = 0;
1292     }
1293     if (st != NULL) {
1294         *st = tst;
1295     }
1296     return rc;
1297 }
1298
1299
1300 /*
1301  * cfg_UpdateServerQueryStatus() -- Query status of Update server on host.
1302  */
1303 int ADMINAPI
1304 cfg_UpdateServerQueryStatus(void *hostHandle,   /* host config handle */
1305                             short *isUpserverP, /* update server configured */
1306                             short *isSysCtrlP,  /* system control configured */
1307                             short *isBinDistP,  /* binary dist configured */
1308                             afs_status_p st)    /* completion status */
1309 {
1310     int rc = 1;
1311     afs_status_t tst2, tst = 0;
1312     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1313
1314     /* validate parameters and prepare host handle for bos functions */
1315
1316     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1317         tst = tst2;
1318     } else if (isUpserverP == NULL ||
1319                isSysCtrlP == NULL || isBinDistP == NULL) {
1320         tst = ADMCFGUPSERVERCONFIGFLAGPNULL;
1321     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1322         tst = tst2;
1323     }
1324
1325     /* query bosserver to determine if, and how, upserver is configured */
1326
1327     if (tst == 0) {
1328         void *cmdIter;
1329
1330         *isUpserverP = *isSysCtrlP = *isBinDistP = 0;
1331
1332         if (!bos_ProcessParameterGetBegin(cfg_host->bosHandle,
1333                                           UPSERVER_BOSNAME,
1334                                           &cmdIter, &tst2)) {
1335             tst = tst2;
1336         } else {
1337             char cmdString[BOS_MAX_NAME_LEN];
1338
1339             if (!bos_ProcessParameterGetNext(cmdIter, cmdString, &tst2)) {
1340                 /* no upserver instance (or error) */
1341                 if (tst2 != BZNOENT) {
1342                     tst = tst2;
1343                 }
1344             } else {
1345                 /* parse upserver command line to determine how configured */
1346                 short hasSysPath, hasBinPath;
1347
1348                 UpdateCommandParse(cmdString, &hasSysPath, &hasBinPath);
1349
1350                 *isUpserverP = 1;
1351
1352                 if (hasSysPath) {
1353                     *isSysCtrlP = 1;
1354                 }
1355                 if (hasBinPath) {
1356                     *isBinDistP = 1;
1357                 }
1358             }
1359
1360             if (!bos_ProcessParameterGetDone(cmdIter, &tst2)) {
1361                 tst = tst2;
1362             }
1363         }
1364     }
1365
1366     if (tst != 0) {
1367         /* indicate failure */
1368         rc = 0;
1369     }
1370     if (st != NULL) {
1371         *st = tst;
1372     }
1373     return rc;
1374 }
1375
1376
1377 /*
1378  * cfg_SysBinServerStart() -- Start Update server in System Control and/or
1379  *     Binary Distribution machine configuration on host.
1380  *
1381  *     This function is a convenience wrapper for cfg_UpdateServerStart().
1382  */
1383 int ADMINAPI
1384 cfg_SysBinServerStart(void *hostHandle,    /* host config handle */
1385                       short makeSysCtrl,   /* config as sys control mach */
1386                       short makeBinDist,   /* config as binary dist mach */
1387                       afs_status_p st)     /* completion status */
1388 {
1389     char *cryptSysDir = NULL;
1390     char *clearBinDir = NULL;
1391
1392     if (makeSysCtrl) {
1393         cryptSysDir = AFSDIR_CANONICAL_SERVER_ETC_DIRPATH;
1394     }
1395
1396     if (makeBinDist) {
1397         clearBinDir = AFSDIR_CANONICAL_SERVER_BIN_DIRPATH;
1398     }
1399
1400     return cfg_UpdateServerStart(hostHandle, clearBinDir, cryptSysDir, st);
1401 }
1402
1403
1404
1405 /* ---------------- Exported Update Client functions ------------------ */
1406
1407
1408
1409 /*
1410  * cfg_UpdateClientStart() -- Start an Update client on host.
1411  *
1412  *     Argument string import specifies a set of space-separated directories
1413  *     to import.  Argument frequency specifies the import interval in
1414  *     seconds; if the value is zero (0) then the default frequency is used.
1415  *
1416  *     The BOS instance name used is the concatenation of the string constant
1417  *     cfg_upclientBosNamePrefix and the argument string bosSuffix.
1418  */
1419 int ADMINAPI
1420 cfg_UpdateClientStart(void *hostHandle,         /* host config handle */
1421                       const char *bosSuffix,    /* BOS instance suffix */
1422                       const char *upserver,     /* upserver to import from */
1423                       short crypt,              /* import encrypted */
1424                       const char *import,       /* dirs to import */
1425                       unsigned int frequency,   /* import interval in sec. */
1426                       afs_status_p st)          /* completion status */
1427 {
1428     int rc = 1;
1429     afs_status_t tst2, tst = 0;
1430     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1431     char upclientInstance[BOS_MAX_NAME_LEN];
1432
1433     /* validate parameters and prepare host handle for bos functions */
1434
1435     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1436         tst = tst2;
1437     } else if (bosSuffix == NULL) {
1438         tst = ADMCFGUPCLIENTSUFFIXNULL;
1439     } else if ((strlen(UPCLIENT_BOSNAME) +
1440                 strlen(bosSuffix) + 1) > BOS_MAX_NAME_LEN) {
1441         tst = ADMCFGUPCLIENTSUFFIXTOOLONG;
1442     } else if (upserver == NULL || *upserver == '\0') {
1443         tst = ADMCFGUPCLIENTTARGETSERVERNULL;
1444     } else if (import == NULL || *import == '\0') {
1445         tst = ADMCFGUPCLIENTIMPORTDIRNULL;
1446     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1447         tst = tst2;
1448     }
1449
1450     /* stop and delete existing update client instance, if any.
1451      * we do this because the set of imported directores might be changing.
1452      */
1453
1454     if (tst == 0) {
1455         sprintf(upclientInstance, "%s%s", UPCLIENT_BOSNAME, bosSuffix);
1456
1457         if (!BosProcessDelete(cfg_host->bosHandle, upclientInstance, &tst2)) {
1458             tst = tst2;
1459         }
1460     }
1461
1462     /* create and start update client instance */
1463
1464     if (tst == 0) {
1465         char argsBuf[AFSDIR_PATH_MAX], *args;
1466         size_t argsLen = 0;
1467
1468         argsLen += strlen(upserver) + 1;
1469
1470         if (crypt) {
1471             argsLen += strlen("-crypt ");
1472         } else {
1473             argsLen += strlen("-clear ");
1474         }
1475
1476         if (frequency != 0) {
1477             argsLen += strlen("-t ") + 10 /* max uint */ + 1;
1478         }
1479
1480         argsLen += strlen(import) + 1;
1481
1482         if (argsLen <= AFSDIR_PATH_MAX) {
1483             args = argsBuf;
1484         } else {
1485             args = (char *)malloc(argsLen);
1486         }
1487
1488         if (args == NULL) {
1489             tst = ADMNOMEM;
1490         } else {
1491             /* set up argument buffer */
1492             if (crypt) {
1493                 sprintf(args, "%s -crypt ", upserver);
1494             } else {
1495                 sprintf(args, "%s -clear ", upserver);
1496             }
1497             if (frequency != 0) {
1498                 char *strp = args + strlen(args);
1499                 sprintf(strp, "-t %u ", frequency);
1500             }
1501             strcat(args, import);
1502
1503             /* create and start instance */
1504             if (!SimpleProcessStart(cfg_host->bosHandle,
1505                                     upclientInstance,
1506                                     UPCLIENT_EXEPATH, args, &tst2)) {
1507                 tst = tst2;
1508             }
1509
1510             if (args != argsBuf) {
1511                 free(args);
1512             }
1513         }
1514     }
1515
1516     if (tst != 0) {
1517         /* indicate failure */
1518         rc = 0;
1519     }
1520     if (st != NULL) {
1521         *st = tst;
1522     }
1523     return rc;
1524 }
1525
1526
1527 /*
1528  * cfg_UpdateClientStop() -- Stop, and unconfigure, an Update client on host.
1529  */
1530 int ADMINAPI
1531 cfg_UpdateClientStop(void *hostHandle,       /* host config handle */
1532                      const char *bosSuffix,  /* BOS instance suffix */
1533                      afs_status_p st)        /* completion status */
1534 {
1535     int rc = 1;
1536     afs_status_t tst2, tst = 0;
1537     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1538     char upclientInstance[BOS_MAX_NAME_LEN];
1539
1540     /* validate parameters and prepare host handle for bos functions */
1541
1542     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1543         tst = tst2;
1544     } else if (bosSuffix == NULL) {
1545         tst = ADMCFGUPCLIENTSUFFIXNULL;
1546     } else if ((strlen(UPCLIENT_BOSNAME) +
1547                 strlen(bosSuffix) + 1) > BOS_MAX_NAME_LEN) {
1548         tst = ADMCFGUPCLIENTSUFFIXTOOLONG;
1549     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1550         tst = tst2;
1551     }
1552
1553     /* stop and delete specified update client instance */
1554
1555     if (tst == 0) {
1556         sprintf(upclientInstance, "%s%s", UPCLIENT_BOSNAME, bosSuffix);
1557
1558         if (!BosProcessDelete(cfg_host->bosHandle, upclientInstance, &tst2)) {
1559             tst = tst2;
1560         }
1561     }
1562
1563     if (tst != 0) {
1564         /* indicate failure */
1565         rc = 0;
1566     }
1567     if (st != NULL) {
1568         *st = tst;
1569     }
1570     return rc;
1571 }
1572
1573
1574 /*
1575  * cfg_UpdateClientStopAll() -- Stop, and unconfigure, all Update clients
1576  *     on host.
1577  */
1578 int ADMINAPI
1579 cfg_UpdateClientStopAll(void *hostHandle,       /* host config handle */
1580                         afs_status_p st)        /* completion status */
1581 {
1582     int rc = 1;
1583     afs_status_t tst2, tst = 0;
1584     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1585
1586     /* validate parameters and prepare host handle for bos functions */
1587
1588     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1589         tst = tst2;
1590     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1591         tst = tst2;
1592     }
1593
1594     /* find, stop, and delete all update client instances */
1595
1596     if (tst == 0) {
1597         void *procIter;
1598
1599         if (!bos_ProcessNameGetBegin(cfg_host->bosHandle,
1600                                      &procIter,
1601                                      &tst2)) {
1602             tst = tst2;
1603         } else {
1604             /* iterate over process names, looking for update clients */
1605             char procName[BOS_MAX_NAME_LEN];
1606             int procDone = 0;
1607
1608             while (!procDone) {
1609                 if (!bos_ProcessNameGetNext(procIter,
1610                                             procName, &tst2)) {
1611                     /* no more processes (or failure) */
1612                     if (tst2 != ADMITERATORDONE) {
1613                         tst = tst2;
1614                     }
1615                     procDone = 1;
1616
1617                 } else if (!strncmp(UPCLIENT_BOSNAME,
1618                                     procName,
1619                                     (sizeof(UPCLIENT_BOSNAME) - 1))) {
1620                     /* upclient instance prefix; assume is upclient */
1621                     if (!BosProcessDelete(cfg_host->bosHandle,
1622                                           procName,
1623                                           &tst2)) {
1624                         tst = tst2;
1625                         procDone = 1;
1626                     }
1627                 }
1628             }
1629
1630             if (!bos_ProcessNameGetDone(procIter, &tst2)) {
1631                 tst = tst2;
1632             }
1633         }
1634     }
1635
1636     if (tst != 0) {
1637         /* indicate failure */
1638         rc = 0;
1639     }
1640     if (st != NULL) {
1641         *st = tst;
1642     }
1643     return rc;
1644 }
1645
1646
1647 /*
1648  * cfg_UpdateClientQueryStatus() -- Query status of Update clients on host.
1649  */
1650 int ADMINAPI
1651 cfg_UpdateClientQueryStatus(void *hostHandle,   /* host config handle */
1652                             short *isUpclientP, /* an upclient is configured */
1653                             short *isSysP,      /* system control client */
1654                             short *isBinP,      /* binary dist. client */
1655                             afs_status_p st)    /* completion status */
1656 {
1657     int rc = 1;
1658     afs_status_t tst2, tst = 0;
1659     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1660
1661     /* validate parameters and prepare host handle for bos functions */
1662
1663     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1664         tst = tst2;
1665     } else if (isUpclientP == NULL || isSysP == NULL || isBinP == NULL) {
1666         tst = ADMCFGUPCLIENTCONFIGFLAGPNULL;
1667     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
1668         tst = tst2;
1669     }
1670
1671     /* determine if, and how, any upclients are configured */
1672
1673     if (tst == 0) {
1674         void *procIter;
1675
1676         *isUpclientP = *isSysP = *isBinP = 0;
1677
1678         if (!bos_ProcessNameGetBegin(cfg_host->bosHandle,
1679                                      &procIter,
1680                                      &tst2)) {
1681             tst = tst2;
1682         } else {
1683             /* iterate over process names, looking for update clients */
1684             char procName[BOS_MAX_NAME_LEN];
1685             int procDone = 0;
1686
1687             while (!procDone) {
1688                 if (!bos_ProcessNameGetNext(procIter,
1689                                             procName, &tst2)) {
1690                     /* no more processes (or failure) */
1691                     if (tst2 != ADMITERATORDONE) {
1692                         tst = tst2;
1693                     }
1694                     procDone = 1;
1695
1696                 } else if (!strncmp(UPCLIENT_BOSNAME,
1697                                     procName,
1698                                     (sizeof(UPCLIENT_BOSNAME) - 1))) {
1699                     /* upclient instance prefix; assume is upclient */
1700                     void *cmdIter;
1701
1702                     *isUpclientP = 1;
1703
1704                     if (!bos_ProcessParameterGetBegin(cfg_host->bosHandle,
1705                                                       procName,
1706                                                       &cmdIter, &tst2)) {
1707                         tst = tst2;
1708                     } else {
1709                         char cmdString[BOS_MAX_NAME_LEN];
1710
1711                         if (!bos_ProcessParameterGetNext(cmdIter,
1712                                                          cmdString, &tst2)) {
1713                             /* instance deleted out from under us (or error) */
1714                             if (tst2 != BZNOENT) {
1715                                 tst = tst2;
1716                             }
1717                         } else {
1718                             /* parse command line to determine how config */
1719                             short hasSysPath, hasBinPath;
1720
1721                             UpdateCommandParse(cmdString,
1722                                                &hasSysPath, &hasBinPath);
1723
1724                             if (hasSysPath) {
1725                                 *isSysP = 1;
1726                             }
1727                             if (hasBinPath) {
1728                                 *isBinP = 1;
1729                             }
1730                         }
1731
1732                         if (!bos_ProcessParameterGetDone(cmdIter, &tst2)) {
1733                             tst = tst2;
1734                         }
1735                     }
1736
1737                     if (tst != 0) {
1738                         procDone = 1;
1739                     }
1740                 }
1741             } /* while() */
1742
1743             if (!bos_ProcessNameGetDone(procIter, &tst2)) {
1744                 tst = tst2;
1745             }
1746         }
1747     }
1748
1749     if (tst != 0) {
1750         /* indicate failure */
1751         rc = 0;
1752     }
1753     if (st != NULL) {
1754         *st = tst;
1755     }
1756     return rc;
1757 }
1758
1759
1760 /*
1761  * cfg_SysControlClientStart() -- Start an Update client in System Control
1762  *     client configuration on host.
1763  *
1764  *     This function is a convenience wrapper for cfg_UpdateClientStart().
1765  *     The BOS instance suffix used is the constant cfg_upclientSysBosSuffix.
1766  */
1767 int ADMINAPI
1768 cfg_SysControlClientStart(void *hostHandle,      /* host config handle */
1769                           const char *upserver,  /* upserver to import from */
1770                           afs_status_p st)       /* completion status */
1771 {
1772     return cfg_UpdateClientStart(hostHandle,
1773                                  cfg_upclientSysBosSuffix,
1774                                  upserver,
1775                                  1 /* crypt */,
1776                                  AFSDIR_CANONICAL_SERVER_ETC_DIRPATH,
1777                                  0 /* default frequency */,
1778                                  st);
1779 }
1780
1781
1782 /*
1783  * cfg_BinDistClientStart() -- Start an Update client in Binary Distribution
1784  *     client configuration on host.
1785  *
1786  *     This function is a convenience wrapper for cfg_UpdateClientStart().
1787  *     The BOS instance suffix used is the constant cfg_upclientBinBosSuffix.
1788  */
1789 int ADMINAPI
1790 cfg_BinDistClientStart(void *hostHandle,      /* host config handle */
1791                        const char *upserver,  /* upserver to import from */
1792                        afs_status_p st)       /* completion status */
1793 {
1794     return cfg_UpdateClientStart(hostHandle,
1795                                  cfg_upclientBinBosSuffix,
1796                                  upserver,
1797                                  0 /* crypt */,
1798                                  AFSDIR_CANONICAL_SERVER_BIN_DIRPATH,
1799                                  0 /* default frequency */,
1800                                  st);
1801 }
1802
1803
1804
1805 /* ---------------- Local functions ------------------ */
1806
1807 /*
1808  * SimpleProcessStart() -- create and start a simple bosserver instance.
1809  *
1810  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1811  */
1812 static int
1813 SimpleProcessStart(void *bosHandle,
1814                    const char *instance,
1815                    const char *executable,
1816                    const char *args,
1817                    afs_status_p st)
1818 {
1819     int rc = 1;
1820     afs_status_t tst2, tst = 0;
1821     char cmdBuf[AFSDIR_PATH_MAX], *cmd;
1822     size_t cmdLen;
1823
1824     cmdLen = strlen(executable) + 1 + (args == NULL ? 0 : (strlen(args) + 1));
1825
1826     if (cmdLen <= AFSDIR_PATH_MAX) {
1827         cmd = cmdBuf;
1828     } else {
1829         cmd = (char *)malloc(cmdLen);
1830     }
1831
1832     if (cmd == NULL) {
1833         tst = ADMNOMEM;
1834     } else {
1835         if (args == NULL) {
1836             strcpy(cmd, executable);
1837         } else {
1838             sprintf(cmd, "%s %s", executable, args);
1839         }
1840
1841         if (!bos_ProcessCreate(bosHandle,
1842                                instance,
1843                                BOS_PROCESS_SIMPLE,
1844                                cmd,
1845                                NULL, NULL, &tst2) &&
1846             tst2 != BZEXISTS) {
1847             /* failed to create instance (and not because existed) */
1848             tst = tst2;
1849         } else if (!bos_ProcessExecutionStateSet(bosHandle,
1850                                                  instance,
1851                                                  BOS_PROCESS_RUNNING,
1852                                                  &tst2)) {
1853             /* failed to set instance state to running */
1854             tst = tst2;
1855         }
1856
1857         if (cmd != cmdBuf) {
1858             free(cmd);
1859         }
1860     }
1861
1862     if (tst != 0) {
1863         /* indicate failure */
1864         rc = 0;
1865     }
1866     if (st != NULL) {
1867         *st = tst;
1868     }
1869     return rc;
1870 }
1871
1872
1873
1874 /*
1875  * FsProcessStart() -- create and start a fs bosserver instance.
1876  *
1877  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1878  */
1879 static int
1880 FsProcessStart(void *bosHandle,
1881                const char *instance,
1882                const char *fileserverExe,
1883                const char *volserverExe,
1884                const char *salvagerExe,
1885                afs_status_p st)
1886 {
1887     int rc = 1;
1888     afs_status_t tst2, tst = 0;
1889
1890     if (!bos_FSProcessCreate(bosHandle,
1891                              instance,
1892                              fileserverExe, volserverExe, salvagerExe,
1893                              NULL, &tst2) &&
1894         tst2 != BZEXISTS) {
1895         /* failed to create instance (and not because existed) */
1896         tst = tst2;
1897     } else if (!bos_ProcessExecutionStateSet(bosHandle,
1898                                              instance,
1899                                              BOS_PROCESS_RUNNING,
1900                                              &tst2)) {
1901         /* failed to set instance state to running */
1902         tst = tst2;
1903     }
1904
1905     if (tst != 0) {
1906         /* indicate failure */
1907         rc = 0;
1908     }
1909     if (st != NULL) {
1910         *st = tst;
1911     }
1912     return rc;
1913 }
1914
1915
1916
1917 /*
1918  * BosProcessDelete() -- stop and delete a bosserver instance, if it exists.
1919  *
1920  * RETURN CODES: 1 success, 0 failure  (st indicates why)
1921  */
1922 static int
1923 BosProcessDelete(void *bosHandle,
1924                  const char *instance,
1925                  afs_status_p st)
1926 {
1927     int rc = 1;
1928     afs_status_t tst2, tst = 0;
1929
1930     if (!bos_ProcessExecutionStateSet(bosHandle,
1931                                       instance,
1932                                       BOS_PROCESS_STOPPED,
1933                                       &tst2)) {
1934         /* failed to set instance state to stopped (or does not exist) */
1935         if (tst2 != BZNOENT) {
1936             tst = tst2;
1937         }
1938
1939     } else if (!bos_ProcessAllWaitTransition(bosHandle, &tst2)) {
1940         /* failed to wait for process to stop */
1941         tst = tst2;
1942
1943     } else if (!bos_ProcessDelete(bosHandle, instance, &tst2)) {
1944         /* failed to delete instance (or does not exist) */
1945         if (tst2 != BZNOENT) {
1946             tst = tst2;
1947         }
1948     }
1949
1950     if (tst != 0) {
1951         /* indicate failure */
1952         rc = 0;
1953     }
1954     if (st != NULL) {
1955         *st = tst;
1956     }
1957     return rc;
1958 }
1959
1960
1961 /*
1962  * UpdateCommandParse() -- Parse an upserver or upclient command to determine:
1963  *         1) if it explicitly exports/imports the system control directory
1964  *         2) if it explicitly exports/imports the binary directory
1965  *
1966  *    NOTE: cmdString altered (made all lower case and forward slashes)
1967  */
1968 static void
1969 UpdateCommandParse(char *cmdString,
1970                    short *hasSysPathP,
1971                    short *hasBinPathP)
1972 {
1973     char *argp, *dirp;
1974
1975     *hasSysPathP = *hasBinPathP = 0;
1976
1977     /* make command string all lower case and forward slashes */
1978
1979     for (argp = cmdString; *argp != '\0'; argp++) {
1980         if (isupper(*argp)) {
1981             *argp = tolower(*argp);
1982         } else if (*argp == '\\') {
1983             *argp = '/';
1984         }
1985     }
1986
1987     /* find end of update executable path (and hence beginning of arguments */
1988
1989     argp = cmdString;
1990
1991     while (isspace(*argp)) {
1992         argp++;
1993     }
1994     while (*argp != '\0' && !isspace(*argp)) {
1995         argp++;
1996     }
1997
1998     /* search for well-known system control directory */
1999
2000     dirp = strstr(argp, AFSDIR_CANONICAL_SERVER_ETC_DIRPATH);
2001
2002     if (dirp != NULL) {
2003         /* check that not a portition of a larger path */
2004         char oneBefore, oneAfter, twoAfter;
2005
2006         oneBefore = *(dirp - 1);
2007         oneAfter  = *(dirp + sizeof(AFSDIR_CANONICAL_SERVER_ETC_DIRPATH) - 1);
2008
2009         if (oneAfter != '\0') {
2010             twoAfter  = *(dirp + sizeof(AFSDIR_CANONICAL_SERVER_ETC_DIRPATH));
2011         }
2012
2013         if (isspace(oneBefore)) {
2014             if ((isspace(oneAfter)) ||
2015                 (oneAfter == '\0') ||
2016                 (oneAfter == '/' && (isspace(twoAfter) || twoAfter == '\0'))) {
2017                 *hasSysPathP = 1;
2018             }
2019         }
2020     }
2021
2022     /* search for well-known binary directory */
2023
2024     dirp = strstr(argp, AFSDIR_CANONICAL_SERVER_BIN_DIRPATH);
2025
2026     if (dirp != NULL) {
2027         /* check that not a portition of a larger path */
2028         char oneBefore, oneAfter, twoAfter;
2029
2030         oneBefore = *(dirp - 1);
2031         oneAfter  = *(dirp + sizeof(AFSDIR_CANONICAL_SERVER_BIN_DIRPATH) - 1);
2032
2033         if (oneAfter != '\0') {
2034             twoAfter  = *(dirp + sizeof(AFSDIR_CANONICAL_SERVER_BIN_DIRPATH));
2035         }
2036
2037         if (isspace(oneBefore)) {
2038             if ((isspace(oneAfter)) ||
2039                 (oneAfter == '\0') ||
2040                 (oneAfter == '/' && (isspace(twoAfter) || twoAfter == '\0'))) {
2041                 *hasBinPathP = 1;
2042             }
2043         }
2044     }
2045 }
2046
2047
2048
2049 /*
2050  * UbikQuorumCheck() -- Determine if Ubik has achieved quorum for a specified
2051  *     database instance in host's cell.
2052  *
2053  * RETURN CODES: 1 success, 0 failure  (st indicates why)
2054  */
2055 static int
2056 UbikQuorumCheck(cfg_host_p cfg_host,
2057                 const char *dbInstance,
2058                 short *hasQuorum,
2059                 afs_status_p st)
2060 {
2061     int rc = 1;
2062     afs_status_t tst2, tst = 0;
2063     void *dbIter;
2064
2065     *hasQuorum = 0;
2066
2067     if (!bos_HostGetBegin(cfg_host->bosHandle,
2068                           &dbIter,
2069                           &tst2)) {
2070         tst = tst2;
2071     } else {
2072         /* iterate over server CellServDb, looking for dbserver sync site */
2073         char dbhostName[BOS_MAX_NAME_LEN];
2074         int dbhostAddr;
2075         unsigned short dbhostPort = 0;
2076         int dbhostDone = 0;
2077         int dbhostQueries = 0;
2078
2079         if (!strcmp(dbInstance, KASERVER_BOSNAME)) {
2080             dbhostPort = AFSCONF_KAUTHPORT;
2081         } else if (!strcmp(dbInstance, PTSERVER_BOSNAME)) {
2082             dbhostPort = AFSCONF_PROTPORT;
2083         } else if (!strcmp(dbInstance, VLSERVER_BOSNAME)) {
2084             dbhostPort = AFSCONF_VLDBPORT;
2085         } else if (!strcmp(dbInstance, BUSERVER_BOSNAME)) {
2086             dbhostPort = AFSCONF_BUDBPORT;
2087         }
2088
2089         while (!dbhostDone) {
2090             if (!bos_HostGetNext(dbIter,
2091                                  dbhostName, &tst2)) {
2092                 /* no more entries (or failure) */
2093                 if (tst2 == ADMITERATORDONE) {
2094                     if (dbhostQueries == 0) {
2095                         /* consider quorum to have been achieved when no
2096                          * database servers in cell; otherwise higher-level
2097                          * functions will timeout and fail.
2098                          */
2099                         *hasQuorum = 1;
2100                     }
2101                 } else {
2102                     tst = tst2;
2103                 }
2104                 dbhostDone = 1;
2105
2106             } else if (!util_AdminServerAddressGetFromName(dbhostName,
2107                                                            &dbhostAddr,
2108                                                            &tst2)) {
2109                 tst = tst2;
2110                 dbhostDone = 1;
2111             } else {
2112                 short isSyncSite, isWriteReady;
2113
2114                 /* ignore errors fetching Ubik vote status; there might be
2115                  * an unreachable dbserver yet a reachable sync site.
2116                  */
2117                 dbhostQueries++;
2118
2119                 if (UbikVoteStatusFetch(dbhostAddr,
2120                                         dbhostPort,
2121                                         &isSyncSite,
2122                                         &isWriteReady,
2123                                         &tst2)) {
2124                     /* have quorum if is sync site AND is ready for updates */
2125                     if (isSyncSite) {
2126                         if (isWriteReady) {
2127                             *hasQuorum = 1;
2128                         }
2129                         dbhostDone = 1;
2130                     }
2131                 }
2132             }
2133         }
2134
2135         if (!bos_HostGetDone(dbIter, &tst2)) {
2136             tst = tst2;
2137         }
2138     }
2139
2140     if (tst != 0) {
2141         /* indicate failure */
2142         rc = 0;
2143     }
2144     if (st != NULL) {
2145         *st = tst;
2146     }
2147     return rc;
2148 }
2149
2150
2151 /*
2152  * UbikVoteStatusFetch() -- Fetch Ubik vote status parameters of interest from
2153  *     specified server and port.
2154  *
2155  * RETURN CODES: 1 success, 0 failure  (st indicates why)
2156  */
2157 static int
2158 UbikVoteStatusFetch(int serverAddr,
2159                     unsigned short serverPort,
2160                     short *isSyncSite,
2161                     short *isWriteReady,
2162                     afs_status_p st)
2163 {
2164     int rc = 1;
2165     afs_status_t tst = 0;
2166     struct rx_securityClass *nullSecurity;
2167     struct rx_connection *serverConn;
2168
2169     nullSecurity = rxnull_NewClientSecurityObject();  /* never fails */
2170
2171     if ((serverConn = rx_GetCachedConnection(htonl(serverAddr),
2172                                              htons(serverPort),
2173                                              VOTE_SERVICE_ID,
2174                                              nullSecurity,
2175                                              0)) == NULL) {
2176         tst = ADMCFGUBIKVOTENOCONNECTION;
2177     } else {
2178         int rpcCode;
2179         struct ubik_debug udebugInfo;
2180         extern int VOTE_Debug(), VOTE_DebugOld();
2181
2182         if ((rpcCode = VOTE_Debug(serverConn, &udebugInfo)) == 0) {
2183             /* talking to a 3.5 or later server */
2184             *isSyncSite = (udebugInfo.amSyncSite ? 1 : 0);
2185             *isWriteReady = 0;
2186
2187             if (*isSyncSite) {
2188                 /* as of 3.5 the database is writeable if "labeled" or if all
2189                  * prior recovery states have been achieved; see defect 9477.
2190                  */
2191                 if (((udebugInfo.recoveryState & UBIK_RECLABELDB )) ||
2192
2193                     ((udebugInfo.recoveryState & UBIK_RECSYNCSITE) &&
2194                      (udebugInfo.recoveryState & UBIK_RECFOUNDDB ) &&
2195                      (udebugInfo.recoveryState & UBIK_RECHAVEDB  ))) {
2196                     *isWriteReady = 1;
2197                 }
2198             }
2199
2200         } else if (rpcCode == RXGEN_OPCODE) {
2201             /* talking to old (pre 3.5) server */
2202             struct ubik_debug_old udebugInfo;
2203
2204             if ((rpcCode = VOTE_DebugOld(serverConn, &udebugInfo)) == 0) {
2205                 *isSyncSite = (udebugInfo.amSyncSite ? 1 : 0);
2206                 *isWriteReady = 0;
2207
2208                 if (*isSyncSite) {
2209                     /* pre 3.5 the database is writeable only if "labeled" */
2210                     if (udebugInfo.recoveryState & UBIK_RECLABELDB) {
2211                         *isWriteReady = 1;
2212                     }
2213                 }
2214             }
2215         }
2216
2217         (void) rx_ReleaseCachedConnection(serverConn);
2218
2219         tst = rpcCode;
2220     }
2221
2222     if (tst != 0) {
2223         /* indicate failure */
2224         rc = 0;
2225     }
2226     if (st != NULL) {
2227         *st = tst;
2228     }
2229     return rc;
2230 }