Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / afssvrcfg / config_server_page.cpp
1 /*
2  * Copyright (C) 1998  Transarc Corporation.
3  * All rights reserved.
4  *
5  */
6
7
8 /*
9  * INCLUDES _________________________________________________________________
10  *
11  */
12 extern "C" {
13 #include <afs/param.h>
14 #include <afs/stds.h>
15 }
16
17 #include "afscfg.h"
18 #include "resource.h"
19 #include "cfg_utils.h"
20 extern "C" {
21 #include <afs\afs_vosAdmin.h>
22 #include <afs\afs_bosAdmin.h>
23 #include <afs\afs_clientAdmin.h>
24 #include <afs\volser.h>
25 #include <afs\dirpath.h>
26 }
27 #include "config.h"
28 #include "graphics.h"
29 #include "get_pw_dlg.h"
30 #include <stdio.h>
31 #include <io.h>
32 #include "get_cur_config.h"
33
34
35 /*
36  * PROTOTYPES _________________________________________________________________
37  *
38  */
39 static void OnInitDialog(HWND hwndDlg);
40 static void SetupConfigSteps();
41 static void OnConfig();
42 static DWORD WINAPI ConfigServer(LPVOID param);
43 static BOOL CheckCancel();
44 static void ShowCurrentStep(UINT uiMsgID);
45 static void ShowCurrentStep(TCHAR *pszMsg);
46 static void InitProgressBar();
47 static BOOL CheckResult(int nResult, int nStatus);
48 static BOOL VosOpenServer();
49 static void VosCloseServer();
50 static BOOL ConfigPartition();
51 static BOOL DefineCellForServer();
52 static BOOL DefineCellForClient();
53 static BOOL StartBosServer();
54 static BOOL StartAuthServer();
55 static BOOL CreatePrincipalAndKey();
56 static BOOL StartDbServers();
57 static BOOL CreateAdminPrincipal();
58 static BOOL StartFsVlAndSalvager();
59 static BOOL ConfigSCC();
60 static BOOL ConfigSCS();
61 static BOOL CreateRootAfs();
62 static BOOL StartClient();
63 static BOOL SetRootAcl();
64 static BOOL CreateRootCell();
65 static BOOL MountRootCellStandard();
66 static BOOL SetRootCellAcl();
67 static BOOL MountRootCellRW();
68 static BOOL Replicate();
69 static BOOL EnableAuthChecking();
70 static BOOL UpgradeLibHandles();
71 static BOOL RestartServers();
72 static BOOL AddToCellServDB();
73 static BOOL RemoveFromCellServDB();
74 static BOOL UpdateCellServDB(BOOL bAdding);
75 static BOOL RestartAllDbServers();
76 static BOOL UnconfigDB();
77 static BOOL UnconfigBak();
78 static BOOL UnconfigFS();
79 static BOOL UnconfigSCS();
80 static BOOL UnconfigSCC();
81 static BOOL PostConfig();
82 static void ShowConfigControls(BOOL bShow = TRUE);
83 static void UpdateConfigProgress(int nStepNum);
84 static void ShowTitle();
85 static void ViewLog();
86 static void LogConfigState();
87 static void ShowConfigFailedMsg();
88 static BOOL Unconfiguring();
89 static void SetButtonText(UINT uiMsgID);
90 static char *GetAfsRootDir();
91 static BOOL GetCellServDB(char **ppszCellServDB);
92 static BOOL FreeCellServDB();
93 static char *GetVicepName();
94 static void ShowViewLogButton();
95 static BOOL CreateRootAfsDriveMapping();
96 static BOOL StopClient();
97 static BOOL GetRootVolumeInfo();
98
99 BOOL CALLBACK ConfigServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
100
101
102 /*
103  * DEFINITIONS _________________________________________________________________
104  *
105  */
106 #define QUORUM_WAIT_TIMEOUT                     3 * 60                              // 3 minutes in seconds
107 #define ROOT_VOLUMES_QUOTA                      5000                                // k bytes
108 #define RX_TIMEOUT                                      15 * 1000                           // 15 seconds in milleseconds
109 #define CELLSERVDB_UPDATE_TIMEOUT       RX_TIMEOUT * 2
110 #define INVALID_PARTITION_ID            (UINT)VOLMAXPARTS + 1
111 #define CLIENT_START_TIMEOUT        2 * 60                  // 2 minutes in seconds
112 #define CLIENT_STOP_TIMEOUT         3 * 60                  // 2 minutes in seconds
113
114
115 // The code below is used in so many places that I decided to put it
116 // into macros so that the main line of the code would be easier to follow.
117 #define CHECK_CANCEL    if (CheckCancel()) return FALSE
118 #define CHECK_RESULT    if (!CheckResult(m_nResult, m_nStatus)) return FALSE
119
120 #define IF_WIZ(x)       { if (g_pWiz) (x); }
121
122 // Global variables
123 static HWND                     m_hDlg =                        0;              // Window handle of this dialog
124 static BOOL                     m_bConfiguring =        FALSE;  // TRUE if configuring
125 static BOOL                     m_bConfigured =         FALSE;  // TRUE if configuration was successful
126 static BOOL                     m_bConfigFailed =       FALSE;  // TRUE if configuration failed
127 static BOOL                     m_bCheckCancel =        FALSE;  // TRUE if user pressed cancel - we will ask for confirmation
128 static BOOL                     m_bCancel =                     FALSE;  // TRUE if user confirmed cancel - we will cancel configuration
129 static void*            m_hvosServer =          0;              // vos library server handle
130 static const            MAX_STEPS =                     34;             // Max number of config steps
131 static afs_status_t     m_nStatus;                                      // Error code if a cfg library function fails
132 static int                      m_nResult;                                      // Boolean return code from cfg library calls
133 static int                      m_nNumSteps;                            // Number of config steps that will be performed
134 static BOOL                     m_bDbServersRestarted;          // TRUE if all Database servers were restarted
135 static BOOL                     m_bMustChangeClientCell;        // TRUE if client is in different cell than server
136 static HANDLE           m_hCellServDBUpdateEvent;
137 static LPCTSTR      m_pszCellServDBUpdateEventName = TEXT("CellServDBUpdateEvent");
138 static const            m_CallBackID = 6;
139 static UINT                     m_nPartitionID = INVALID_PARTITION_ID;
140 static BOOL                     m_bCellServDbUpdateErr = FALSE;
141 static TCHAR            m_szCellServDbUpdateErrMsg[cchRESOURCE];
142 static LONG                     m_nServerUpdates = 0;
143 static char *           m_pszCellDbHosts = 0;
144 static BOOL                     m_bNoAuthMode = TRUE;
145 static char             m_szVicepName[9];
146 static BOOL                     m_bMustExit;
147 static BOOL                     m_bCfgInfoInvalidated;
148 static BOOL                     m_bUnconfiguringLastDBServer;
149 static BOOL         m_bClientTokensSet;
150 static BOOL         m_bRootAfsDriveMappingCreated;
151 static char         m_szDriveToMapTo[3];
152 static BOOL         m_bWeCreatedRootAfs;
153 static BOOL         m_bWeCreatedRootCell;
154
155 static CRITICAL_SECTION m_CritSec;
156
157 typedef BOOL (*STEP_FUNC)();                            // All config functions have this signature
158
159
160 /*
161  *      Structure that is used to form an array of all possible configuration steps.
162  */
163 struct CONFIG_STEP {
164         STEP_STATE eState;              // Where we are at in performing this step
165         STEP_FUNC pFunc;                // Function used to perform this step
166         UINT nDescCtrlID;               // Control ID of the static used to hold the steps description
167         UINT nGraphicCtrlID;    // Control ID of the static that will display the graphic
168         UINT nMsgID;                    // Message to show when performing this step
169         UINT nDescID;                   // Step description to show
170 };
171
172
173 /*
174  *      Structure that holds info about the static controls that show the step description
175  *      and the graphic that corresponds to the step state.  There are 8 of these; they 
176  *      are configured at runtime depending on what configuration steps are to be performed.
177  *      These are assigned into the appropriate step structure above.  I could have just
178  *      stuck an index from the array of these structures into the struct above, but decided
179  *      to just reproduce the two fields for ease of use.
180  */
181 struct STEP_GUI_INFO {
182         UINT nDescCtrlID;               // Control ID of the static used to hold the steps description
183         UINT nGraphicCtrlID;    // Control ID of the static that will display the graphic
184 };
185
186 static STEP_GUI_INFO StepGuiCtrlIDs[] = {
187         { IDC_STEP1, IDC_STEP1_GRAPHIC },
188         { IDC_STEP2, IDC_STEP2_GRAPHIC },
189         { IDC_STEP3, IDC_STEP3_GRAPHIC },
190         { IDC_STEP4, IDC_STEP4_GRAPHIC },
191         { IDC_STEP5, IDC_STEP5_GRAPHIC },
192         { IDC_STEP6, IDC_STEP6_GRAPHIC },
193         { IDC_STEP7, IDC_STEP7_GRAPHIC },
194         { IDC_STEP8, IDC_STEP8_GRAPHIC }
195 };
196
197
198 /*
199  *      Each step that can possibly be performed is given an ID here.  Each
200  *      ID corresponds to an index of a config step in the array below.  This
201  *      enum MUST match the order of the steps in the STEPS arrray below.
202  */
203 enum STEP_ID {
204         SID_CONFIG_PARTITION,
205         SID_DEFINE_CELL_FOR_SERVER,
206         SID_DEFINE_CELL_FOR_CLIENT,
207         SID_START_BOS,
208         SID_START_AUTH,
209         SID_CREATE_PRINCIPAL_AND_KEY,
210         SID_START_DB,
211         SID_START_DB_AND_BAK,
212         SID_START_BAK,
213         SID_CREATE_ADMIN_PRINCIPAL,
214         SID_START_FS_VL_AND_SALVAGER,
215         SID_CONFIG_SCC,
216         SID_CONFIG_SCS,
217         SID_CREATE_ROOT_AFS,
218         SID_START_CLIENT,
219         SID_SET_ROOT_ACL,
220         SID_CREATE_ROOT_CELL,
221         SID_MOUNT_ROOT_CELL_STANDARD,
222         SID_SET_ROOT_CELL_ACL,
223         SID_MOUNT_ROOT_CELL_RW,
224         SID_REPLICATE,
225         SID_ENABLE_AUTH_CHECKING,
226         SID_RESTART_SERVERS,
227         SID_ADD_TO_CELLSERVDB,
228         SID_RESTART_ALL_DB_SERVERS,
229         SID_VOS_OPEN_SERVER,
230         SID_UNCONFIG_DB,
231         SID_UNCONFIG_BAK,
232         SID_UNCONFIG_FS,
233         SID_UNCONFIG_SCS,
234         SID_UNCONFIG_SCC,
235         SID_POST_CONFIG,
236         SID_GET_CREDENTIALS,
237     SID_GET_ROOT_VOLUME_INFO
238 };
239
240 static CONFIG_STEP m_ConfigSteps[MAX_STEPS * 2];        // Filled with ID's of steps needed to perform configuration
241
242 /*
243  *      This is our array of config steps.  There is one entry for each possible configuration
244  *      step.  Not all steps will necessarily be performed; it depends on the current config of
245  *      the machine and what the user chooses.  We prefill here the config function, the res
246  *      string to show for the step status message, and if applicable, the res string to show
247  *      for the step description.  All steps have a status message that is shown right above the
248  *      progress bar during configuration; only certain steps have a description.  These are the
249  *      ones that show up in the list of config steps on the dialog and have a state graphic next
250  *      to them.  All other values here are changed to their actual value at runtime.  The order
251  *      the steps appear here is NOT necessarily the order they will be performed in.  That order
252  *      depends on exactly how the server is being configured.
253  */
254 static CONFIG_STEP STEPS[MAX_STEPS] = {
255         { SS_STEP_TO_BE_DONE, ConfigPartition,          0, 0, IDS_PARTITION_STEP,                                       IDS_PARTITION_STEP_DESC },
256         { SS_STEP_TO_BE_DONE, DefineCellForServer,      0, 0, IDS_DEFINE_CELL_NAME_STEP,                        0 },
257         { SS_STEP_TO_BE_DONE, DefineCellForClient,      0, 0, IDS_DEFINE_CELL_MEMBERSHIP_STEP,          0 },
258         { SS_STEP_TO_BE_DONE, StartBosServer,           0, 0, IDS_START_BOS_SERVER_STEP,                        0 },
259         { SS_STEP_TO_BE_DONE, StartAuthServer,          0, 0, IDS_START_AUTH_SERVER_STEP,                       0 },
260         { SS_STEP_TO_BE_DONE, CreatePrincipalAndKey,0, 0, IDS_CREATE_PRINCIPAL_AND_KEY_STEP,    0 },
261         { SS_STEP_TO_BE_DONE, StartDbServers,           0, 0, IDS_START_DB_STEP,                                        IDS_DB_STEP_DESC },
262         { SS_STEP_TO_BE_DONE, StartDbServers,           0, 0, IDS_START_DB_AND_BK_STEP,                         IDS_DB_AND_BK_STEP_DESC },
263         { SS_STEP_TO_BE_DONE, StartDbServers,           0, 0, IDS_START_BK_STEP,                                        IDS_BK_STEP_DESC },     
264         { SS_STEP_TO_BE_DONE, CreateAdminPrincipal,     0, 0, IDS_CREATE_ADMIN_PRINCIPAL_STEP,          0 },
265         { SS_STEP_TO_BE_DONE, StartFsVlAndSalvager,     0, 0, IDS_START_FS_STEP,                                        IDS_FS_STEP_DESC },
266         { SS_STEP_TO_BE_DONE, ConfigSCC,                        0, 0, IDS_START_SCC_STEP,                                       IDS_SCC_STEP_DESC },
267         { SS_STEP_TO_BE_DONE, ConfigSCS,                        0, 0, IDS_START_SCS_STEP,                                       IDS_SCS_STEP_DESC },
268         { SS_STEP_TO_BE_DONE, CreateRootAfs,            0, 0, IDS_CREATE_ROOT_AFS_STEP,                         IDS_ROOT_AFS_STEP_DESC },
269         { SS_STEP_TO_BE_DONE, StartClient,                      0, 0, IDS_START_CLIENT_STEP,                            0 },
270         { SS_STEP_TO_BE_DONE, SetRootAcl,                       0, 0, IDS_SET_ROOT_ACL_STEP,                            0 },
271         { SS_STEP_TO_BE_DONE, CreateRootCell,           0, 0, IDS_CREATE_ROOT_CELL_STEP,                        0 },
272         { SS_STEP_TO_BE_DONE, MountRootCellStandard,0, 0, IDS_MOUNT_ROOT_CELL_STANDARD_STEP,    0 },
273         { SS_STEP_TO_BE_DONE, SetRootCellAcl,           0, 0, IDS_SET_ROOT_CELL_ACL_STEP,                       0 },
274         { SS_STEP_TO_BE_DONE, MountRootCellRW,          0, 0, IDS_MOUNT_ROOT_CELL_RW_STEP,                      0 },
275         { SS_STEP_TO_BE_DONE, Replicate,                        0, 0, IDS_REP_STEP,                                                     IDS_REP_STEP_DESC },
276         { SS_STEP_TO_BE_DONE, EnableAuthChecking,       0, 0, IDS_ENABLE_AUTH_CHECKING_STEP,            0 },
277         { SS_STEP_TO_BE_DONE, RestartServers,           0, 0, IDS_RESTART_SERVERS_STEP,                         0 },
278         { SS_STEP_TO_BE_DONE, AddToCellServDB,          0, 0, IDS_ADD_TO_CELLSERVDB_STEP,                       0 },
279         { SS_STEP_TO_BE_DONE, RestartAllDbServers,      0, 0, IDS_RESTART_ALL_DB_SERVERS_STEP,          0 },
280         { SS_STEP_TO_BE_DONE, VosOpenServer,            0, 0, IDS_NO_MSG_STEP,                                          0 },
281         { SS_STEP_TO_BE_DONE, UnconfigDB,                       0, 0, IDS_UNCONFIG_DB_STEP,                                     IDS_UNCONFIG_DB_STEP_DESC },
282         { SS_STEP_TO_BE_DONE, UnconfigBak,                      0, 0, IDS_UNCONFIG_BK_STEP,                                     IDS_UNCONFIG_BK_STEP_DESC },    
283         { SS_STEP_TO_BE_DONE, UnconfigFS,                       0, 0, IDS_UNCONFIG_FS_STEP,                                     IDS_UNCONFIG_FS_STEP_DESC },
284         { SS_STEP_TO_BE_DONE, UnconfigSCS,                      0, 0, IDS_UNCONFIG_SCS_STEP,                            IDS_UNCONFIG_SCS_STEP_DESC },
285         { SS_STEP_TO_BE_DONE, UnconfigSCC,                      0, 0, IDS_UNCONFIG_SCC_STEP,                            IDS_UNCONFIG_SCC_STEP_DESC },
286         { SS_STEP_TO_BE_DONE, PostConfig,           0, 0, IDS_NO_MSG_STEP,                              0 },
287         { SS_STEP_TO_BE_DONE, UpgradeLibHandles,        0, 0, IDS_GET_CREDENTIALS_STEP,                         0 },
288         { SS_STEP_TO_BE_DONE, GetRootVolumeInfo,    0, 0, IDS_NO_MSG_STEP,                              0 },
289 };
290
291
292 /*
293  *      These are the steps to perform when configuring the very first server.
294  */
295 static STEP_ID FirstServerSteps[] = {
296         SID_CONFIG_PARTITION,
297         SID_DEFINE_CELL_FOR_SERVER,
298         SID_DEFINE_CELL_FOR_CLIENT,
299         SID_START_BOS,
300         SID_START_AUTH,
301         SID_CREATE_PRINCIPAL_AND_KEY,
302         SID_START_DB,
303         SID_CREATE_ADMIN_PRINCIPAL,
304         SID_START_FS_VL_AND_SALVAGER,
305         SID_CONFIG_SCS,
306         SID_VOS_OPEN_SERVER,
307         SID_CREATE_ROOT_AFS,
308         SID_START_CLIENT,
309         SID_SET_ROOT_ACL,
310         SID_CREATE_ROOT_CELL,
311         SID_MOUNT_ROOT_CELL_STANDARD,
312         SID_SET_ROOT_CELL_ACL,
313         SID_MOUNT_ROOT_CELL_RW,
314         SID_REPLICATE,
315         SID_ENABLE_AUTH_CHECKING
316 };
317
318 static STEP_ID InvalidServerInfoSteps[] = {
319         SID_DEFINE_CELL_FOR_SERVER,
320         SID_START_BOS,
321         SID_CREATE_PRINCIPAL_AND_KEY,
322         SID_CREATE_ADMIN_PRINCIPAL,
323         SID_ENABLE_AUTH_CHECKING
324 };
325
326 static STEP_ID InvalidClientInfoSteps[] = {
327         SID_DEFINE_CELL_FOR_CLIENT,
328         SID_START_CLIENT
329 };
330
331 static STEP_ID PreconfigSteps[] = {
332         SID_GET_CREDENTIALS,    // Always do this so we will always have credentials - need this for the config manager.
333     SID_VOS_OPEN_SERVER     // We'll always do this step so we know we can make vos calls 
334 };
335
336 static STEP_ID UnconfigDbSteps[] = {
337         SID_UNCONFIG_DB,
338         SID_RESTART_ALL_DB_SERVERS
339 };
340
341 static STEP_ID UnconfigBakSteps[] = {
342         SID_UNCONFIG_BAK,
343         SID_GET_CREDENTIALS
344 };
345
346 static STEP_ID UnconfigFsSteps[] = {
347         SID_UNCONFIG_FS
348 };
349
350 static STEP_ID UnconfigScsSteps[] = {
351         SID_UNCONFIG_SCS
352 };
353
354 static STEP_ID UnconfigSccSteps[] = {
355         SID_UNCONFIG_SCC
356 };
357
358 static STEP_ID FsSteps[] = {
359         SID_START_FS_VL_AND_SALVAGER
360 };
361
362 static STEP_ID DbSteps[] = {
363         SID_ADD_TO_CELLSERVDB,
364         SID_START_DB,
365         SID_RESTART_ALL_DB_SERVERS
366 };
367
368 static STEP_ID DbAndBakSteps[] = {
369         SID_ADD_TO_CELLSERVDB,
370         SID_START_DB_AND_BAK,
371         SID_RESTART_ALL_DB_SERVERS
372 };
373
374 static STEP_ID BakOnlySteps[] = {
375         SID_START_BAK,
376 };
377
378 static STEP_ID PartitionSteps[] = {
379         SID_CONFIG_PARTITION
380 };
381
382 static STEP_ID CheckRootVolumesSteps[] = {
383     SID_GET_ROOT_VOLUME_INFO
384 };
385
386 static STEP_ID RootVolumesSteps[] = {
387         SID_CREATE_ROOT_AFS,
388         SID_START_CLIENT,               // TODO:  Must check what happens if client started previously and failed because root.afs didn't exist.
389         SID_SET_ROOT_ACL,
390         SID_CREATE_ROOT_CELL,
391         SID_MOUNT_ROOT_CELL_STANDARD,
392         SID_SET_ROOT_CELL_ACL,
393         SID_MOUNT_ROOT_CELL_RW,
394 };
395
396 static STEP_ID ReplicationSteps[] = {
397         SID_REPLICATE
398 };
399
400 static STEP_ID ScsSteps[] = {
401         SID_CONFIG_SCS
402 };
403
404 static STEP_ID SccSteps[] = {
405         SID_CONFIG_SCC
406 };
407
408 static STEP_ID PostConfigSteps[] = {
409         SID_POST_CONFIG
410 };
411
412
413
414 /*
415  * EXPORTED FUNCTIONS _________________________________________________________________
416  *
417  */
418 BOOL Configure(HWND hParent, BOOL& bMustExit)
419 {       
420         int nResult = ModalDialog(IDD_CONFIG_SERVER, hParent, (DLGPROC)ConfigServerPageDlgProc);
421
422     bMustExit = m_bMustExit;
423
424         return !m_bConfigFailed;
425 }
426
427
428 /*
429  * Dialog Proc _________________________________________________________________
430  *
431  */
432 BOOL CALLBACK ConfigServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp)
433 {
434         switch (msg) {
435                 case WM_INITDIALOG:
436                         OnInitDialog(hwndDlg);
437                         break;
438
439                 case WM_COMMAND:
440                         switch (LOWORD(wp)) {
441                                 case IDNEXT:
442                                         OnConfig();
443                                         break;
444
445                                 case IDBACK:
446                                    IF_WIZ(g_pWiz->SetState(sidSTEP_ELEVEN));
447                                    break;
448
449                                 case IDCANCEL:
450                                         // If configuring, handle cancel here.
451                                         // Otherwise, use common handler below.
452                                         if (m_bConfiguring) {
453                                                 ShowCurrentStep(IDS_CANCEL_PENDING);
454                                                 m_bCheckCancel = TRUE;
455                                                 return FALSE;
456                                         } else if (!g_pWiz)
457                                                 EndDialog(m_hDlg, m_bConfigured);
458                                         break;
459                         }
460                         break;
461         }
462
463         if (g_pWiz) {
464                 if (WizStep_Common_DlgProc (hwndDlg, msg, wp, lp))
465                         return FALSE;
466         }
467
468         return FALSE;
469 }
470
471 /*
472  * STATIC FUNCTIONS _________________________________________________________________
473  *
474  */
475
476
477 /*
478  *      This function is the window proc for the static controls used to display the picture
479  *      (blue dot, checkmark, or red X) that indicates the state of each step.
480  *
481  *      Which step to draw the state for is determined and then the state picture is displayed.
482  */
483 static BOOL CALLBACK StepGrahpicDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
484 {
485         // We only handle the paint message.  All other message are passed on to the
486         // static control's normal window proc.
487         if (uMsg == WM_PAINT) {
488                 for (int ii = 0; ii < m_nNumSteps; ii++) {
489                         // Find the step that corresponds to the window we are supposed to paint
490                         if (hwnd == GetDlgItem(m_hDlg, m_ConfigSteps[ii].nGraphicCtrlID)) {
491                                 PaintStepGraphic(hwnd, m_ConfigSteps[ii].eState);       // Show the graphic for this step
492                                 return 0;
493                         }
494                 }
495         }               
496
497         return CallWindowProc((WNDPROC)Subclass_FindNextHook(hwnd, StepGrahpicDlgProc), hwnd, uMsg, wParam, lParam);
498 }
499
500
501
502 /*
503  * Event Handler Functions _________________________________________________________________
504  *
505  */
506 static void OnInitDialog(HWND hwndDlg)
507 {
508         m_hDlg = hwndDlg;
509
510         // Initialize our global variables - only the ones that should not
511         // preserve their state if the user leaves and returns to this page.
512         m_bConfiguring = FALSE;
513         m_bConfigured = FALSE;
514         m_bConfigFailed = FALSE;
515         m_bCheckCancel = FALSE;
516         m_bCancel = FALSE;
517         m_bMustChangeClientCell = FALSE;
518         m_hvosServer = 0;
519         m_nPartitionID = INVALID_PARTITION_ID;
520         m_bCellServDbUpdateErr = FALSE;
521         m_nServerUpdates = 0;
522         m_pszCellDbHosts = 0;
523         m_nNumSteps = 0;
524         m_bNoAuthMode = !g_CfgData.bValidServerInfo;
525         m_szVicepName[0] = 0;
526         m_bMustExit = FALSE;
527         m_bCfgInfoInvalidated = FALSE;
528         m_bUnconfiguringLastDBServer = g_CfgData.bLastDBServer && ShouldUnconfig(g_CfgData.configDB);
529     m_bClientTokensSet = FALSE;
530     m_bRootAfsDriveMappingCreated = FALSE;
531     m_bWeCreatedRootAfs = FALSE;
532     m_bWeCreatedRootCell = FALSE;
533
534         IF_WIZ(g_pWiz->EnableButtons(BACK_BUTTON));
535         IF_WIZ(g_pWiz->SetButtonText(IDNEXT, IDS_CONFIGURE));
536         IF_WIZ(g_pWiz->SetDefaultControl(IDNEXT));
537
538         TCHAR szMsg[cchRESOURCE] = TEXT("");
539
540         // Show the cellname in the title
541         ShowTitle();
542
543         // If this is the wizard, then check if there is nothing to do and
544         // inform the user.  If this is not the wizard, then we should
545         // not even get to this point.  The config tool will not call
546         // the config function if there is nothing to do.
547         if (g_pWiz) {
548                 // Is everything already configured?
549                 if ((g_CfgData.configFS == CS_ALREADY_CONFIGURED) &&
550                    (g_CfgData.configDB == CS_ALREADY_CONFIGURED) &&
551                    (g_CfgData.configBak == CS_ALREADY_CONFIGURED) &&
552                    (g_CfgData.configPartition == CS_ALREADY_CONFIGURED) &&
553                    (g_CfgData.configRootVolumes == CS_ALREADY_CONFIGURED) &&
554                    (g_CfgData.configRep == CS_ALREADY_CONFIGURED) &&
555                    ((g_CfgData.configSCS == CS_ALREADY_CONFIGURED) ||
556                    (g_CfgData.configSCC == CS_ALREADY_CONFIGURED)))
557                 {
558                         GetString(szMsg, IDS_ALREADY_CONFIGURED);
559                 }       
560                 // Is there nothing to configure?
561                 else if ((g_CfgData.configFS != CS_CONFIGURE) &&
562                    (g_CfgData.configDB != CS_CONFIGURE) &&
563                    (g_CfgData.configBak != CS_CONFIGURE) &&
564                    (g_CfgData.configPartition != CS_CONFIGURE) &&
565                    (g_CfgData.configRootVolumes != CS_CONFIGURE) &&
566                    (g_CfgData.configRep != CS_CONFIGURE) &&
567                    (g_CfgData.configSCS != CS_CONFIGURE) &&
568                    (g_CfgData.configSCC != CS_CONFIGURE))
569                 {
570                         GetString(szMsg, IDS_NOTHING_TO_CONFIGURE);
571                 }
572
573                 // If there's a can't configure message, then show it
574                 if (*szMsg) {
575                         // Hide all controls except for the message window
576                         ShowWnd(m_hDlg, IDC_ALL_NEEDED_MSG, FALSE);
577                         ShowWnd(m_hDlg, IDC_FOLLOWING_STEPS_MSG, FALSE);
578
579                         for (int i = 0; i < sizeof(StepGuiCtrlIDs) / sizeof(StepGuiCtrlIDs[0]); i++) {
580                                 ShowWnd(m_hDlg, StepGuiCtrlIDs[i].nDescCtrlID, FALSE);
581                                 ShowWnd(m_hDlg, StepGuiCtrlIDs[i].nGraphicCtrlID, FALSE);
582                         }
583                         
584                         ShowWnd(m_hDlg, IDC_CURRENT_STEP_LABEL, FALSE);
585                         ShowWnd(m_hDlg, IDC_CURRENT_STEP, FALSE);
586                         ShowWnd(m_hDlg, IDC_CONFIG_PROGRESS, FALSE);
587                         ShowWnd(m_hDlg, IDC_PERCENT_COMPLETE_LABEL, FALSE);
588                         ShowWnd(m_hDlg, IDC_PERCENT_COMPLETE, FALSE);
589                         ShowWnd(m_hDlg, IDC_CANT_CONFIG_MSG, FALSE);
590
591                         // Show the message
592                         SetWndText(m_hDlg, IDC_STATUS_MSG, szMsg);
593
594                         return;
595                 }
596         }
597
598         // Determine which steps to perform and which should be displayed
599         SetupConfigSteps();
600
601         if (!g_CfgData.bWizard) {
602                 OnConfig();
603                 return;
604         }
605
606         // This must be done after SetupConfigSteps(), which assings a nGraphicCtrlID
607         // value to the appropriate steps.  After the following code is executed, the graphic
608         // for each step will be drawn automatically whenever the dialog is repainted.
609         for (UINT ii = 0; ii < MAX_STEPS; ii++) {
610                 if (m_ConfigSteps[ii].nGraphicCtrlID)
611                         Subclass_AddHook(GetDlgItem(m_hDlg, m_ConfigSteps[ii].nGraphicCtrlID), StepGrahpicDlgProc);
612         }
613
614         IF_WIZ(g_pWiz->EnableButtons(BACK_BUTTON | NEXT_BUTTON));
615 }
616
617 // User has pressed the Config (or Exit) button.
618 static void OnConfig()
619 {
620         ASSERT(g_CfgData.szCellName[0]);
621
622         // Has user pressed the Exit button?
623         if (m_bConfigured) {
624                 IF_WIZ(g_pWiz->Show(FALSE));
625                 return;
626         }
627
628         // Has user pressed the View Log button?
629         if (m_bConfigFailed) {
630                 ViewLog();
631                 return;
632         }
633
634         // Must we change the client's cell?  See if user minds...
635         if (m_bMustChangeClientCell) {
636                 if (ShowWarning(m_hDlg, IDS_CLIENT_CELL_WILL_CHANGE) == IDCANCEL) {
637                         return;
638                 }
639         }
640
641         // Create a thread to perform the configuration steps
642         DWORD dwThreadID;
643         
644         // Start configuring...
645         HANDLE hThread = CreateThread(0, 0, ConfigServer, 0, 0, &dwThreadID);
646
647         CloseHandle(hThread);
648 }
649
650
651 /*
652  * Utility Functions _________________________________________________________________
653  *
654  */
655 static void ShowExitButton()
656 {
657         if (g_pWiz)
658                 g_pWiz->SetButtonText(IDNEXT, IDS_EXIT);
659         else {
660         if (m_bMustExit)
661             SetWndText(m_hDlg, IDCANCEL, IDS_EXIT);
662         else
663                     SetWndText(m_hDlg, IDCANCEL, IDS_CLOSE);
664     }
665 }
666
667 static void ShowTitle()
668 {
669         ASSERT(g_CfgData.szCellName[0]);
670         
671         TCHAR szMsg[cchRESOURCE];
672
673         GetString(szMsg, IDS_CONFIG_INTO_CELL_MSG);
674
675         lstrcat(szMsg, g_CfgData.szCellName);
676         lstrcat(szMsg, TEXT("."));
677
678         SetWndText(m_hDlg, IDC_TITLE, szMsg);
679 }
680
681 static BOOL Unconfiguring()
682 {
683         return  ShouldUnconfig(g_CfgData.configFS)  ||
684                         ShouldUnconfig(g_CfgData.configDB)  ||
685                         ShouldUnconfig(g_CfgData.configBak) ||
686                         ShouldUnconfig(g_CfgData.configSCS) ||
687                         ShouldUnconfig(g_CfgData.configSCC);
688 }
689
690 static void AddSteps(STEP_ID *pSteps, int nNumNewSteps)
691 {
692         ASSERT(pSteps);
693         ASSERT(nNumNewSteps > 0);
694
695         if (m_nNumSteps + nNumNewSteps > MAX_STEPS) {
696                 ASSERT(FALSE);
697                 return;
698         }
699
700         // Add the new steps to the array of steps
701         for (int nNewStep = 0; nNewStep < nNumNewSteps; nNewStep++) {
702                 STEP_ID nStepID = pSteps[nNewStep];
703
704                 // Add the new step
705                 m_ConfigSteps[m_nNumSteps++] = STEPS[nStepID];
706         }
707 }
708
709 static void GetStepsToPerform()
710 {
711 #define NUM_STEPS(x)    (sizeof((x)) / sizeof(STEP_ID))
712
713         // Is this the first server?
714         if (g_CfgData.bFirstServer) {
715                 // We may have to change the FirstServerSteps, so loop over them
716                 // and only add the ones we need.  All of the FirstServerSteps
717                 // are required except for the ones for backup and sys control
718                 // machine.  If the user doesn't want those then we won't put
719                 // them into the array of steps to perform.  Also, we may not need
720                 // to make the AFS partition (if it already exists).
721                 for (int i = 0; i < NUM_STEPS(FirstServerSteps); i++) {
722                         STEP_ID curStep = FirstServerSteps[i];
723
724                         if ((curStep == SID_START_DB) && ShouldConfig(g_CfgData.configBak))
725                                 curStep = SID_START_DB_AND_BAK;
726                         else if ((curStep == SID_CONFIG_SCS) && !ShouldConfig(g_CfgData.configSCS))
727                                 continue;
728                         else if ((curStep == SID_CONFIG_PARTITION) && !ShouldConfig(g_CfgData.configPartition))
729                                 continue;
730
731                         AddSteps(&curStep, 1);
732                 }
733
734                 return;
735         }
736
737         // Make sure client info is valid
738         if (!g_CfgData.bValidClientInfo || (lstrcmp(g_CfgData.szClientCellName, g_CfgData.szCellName) != 0)) {
739         m_bMustChangeClientCell = TRUE;
740                 AddSteps(InvalidClientInfoSteps, NUM_STEPS(InvalidClientInfoSteps));
741     }
742
743         // Make sure server info is valid
744         if (!g_CfgData.bValidServerInfo)
745                 AddSteps(InvalidServerInfoSteps, NUM_STEPS(InvalidServerInfoSteps));
746
747         // Add steps that should always be performed
748         AddSteps(PreconfigSteps, NUM_STEPS(PreconfigSteps));
749
750         /*
751          *      Do unconfiguration first
752          */
753
754         // Unconfigure File Server?
755         if (ShouldUnconfig(g_CfgData.configFS))
756                 AddSteps(UnconfigFsSteps, NUM_STEPS(UnconfigFsSteps));
757
758         // Unconfigure Database Server? Will also automatically unconfig backup server.
759         if (ShouldUnconfig(g_CfgData.configDB))
760                 AddSteps(UnconfigDbSteps, NUM_STEPS(UnconfigDbSteps));
761         // Unconfigure Backup Server?
762         else if (ShouldUnconfig(g_CfgData.configBak))
763                 AddSteps(UnconfigBakSteps, NUM_STEPS(UnconfigBakSteps));
764
765         // Unconfigure System Control Server?
766         if (ShouldUnconfig(g_CfgData.configSCS))
767                 AddSteps(UnconfigScsSteps, NUM_STEPS(UnconfigScsSteps));
768
769         // Unconfigure System Control Client?
770         if (ShouldUnconfig(g_CfgData.configSCC))
771                 AddSteps(UnconfigSccSteps, NUM_STEPS(UnconfigSccSteps));
772
773         /*
774          *      Now do configuration
775          */
776
777         // AFS Partition
778         if (ShouldConfig(g_CfgData.configPartition))
779                 AddSteps(PartitionSteps, NUM_STEPS(PartitionSteps));
780
781         // Database and backup server
782         if (ShouldConfig(g_CfgData.configDB)) {
783                 if (ShouldConfig(g_CfgData.configBak))
784                         AddSteps(DbAndBakSteps, NUM_STEPS(DbAndBakSteps));
785                 else
786                         AddSteps(DbSteps, NUM_STEPS(DbSteps));
787         } else if (ShouldConfig(g_CfgData.configBak))
788                 AddSteps(BakOnlySteps, NUM_STEPS(BakOnlySteps));
789
790         // File server
791         if (ShouldConfig(g_CfgData.configFS))
792                 AddSteps(FsSteps, NUM_STEPS(FsSteps));
793
794     if (!g_CfgData.bRootVolumesExistanceKnown || !g_CfgData.bRootVolumesReplicationKnown)
795         AddSteps(CheckRootVolumesSteps, NUM_STEPS(CheckRootVolumesSteps));
796
797         // Root volumes
798         if (ShouldConfig(g_CfgData.configRootVolumes))
799                 AddSteps(RootVolumesSteps, NUM_STEPS(RootVolumesSteps));
800
801         // Replication
802         if (ShouldConfig(g_CfgData.configRep))
803                 AddSteps(ReplicationSteps, NUM_STEPS(ReplicationSteps));
804
805         // System control server
806         if (ShouldConfig(g_CfgData.configSCS))
807                 AddSteps(ScsSteps, NUM_STEPS(ScsSteps));
808
809         // System control client
810         if (ShouldConfig(g_CfgData.configSCC))
811                 AddSteps(SccSteps, NUM_STEPS(SccSteps));
812
813     // Perform any steps necessary after all normal configuration has finished.
814     // For instance, if all servers were shut down, then we ask the user if they
815     // want the config info invalidated.  Also, if the last db server was stopped,
816     // then we will stop the client as well.
817         AddSteps(PostConfigSteps, NUM_STEPS(PostConfigSteps));
818 }
819
820 // For steps that should have a place on the dialog, assign them the
821 // next available position.
822 static void SetupStepGUI(CONFIG_STEP& step, UINT& nDispPos)
823 {
824         step.eState = SS_STEP_TO_BE_DONE;
825
826         // If this step has a msg ID then it is a step that gets displayed to the
827         // user.  Show it in the dialog.
828         if (step.nDescID) {
829                 // Give this step a position on the dialog in which to show its message
830                 step.nDescCtrlID = StepGuiCtrlIDs[nDispPos].nDescCtrlID;
831                 step.nGraphicCtrlID = StepGuiCtrlIDs[nDispPos].nGraphicCtrlID;
832
833                 // Show this step's text in the proper static control
834                 SetWndText(m_hDlg, step.nDescCtrlID, step.nDescID);
835
836                 // Show the static control
837                 ShowWnd(m_hDlg, step.nDescCtrlID);
838         
839                 // Show the graphic control
840                 ShowWnd(m_hDlg, step.nGraphicCtrlID);
841         
842                 nDispPos++;
843         }
844 }
845
846 static void SetupConfigSteps()
847 {
848         UINT nDispPos = 0;      // Which StepGuiCtrlID to use, if applicable
849         int nStep = 0;
850
851         // Determine which steps are going to be performed.  For the ones that
852         // will be, show their description message in the appropriate place on
853         // the dialog.
854         GetStepsToPerform();
855         ASSERT(m_nNumSteps > 0);
856
857         // For steps that should have a place on the dialog, assign them the
858         // next available position.
859         for (int i = 0; i < m_nNumSteps; i++)
860                 SetupStepGUI(m_ConfigSteps[i], nDispPos);
861 }
862
863 static BOOL CheckResult(int nResult, int nStatus)
864 {
865         CHECK_CANCEL;
866
867         if (nResult)
868                 return TRUE;
869
870         ShowError(m_hDlg, nStatus, IDS_CONFIG_ERROR);
871
872         return FALSE;
873 }
874
875 static BOOL CheckCancel()
876 {
877         // If we already know we are cancelling then return
878         if (m_bCancel)
879                 return TRUE;
880
881         // If user didn't press Cancel button, then return FALSE
882         if (!m_bCheckCancel)
883                 return FALSE;
884
885         ASSERT(m_bConfiguring);
886
887         TCHAR szMsg[cchRESOURCE];
888         TCHAR szTitle[cchRESOURCE];
889
890         GetString(szMsg, IDS_CANCEL_CONFIG_MSG);
891         GetString(szTitle, GetAppTitleID());
892         
893         // Ask user if they really want to cancel
894         int nChoice = MessageBox(m_hDlg, szMsg, szTitle, MB_YESNO | MB_ICONQUESTION);
895         
896         m_bCancel = (nChoice == IDYES);
897
898         m_bCheckCancel = FALSE;
899
900         return m_bCancel;
901 }
902
903 /*
904  *      Show the current config step, UNLESS the user has pressed the Cancel
905  *      button, in which case a "cancel pending" message is already being 
906  *      displayed and we don't want it replace with this new message.
907  */
908 static void ShowCurrentStep(UINT uiMsgID)
909 {
910         if (!m_bCheckCancel && uiMsgID) {
911                 SetWndText(m_hDlg, IDC_CURRENT_STEP, uiMsgID);
912         ForceUpdateWindow(m_hDlg, IDC_CURRENT_STEP);
913     }
914 }
915
916 static void ShowCurrentStep(TCHAR *pszMsg)
917 {
918         if (!m_bCheckCancel && pszMsg) {
919                 SetWndText(m_hDlg, IDC_CURRENT_STEP, pszMsg);
920         ForceUpdateWindow(m_hDlg, IDC_CURRENT_STEP);
921     }
922 }
923
924 // Set the range and step increment for the progress bar.
925 static void InitProgressBar()
926 {
927         SendDlgItemMessage(m_hDlg, IDC_CONFIG_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, m_nNumSteps));
928         SendDlgItemMessage(m_hDlg, IDC_CONFIG_PROGRESS, PBM_SETSTEP, 1, 0);
929 }
930
931 static char *GetVicepName()
932 {
933         ASSERT((lstrlen(g_CfgData.szPartitionName) == 1) || (lstrlen(g_CfgData.szPartitionName) == 2));
934
935         // Construct the partition name
936         if (!m_szVicepName[0]) 
937                 sprintf(m_szVicepName, "/vicep%s", GetPartitionNameA());
938
939         return m_szVicepName;
940 }
941
942
943 /*
944  * Configuration Functions _________________________________________________________________
945  *
946  */
947
948 static BOOL VosOpenServer()
949 {
950         ASSERT(m_hvosServer == 0);
951         ASSERT(g_CfgData.szHostname[0]);
952         ASSERT(g_hCell);
953
954         g_LogFile.Write("Opening server %s.\r\n", GetHostnameA());
955
956         m_nResult = vos_ServerOpen(g_hCell, GetHostnameA(), &m_hvosServer, &m_nStatus);
957         CHECK_RESULT;
958
959         return TRUE;
960 }
961
962 static BOOL ConfigPartition()
963 {
964         ASSERT(g_hServer);
965         ASSERT(g_CfgData.chDeviceName);
966
967         // Constuct the device name
968         char szDevName[] = "?:";
969         szDevName[0] = GetDeviceNameA();
970
971         g_LogFile.Write("Adding an AFS partition on device '%s' with name '%s'.\r\n", szDevName, GetVicepName());
972
973         m_nResult = cfg_HostPartitionTableAddEntry(g_hServer, GetVicepName(), szDevName, &m_nStatus);
974         CHECK_RESULT;
975         
976         return TRUE;
977 }
978
979 static BOOL FreeCellServDB()
980 {
981         afs_status_t nIgnore;
982
983         if (m_pszCellDbHosts) {
984                 cfg_StringDeallocate(m_pszCellDbHosts, &nIgnore);
985                 CHECK_RESULT;
986         }
987
988         return TRUE;
989 }
990
991 static BOOL GetCellServDB(char **ppszCellServDB)
992 {
993         ASSERT(g_CfgData.szCellServDbHostname[0]);
994
995         afs_status_t nIgnore;
996         char *pszCellname = 0;
997
998         g_LogFile.Write("Getting CellServDB from host %s.\r\n", GetCellServDbHostnameA());
999         m_nResult = cfg_CellServDbEnumerate(GetCellServDbHostnameA(), &pszCellname, ppszCellServDB, &m_nStatus);
1000         CHECK_RESULT;
1001
1002         // The cell name from this call better match the cell name we got previously
1003         if (strcmp(GetCellNameA(), pszCellname) != 0) {
1004                 ShowError(m_hDlg, 0, IDS_WRONG_CELL);
1005                 m_nResult = 0;
1006                 return FALSE;
1007         }
1008
1009         cfg_StringDeallocate(pszCellname, &nIgnore);
1010
1011         g_LogFile.WriteMultistring(*ppszCellServDB);
1012
1013         return TRUE;
1014 }
1015
1016 // Define cell name and cell membership for server
1017 static BOOL DefineCellForServer()
1018 {
1019         ASSERT(g_hServer);
1020         ASSERT(g_CfgData.szCellName[0]);
1021
1022         // CellServDB entries
1023         char *pszEntries = 0;
1024
1025         // If not first server, get list of other hosts
1026         if (!g_CfgData.bFirstServer) {
1027                 if (!m_pszCellDbHosts) {
1028                         m_nResult = GetCellServDB(&m_pszCellDbHosts);
1029                         if (!m_nResult)
1030                                 return m_nResult;
1031                 }
1032                 pszEntries = m_pszCellDbHosts;
1033         } else {
1034                 // Make the hostname a multistring
1035                 _tcsncat(g_CfgData.szHostname, TEXT("\0"), MAX_PARTITION_NAME_LEN);
1036                 pszEntries = GetHostnameA();
1037         }
1038
1039         g_LogFile.Write("Putting this host in cell '%s'.\r\n", GetCellNameA());
1040
1041         ASSERT(g_CfgData.szCellName[0]);
1042                 
1043         m_nResult = cfg_HostSetCell(g_hServer, GetCellNameA(), pszEntries, &m_nStatus);
1044         CHECK_RESULT;
1045
1046         g_CfgData.bValidServerInfo = TRUE;
1047
1048         return TRUE;
1049 }
1050
1051 static BOOL StopClient()
1052 {
1053     g_LogFile.Write("Stopping the client.\r\n");
1054
1055     m_nResult = cfg_ClientStop(g_hClient, CLIENT_STOP_TIMEOUT, &m_nStatus);
1056     CHECK_RESULT;
1057
1058     return TRUE;
1059 }
1060
1061 static BOOL DefineCellForClient()
1062 {
1063         ASSERT(g_hClient);
1064         ASSERT(g_CfgData.szCellName[0]);
1065
1066     // Stop the client
1067     if (!StopClient())
1068         return FALSE;
1069
1070         // CellServDB entries
1071         char *pszEntries = 0;
1072
1073         // If not first server, get list of other hosts
1074         if (!g_CfgData.bFirstServer) {
1075                 if (!m_pszCellDbHosts) {
1076                         m_nResult = GetCellServDB(&m_pszCellDbHosts);
1077                         if (!m_nResult)
1078                                 return m_nResult;
1079                 }
1080         pszEntries = m_pszCellDbHosts;
1081         } else {
1082                 // Make the hostname a multistring
1083                 _tcsncat(g_CfgData.szHostname, TEXT("\0"), MAX_PARTITION_NAME_LEN);
1084                 pszEntries = GetHostnameA();
1085         }
1086
1087         g_LogFile.Write("Putting the AFS Client in this host's cell.\r\n");
1088
1089         m_nResult = cfg_ClientSetCell(g_hClient, GetCellNameA(), pszEntries, &m_nStatus);
1090         CHECK_RESULT;
1091
1092         // Update our state info about the client
1093         g_CfgData.bValidClientInfo = TRUE;
1094         lstrcpy(g_CfgData.szClientCellName, g_CfgData.szCellName);
1095
1096         if (!g_CfgData.bFirstServer) {
1097                 if (!UpgradeLibHandles())
1098                         return FALSE;
1099         }
1100
1101         return TRUE;
1102 }
1103
1104 static BOOL StartBosServer()
1105 {
1106         ASSERT(g_hServer);
1107
1108         g_LogFile.Write("Starting the bos server in %s mode.\r\n", m_bNoAuthMode ? "no auth" : "auth");
1109         
1110         m_nResult = cfg_BosServerStart(g_hServer, m_bNoAuthMode, BOSSERVER_START_TIMEOUT, &m_nStatus);
1111         CHECK_RESULT;
1112
1113         return TRUE;
1114 }
1115
1116 static BOOL StartAuthServer()
1117 {
1118         ASSERT(g_hServer);
1119         ASSERT(g_CfgData.bFirstServer);
1120
1121         g_LogFile.Write("Starting the authentication server.\r\n");
1122
1123         m_nResult = cfg_AuthServerStart(g_hServer, &m_nStatus);
1124         CHECK_RESULT;
1125
1126         g_CfgData.bAuthServerRunning = TRUE;
1127
1128         return TRUE;
1129 }
1130
1131 static BOOL CreatePrincipalAndKey()
1132 {
1133         ASSERT(g_hServer);
1134
1135         if (!UpgradeLibHandles())
1136                 return FALSE;
1137
1138         // Create AFS server principal and put key in local Keyfile
1139         LPTSTR pszServerPW = 0;
1140
1141         g_LogFile.Write("Setting the AFS Principal.\r\n");
1142
1143         if (g_CfgData.bFirstServer) {
1144                 ASSERT(g_CfgData.szServerPW[0]);
1145                 pszServerPW = GetServerPW();
1146         }
1147
1148     BOOL bDone = FALSE;
1149
1150     while (!bDone) {
1151         m_nResult = cfg_HostSetAfsPrincipal(g_hServer, (short)g_CfgData.bFirstServer, S2A(pszServerPW), &m_nStatus);
1152
1153         if (m_nStatus == ADMCFGAFSPASSWDINVALID)
1154             MsgBox(m_hDlg, IDS_BAD_PW, GetAppTitleID(), MB_OK);
1155
1156         if ((m_nStatus == ADMCFGAFSPASSWDINVALID) || (m_nStatus == ADMCFGAFSKEYNOTAVAILABLE)) {
1157             // Ask user for the AFS principal password
1158             if (!GetAfsPrincipalPassword(m_hDlg, pszServerPW)) {
1159                 m_bCancel = TRUE;
1160                 return FALSE;
1161             }
1162         } else
1163             bDone = TRUE;
1164     }
1165
1166         CHECK_RESULT;
1167
1168         return TRUE;
1169 }
1170
1171 static BOOL StartDbServers()
1172 {
1173         ASSERT(g_hServer);
1174
1175         g_LogFile.Write("Starting the following servers: Protection   Volume Location   ");
1176         if (IsStepEnabled(g_CfgData.configBak))
1177                 g_LogFile.Write("Backup   ");
1178         if (!g_CfgData.bFirstServer)
1179                 g_LogFile.Write("Authentication");
1180         g_LogFile.Write("\r\n");
1181
1182         // Start Protection, Volume Location, and Backup (optional) database servers
1183         m_nResult = cfg_DbServersStart(g_hServer, ShouldConfig(g_CfgData.configBak), &m_nStatus);
1184         CHECK_RESULT;
1185
1186         // Must wait for this now so we can then talk to them.
1187         if (g_CfgData.bFirstServer) {
1188                 g_LogFile.Write("Waiting for database servers to reach quorum.\r\n");
1189                 m_nResult = cfg_DbServersWaitForQuorum(g_hServer, QUORUM_WAIT_TIMEOUT, &m_nStatus);
1190                 CHECK_RESULT;
1191         }
1192
1193         return TRUE;
1194 }
1195
1196 static BOOL CreateAdminPrincipal()
1197 {
1198         ASSERT(g_hServer);
1199         ASSERT(g_CfgData.szAdminName[0]);
1200         
1201         // Create generic admin principal and put in local Userlist
1202         char *pszAdminPW = 0;
1203         int nUID = 0;
1204
1205         if (g_CfgData.bFirstServer) {
1206                 ASSERT(g_CfgData.szAdminPW[0]);
1207                 ASSERT(g_CfgData.szAdminUID[0]);
1208                 pszAdminPW = GetAdminPWA();
1209                 nUID = atoi(GetAdminUIDA());
1210                 g_LogFile.Write("Setting Admin Principal to '%s' and UID to %d.\r\n", GetAdminNameA(), nUID);
1211         } else
1212                 g_LogFile.Write("Setting Admin Principal to '%s'.\r\n", GetAdminNameA());
1213
1214         m_nResult = cfg_HostSetAdminPrincipal(g_hServer, (short)g_CfgData.bFirstServer, GetAdminNameA(), pszAdminPW, nUID, &m_nStatus);
1215         CHECK_RESULT;
1216
1217         g_CfgData.bAdminPrincipalCreated = TRUE;
1218
1219         if (g_CfgData.bFirstServer) {
1220                 if (!UpgradeLibHandles())
1221                         return FALSE;
1222         }
1223
1224         return TRUE;
1225 }
1226
1227 static BOOL StartFsVlAndSalvager()
1228 {
1229         ASSERT(g_hServer);
1230
1231         g_LogFile.Write("Starting the File Server.\r\n");
1232
1233         m_nResult = cfg_FileServerStart(g_hServer, &m_nStatus);
1234         CHECK_RESULT;
1235                 
1236         return TRUE;
1237 }
1238
1239 static BOOL ConfigSCS()
1240 {
1241         ASSERT(g_hServer);
1242
1243         g_LogFile.Write("Configuring the System Control Server.\r\n");
1244
1245         m_nResult = cfg_SysBinServerStart(g_hServer, TRUE, FALSE, &m_nStatus);
1246         CHECK_RESULT;
1247
1248         return TRUE;
1249 }
1250
1251 static BOOL ConfigSCC()
1252 {
1253         ASSERT(g_hServer);
1254         ASSERT(g_CfgData.szSysControlMachine[0]);
1255         
1256         g_LogFile.Write("Configuring the System Control Client.\r\n");
1257
1258         m_nResult = cfg_SysControlClientStart(g_hServer, GetSysControlMachineA(), &m_nStatus);
1259         CHECK_RESULT;
1260
1261         return TRUE;
1262 }
1263
1264 static BOOL GetPartitionID()
1265 {
1266         if (m_nPartitionID != INVALID_PARTITION_ID)
1267                 return TRUE;
1268
1269         g_LogFile.Write("Translating the parition name '%s' to an ID.\r\n", GetVicepName());
1270
1271         m_nResult = vos_PartitionNameToId(GetVicepName(), &m_nPartitionID, &m_nStatus);
1272         CHECK_RESULT;
1273
1274         g_LogFile.Write("The ID for partition '%s' is %d.\r\n", GetVicepName(), m_nPartitionID);
1275
1276         ASSERT(m_nPartitionID != INVALID_PARTITION_ID);
1277
1278         return TRUE;
1279 }
1280
1281 static BOOL GetRootVolumeInfo()
1282 {
1283     BOOL bResult;
1284
1285     if (!g_CfgData.bRootVolumesExistanceKnown) {
1286         m_nStatus = DoRootVolumesExist(bResult);
1287         m_nResult = !m_nStatus;
1288         CHECK_RESULT;
1289     
1290         g_CfgData.bRootVolumesExistanceKnown = TRUE;
1291     }
1292
1293     if (!g_CfgData.bRootVolumesReplicationKnown) {
1294         m_nStatus = AreRootVolumesReplicated(bResult);
1295         m_nResult = !m_nStatus;
1296         CHECK_RESULT;
1297     
1298         g_CfgData.bRootVolumesReplicationKnown = TRUE;
1299     }
1300
1301     return TRUE;
1302 }
1303
1304 static BOOL CreateRootAfs()
1305 {
1306         ASSERT(g_hCell);
1307         ASSERT(m_hvosServer);
1308
1309     // If the root.afs volume already exists, then just return.  We can get to this step
1310     // and root.afs already exist if:
1311     //
1312     //    1)  We could not determine the status of the root volumes when the app was started 
1313     //        and the user asked us to create the root volumes if they don't exist.
1314     //
1315     //    2)  Since there is only one page from which the user decides if they want to create
1316     //        the root volumes, there is a chance that one of the root volumes may exist while
1317     //        the other doesn't.  If that is the case the user is told that the "root volumes"
1318     //        don't exist.  If they choose to create them, we do this check here to make sure
1319     //        we only create the one that doesn't exist.
1320     //
1321     if (g_CfgData.bRootAfsExists)
1322                 return TRUE;
1323
1324         if (!GetPartitionID())
1325                 return FALSE;
1326     
1327     // If the client is running then stop it - creating root.afs will confuse it.
1328     // It will be started again after root.afs is created.
1329     if (!StopClient())
1330         return FALSE;
1331
1332         g_LogFile.Write("Creating volume root.afs on partition %d with a quota of %d.\r\n", m_nPartitionID, ROOT_VOLUMES_QUOTA);
1333
1334         m_nResult = vos_VolumeCreate(g_hCell, m_hvosServer, 0, m_nPartitionID, "root.afs", ROOT_VOLUMES_QUOTA, &g_CfgData.nRootAfsID, &m_nStatus);
1335
1336         CHECK_RESULT;
1337
1338         g_LogFile.Write("Volume root.afs was created with an ID of %d.\r\n", g_CfgData.nRootAfsID);
1339
1340         g_CfgData.bRootAfsExists = TRUE;
1341
1342     m_bWeCreatedRootAfs = TRUE;
1343
1344         return TRUE;
1345 }
1346
1347 static BOOL StartClient()
1348 {
1349         ASSERT(g_hClient);
1350     
1351         g_LogFile.Write("Starting the AFS Client.\r\n");
1352
1353         m_nResult = cfg_ClientStart(g_hClient, CLIENT_START_TIMEOUT, &m_nStatus);
1354         CHECK_RESULT;
1355         
1356         return TRUE;
1357 }
1358
1359 static BOOL SetTokensInClient()
1360 {
1361     if (m_bClientTokensSet)
1362         return TRUE;
1363
1364         g_LogFile.Write("Putting our tokens into the AFS Client.\r\n");
1365
1366     m_nResult = afsclient_TokenSet(g_hToken, &m_nStatus);
1367     CHECK_RESULT;
1368
1369     m_bClientTokensSet = TRUE;
1370
1371     return TRUE;
1372 }
1373
1374 static BOOL CreateRootAfsDriveMapping()
1375 {
1376     if (m_bRootAfsDriveMappingCreated)
1377         return TRUE;
1378
1379     g_LogFile.Write("Attempting to create a drive mapping into AFS.\r\n");
1380
1381         char szAfsRootDir[_MAX_PATH];
1382         sprintf(szAfsRootDir, "\\\\%s\\all", GetClientNetbiosNameA());
1383
1384     strcpy(m_szDriveToMapTo, "_:");
1385
1386     // We will try all drives from D to Z.
1387     char chDriveLetter;
1388
1389 try_again:
1390     for (chDriveLetter = 'D'; (chDriveLetter <= 'Z') && !m_bRootAfsDriveMappingCreated; chDriveLetter++) {
1391         m_szDriveToMapTo[0] = chDriveLetter;
1392         g_LogFile.Write("Attempting to map %s to %s: ", m_szDriveToMapTo, szAfsRootDir);
1393         m_bRootAfsDriveMappingCreated = (WNetAddConnection(A2S(szAfsRootDir), TEXT(""), A2S(m_szDriveToMapTo)) == NO_ERROR);
1394         g_LogFile.Write(m_bRootAfsDriveMappingCreated ? "succeeded.\r\n" : "failed.\r\n");
1395     }        
1396
1397     // If we couldn't map a drive, then ask the user to unmap something.
1398     if (!m_bRootAfsDriveMappingCreated) {
1399         int nChoice = MsgBox(m_hDlg, IDS_CANT_MAP_ROOT_AFS, GetAppTitleID(), MB_ICONEXCLAMATION | MB_OKCANCEL);
1400         if (nChoice == IDOK)
1401             goto try_again;
1402     }            
1403
1404     return m_bRootAfsDriveMappingCreated;
1405 }
1406
1407 static BOOL SetRootAcl()
1408 {
1409     // Only do this if we just created root.afs
1410     if (!m_bWeCreatedRootAfs)
1411         return TRUE;
1412
1413     if (!SetTokensInClient())
1414         return FALSE;
1415
1416     if (!CreateRootAfsDriveMapping())
1417         return FALSE;
1418
1419         g_LogFile.Write("Setting the ACL on root.afs.\r\n");
1420
1421         acl_t acl = { READ, NO_WRITE, LOOKUP, NO_DELETE, NO_INSERT, NO_LOCK, NO_ADMIN };
1422
1423         m_nResult = afsclient_ACLEntryAdd(m_szDriveToMapTo, "system:anyuser", &acl, &m_nStatus);
1424         CHECK_RESULT;
1425         
1426         return TRUE;
1427 }
1428
1429
1430 static BOOL CreateRootCell()
1431 {
1432         ASSERT(g_hCell);
1433         ASSERT(m_hvosServer);
1434
1435     // If the root.cell volume already exists, then just return.  We can get to this step
1436     // and root.cell already exist if:
1437     //
1438     //    1)  We could not determine the status of the root volumes when the app was started 
1439     //        and the user asked us to create the root volumes if they don't exist.
1440     //
1441     //    2)  Since there is only one page from which the user decides if they want to create
1442     //        the root volumes, there is a chance that one of the root volumes may exist while
1443     //        the other doesn't.  If that is the case the user is told that the "root volumes"
1444     //        don't exist.  If they choose to create them, we do this check here to make sure
1445     //        we only create the one that doesn't exist.
1446     //
1447     if (g_CfgData.bRootCellExists)
1448                 return TRUE;
1449
1450     // If root.afs exists and we did not just create it, then we cannot make root.cell.  For
1451     // now, just pretend we succeeded.  TODO: We must handle this better in a future version.
1452     // We can't at this time because the program strings are frozen - we can't add new
1453     // error messages.
1454         if (g_CfgData.bRootAfsExists && !m_bWeCreatedRootAfs)
1455         return TRUE;
1456         
1457         if (!GetPartitionID())
1458                 return FALSE;
1459
1460         g_LogFile.Write("Creating volume root.cell on partition %d with a quota of %d.\r\n", m_nPartitionID, ROOT_VOLUMES_QUOTA);
1461
1462         m_nResult = vos_VolumeCreate(g_hCell, m_hvosServer, 0, m_nPartitionID, "root.cell", ROOT_VOLUMES_QUOTA, &g_CfgData.nRootCellID, &m_nStatus);
1463         CHECK_RESULT;
1464         
1465         g_LogFile.Write("Volume root.cell was created with an ID of %d.\r\n", g_CfgData.nRootCellID);
1466
1467         g_CfgData.bRootCellExists = TRUE;
1468
1469     m_bWeCreatedRootCell = TRUE;
1470
1471         return TRUE;
1472 }
1473
1474 static char *GetRootCellDir()
1475 {
1476         static char szDir[MAX_CELL_NAME_LEN + 5] = "";
1477
1478         if (!szDir[0]) {
1479                 ASSERT(g_CfgData.szCellName[0]);
1480                 sprintf(szDir, "%s\\%s", m_szDriveToMapTo, GetCellNameA());
1481         }
1482
1483         return szDir;
1484 }
1485
1486 static char *GetRootCellReadWriteDir()
1487 {
1488         static char szDir[MAX_CELL_NAME_LEN + 5] = "";
1489
1490         if (!szDir[0]) {
1491                 ASSERT(g_CfgData.szCellName[0]);
1492                 sprintf(szDir, "%s\\.%s", m_szDriveToMapTo, GetCellNameA());
1493         }
1494
1495         return szDir;
1496 }
1497
1498 static BOOL MountRootCellStandard()
1499 {
1500         ASSERT(g_CfgData.szCellName[0]);
1501
1502     // Only do this if we just created root.cell
1503     if (!m_bWeCreatedRootCell)
1504         return TRUE;
1505
1506     if (!SetTokensInClient())
1507         return FALSE;
1508
1509     if (!CreateRootAfsDriveMapping())
1510         return FALSE;
1511
1512         g_LogFile.Write("Mouting root.cell with a Standard mount point at path %s.\r\n", GetRootCellDir());
1513         
1514         m_nResult = afsclient_MountPointCreate(g_hCell, GetRootCellDir(), "root.cell", READ_ONLY, CHECK_VOLUME, &m_nStatus);
1515         CHECK_RESULT;
1516
1517         return TRUE;
1518 }
1519
1520 static BOOL SetRootCellAcl()
1521 {
1522     // Only do this if we just created root.cell
1523     if (!m_bWeCreatedRootCell)
1524         return TRUE;
1525
1526     if (!SetTokensInClient())
1527         return FALSE;
1528
1529         g_LogFile.Write("Setting the ACL on root.cell (dir %s).\r\n", GetRootCellDir());
1530
1531         acl_t acl = { READ, NO_WRITE, LOOKUP, NO_DELETE, NO_INSERT, NO_LOCK, NO_ADMIN };
1532
1533         m_nResult = afsclient_ACLEntryAdd(GetRootCellDir(), "system:anyuser", &acl, &m_nStatus);
1534         CHECK_RESULT;
1535
1536         return TRUE;
1537 }
1538
1539 static BOOL MountRootCellRW()
1540 {
1541         ASSERT(g_CfgData.szCellName[0]);
1542
1543     // Only do this if we just created root.cell
1544     if (!m_bWeCreatedRootCell)
1545         return TRUE;
1546
1547     if (!SetTokensInClient())
1548         return FALSE;
1549
1550     if (!CreateRootAfsDriveMapping())
1551         return FALSE;
1552
1553         g_LogFile.Write("Mounting root.cell with a Read/Write mount point at path %s.\r\n", GetRootCellReadWriteDir());
1554
1555         m_nResult = afsclient_MountPointCreate(g_hCell, GetRootCellReadWriteDir(), "root.cell", READ_WRITE, CHECK_VOLUME, &m_nStatus);
1556         CHECK_RESULT;
1557
1558         return TRUE;
1559 }
1560
1561 static BOOL Replicate()
1562 {
1563         ASSERT(g_hCell);
1564         ASSERT(m_hvosServer);
1565
1566         if (!GetPartitionID())
1567                 return FALSE;
1568
1569         // If only one of the volumes is not replicated, then only replicate
1570         // that volume, or if we could not determine if they were replicated
1571     // until configuration began, and they are replicated, then do nothing.
1572
1573         if (g_CfgData.bRootAfsExists && !g_CfgData.bRootAfsReplicated) {
1574                 g_LogFile.Write("Creating a read only site for volume root.afs using partition ID %d and volume ID %d.\r\n", m_nPartitionID, g_CfgData.nRootAfsID);
1575         
1576                 m_nResult = vos_VLDBReadOnlySiteCreate(g_hCell, m_hvosServer, 0, m_nPartitionID, g_CfgData.nRootAfsID, &m_nStatus);
1577                 CHECK_RESULT;
1578         
1579                 g_LogFile.Write("Releasing the root.afs volume using volume ID %d.\r\n", g_CfgData.nRootAfsID);
1580         
1581                 m_nResult = vos_VolumeRelease(g_hCell, 0, g_CfgData.nRootAfsID, VOS_NORMAL, &m_nStatus);
1582                 CHECK_RESULT;
1583
1584                 g_CfgData.bRootAfsReplicated = TRUE;
1585         }
1586
1587         if (g_CfgData.bRootCellExists && !g_CfgData.bRootCellReplicated) {
1588                 g_LogFile.Write("Creating a read only site for volume root.cell using partition ID %d and volume ID %d.\r\n", m_nPartitionID, g_CfgData.nRootCellID);
1589         
1590                 m_nResult = vos_VLDBReadOnlySiteCreate(g_hCell, m_hvosServer, 0, m_nPartitionID, g_CfgData.nRootCellID, &m_nStatus);
1591                 CHECK_RESULT;
1592         
1593                 g_LogFile.Write("Releasing the root.cell volume using volume ID %d.\r\n", g_CfgData.nRootCellID);
1594         
1595                 m_nResult = vos_VolumeRelease(g_hCell, 0, g_CfgData.nRootCellID, VOS_NORMAL, &m_nStatus);
1596                 CHECK_RESULT;
1597
1598                 g_CfgData.bRootCellReplicated = TRUE;
1599         }
1600
1601         return TRUE;
1602 }
1603
1604 static BOOL EnableAuthChecking()
1605 {
1606         ASSERT(g_hCell);
1607         ASSERT(g_CfgData.szHostname[0]);
1608
1609         void *hbosServer;
1610
1611         g_LogFile.Write("Bos open of server '%s'.\r\n", GetHostnameA());
1612         m_nResult = bos_ServerOpen(g_hCell, GetHostnameA(), &hbosServer, &m_nStatus);
1613         CHECK_RESULT;
1614
1615         ASSERT(hbosServer);
1616
1617         g_LogFile.Write("Enabling auth checking.\r\n");
1618         m_nResult = bos_AuthSet(hbosServer, BOS_AUTH_REQUIRED, &m_nStatus);
1619         CHECK_RESULT;
1620
1621         g_LogFile.Write("Closing bos server connection.\r\n");
1622         m_nResult = bos_ServerClose(hbosServer, &m_nStatus);
1623         CHECK_RESULT;
1624
1625         return TRUE;
1626 }
1627
1628 static BOOL UpgradeLibHandles()
1629 {
1630         ASSERT(g_CfgData.szCellName[0]);
1631         ASSERT(g_CfgData.szAdminName[0]);
1632         ASSERT(g_CfgData.szAdminPW[0]);
1633
1634         g_LogFile.Write("Getting credentials in cell '%s' as admin '%s'.\r\n", GetCellNameA(), GetAdminNameA());
1635
1636         m_nResult = GetLibHandles(&m_nStatus);
1637         CHECK_RESULT;
1638
1639         return TRUE;
1640 }
1641
1642 static BOOL RestartServers()
1643 {
1644         ASSERT(g_hCell);
1645         
1646         void *hbosServer;
1647
1648         g_LogFile.Write("Bos open of server '%s'.\r\n", GetHostnameA());
1649         m_nResult = bos_ServerOpen(g_hCell, GetHostnameA(), &hbosServer, &m_nStatus);
1650         CHECK_RESULT;
1651
1652         ASSERT(hbosServer);
1653
1654         g_LogFile.Write("Stopping and restarting all bos processes.\r\n");
1655         m_nResult = bos_ProcessAllStopAndRestart(hbosServer, BOS_RESTART_BOS_SERVER, &m_nStatus);
1656         CHECK_RESULT;
1657
1658         g_LogFile.Write("Closing bos server connection.\r\n");
1659         m_nResult = bos_ServerClose(hbosServer, &m_nStatus);
1660         CHECK_RESULT;
1661
1662         return TRUE;
1663 }
1664
1665 void DbAddHostCallback(void *callBackId, cfg_cellServDbStatus_t *statusItemP, int status)
1666 {
1667         // Is this our call back ID?
1668         if (callBackId != (void *)&m_CallBackID)
1669                 return;
1670
1671         // Update the var that tracks server updates so the main config thread won't
1672         // think we have timed out.
1673         EnterCriticalSection(&m_CritSec);
1674         m_nServerUpdates++;
1675         LeaveCriticalSection(&m_CritSec);
1676
1677         // Is this the last callback?
1678         if (!statusItemP) {
1679                 m_nStatus = status;
1680                 m_nResult = !status;
1681                 SetEvent(m_hCellServDBUpdateEvent);             // Signal config thread that we are done
1682                 return;
1683         }
1684
1685         UINT nStrID;
1686
1687         // Did the update of the current host succeed?
1688         if (statusItemP->status == 0)
1689                 nStrID = IDS_UPDATING_CELLSERVDB_HOST_SUCCEEDED;
1690         else {
1691                 m_bCellServDbUpdateErr = TRUE;
1692                 nStrID = IDS_UPDATING_CELLSERVDB_HOST_FAILED;
1693         }
1694
1695         TCHAR szMsg[cchRESOURCE];
1696         GetString(szMsg, nStrID);
1697         _tcscat(szMsg, A2S(statusItemP->fsDbHost));
1698         ShowCurrentStep(szMsg);
1699
1700         g_LogFile.Write("Update of the CellServDB file on host %s %s.\r\n", statusItemP->fsDbHost, statusItemP->status ? "failed" : "succeeded");
1701
1702         cfg_CellServDbStatusDeallocate(statusItemP, &m_nStatus);
1703         // We don't care if this fails
1704 }
1705
1706 static BOOL UpdateCellServDB(BOOL bAdding)
1707 {
1708         ASSERT(g_hServer);
1709         ASSERT(g_CfgData.szCellName[0]);
1710
1711         int nMaxUpdates;                                 
1712
1713         cfg_cellServDbUpdateCallBack_t callBack = (cfg_cellServDbUpdateCallBack_t)DbAddHostCallback;
1714
1715         // Create the event that the callback routine will use to tell us it is finished.
1716         // If we fail to create the event then don't use a callback routine.
1717         m_hCellServDBUpdateEvent = CreateEvent(NULL /* Sec */, FALSE /* Manual Reset */, FALSE /* Initial State */, m_pszCellServDBUpdateEventName);
1718         if (!m_hCellServDBUpdateEvent) {
1719                 // Cause the CHECK_RESULT below to fail
1720                 m_nResult = 0;
1721                 m_nStatus = GetLastError();
1722         }
1723         CHECK_RESULT;
1724
1725         // Create our critical section
1726         InitializeCriticalSection(&m_CritSec);
1727
1728         // Update CellServDB via a SCM if the user specified one
1729         char *pszSCM = 0;
1730         if (g_CfgData.szSysControlMachine[0]) {
1731                 pszSCM = GetSysControlMachineA();
1732                 g_LogFile.Write("We will update CellServDB using Sys Control Machine %s.\r\n", pszSCM);
1733         }
1734
1735         // Update CellServDB on all servers
1736         g_LogFile.Write("Updating CellServDB on all servers in the cell.\r\n");
1737         if (bAdding)    
1738                 m_nResult = cfg_CellServDbAddHost(g_hServer, pszSCM, callBack, (void *)&m_CallBackID, &nMaxUpdates, &m_nStatus);
1739         else
1740                 m_nResult = cfg_CellServDbRemoveHost(g_hServer, pszSCM, callBack, (void *)&m_CallBackID, &nMaxUpdates, &m_nStatus);
1741         CHECK_RESULT;
1742
1743         // Update CellServDB for the client on this machine
1744         g_LogFile.Write("Updating the client's CellServDB.\r\n");
1745         if (bAdding)
1746                 cfg_ClientCellServDbAdd(g_hClient, GetCellNameA(), GetHostnameA(), &m_nStatus);
1747         else
1748                 cfg_ClientCellServDbRemove(g_hClient, GetCellNameA(), GetHostnameA(), &m_nStatus);
1749         CHECK_RESULT;
1750
1751         BOOL bDone = FALSE;
1752
1753         while (!bDone) {
1754                 switch (WaitForSingleObject(m_hCellServDBUpdateEvent, CELLSERVDB_UPDATE_TIMEOUT))
1755                 {
1756                         case WAIT_OBJECT_0:     bDone = TRUE;   // The callback function signalled us that it is done.  
1757                                                                 break;
1758
1759                         case WAIT_TIMEOUT: 
1760                                 // We timed out so see if a server was updated.  If it was then all is cool
1761                                 // and we can keep going (after clearing the server update count flag).
1762                                 EnterCriticalSection(&m_CritSec);
1763
1764                                 if (m_nServerUpdates)
1765                                         m_nServerUpdates = 0;
1766                                 else {
1767                                         // There were no server updates, so we really did timeout
1768                                         TCHAR szMsg[cchRESOURCE];
1769                                         GetString(szMsg, IDS_CELLSERVDB_UPDATE_PROBLEM);
1770                                         _tcscat(m_szCellServDbUpdateErrMsg, szMsg);
1771                                         bDone = TRUE;
1772                                 }
1773                         
1774                                 LeaveCriticalSection(&m_CritSec);
1775
1776                                 break;
1777                         default:
1778                                 // No other return values are valid when waiting on an event object
1779                                 ASSERT(FALSE);
1780                                 break;
1781                 }
1782         }
1783
1784         DeleteCriticalSection(&m_CritSec);
1785
1786         CloseHandle(m_hCellServDBUpdateEvent);
1787
1788         // See if a failure occurred in the callback
1789         CHECK_RESULT;
1790
1791         return TRUE;
1792 }
1793
1794 static BOOL AddToCellServDB()
1795 {
1796         return UpdateCellServDB(TRUE);
1797 }
1798
1799 static BOOL RemoveFromCellServDB()
1800 {
1801         return UpdateCellServDB(FALSE);
1802 }
1803
1804 static BOOL RestartAllDbServers()
1805 {
1806         ASSERT(g_hServer);
1807
1808         if (m_bUnconfiguringLastDBServer)
1809                 return TRUE;
1810
1811         g_LogFile.Write("Restarting all DB servers.\r\n");
1812         m_nResult = cfg_DbServersRestartAll(g_hServer, &m_nStatus);
1813         CHECK_RESULT;
1814
1815         g_LogFile.Write("Waiting for all DB servers to reach quorum.\r\n");
1816         m_nResult = cfg_DbServersWaitForQuorum(g_hServer, QUORUM_WAIT_TIMEOUT, &m_nStatus);
1817         CHECK_RESULT;
1818
1819         m_bDbServersRestarted = TRUE;
1820
1821         return TRUE;
1822 }
1823
1824 static BOOL UnconfigDB()
1825 {
1826         ASSERT(g_hServer);
1827
1828         m_nResult = RemoveFromCellServDB();
1829         CHECK_RESULT;
1830
1831         m_nResult = cfg_DbServersStop(g_hServer, &m_nStatus);
1832         CHECK_RESULT;
1833
1834         return TRUE;
1835 }
1836
1837 static BOOL UnconfigBak()
1838 {
1839         ASSERT(g_hServer);
1840         
1841         int nResult = cfg_DbServersStopAllBackup(g_hServer, &m_nStatus);
1842         CHECK_RESULT;
1843
1844         return TRUE;
1845 }
1846
1847 static BOOL UnconfigFS()
1848 {
1849         ASSERT(g_hServer);
1850
1851         m_nResult = cfg_FileServerStop(g_hServer, &m_nStatus);
1852         CHECK_RESULT;
1853
1854         return TRUE;
1855 }
1856
1857 static BOOL UnconfigSCS()
1858 {
1859         ASSERT(g_hServer);
1860
1861         m_nResult = cfg_UpdateServerStop(g_hServer, &m_nStatus);
1862         CHECK_RESULT;
1863
1864     // Since we are no longer the SCS machine, we better null this guy.
1865     g_CfgData.szSysControlMachine[0] = 0;
1866
1867         return TRUE;
1868 }
1869
1870 static BOOL UnconfigSCC()
1871 {
1872         ASSERT(g_hServer);
1873
1874         m_nResult = cfg_UpdateClientStop(g_hServer, cfg_upclientSysBosSuffix, &m_nStatus);
1875         CHECK_RESULT;
1876
1877         return TRUE;
1878 }
1879
1880 static BOOL AllServicesUnconfigured()
1881 {
1882 #define NOTCONFIGURED(x)        (((x) == CS_UNCONFIGURE) || ((x) == CS_NULL))
1883
1884         return (NOTCONFIGURED(g_CfgData.configFS) &&
1885                         NOTCONFIGURED(g_CfgData.configDB) &&
1886                         NOTCONFIGURED(g_CfgData.configBak) &&
1887                         NOTCONFIGURED(g_CfgData.configSCS) &&
1888                         NOTCONFIGURED(g_CfgData.configSCC));
1889 }
1890
1891 static BOOL PostConfig()
1892 {
1893         ASSERT(g_hServer);
1894
1895         short isStarted = 0, bosProcsRunning = 0;
1896
1897         if (!AllServicesUnconfigured())
1898                 return TRUE;
1899
1900     // If there is now no cell, then stop the client
1901     if (m_bUnconfiguringLastDBServer) {
1902         ShowCurrentStep(IDS_STOP_CLIENT_STEP);
1903         StopClient();
1904         m_bMustExit = TRUE;
1905     }
1906
1907     // Ask user if the config info should be invalided
1908         g_LogFile.Write("No services are configured so we will ask the user if they want to invalidate the server config info.\r\n");
1909         int nChoice = Message(MB_OK | MB_YESNO, GetAppTitleID(), IDS_INVALIDATE_CFG_INFO);
1910         if (nChoice == IDNO) {
1911                 g_LogFile.Write("User has chosen NOT to invalidate the server config info.\r\n");
1912                 return TRUE;
1913         }
1914
1915         ShowCurrentStep(IDS_INVALIDATE_CONFIG_INFO_STEP);
1916         
1917     g_LogFile.Write("User has chosen to invalidate the server config info.\r\n");
1918
1919         g_LogFile.Write("Stopping the bos server.\r\n");
1920         m_nResult = cfg_BosServerStop(g_hServer, BOSSERVER_STOP_TIMEOUT, &m_nStatus);
1921         CHECK_RESULT;
1922
1923         g_LogFile.Write("Invalidating the config info.\r\n");
1924         m_nResult = cfg_HostInvalidate(g_hServer, &m_nStatus);
1925         CHECK_RESULT;
1926
1927         m_bCfgInfoInvalidated = TRUE;
1928
1929     m_bMustExit = TRUE;
1930
1931         return TRUE;
1932 }
1933
1934 static void VosCloseServer()
1935 {
1936         if (m_hvosServer) {
1937                 g_LogFile.Write("Closing the connection to this server.\r\n");
1938                 vos_ServerClose(m_hvosServer, &m_nStatus);
1939                 m_hvosServer = 0;
1940         }
1941 }
1942
1943 static void ShowConfigControls(BOOL bShow)
1944 {
1945         ShowWnd(m_hDlg, IDC_CURRENT_STEP_LABEL, bShow);
1946         ShowWnd(m_hDlg, IDC_CURRENT_STEP, bShow);
1947         ShowWnd(m_hDlg, IDC_CONFIG_PROGRESS, bShow);
1948         ShowWnd(m_hDlg, IDC_PERCENT_COMPLETE_LABEL, bShow);
1949         ShowWnd(m_hDlg, IDC_PERCENT_COMPLETE, bShow);
1950         ShowWnd(m_hDlg, IDC_STATUS_MSG, !bShow);
1951 }
1952
1953 static void UpdateConfigProgress(int nStepNum)
1954 {
1955         // Update the progress bar
1956         SendDlgItemMessage(m_hDlg, IDC_CONFIG_PROGRESS, PBM_STEPIT, 0, 0);
1957                         
1958         // Update the percent complete
1959         TCHAR buf[16];
1960         _stprintf(buf, TEXT("%2d%%"), nStepNum * 100 / m_nNumSteps);
1961
1962         SetWndText(m_hDlg, IDC_PERCENT_COMPLETE, buf);
1963         ForceUpdateWindow(m_hDlg, IDC_PERCENT_COMPLETE);
1964 }
1965
1966 static void ViewLog()
1967 {
1968         char szCmdLine[MAX_PATH];
1969
1970         if (_access(g_LogFile.GetPath(), 0) != 0) {
1971                 ShowError(m_hDlg, 0, IDS_ERROR_NO_LOG_FILE);
1972                 return;
1973         }
1974
1975         sprintf(szCmdLine, "notepad.exe %s", g_LogFile.GetPath());
1976         
1977         UINT result = WinExec(szCmdLine, SW_SHOW);
1978         if (result < 32)
1979                 ShowError(m_hDlg, result, IDS_VIEW_LOG_ERROR);
1980 }
1981
1982 static void AssignFailure(int nCurStep, int nLastMainStep)
1983 {
1984         // A config step has failed, so we will first set its state to
1985         // failure.  If the step does not have a place on the dialog
1986         // the we must find some other control to show the red X that
1987         // indicates failure.
1988
1989         m_ConfigSteps[nCurStep].eState = SS_STEP_FAILED;
1990
1991         // Is the step displayed on the dialog?
1992         if (m_ConfigSteps[nCurStep].nDescID != 0)
1993                 return;
1994
1995         // It isn't so find nearest one that is
1996         for (int ii = nCurStep + 1; ii < m_nNumSteps; ii++) {
1997                 CONFIG_STEP& step = m_ConfigSteps[ii];
1998                 if (step.nDescID != 0) {
1999                         step.eState = SS_STEP_FAILED;
2000                         IF_WIZ(ForceUpdateWindow(m_hDlg, step.nGraphicCtrlID));
2001                         return;
2002                 }
2003         }
2004
2005         // There is no step on the dialog that is after us, so
2006         // use the last one that is there.
2007         m_ConfigSteps[nLastMainStep].eState = SS_STEP_FAILED;
2008         IF_WIZ(ForceUpdateWindow(m_hDlg, m_ConfigSteps[nLastMainStep].nGraphicCtrlID));
2009 }
2010
2011 static int GetLastMainStep()
2012 {
2013         for (int ii = m_nNumSteps - 1; ii >= 0; ii--) {
2014                 if (m_ConfigSteps[ii].nDescID != 0)
2015                         return ii;
2016         }
2017
2018         ASSERT(FALSE);  // This should never happen!
2019
2020         return 0;
2021 }
2022
2023 static void ShowConfigFailedMsg()
2024 {
2025         LPTSTR pszMsg = FormatString(IDS_CONFIG_FAILED, TEXT("%hs%hs"), LOG_FILE_NAME, AFSDIR_SERVER_LOGS_DIRPATH);
2026
2027         SetWndText(m_hDlg, IDC_STATUS_MSG, pszMsg);
2028
2029         FreeString(pszMsg);
2030 }
2031
2032 static void ShowViewLogButton()
2033 {
2034         if (g_pWiz)
2035                 g_pWiz->SetButtonText(IDNEXT, IDS_VIEW_LOG);
2036         else {
2037                 ShowAndEnable(m_hDlg, IDNEXT);
2038                 MoveWnd(m_hDlg, IDCANCEL, -45, 0);
2039         // I had to add the code below because of a problem, where if config failed, the
2040         // error message dialog would display over our window, and when our window next got
2041         // fully displayed, the buttons would be misdrawn.
2042         ForceUpdateWindow(m_hDlg, IDNEXT);
2043         ForceUpdateWindow(m_hDlg, IDCANCEL);
2044         }
2045 }
2046
2047 static DWORD WINAPI ConfigServer(LPVOID param)
2048 {
2049         HWND hLogo;
2050         
2051         if (!g_pWiz) {
2052                 hLogo = GetDlgItem(m_hDlg, IDC_LOGO);
2053                 AfsAppLib_StartAnimation(hLogo);
2054         }
2055
2056         // Disable all buttons (doesn't affect the Cancel button)
2057         IF_WIZ(g_pWiz->EnableButtons(0));
2058
2059         g_LogFile.Write("Configuring server\r\n");
2060
2061         m_bConfiguring = TRUE;
2062         m_bConfigured = FALSE;
2063         m_bDbServersRestarted = FALSE;
2064
2065         // Hide the message window and show the config controls
2066         ShowConfigControls();
2067
2068         InitProgressBar();
2069
2070         BOOL bResult = TRUE;
2071         int nStepNum = 0;
2072
2073     int nLastMainStep;
2074         IF_WIZ(nLastMainStep = GetLastMainStep());
2075         
2076         // Loop over each config step performing the ones that are enabled.
2077         for (int nCurStep = 0; (nCurStep < m_nNumSteps) && bResult; nCurStep++) {
2078                 CONFIG_STEP& step = m_ConfigSteps[nCurStep];
2079
2080                 nStepNum++;
2081
2082                 // Show this step's status message
2083                 ShowCurrentStep(step.nMsgID);
2084
2085                 step.eState = SS_STEP_IN_PROGRESS;
2086
2087                 // If this is a displayed step, then update its display
2088                 if (step.nGraphicCtrlID)
2089                         IF_WIZ(ForceUpdateWindow(m_hDlg, step.nGraphicCtrlID));
2090
2091                 if (CheckCancel())
2092                         break;
2093
2094                 // Perform the config function
2095                 bResult = step.pFunc();
2096
2097                 if (bResult) {
2098             if (g_pWiz) {
2099                         // Changing a step's state changes what picture is shown on the dialog
2100                         // (if that state is displayed on the dialog).
2101                         
2102                         // If this is the last displayed step, then don't change its state
2103                         // because there may still be more steps to perform.  We want to use
2104                         // the last step's picture to indicate the final config state.
2105                         
2106                         // If not last step, then change state
2107                         if (nCurStep != nLastMainStep)
2108                                 step.eState = SS_STEP_FINISHED;
2109     
2110                         // If last step then go ahead and show the state - but do it on the last
2111                         // step displayed on the dialog (nLastMainStep).
2112                         if (nCurStep == m_nNumSteps - 1) {
2113                                 m_ConfigSteps[nLastMainStep].eState = SS_STEP_FINISHED;
2114                                 // Do the next line so ForceUpdateWindow below will redraw the
2115                                 // correct control
2116                                 step = m_ConfigSteps[nLastMainStep];
2117                         }
2118             }
2119
2120                         UpdateConfigProgress(nStepNum);
2121                 } else {
2122                         // Show the 'X' error marker on the next unprocessed step, or on the
2123                         // last step if there are no more.
2124                         IF_WIZ(AssignFailure(nCurStep, nLastMainStep));
2125                         step.eState = SS_STEP_FAILED;
2126                 }
2127
2128                 if (step.nGraphicCtrlID)
2129                         IF_WIZ(ForceUpdateWindow(m_hDlg, step.nGraphicCtrlID));
2130         }
2131
2132         // Close m_hvosServer if it is open
2133         VosCloseServer();
2134
2135         // Hide the config controls and show the message window
2136         ShowConfigControls(FALSE);
2137
2138         // Did we succeed?
2139         if (CheckCancel()) {
2140                 g_LogFile.Write("User has canceled configuration.\r\n");
2141                 SetWndText(m_hDlg, IDC_STATUS_MSG, IDS_CONFIG_CANCELED);
2142                 IF_WIZ(g_pWiz->EnableButtons(BACK_BUTTON));
2143                 if (!g_pWiz)
2144                         ShowExitButton();
2145         } else if (bResult) {
2146                 g_LogFile.Write("Configuration succeeded.\r\n");
2147                 if (g_CfgData.bFirstServer)
2148                         SetWndText(m_hDlg, IDC_STATUS_MSG, IDS_CONFIG_SUCCEEDED_FIRST_SERVER);
2149                 else if (m_bDbServersRestarted)
2150                         SetWndText(m_hDlg, IDC_STATUS_MSG, IDS_CONFIG_SUCCEEDED_NEED_CELLSERVDB_UPDATE);
2151                 else
2152                         SetWndText(m_hDlg, IDC_STATUS_MSG, IDS_CONFIG_SUCCEEDED);
2153                 IF_WIZ(g_pWiz->EnableButtons(NEXT_BUTTON));
2154                 ShowExitButton();
2155                 m_bConfigured = TRUE;
2156                 
2157                 // Disable cancel button
2158                 IF_WIZ(SetEnable(g_pWiz->GetWindow(), IDCANCEL, ES_DISABLE));
2159         } else {
2160                 g_LogFile.Write("Configuration has failed.\r\n");
2161                 ShowConfigFailedMsg();
2162                 // Prevent partial configuration
2163                 if (!g_CfgData.bValidServerInfo)
2164                         cfg_HostInvalidate(g_hServer, &m_nStatus);
2165                 IF_WIZ(g_pWiz->EnableButtons(NEXT_BUTTON));
2166                 ShowViewLogButton();
2167                 m_bConfigFailed = TRUE;
2168         }
2169
2170         if (!g_pWiz)
2171                 AfsAppLib_StopAnimation(hLogo);
2172
2173         IF_WIZ(g_pWiz->SetDefaultControl(IDNEXT));
2174
2175         m_bConfiguring = FALSE;
2176
2177     // Show the user any CellServDB update errors
2178     if (m_bCellServDbUpdateErr) {
2179                 TCHAR szTitle[cchRESOURCE], szMsg[cchRESOURCE + MAX_PATH];
2180                 GetString(szTitle, GetAppTitleID());
2181                 GetString(szMsg, IDS_CELLSERVDB_UPDATE_ERRORS_ARE_IN_LOG_FILE);
2182                 lstrcat(szMsg, A2S(g_LogFile.GetPath()));
2183                 lstrcat(szMsg, TEXT("."));
2184                 MessageBox(m_hDlg, szMsg, szTitle, MB_OK | MB_ICONEXCLAMATION);
2185         }
2186
2187     // Warn the user if we are going to force the config manager to exit
2188         if (m_bMustExit) {
2189         if (m_bUnconfiguringLastDBServer)    
2190                 MsgBox(m_hDlg, IDS_CELL_IS_GONE_MUST_EXIT, GetAppTitleID(), MB_OK | MB_ICONSTOP);
2191         else
2192                     MsgBox(m_hDlg, IDS_CONFIG_INFO_INVALIDATED, GetAppTitleID(), MB_OK);
2193         }
2194
2195     // Unmap the root afs drive if we had one
2196     if (m_bRootAfsDriveMappingCreated) {
2197         // Let's not bother the user if this fails; we'll just log it.
2198         g_LogFile.Write("Attempting to remove the drive mapping to root.afs: ");
2199         BOOL bCanceled = WNetCancelConnection(A2S(m_szDriveToMapTo), TRUE) == NO_ERROR;
2200         g_LogFile.Write(bCanceled ? "succeeded.\r\n" : "failed.\r\n");
2201         m_bRootAfsDriveMappingCreated = !bCanceled;
2202     }
2203
2204         return 0;
2205 }
2206