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