Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

724 lines
18 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <conio.h>
  4. #include <stdlib.h>
  5. #include <ntddsac.h>
  6. #include <emsapi.h>
  7. #include <ASSERT.h>
  8. #include "resource.h"
  9. #define ANNOUNCE_CHANNEL 0
  10. #define DUMPFILE_MESSAGE 1
  11. #define FILLINFILE_MESSAGE 2
  12. #define SUCCESS_MESSAGE 3
  13. #define FAILURE_MESSAGE 4
  14. #define NO_UNATTEND_FILE 5
  15. typedef struct _UserInputParams {
  16. EMSRawChannel* Channel; // headless channel object
  17. HANDLE hInputCompleteEvent; //signals that the user is done.
  18. HANDLE hRemoveUI; //signals that we should abort.
  19. } UserInputParams, *PUserInputParams;
  20. SAC_CHANNEL_OPEN_ATTRIBUTES GlobalChannelAttributes;
  21. BOOL
  22. IsWorkToDo(
  23. )
  24. {
  25. WCHAR PathToUnattendFile[MAX_PATH];
  26. WCHAR Buffer[256];
  27. WIN32_FIND_DATA finddata;
  28. HANDLE hFile;
  29. ExpandEnvironmentStrings(
  30. L"%systemroot%\\system32\\$winnt$.inf",
  31. PathToUnattendFile,
  32. sizeof(PathToUnattendFile)/sizeof(WCHAR));
  33. //
  34. // see if there is an unattend file. If there isn't then we must have
  35. // work to do.
  36. //
  37. if (!(hFile = FindFirstFile(PathToUnattendFile,&finddata))) {
  38. return(TRUE);
  39. }
  40. FindClose(hFile);
  41. //
  42. // Now look at that unattend file and see if it has the
  43. // "unattendmode=fullunattend" flag in it. If it doesn't,
  44. // then we have work to do.
  45. //
  46. if (GetPrivateProfileString(
  47. L"Data",
  48. L"UnattendMode",
  49. L"",
  50. Buffer,
  51. sizeof(Buffer),
  52. PathToUnattendFile) &&
  53. !wcscmp(Buffer,L"FullUnattended")) {
  54. return(FALSE);
  55. }
  56. return(TRUE);
  57. }
  58. BOOL
  59. IsHeadlessPresent(
  60. OUT EMSRawChannel **Channel
  61. )
  62. {
  63. BOOL RetVal;
  64. *Channel = EMSRawChannel::Construct(GlobalChannelAttributes);
  65. RetVal = (*Channel != NULL);
  66. return(RetVal);
  67. }
  68. INT
  69. StartSetupProcess(
  70. IN int argc,
  71. IN WCHAR *argv[]
  72. )
  73. {
  74. BOOL Status;
  75. STARTUPINFO si;
  76. PROCESS_INFORMATION pi;
  77. PWSTR CmdLineArgs;
  78. int i,len;
  79. WCHAR PathToSetup[MAX_PATH];
  80. RtlZeroMemory(&si,sizeof(si));
  81. RtlZeroMemory(&pi,sizeof(pi));
  82. GetStartupInfo(&si);
  83. ExpandEnvironmentStrings( L"%systemroot%\\system32\\setup.exe",
  84. PathToSetup,
  85. sizeof(PathToSetup)/sizeof(WCHAR));
  86. len = 0;
  87. for (i = 1; i< argc; i++) {
  88. len += (wcslen(argv[i])+1)*sizeof(WCHAR);
  89. }
  90. CmdLineArgs = NULL;
  91. if (len) {
  92. CmdLineArgs = (PWSTR)HeapAlloc(GetProcessHeap(),0,len);
  93. if (CmdLineArgs) {
  94. CmdLineArgs[0] = L'\0';
  95. for (i = 1; i< argc; i++) {
  96. wcscat(CmdLineArgs, argv[i]);
  97. wcscat(CmdLineArgs, L"\0");
  98. }
  99. }
  100. }
  101. Status = CreateProcess(
  102. PathToSetup,
  103. CmdLineArgs,
  104. NULL,
  105. NULL,
  106. FALSE,
  107. DETACHED_PROCESS|NORMAL_PRIORITY_CLASS,
  108. NULL,
  109. NULL,
  110. &si,
  111. &pi);
  112. if (Status == TRUE) {
  113. CloseHandle(pi.hThread);
  114. CloseHandle(pi.hProcess);
  115. }
  116. if (CmdLineArgs) {
  117. HeapFree(GetProcessHeap(),0,CmdLineArgs);
  118. }
  119. return( (Status == TRUE)
  120. ? 0
  121. : 1 );
  122. }
  123. BOOL
  124. PromptUserForData(
  125. EMSRawChannel *Channel,
  126. DWORD ResourceId
  127. )
  128. {
  129. PCWSTR Text;
  130. DWORD Len;
  131. switch(ResourceId) {
  132. case ANNOUNCE_CHANNEL:
  133. Text = L"\r\n\r\n\r\nWelcome to GUI-mode setup. It appears that you have not configured the\r\nsetup process to run completely unattended.\r\nWould you like to input an unattend file now?\r\nPress \"Y\" to indicate Yes, \"D\" to indicate yes (also dump the current\r\nunattended file settings), anything else to indicate no.\r\n";
  134. break;
  135. case DUMPFILE_MESSAGE:
  136. Text = L"\r\n\r\nThis is the current contents of the unattended text file.\r\n";
  137. break;
  138. case FILLINFILE_MESSAGE:
  139. Text = L"\r\nInput a new unattend text file, followed by the EOF specifier (<control>+Z)\r\n";
  140. break;
  141. case SUCCESS_MESSAGE:
  142. Text = L"Successfully created an unattend setup file. Setup will now proceeed in an unattended manner.\r\n";
  143. break;
  144. case FAILURE_MESSAGE:
  145. Text = L"\r\nCould not create a valid unattend setup file. Setup will proceeed, but it may\r\nnot be fully unattended.\r\n";
  146. break;
  147. case NO_UNATTEND_FILE:
  148. Text = L"There is currently no unattend file present on the system.\r\n";
  149. break;
  150. default:
  151. Text = L"Unknown Resource ID.\r\n";
  152. assert(FALSE);
  153. break;
  154. }
  155. return(Channel->Write((PCBYTE)Text,wcslen(Text)*sizeof(WCHAR)));
  156. }
  157. BOOL
  158. OpenFile(
  159. LPCTSTR FileName,
  160. DWORD AccessMode,
  161. DWORD CreationFlags,
  162. PHANDLE hFile)
  163. {
  164. WCHAR FullPathToFile[MAX_PATH];
  165. ExpandEnvironmentStrings(
  166. FileName,
  167. FullPathToFile,
  168. sizeof(FullPathToFile)/sizeof(WCHAR));
  169. *hFile = CreateFile(
  170. FullPathToFile,
  171. AccessMode,
  172. FILE_SHARE_READ,
  173. NULL,
  174. CreationFlags,
  175. FILE_ATTRIBUTE_NORMAL,
  176. NULL);
  177. if (*hFile == INVALID_HANDLE_VALUE) {
  178. return(FALSE);
  179. }
  180. return(TRUE);
  181. }
  182. BOOL
  183. MyDeleteFile(
  184. LPCTSTR FileName
  185. )
  186. {
  187. WCHAR FullPathToFile[MAX_PATH];
  188. ExpandEnvironmentStrings(
  189. FileName,
  190. FullPathToFile,
  191. sizeof(FullPathToFile)/sizeof(WCHAR));
  192. return(DeleteFile(FullPathToFile));
  193. }
  194. BOOL
  195. MyCopyFile(
  196. LPCTSTR SourceFileName,
  197. LPCTSTR DestFileName,
  198. BOOL FailIfExists)
  199. {
  200. WCHAR SourceFullPath[MAX_PATH],DestFullPath[MAX_PATH];
  201. ExpandEnvironmentStrings(
  202. SourceFileName,
  203. SourceFullPath,
  204. sizeof(SourceFullPath)/sizeof(WCHAR));
  205. ExpandEnvironmentStrings(
  206. DestFileName,
  207. DestFullPath,
  208. sizeof(DestFullPath)/sizeof(WCHAR));
  209. return (CopyFile(SourceFullPath,
  210. DestFullPath,
  211. FailIfExists));
  212. }
  213. BOOL
  214. DumpFileToHeadlessPort(
  215. EMSRawChannel *Channel,
  216. LPCTSTR FileName
  217. )
  218. {
  219. HANDLE hFile;
  220. DWORD Size,SizeHigh,ActuallyRead;
  221. LONG High = 0;
  222. CHAR Buffer[80];
  223. if (!OpenFile(FileName, GENERIC_READ, OPEN_EXISTING, &hFile)) {
  224. PromptUserForData(Channel,NO_UNATTEND_FILE);
  225. return(FALSE);
  226. }
  227. SetFilePointer(hFile,0,&High,FILE_BEGIN);
  228. //bugbug handle large files
  229. Size = GetFileSize(hFile,&SizeHigh);
  230. while(Size != 0) {
  231. if (ReadFile(hFile,Buffer,sizeof(Buffer),&ActuallyRead,NULL)) {
  232. Channel->Write((PCBYTE)Buffer,ActuallyRead);
  233. Size -= ActuallyRead;
  234. } else {
  235. break;
  236. }
  237. }
  238. CloseHandle(hFile);
  239. return(TRUE);
  240. }
  241. BOOL
  242. AppendDataToFile(
  243. EMSRawChannel *Channel,
  244. HANDLE hFile,
  245. PBOOL UserComplete
  246. )
  247. {
  248. CHAR Buffer[80];
  249. DWORD BytesRead,BytesWritten;
  250. DWORD i;
  251. BOOL Done = FALSE;
  252. while(!Done) {
  253. if (!Channel->Read((PBYTE)Buffer,sizeof(Buffer),&BytesRead)) {
  254. return(FALSE);
  255. }
  256. //
  257. // zero-based
  258. //
  259. if (Buffer[BytesRead-1] == 0x1a) {
  260. *UserComplete = TRUE;
  261. BytesRead -=1;
  262. Done = TRUE;
  263. }
  264. if (BytesRead) {
  265. if (!WriteFile(
  266. hFile,
  267. Buffer,
  268. BytesRead,
  269. &BytesWritten,
  270. NULL) ||
  271. BytesWritten != BytesRead) {
  272. return(FALSE);
  273. }
  274. //
  275. // echo back to user
  276. //
  277. Channel->Write((PBYTE)Buffer,BytesRead);
  278. }
  279. if (!Done) {
  280. BOOL NewData = FALSE;
  281. if (!Channel->HasNewData(&NewData) || NewData == FALSE) {
  282. Done = TRUE;
  283. }
  284. }
  285. }
  286. return(TRUE);
  287. }
  288. BOOL
  289. ValidateTempUnattendFile(
  290. PCWSTR FileName
  291. )
  292. {
  293. WCHAR PathToUnattendFile[MAX_PATH];
  294. WCHAR Buffer[256];
  295. ExpandEnvironmentStrings(
  296. FileName,
  297. PathToUnattendFile,
  298. sizeof(PathToUnattendFile)/sizeof(WCHAR));
  299. //
  300. // look at that unattend file and see if it has the
  301. // "unattendmode=fullunattend" flag in it. If it doesn't,
  302. // then the inf is not valid.
  303. //
  304. if (GetPrivateProfileString(
  305. L"Data",
  306. L"UnattendMode",
  307. L"",
  308. Buffer,
  309. sizeof(Buffer),
  310. PathToUnattendFile) &&
  311. !_wcsicmp(Buffer,L"FullUnattended")) {
  312. return(TRUE);
  313. }
  314. return(FALSE);
  315. }
  316. DWORD
  317. PromptForUserInputThreadOverHeadlessConnection(
  318. PVOID params
  319. )
  320. {
  321. PUserInputParams Params = (PUserInputParams)params;
  322. EMSRawChannel *Channel = Params->Channel;
  323. HANDLE Handles[2];
  324. BOOL Abort = FALSE;
  325. BOOL Verbose = TRUE;
  326. BOOL UserComplete = FALSE;
  327. CHAR Buffer[10];
  328. ULONG BytesRead;
  329. HANDLE hTempUnattendFile;
  330. DWORD Result;
  331. //
  332. // prompt the user so they know what's going on.
  333. //
  334. PromptUserForData(Channel,ANNOUNCE_CHANNEL);
  335. Handles[0] = Params->hRemoveUI;
  336. //
  337. // bugbug should really get this from our object...
  338. //
  339. Handles[1] = GlobalChannelAttributes.HasNewDataEvent;
  340. //
  341. // wait for user input.
  342. //
  343. Result = WaitForMultipleObjects(2,Handles,FALSE,INFINITE);
  344. //
  345. // Did user abort?
  346. //
  347. if (Result == WAIT_OBJECT_0) {
  348. Abort = TRUE;
  349. goto ExitThread;
  350. }
  351. //
  352. // We got new data.
  353. //
  354. if (Result == WAIT_OBJECT_0+1) {
  355. if (!Channel->Read(
  356. (PBYTE)Buffer,
  357. sizeof(Buffer),
  358. &BytesRead)) {
  359. //
  360. // error reading data. bail out.
  361. //
  362. PromptUserForData(Channel,FAILURE_MESSAGE);
  363. goto ExitThread;
  364. }
  365. }
  366. assert(Result == WAIT_OBJECT_0+1);
  367. if (Buffer[0] != 'Y' && Buffer[0] != 'y' &&
  368. Buffer[0] != 'D' && Buffer[0] != 'd') {
  369. PromptUserForData(Channel,FAILURE_MESSAGE);
  370. goto ExitThread;
  371. }
  372. if (Buffer[0] == 'D' || Buffer[0] == 'd') {
  373. Verbose = TRUE;
  374. } else {
  375. Verbose = FALSE;
  376. }
  377. //
  378. // now dump the unattend file over the port so they know what they have...
  379. //
  380. if (Verbose) {
  381. PromptUserForData(Channel,DUMPFILE_MESSAGE);
  382. DumpFileToHeadlessPort(Channel,L"%systemroot%\\system32\\$winnt$.inf");
  383. }
  384. if (!OpenFile(
  385. L"%systemroot%\\system32\\EMSUnattend.txt",
  386. GENERIC_READ | GENERIC_WRITE,
  387. CREATE_ALWAYS,
  388. &hTempUnattendFile)) {
  389. PromptUserForData(Channel,FAILURE_MESSAGE);
  390. goto ExitThread;
  391. }
  392. Handles[0] = Params->hRemoveUI;
  393. //
  394. // bugbug should really get this from our object...
  395. //
  396. Handles[1] = GlobalChannelAttributes.HasNewDataEvent;
  397. //
  398. // now wait for the user to finish providing input.
  399. //
  400. PromptUserForData(Channel,FILLINFILE_MESSAGE);
  401. while(1) {
  402. Result = WaitForMultipleObjects(2,Handles,FALSE,INFINITE);
  403. if (Result = WAIT_OBJECT_0) {
  404. Abort = TRUE;
  405. CloseHandle(hTempUnattendFile);
  406. MyDeleteFile(L"%systemroot%\\system32\\EMSUnattend.txt");
  407. break;
  408. }
  409. if (!AppendDataToFile(
  410. Channel,
  411. hTempUnattendFile,
  412. &UserComplete)) {
  413. PromptUserForData(Channel,FAILURE_MESSAGE);
  414. CloseHandle(hTempUnattendFile);
  415. MyDeleteFile(L"%systemroot%\\system32\\EMSUnattend.txt");
  416. break;
  417. }
  418. if (UserComplete) {
  419. CloseHandle(hTempUnattendFile);
  420. if (ValidateTempUnattendFile(
  421. L"%systemroot%\\system32\\EMSUnattend.txt")) {
  422. MyCopyFile(
  423. L"%systemroot%\\system32\\$winnt$.inf",
  424. L"%systemroot%\\system32\\$winnt$.bak",
  425. TRUE);
  426. MyCopyFile(
  427. L"%systemroot%\\system32\\EMSUnattend.txt",
  428. L"%systemroot%\\system32\\$winnt$.inf",
  429. FALSE);
  430. PromptUserForData(Channel,SUCCESS_MESSAGE);
  431. } else {
  432. PromptUserForData(Channel,FAILURE_MESSAGE);
  433. }
  434. break;
  435. }
  436. }
  437. ExitThread:
  438. SetEvent(Params->hInputCompleteEvent);
  439. Sleep(5000);
  440. return 0;
  441. }
  442. INT_PTR CALLBACK
  443. UserInputAbortProc(
  444. HWND hwndDlg, // handle to dialog box
  445. UINT uMsg, // message
  446. WPARAM wParam, // first message parameter
  447. LPARAM lParam // second message parameter
  448. )
  449. {
  450. BOOL retval = FALSE;
  451. static UINT_PTR TimerId;
  452. static HANDLE hRemoveUI;
  453. switch(uMsg) {
  454. case WM_INITDIALOG:
  455. hRemoveUI = (HANDLE)lParam;
  456. if (!(TimerId = SetTimer(hwndDlg,0,1000,NULL))) {
  457. EndDialog(hwndDlg,0);
  458. }
  459. break;
  460. case WM_TIMER:
  461. if (WaitForSingleObject(hRemoveUI,0) == WAIT_OBJECT_0) {
  462. KillTimer(hwndDlg,TimerId);
  463. EndDialog(hwndDlg,1);
  464. }
  465. break;
  466. case WM_COMMAND:
  467. switch (HIWORD( wParam ))
  468. {
  469. case BN_CLICKED:
  470. switch (LOWORD( wParam ))
  471. {
  472. case IDOK:
  473. case IDCANCEL:
  474. EndDialog(hwndDlg,2);
  475. }
  476. };
  477. }
  478. return(retval);
  479. }
  480. DWORD
  481. PromptForUserInputThreadViaLocalDialog(
  482. PVOID params
  483. )
  484. {
  485. PUserInputParams Params = (PUserInputParams)params;
  486. DialogBoxParam(
  487. GetModuleHandle(NULL),
  488. MAKEINTRESOURCE(IDD_ABORTDIALOG),
  489. NULL,
  490. UserInputAbortProc,
  491. (LPARAM)Params->hRemoveUI);
  492. SetEvent(Params->hInputCompleteEvent);
  493. return 0;
  494. }
  495. BOOL
  496. InitializeGlobalChannelAttributes(
  497. PSAC_CHANNEL_OPEN_ATTRIBUTES ChannelAttributes
  498. )
  499. {
  500. RtlZeroMemory(ChannelAttributes,sizeof(SAC_CHANNEL_OPEN_ATTRIBUTES));
  501. ChannelAttributes->Type = ChannelTypeRaw;
  502. ChannelAttributes->Name = L"Unattended Setup Channel";
  503. ChannelAttributes->Description = L"Gives the ability to input unattended setup parameters before proceeding with GUI-Setup.";
  504. ChannelAttributes->Flags = SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT;
  505. ChannelAttributes->HasNewDataEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  506. ChannelAttributes->ApplicationType = NULL;
  507. return((ChannelAttributes->HasNewDataEvent != NULL)
  508. ? TRUE
  509. : FALSE);
  510. }
  511. int __cdecl
  512. wmain(
  513. IN int argc,
  514. WCHAR *argvW[]
  515. )
  516. {
  517. UserInputParams Params,ParamsDialog;
  518. EMSRawChannel *Channel = NULL;
  519. DWORD ThreadId;
  520. HANDLE Handles[2];
  521. HANDLE hThreadHeadless = NULL,hThreadUI = NULL;
  522. RtlZeroMemory(&Params,sizeof(Params));
  523. RtlZeroMemory(&ParamsDialog,sizeof(ParamsDialog));
  524. InitializeGlobalChannelAttributes(&GlobalChannelAttributes);
  525. //
  526. // Check if headless feature is present on this machine. If not, just
  527. // run setup like normal.
  528. //
  529. if(!IsHeadlessPresent(&Channel)) {
  530. goto run_setup;
  531. }
  532. //
  533. // Check if there is any work for us to do. If not, just run setup like
  534. // normal.
  535. //
  536. if (!IsWorkToDo()) {
  537. goto run_setup;
  538. }
  539. //
  540. // Create another thread for getting data from the user.
  541. //
  542. Params.Channel = Channel;
  543. Params.hInputCompleteEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  544. ParamsDialog.hInputCompleteEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  545. Params.hRemoveUI = ParamsDialog.hRemoveUI = CreateEvent(NULL,TRUE,FALSE,NULL);
  546. if (!Params.hInputCompleteEvent ||
  547. !ParamsDialog.hInputCompleteEvent ||
  548. !Params.hRemoveUI) {
  549. goto run_setup;
  550. }
  551. if (!(hThreadHeadless = CreateThread(
  552. NULL,
  553. 0,
  554. &PromptForUserInputThreadOverHeadlessConnection,
  555. &Params,
  556. 0,
  557. &ThreadId))) {
  558. goto run_setup;
  559. }
  560. if (!(hThreadUI = CreateThread(
  561. NULL,
  562. 0,
  563. &PromptForUserInputThreadViaLocalDialog,
  564. &ParamsDialog,
  565. 0,
  566. &ThreadId))) {
  567. goto run_setup;
  568. }
  569. Handles[0] = Params.hInputCompleteEvent;
  570. Handles[1] = ParamsDialog.hInputCompleteEvent;
  571. WaitForMultipleObjects(2,Handles,FALSE,INFINITE);
  572. SetEvent(Params.hRemoveUI);
  573. Handles[0] = hThreadHeadless;
  574. Handles[1] = hThreadUI;
  575. WaitForMultipleObjects(2,Handles,TRUE,INFINITE);
  576. run_setup:
  577. if (hThreadHeadless) {
  578. CloseHandle(hThreadHeadless);
  579. }
  580. if (hThreadUI) {
  581. CloseHandle(hThreadUI);
  582. }
  583. if (Params.hInputCompleteEvent) {
  584. CloseHandle(Params.hInputCompleteEvent);
  585. }
  586. if (ParamsDialog.hInputCompleteEvent) {
  587. CloseHandle(ParamsDialog.hInputCompleteEvent);
  588. }
  589. if (Params.hRemoveUI) {
  590. CloseHandle(Params.hRemoveUI);
  591. }
  592. if (Channel) {
  593. delete (Channel);
  594. }
  595. return (StartSetupProcess(argc, argvW));
  596. }