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