#include "precomp.h" #pragma hdrstop #include #include #include #include #if DBG VOID AssertFail( IN PSTR FileName, IN UINT LineNumber, IN PSTR Condition ) { int i; CHAR Name[MAX_PATH]; PCHAR p; CHAR Msg[4096]; // // Use dll name as caption // GetModuleFileNameA(NULL,Name,MAX_PATH); if(p = strrchr(Name,'\\')) { p++; } else { p = Name; } wsprintfA( Msg, "Assertion failure at line %u in file %s: %s\n\nCall DebugBreak()?", LineNumber, FileName, Condition ); OutputDebugStringA(Msg); i = MessageBoxA( NULL, Msg, p, MB_YESNO | MB_TASKMODAL | MB_ICONSTOP | MB_SETFOREGROUND ); if(i == IDYES) { DebugBreak(); } } #define MYASSERT(x) if(!(x)) { AssertFail(__FILE__,__LINE__,#x); } #else #define MYASSERT( exp ) #endif // DBG // // App instance. // HINSTANCE hInst; // // Global version information structure. // OSVERSIONINFO OsVersionInfo; // // Specification of master inf, from command line. // TCHAR InfPath[MAX_PATH]; TCHAR InfDir[MAX_PATH]; // // Source path for installation files, etc. // TCHAR SourcePath[MAX_PATH]; TCHAR UnattendPath[MAX_PATH]; // If Unattened BOOL bUnattendInstall; // // Whether to force the specified master inf to be treated as new // (from command line) // BOOL ForceNewInf; // // whether we need to pass language callback to components // BOOL LanguageAware; // // Whether to run without UI // BOOL QuietMode; // // Whether to delete all subcomponent entries listed in the master inf // (from command line) // BOOL KillSubcomponentEntries; // If set and /U then reboot is suppressed BOOL bNoReboot; // if this is set and we're running /unattend, then warn on reboot BOOL bWarnOnReboot; // if this is set then we want sysocmgr.exe to enforce the admin check. BOOL bDoAdminCheck = FALSE; // Flag for Derminineing Starting or Ending message BOOL bStarting; // // OC Manager context 'handle' // PVOID OcManagerContext; // // Generic app title string id. // UINT AppTitleStringId; BOOL NeedToReboot; BOOL SkipBillboard; BOOL ForceExternalProgressIndicator; BOOL AllowCancel = TRUE; VOID OcSetReboot( VOID ); // // Callback routines. // OCM_CLIENT_CALLBACKS CallbackRoutines = { OcFillInSetupDataA, OcLogError, OcSetReboot #ifdef UNICODE ,OcFillInSetupDataW #endif ,NULL, // No callback for show,hide wizard NULL, // No callback for progress feedback, they are only needed for setup NULL, // No callback to set the progress text NULL // No logging callback. }; BOOL DoIt( VOID ); BOOL ParseArgs( IN int argc, IN TCHAR *argv[] ); DWORD ExpandPath( IN LPCTSTR lpFileName, OUT LPTSTR lpBuffer, OUT LPTSTR *lpFilePart ); void ShutDown() { extern void RestartDialogEx(VOID *, VOID *, DWORD, DWORD); if (!bNoReboot) { if ( bUnattendInstall && !bWarnOnReboot ) { // // NT is always UNICODE and W9x is alwasy Ansii // #ifdef UNICODE HANDLE hToken; TOKEN_PRIVILEGES tkp; // Get a token for this process. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken)) sapiAssert("OpenProcessToken"); // Get the LUID for the shutdown privilege. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; // one privilege to set tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // Get the shutdown privilege for this process. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); // Cannot test the return value of AdjustTokenPrivileges. if (GetLastError() == ERROR_SUCCESS) { sapiAssert("AdjustTokenPrivileges"); } #endif // // Shut down the system and force all applications to close. // if (! ExitWindowsEx(EWX_REBOOT|EWX_FORCE , 0) ) { _RPT0(_CRT_WARN,"Sysocmgr:Failed to ExitWindows"); sapiAssert(FALSE); } } else { RestartDialogEx(NULL,NULL,EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG \ | SHTDN_REASON_FLAG_PLANNED ); } } } VOID __cdecl #ifdef UNICODE wmain( #else main( #endif IN int argc, IN TCHAR *argv[] ) { INITCOMMONCONTROLSEX ControlInit; // // Preliminaries // ControlInit.dwSize = sizeof(INITCOMMONCONTROLSEX); ControlInit.dwICC = ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_UPDOWN_CLASS | ICC_PROGRESS_CLASS | ICC_HOTKEY_CLASS | ICC_ANIMATE_CLASS | ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES | ICC_COOL_CLASSES; #if (_WIN32_IE >= 0x0400) ControlInit.dwICC = ControlInit.dwICC | ICC_INTERNET_CLASSES | ICC_PAGESCROLLER_CLASS; #endif InitCommonControlsEx( &ControlInit ); hInst = GetModuleHandle(NULL); OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&OsVersionInfo); AppTitleStringId = IS_NT() ? IDS_WINNT_SETUP : IDS_WIN9X_SETUP; // // Parse arguments and do it. // if (ParseArgs(argc,argv)) { DoIt(); } // // If we need to reboot, do that now. // if (NeedToReboot) { ShutDown(); } } BOOL ParseArgs( IN int argc, IN TCHAR *argv[] ) /*++ Routine Description: Parse and syntactically validate arguments specified on the comment line. The following arguments are valid: /a forces the external progress indicator on the setup page /c disallow cancel during final installation phase /i: specifies the master OC inf (required). /n forces specified master_oc_inf to be treated as new. /s: specifies the source path (required). /u: specifies unattended operation parameters. /x supresses the 'initializing' banner /q run wizard invisibly /r supress reboot if need on unattended operation /w warn on reboot on unattended operation Arguments: Standard main argc/argv. Return Value: Boolean value indicating whether the arguments specified are valid. If successful, various global variables will have been filled in. If not, the user will have been informed. --*/ { BOOL Valid; LPCTSTR SourcePathSpec = NULL; LPCTSTR InfSpec = NULL; LPCTSTR UnattendSpec = NULL; LPTSTR FilePart; DWORD u; // // Skip program name. // if (argc) { argc--; } Valid = TRUE; ForceNewInf = FALSE; QuietMode = FALSE; KillSubcomponentEntries = FALSE; while (Valid && argc--) { argv++; if ((argv[0][0] == TEXT('-')) || (argv[0][0] == TEXT('/'))) { switch (argv[0][1]) { case TEXT('a'): case TEXT('A'): if (!ForceExternalProgressIndicator && !argv[0][2]) { ForceExternalProgressIndicator = TRUE; } else { Valid = FALSE; } break; case TEXT('c'): case TEXT('C'): if (AllowCancel && !argv[0][2]) { AllowCancel = FALSE; } else { Valid = FALSE; } break; case TEXT('f'): case TEXT('F'): ForceNewInf = TRUE; KillSubcomponentEntries = TRUE; break; case TEXT('i'): case TEXT('I'): if (!InfSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) { InfSpec = &(argv[0][3]); } else { Valid = FALSE; } break; case TEXT('l'): case TEXT('L'): LanguageAware = TRUE; break; case TEXT('n'): case TEXT('N'): ForceNewInf = TRUE; break; case TEXT('q'): case TEXT('Q'): if (!QuietMode && !argv[0][2]) { QuietMode = TRUE; SkipBillboard = TRUE; } else { Valid = FALSE; } break; case TEXT('r'): case TEXT('R'): bNoReboot = TRUE; break; case TEXT('s'): case TEXT('S'): if (!SourcePathSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) { SourcePathSpec = &argv[0][3]; } else { Valid = FALSE; } break; case TEXT('u'): case TEXT('U'): // // accept unattend, unattended, u all as the same // if(!_tcsnicmp(&argv[0][1],TEXT("unattended"),10)) { u = 11; } else if(!_tcsnicmp(&argv[0][1],TEXT("unattend"),8)) { u = 9; } else if(!_tcsnicmp(&argv[0][1],TEXT("u"),1)) { u = 2; } else { Valid = FALSE; u = 0; } if (!UnattendSpec ) { bUnattendInstall = TRUE; // If you have the : then you must also have the arg if (argv[0][u] == TEXT(':')) { if ( argv[0][u+1]) { UnattendSpec = &argv[0][u+1]; } else { Valid = FALSE; } } else { Valid = FALSE; } } else { Valid = FALSE; } break; case TEXT('w'): case TEXT('W'): bWarnOnReboot = TRUE; break; case TEXT('x'): case TEXT('X'): if (!SkipBillboard && !argv[0][2]) { SkipBillboard = TRUE; } else { Valid = FALSE; } break; // For ISSUE NTBUG9:295052 (389583): We want to do a top level admin check so we get a more friendly message. // It is possible for people to have been using sysocmgr.exe with their own custom master oc.inf // (the one passed in with the /i: switch) and they may not need this admin check. So, we did // not want to do this admin check unconditionally. We will have the control panel applet that // is launching sysocmgr.exe to pass in this /y switch. // case TEXT('y'): case TEXT('Y'): bDoAdminCheck = TRUE; break; case TEXT('z'): case TEXT('Z'): // Stop parsing Arguments All other args past this point are // Component Arguments argc = 0; break; default: Valid = FALSE; break; } } else { Valid = FALSE; } } if (Valid && !InfSpec) { Valid = FALSE; } if (Valid) { // // Expand the inf spec to a full path. // ExpandPath(InfSpec,InfPath,&FilePart); _tcscpy(InfDir, InfSpec); if (_tcsrchr(InfDir, TEXT('\\'))) *_tcsrchr(InfDir,TEXT('\\')) = 0; else GetCurrentDirectory(MAX_PATH, InfDir); // If the user specified /s then expand it too, otherwise // use the dir in the /i as the /s arg. if (SourcePathSpec) { ExpandPath(SourcePathSpec,SourcePath,&FilePart); } else { lstrcpy(SourcePath,InfPath); if (_tcsrchr(SourcePath,TEXT('\\'))) { *_tcsrchr(SourcePath,TEXT('\\')) = 0; } } SetCurrentDirectory(InfDir); if (UnattendSpec) { ExpandPath(UnattendSpec,UnattendPath,&FilePart); }else{ // Allow /Q only if /U was specified QuietMode = FALSE; SkipBillboard = FALSE; } } else { MessageBoxFromMessage( NULL, MSG_ARGS, FALSE, MAKEINTRESOURCE(AppTitleStringId), MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL ); } return (Valid); } INT_PTR BillboardDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { BOOL b; RECT rect1,rect2; static HCURSOR hOldCursor; switch (msg) { case WM_INITDIALOG: // // Center on-screen. // GetWindowRect(hdlg,&rect1); SystemParametersInfo(SPI_GETWORKAREA,0,&rect2,0); MoveWindow( hdlg, rect2.left + (((rect2.right - rect2.left) - (rect1.right - rect1.left)) / 2), rect2.top + (((rect2.bottom - rect2.top) - (rect1.bottom - rect1.top)) / 2), rect1.right - rect1.left, rect1.bottom - rect1.top, FALSE ); *(HWND *)lParam = hdlg; b = TRUE; hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); break; case WM_APP: EndDialog(hdlg,0); SetCursor( hOldCursor ); b = TRUE; break; default: b = FALSE; break; } return (b); } DWORD DisplayMessage( IN LPVOID ThreadParameter ) { int i; i = (int)DialogBoxParam( hInst, MAKEINTRESOURCE(bStarting?IDD_STARTING:IDD_FINISHING), NULL, BillboardDlgProc, (LPARAM)ThreadParameter ); if (i == -1) { // // Force caller out of wait loop // *(HWND *)ThreadParameter = (HWND)(-1); } return (0); } /*---------------------------------------------------------------------------*\ Function: RunningAsAdministrator() |*---------------------------------------------------------------------------*| Description: Checks whether we are running as administrator on the machine or not. Code taken from ntoc.dll \*---------------------------------------------------------------------------*/ BOOL RunningAsAdministrator( VOID ) { BOOL fAdmin; HANDLE hThread; TOKEN_GROUPS *ptg = NULL; DWORD cbTokenGroups; DWORD dwGroup; PSID psidAdmin; SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY; // First we must open a handle to the access token for this thread. if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread)) { if ( GetLastError() == ERROR_NO_TOKEN) { // If the thread does not have an access token, we'll examine the // access token associated with the process. if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY, &hThread)) return ( FALSE); } else return ( FALSE); } // Then we must query the size of the group information associated with // the token. Note that we expect a FALSE result from GetTokenInformation // because we've given it a NULL buffer. On exit cbTokenGroups will tell // the size of the group information. if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups)) return ( FALSE); // Here we verify that GetTokenInformation failed for lack of a large // enough buffer. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER) return ( FALSE); // Now we allocate a buffer for the group information. // Since _alloca allocates on the stack, we don't have // to explicitly deallocate it. That happens automatically // when we exit this function. if ( ! ( ptg= (TOKEN_GROUPS *)malloc ( cbTokenGroups))) return ( FALSE); // Now we ask for the group information again. // This may fail if an administrator has added this account // to an additional group between our first call to // GetTokenInformation and this one. if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups, &cbTokenGroups) ) { free(ptg); return ( FALSE); } // Now we must create a System Identifier for the Admin group. if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin) ) { free(ptg); return ( FALSE); } // Finally we'll iterate through the list of groups for this access // token looking for a match against the SID we created above. fAdmin= FALSE; for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++) { if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin)) { fAdmin = TRUE; break; } } // Before we exit we must explicity deallocate the SID we created. FreeSid ( psidAdmin); free(ptg); return ( fAdmin); } BOOL DoIt( VOID ) { BOOL ShowErr; HANDLE hThread; DWORD ThreadId; HANDLE hMutex; TCHAR Fname[MAX_PATH]; TCHAR MutexName[MAX_PATH]; DWORD Flags; HWND StartingMsgWindow = NULL; HCURSOR hOldCursor; if (bDoAdminCheck && !RunningAsAdministrator()) { MessageBoxFromMessage( StartingMsgWindow, MSG_NOT_ADMIN, FALSE, MAKEINTRESOURCE(AppTitleStringId), MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL ); return FALSE; } // // Create a Mutex from the Base Name of the Inf file // This will prevent OCM from running on the same inf file // in two or more instances // _tsplitpath( InfPath, NULL, NULL, Fname, NULL ); lstrcpy( MutexName, TEXT("Global\\")); lstrcat( MutexName, Fname ); hMutex = CreateMutex( NULL, TRUE, MutexName ); if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError()) { MessageBoxFromMessage( NULL, MSG_ONLY_ONE_INST, FALSE, MAKEINTRESOURCE(AppTitleStringId), MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_SYSTEMMODAL ); ReleaseMutex(hMutex); return FALSE; } // // Initialize the OC Manager. Show the user an "initializing setup" // dialog while this is happening, as it can take a while. // if (!SkipBillboard) { bStarting = TRUE; StartingMsgWindow = NULL; hThread = CreateThread( NULL, 0, DisplayMessage, &StartingMsgWindow, 0, &ThreadId ); if (hThread) { CloseHandle(hThread); Sleep(50); } else { DisplayMessage(0); } } // // Make sure the window has actually been created, // or we could have a timing window where the PostMessage fails // and the billboard shows up on top of the wizard. // if (!SkipBillboard) { while (!StartingMsgWindow) { Sleep(50); } } Flags = ForceNewInf ? OCINIT_FORCENEWINF : 0; Flags |= KillSubcomponentEntries ? OCINIT_KILLSUBCOMPS : 0; Flags |= QuietMode ? OCINIT_RUNQUIET : 0; Flags |= LanguageAware ? OCINIT_LANGUAGEAWARE : 0 ; hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); OcManagerContext = OcInitialize( &CallbackRoutines, InfPath, Flags, &ShowErr, NULL ); if (!OcManagerContext) { SetCursor( hOldCursor ); if (ShowErr) { MessageBoxFromMessage( StartingMsgWindow, MSG_CANT_INIT, FALSE, MAKEINTRESOURCE(AppTitleStringId), MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL ); } ReleaseMutex(hMutex); return (FALSE); } // // Do the wizard. // DoWizard(OcManagerContext,StartingMsgWindow, hOldCursor); SetCursor( hOldCursor ); hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); // the Terminate can take a while too.. if (!SkipBillboard) { bStarting = FALSE; StartingMsgWindow = NULL; hThread = CreateThread( NULL, 0, DisplayMessage, &StartingMsgWindow, 0, &ThreadId ); if (hThread) { CloseHandle(hThread); Sleep(50); } else { DisplayMessage(0); } } // // Clean up, we're done. // OcTerminate(&OcManagerContext); if (!SkipBillboard) { // // Make sure the window has actually been created, // or we could have a timing window where the PostMessage fails // and the billboard shows up on top of the wizard. // while (!StartingMsgWindow) { Sleep(50); } SendMessage(StartingMsgWindow,WM_APP,0,0); } ReleaseMutex(hMutex); SetCursor( hOldCursor ); return (TRUE); } VOID OcSetReboot( VOID ) { NeedToReboot = TRUE; } DWORD ExpandPath( IN LPCTSTR lpFileName, OUT LPTSTR lpBuffer, OUT LPTSTR *lpFilePart ) { TCHAR buf[MAX_PATH]; ExpandEnvironmentStrings(lpFileName, buf, MAX_PATH); return GetFullPathName(buf, MAX_PATH, lpBuffer, lpFilePart); }