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