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