d74810043cb1cbe37eb7637a5c0d21eff35fd6a5
[openafs.git] / src / WINNT / afssvrcfg / afscfg.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
15 #include <winsock2.h>
16 #include <ws2tcpip.h>
17
18 extern "C" {
19 #include <afs/param.h>
20 #include <afs/stds.h>
21 }
22
23 #include "afscfg.h"
24 #include "resource.h"
25 #include "graphics.h"
26 #include <string.h>
27 #include "get_cur_config.h"
28 #include "partition_utils.h"
29 extern "C" {
30 #include <afs\dirpath.h>
31 #include <afs\afs_clientAdmin.h>
32 }
33 #include <lanahelper.h>
34
35
36 /*
37  * DEFINITIONS _________________________________________________________________
38  *
39  */
40
41
42 /*
43  * PROTOTYPES _________________________________________________________________
44  *
45  */
46 static void SetConfigDefaults();
47 static void RunCfgTool();
48 static void RunWizard();
49 static void CloseLibHandles(BOOL bExiting = FALSE);
50
51 void RegisterWizardHelp();
52 void RegisterConfigToolHelp();
53
54 // These are the prototypes for the dialog procs of each wizard page.
55 BOOL CALLBACK IntroPageDlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp);
56 BOOL CALLBACK InfoPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
57 BOOL CALLBACK InfoPage2DlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp);
58 BOOL CALLBACK FileServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
59 BOOL CALLBACK DBServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
60 BOOL CALLBACK BackupPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
61 BOOL CALLBACK PartitionPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
62 BOOL CALLBACK RootAfsPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
63 BOOL CALLBACK ReplicationPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
64 BOOL CALLBACK SysControlPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
65 BOOL CALLBACK ConfigServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
66
67
68 static WIZARD_STATE g_aStates[] = {
69     { sidSTEP_ONE,      IDD_INTRO_PAGE,                 (DLGPROC)IntroPageDlgProc, 0 },
70     { sidSTEP_TWO,      IDD_INFO_PAGE,                  (DLGPROC)InfoPageDlgProc, 0 },
71     { sidSTEP_THREE,    IDD_INFO_PAGE2_FIRST_SERVER,    (DLGPROC)InfoPage2DlgProc, 0 },
72     { sidSTEP_FOUR,     IDD_INFO_PAGE2_NOT_FIRST_SERVER,(DLGPROC)InfoPage2DlgProc, 0 },
73     { sidSTEP_FIVE,     IDD_FILE_SERVER_PAGE,           (DLGPROC)FileServerPageDlgProc, 0 },
74     { sidSTEP_SIX,      IDD_DB_SERVER_PAGE,             (DLGPROC)DBServerPageDlgProc, 0 },
75     { sidSTEP_SEVEN,    IDD_BACKUP_SERVER_PAGE,         (DLGPROC)BackupPageDlgProc, 0 },
76     { sidSTEP_EIGHT,    IDD_PARTITION_PAGE,             (DLGPROC)PartitionPageDlgProc, 0 },
77     { sidSTEP_NINE,     IDD_ROOT_VOLUMES_PAGE,          (DLGPROC)RootAfsPageDlgProc, 0 },
78     { sidSTEP_TEN,      IDD_REPLICATION_PAGE,           (DLGPROC)ReplicationPageDlgProc, 0 },
79     { sidSTEP_ELEVEN,   IDD_SYS_CONTROL_PAGE,           (DLGPROC)SysControlPageDlgProc, 0 },
80     { sidSTEP_TWELVE,   IDD_CONFIG_SERVER_PAGE,         (DLGPROC)ConfigServerPageDlgProc, 0 }
81 };
82
83 size_t g_nNumStates = sizeof(g_aStates) / sizeof(g_aStates[0]);
84
85
86 // Res ID's of text descriptions of each state
87 UINT g_StateDesc[] = {
88         IDS_INTRO_PAGE,
89         IDS_INFO_PAGE,
90         IDS_INFO_PAGE2,
91         IDS_INFO_PAGE2,
92         IDS_FS_PAGE,
93         IDS_DB_PAGE,
94         IDS_BK_PAGE,
95         IDS_PARTITION_PAGE,
96         IDS_ROOT_AFS_PAGE,
97         IDS_REP_PAGE,
98         IDS_SC_PAGE,
99         IDS_CONFIG_PAGE
100 };
101
102
103
104 /*
105  * EXPORTED VARIABLES _________________________________________________________________
106  *
107  */
108 LPWIZARD g_pWiz = NULL;
109 LPPROPSHEET g_pSheet = NULL;
110
111 CFG_DATA g_CfgData;
112
113 void *g_hToken = 0;
114 void *g_hCell = 0;
115 void *g_hClient = 0;
116 void *g_hServer = 0;
117
118 static void *hClientCell = 0;
119
120 LOGFILE g_LogFile;
121
122
123
124 /*
125  * EXPORTED FUNCTIONS _________________________________________________________________
126  *
127  */
128 extern "C" int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR pszCmdLineA, int nCmdShow)
129 {
130     afs_status_t nStatus;
131
132     TaLocale_LoadCorrespondingModule (hInst);
133
134     // Tell the applib our application's name
135     TCHAR szAppName[cchRESOURCE];
136     AfsAppLib_SetAppName(GetResString(GetAppTitleID(), szAppName));
137
138     // Open the admin libraries
139     int nResult = afsclient_Init(&nStatus);
140     if (!nResult) {
141         ShowError(0, nStatus, IDS_CANT_INIT_ADMIN_LIBS);
142         return 0;
143     }
144
145     // Open the log file
146     char szLogPath[MAX_PATH];
147     sprintf(szLogPath, "%s\\%s", AFSDIR_SERVER_LOGS_DIRPATH, LOG_FILE_NAME);
148     if (!g_LogFile.Open(szLogPath))
149         ShowError(0, 0, IDS_CANT_OPEN_LOG_FILE);
150
151     // Register the help file with the applib's help system
152     AfsAppLib_RegisterHelpFile(TEXT("TaAfsCfg.hlp"));
153
154     /* Start up sockets */
155     WSADATA WSAjunk;
156     WSAStartup(0x0101, &WSAjunk);
157
158     memset(&g_CfgData, 0, sizeof(CFG_DATA));
159
160     // Get this machine's local name
161     char szLocalName[sizeof(g_CfgData.szLocalName) / sizeof(TCHAR)];
162     if (gethostname(szLocalName, sizeof(szLocalName)) != 0) {
163         ShowError(GetFocus(), WSAGetLastError(), IDS_ERROR_LOCAL_HOST_NAME);
164         return 0;
165     }
166     CopyAnsiToString(g_CfgData.szLocalName, szLocalName);
167
168     // Turn the local name into a host name
169     struct hostent *pHostEnt = gethostbyname(szLocalName);
170     if (!pHostEnt) {
171         ShowError(GetFocus(), WSAGetLastError(), IDS_ERROR_HOST_NAME);
172         return 0;
173     }
174     CopyAnsiToString(g_CfgData.szHostname, pHostEnt->h_name, sizeof(g_CfgData.szHostname));
175
176     RegisterFastListClass();
177
178     // Get current config status
179     BOOL bCanceled = FALSE;
180     DWORD dwStatus = GetCurrentConfig(NULL, bCanceled);
181     if (dwStatus || bCanceled) {
182         if (!bCanceled)
183             ShowError(GetFocus(), dwStatus, IDS_CONFIG_CHECK_FAILED);
184         return 0;
185     }
186
187     // Run the appropriate interface
188     if ((strstr(_strlwr(pszCmdLineA), "wizard") != 0))
189         RunWizard();
190     else
191         RunCfgTool();
192
193     FreePartitionTable();
194
195     // Disconnect from the config and admin libraries
196     CloseLibHandles(TRUE);
197
198     return 0;
199 }
200
201 /*
202  *      This is a dialog proc that is shared by all of the wizard pages.  It
203  *      handles things that are common to all of them.  Each page also has its
204  *      own dialog proc.
205  */
206 BOOL CALLBACK WizStep_Common_DlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp)
207 {
208     // Get the dialog's resource ID
209     int nIDD = GetWindowLong(hRHS, GWL_ID);
210         
211     if (AfsAppLib_HandleHelp(nIDD, hRHS, msg, wp, lp))
212         return TRUE;
213         
214     switch (msg) {
215     case WM_INITDIALOG: MakeBold(hRHS, IDC_TITLE);
216         RedrawGraphic();
217         break;
218
219     case WM_COMMAND:
220         switch (LOWORD(wp)) {
221         case IDCANCEL:
222             QueryCancelWiz();
223             return TRUE;
224         }
225         break;
226     }
227
228     return FALSE;
229 }
230
231 BOOL QueryCancelWiz()
232 {
233     int nChoice = Message(MB_YESNO, IDS_WIZARD_APP_TITLE, IDS_CANCEL_DESC);
234     if (nChoice == IDYES) {
235         g_LogFile.Write("User has chosen to cancel the program.\r\n");
236         if (g_pWiz)
237             g_pWiz->Show(FALSE);
238         return TRUE;
239     }
240
241     return FALSE;
242 }
243
244
245 /*
246  *  Accessor functions for the g_CfgData variable.  There are versions
247  *  for both TCHARs and char *'s.
248  */
249 TCHAR GetDeviceName()           { return g_CfgData.chDeviceName; }
250 LPTSTR GetPartitionName()       { return g_CfgData.szPartitionName; }
251 LPTSTR GetHostName()            { return g_CfgData.szHostname; }
252 LPTSTR GetSysControlMachine()   { return g_CfgData.szSysControlMachine; }
253 LPTSTR GetCellName()            { return g_CfgData.szCellName; }
254 LPTSTR GetServerPW()            { return g_CfgData.szServerPW; }
255 LPTSTR GetAdminName()           { return g_CfgData.szAdminName; }
256 LPTSTR GetAdminPW()             { return g_CfgData.szAdminPW; }
257 LPTSTR GetAdminUID()            { return g_CfgData.szAdminUID; }
258 LPTSTR GetLocalName()           { return g_CfgData.szLocalName; }
259 LPTSTR GetHostname()            { return g_CfgData.szHostname; }
260 LPTSTR GetCellServDbHostname()  { return g_CfgData.szCellServDbHostname; }
261 LPTSTR GetClientCellName()      { return g_CfgData.szClientCellName; }
262 LPTSTR GetSalvageLogFileName()  { return g_CfgData.szSalvageLogFileName; }
263
264 LPTSTR GetClientNetbiosName()
265 {
266     static TCHAR szValue[MAX_MACHINE_NAME_LEN + 1] = "";
267
268     if ( szValue[0] == 0 )
269         CopyAnsiToString(GetClientNetbiosNameA(), szValue);
270
271     return szValue;
272 }
273
274 char GetDeviceNameA()
275 {
276     static char szValueA[2];
277
278     TCHAR devName[2] = TEXT("X");
279     devName[0] = g_CfgData.chDeviceName;
280
281     CopyStringToAnsi(szValueA, devName);
282
283     return szValueA[0];
284 }
285
286 char *GetPartitionNameA()
287 {
288     static char szValueA[MAX_PARTITION_NAME_LEN + 1];
289
290     CopyStringToAnsi(szValueA, g_CfgData.szPartitionName);
291
292     return szValueA;
293 }
294
295 char *GetSysControlMachineA()
296 {
297     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
298
299     CopyStringToAnsi(szValueA, g_CfgData.szSysControlMachine);
300
301     return szValueA;
302 }
303
304 char *GetCellNameA()
305 {
306     static char szValueA[MAX_CELL_NAME_LEN + 1];
307
308     CopyStringToAnsi(szValueA, g_CfgData.szCellName);
309
310     return szValueA;
311 }
312
313 char *GetServerPWA()
314 {
315     static char szValueA[MAX_SERVER_PW_LEN + 1];
316
317     CopyStringToAnsi(szValueA, g_CfgData.szServerPW);
318
319     return szValueA;
320 }
321
322 char *GetAdminNameA()
323 {
324     static char szValueA[MAX_ADMIN_NAME_LEN + 1];
325
326     CopyStringToAnsi(szValueA, g_CfgData.szAdminName);
327
328     return szValueA;
329 }
330
331 char *GetAdminPWA()
332 {
333     static char szValueA[MAX_ADMIN_PW_LEN + 1];
334
335     CopyStringToAnsi(szValueA, g_CfgData.szAdminPW);
336
337     return szValueA;
338 }
339
340 char *GetAdminUIDA()
341 {
342     static char szValueA[MAX_UID_LEN + 1];
343
344     CopyStringToAnsi(szValueA, g_CfgData.szAdminUID);
345
346     return szValueA;
347 }
348
349 char *GetLocalNameA()
350 {
351     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
352
353     CopyStringToAnsi(szValueA, g_CfgData.szLocalName);
354
355     return szValueA;
356 }
357
358 char *GetHostnameA()
359 {
360     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
361
362     CopyStringToAnsi(szValueA, g_CfgData.szHostname);
363
364     return szValueA;
365 }
366
367 char *GetCellServDbHostnameA()
368 {
369     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
370
371     CopyStringToAnsi(szValueA, g_CfgData.szCellServDbHostname);
372
373     return szValueA;
374 }
375
376 char *GetClientCellNameA()
377 {
378     static char szValueA[MAX_CELL_NAME_LEN + 1];
379
380     CopyStringToAnsi(szValueA, g_CfgData.szClientCellName);
381
382     return szValueA;
383 }
384
385 char *GetClientNetbiosNameA()
386 {
387     static char szValueA[MAX_MACHINE_NAME_LEN + 1]="";
388         
389     if ( szValueA[0] == 0 )
390         lana_GetNetbiosName(szValueA, LANA_NETBIOS_NAME_FULL);
391
392     return szValueA;
393 }
394
395 char *GetSalvageLogFileNameA()
396 {
397     static char szValueA[_MAX_PATH];
398
399     CopyStringToAnsi(szValueA, g_CfgData.szSalvageLogFileName);
400
401     return szValueA;
402 }
403
404
405 BOOL GetLibHandles(afs_status_t *pStatus)
406 {
407     ASSERT(g_CfgData.szHostname[0]);
408
409     int nResult;
410         
411     // ************************* NOTE ********************************
412     // * You MUST have already determined whether or not the host
413     // * and client config info is valid before calling this function.
414     // ***************************************************************
415
416     // This function can be called at any time to get handles to the cell and
417     // the config library.  It will try to get the most powerful handle to the
418     // cell that it can, and then use that to open the config library.  If the
419     // libraries are already open, it will close them first.  Two handles to
420     // the config library will be opened, one to the server to be configured,
421     // and one to the client machine we are configuring from.  
422
423     // There are two types of cell handles, NULL and Standard.  A null handle
424     // can make calls to any server except DB servers.  We need this primarily
425     // to talk to the bos server to determine the machine's current configuration,
426     // and to configure the client information if it is not already.  A standard 
427     // handle can talk to any server.  Standard handles can be either authenticated 
428     // or unauthenticated.
429
430     // Close all current handles
431     CloseLibHandles();
432
433     g_LogFile.Write("Getting handles to the cell and the config library.\r\n");
434
435     if (!hClientCell) {
436         // Start by getting a null cell handle and using it to open the client
437         // connection to the config library.
438         g_LogFile.Write("Opening a NULL cell handle to use with the client config library handle.\r\n");
439         nResult = afsclient_NullCellOpen(&hClientCell, pStatus);
440         if (!nResult) {
441             g_LogFile.Write("Failed to open a NULL cell handle: %lx.\r\n", (unsigned long)*pStatus);
442                 return FALSE;
443         }
444     
445         // Get the client handle.  We never need a better handle than this
446         // for the client, and so this handle will never be upgraded.
447         g_LogFile.Write("Getting config handle for the client.\r\n");
448         if (!cfg_HostOpen(hClientCell, GetHostnameA(), &g_hClient, pStatus)) {
449             g_LogFile.Write("Failed to get config handle:  %lx.\r\n", (unsigned long)*pStatus);
450                 return FALSE;
451         }
452     }
453
454     // Now we need to get the most powerful cell handle that we can and use it
455     // to open the config library for the server.
456
457     // If the client info is valid and we know what cell the server should be in,
458     // and the client has that cell in its CellServDB, then we can get a Standard cell
459     // handle.  However there is an exception: if this is the first server in the 
460     // cell then there may not yet be an authentication server to talk to.  In that
461     // case we use a null cell handle.
462     if (g_CfgData.bValidClientInfo &&   // Client config is valid
463         g_CfgData.szCellName[0] &&      // We have a cell name
464         (!g_CfgData.bFirstServer || g_CfgData.bAuthServerRunning))  // Auth server is running
465     {
466         g_LogFile.Write("Opening a non-NULL cell handle to use with the server config library handle.\r\n");
467
468         // Do we have the user info necessary to authenticate?
469         BOOL bHaveUserInfo = g_CfgData.szAdminName[0] && g_CfgData.szAdminPW[0];
470
471         // Open a standard cell handle.  szAdminName and szAdminPW will either be NULL, or
472         // if they have been entered by the user, will be the admin name and password strings.
473         if ((!g_CfgData.bFirstServer || g_CfgData.bAdminPrincipalCreated) && bHaveUserInfo) {
474             g_LogFile.Write("Getting tokens in cell %s for user '%s'.\r\n", GetCellNameA(), GetAdminNameA());
475             nResult = afsclient_TokenGetNew(GetCellNameA(), GetAdminNameA(), GetAdminPWA(), &g_hToken, pStatus);
476         } else {
477             g_LogFile.Write("Getting unauthenticated tokens for cell '%s'.\r\n", GetCellNameA());
478             nResult = afsclient_TokenGetNew(GetCellNameA(), "", "", &g_hToken, pStatus);
479         }
480
481         if (!nResult) {
482             g_LogFile.Write("Failed to get tokens for the specified cell:  %lx.\r\n", (unsigned long)*pStatus);
483             return FALSE;
484         }
485
486         // If the admin name and password are NULL, then this will be an unauthenticated
487         // connection to the cell.
488         g_LogFile.Write("Getting cell handle for cell %s.\r\n", GetCellNameA());
489         nResult = afsclient_CellOpen(GetCellNameA(), g_hToken, &g_hCell, pStatus);
490         if (!nResult) {
491             g_LogFile.Write("Failed to open the cell:  %lx.\r\n", (unsigned long)*pStatus);
492             return FALSE;
493         }
494     } else {
495         g_LogFile.Write("Opening a NULL cell handle to use with the server config library handle.\r\n");
496         nResult = afsclient_NullCellOpen(&g_hCell, pStatus);
497         if (!nResult) {
498             g_LogFile.Write("Failed to open a NULL cell handle:  %lx.\r\n", (unsigned long)*pStatus);
499             return FALSE;
500         }       
501     }   
502
503     // Get the server handle
504     g_LogFile.Write("Getting config library handle for the server.\r\n");
505     if (!cfg_HostOpen(g_hCell, GetHostnameA(), &g_hServer, pStatus)) {
506         g_LogFile.Write("Failed to get config library handle for the server:  %lx.\r\n", (unsigned long)*pStatus);
507         return FALSE;
508     }
509
510     return TRUE;
511 }
512
513 BOOL GetHandles(HWND hParentDlg)
514 {
515     afs_status_t nStatus;
516
517     if (!GetLibHandles(&nStatus)) {
518         ShowError(hParentDlg, nStatus, IDS_GET_TOKENS_ERROR);
519         g_CfgData.szAdminPW[0] = 0;
520         return FALSE;
521     }
522
523     return TRUE;
524 }
525
526
527 /*
528  * Static FUNCTIONS _________________________________________________________________
529  *
530  */
531 static void CloseLibHandles(BOOL bExiting)
532 {
533     afs_status_t nStatus;
534
535     // We will close them in the reverse order of their creation.
536     if (g_hServer) {
537         cfg_HostClose(g_hServer, &nStatus);
538         g_hServer = 0;
539     }
540
541     if (g_hCell) {
542         afsclient_CellClose(g_hCell, &nStatus);
543         g_hCell = 0;
544     }
545
546     if (g_hToken) {
547         afsclient_TokenClose(g_hToken, &nStatus);
548         g_hToken = 0;
549     }
550
551     // Only close the client cfg and cell handles if we are exiting.
552     if (bExiting) {
553         if (g_hClient) {
554             cfg_HostClose(g_hClient, &nStatus);
555             g_hClient = 0;
556         }       
557
558         if (hClientCell) {
559             afsclient_CellClose(hClientCell, &nStatus);
560             hClientCell = 0;
561         }
562     }
563 }
564
565 static void SetConfigDefaults()
566 {
567     if (g_CfgData.bWizard) {
568         if (g_CfgData.configFS == CS_NULL)
569             g_CfgData.configFS = CS_CONFIGURE;
570
571         if (g_CfgData.configDB == CS_NULL)
572             g_CfgData.configDB = CS_CONFIGURE;
573
574         if (g_CfgData.configBak == CS_NULL)
575             g_CfgData.configBak = CS_CONFIGURE;
576
577         if (g_CfgData.configPartition == CS_NULL)
578             g_CfgData.configPartition = CS_CONFIGURE;
579
580         if (g_CfgData.configRootVolumes == CS_NULL)
581             g_CfgData.configRootVolumes = CS_CONFIGURE;
582
583         if (g_CfgData.configRep == CS_NULL)
584             g_CfgData.configRep = CS_CONFIGURE;
585
586         if (g_CfgData.configSCS == CS_NULL)
587             g_CfgData.configSCS = CS_DONT_CONFIGURE;
588
589         if (g_CfgData.configSCC == CS_NULL)
590             g_CfgData.configSCC = CS_DONT_CONFIGURE;
591     }   
592
593     lstrcpy(g_CfgData.szAdminName, TEXT("admin"));
594     lstrcpy(g_CfgData.szAdminUID, TEXT("0"));
595
596     g_CfgData.bUseNextUid = TRUE;
597 }       
598
599         
600 // Prototypes for each property page's dialog proc
601 BOOL CALLBACK PartitionsPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
602 BOOL CALLBACK ServicesPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
603
604 static void RunCfgTool()
605 {
606     // If the client info is invalid, then the config tool cannot run.  Inform the user and
607     // ask if they want to run the wizard instead.
608     if (!g_CfgData.bValidClientInfo) {
609         int nChoice = MsgBox(0, IDS_NEED_CLIENT_INFO, GetAppTitleID(), MB_YESNO | MB_ICONSTOP);
610         if (nChoice == IDYES)
611             RunWizard();
612         return;
613     }   
614
615     // If the server info is invalid, then the config tool cannot run.  The Wizard must be run
616     // to initially configure the server.  Inform the user and ask if they want to run the wizard instead.
617     if (!g_CfgData.bValidServerInfo) {
618         int nChoice = MsgBox(0, IDS_NEED_SERVER_INFO, GetAppTitleID(), MB_OKCANCEL | MB_ICONEXCLAMATION);
619         if (nChoice == IDOK)
620             RunWizard();
621         return;
622     }
623
624     g_CfgData.bWizard = FALSE;
625
626     SetConfigDefaults();
627
628     RegisterConfigToolHelp();
629
630     // Create the prop sheet
631     g_pSheet = PropSheet_Create(IDS_CFG_TOOL_APP_TITLE, TRUE);
632
633     // Add the pages
634     PropSheet_AddTab(g_pSheet, IDS_PARTITIONS_PAGE_TITLE, IDD_PARTITIONS_PAGE, (DLGPROC)PartitionsPageDlgProc, 0, TRUE, TRUE);
635     PropSheet_AddTab(g_pSheet, IDS_SERVICES_PAGE_TITLE, IDD_SERVICES_PAGE, (DLGPROC)ServicesPageDlgProc, 0, TRUE);
636
637     // Let the user see it
638     PropSheet_ShowModal(g_pSheet);
639 }       
640
641 static void RunWizard()
642 {
643     g_CfgData.bWizard = TRUE;
644
645     SetConfigDefaults();
646
647     RegisterWizardHelp();
648
649     g_pWiz = new WIZARD;
650     g_pWiz->SetDialogTemplate(IDD_WIZARD, IDC_WIZARD_LEFTPANE, IDC_WIZARD_RIGHTPANE, IDBACK, IDNEXT);
651     g_pWiz->SetGraphic(IDB_GRAPHIC_16, IDB_GRAPHIC_256);
652     g_pWiz->SetStates(g_aStates, g_nNumStates);
653     g_pWiz->SetGraphicCallback(PaintPageGraphic);
654
655     g_pWiz->SetState(sidSTEP_ONE);
656     g_pWiz->Show();
657
658     delete g_pWiz;
659
660     g_pWiz = 0;
661 }       
662