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.
 
 
 
 
 
 

1070 lines
23 KiB

#include "sacsvr.h"
#include <TChar.h>
//
// Handle to the SAC Driver object
//
// The SAC driver requires us to use the same driver handle
// that we registered with, to unregister.
// Hence, we must keep this handle after we register ourselves
// with the SAC driver so that we can unregister.
//
HANDLE m_SacDriverHandle = INVALID_HANDLE_VALUE;
//
// This event is fired when the SAC driver wants us
// to launch a Command Prompt session
//
HANDLE m_RequestSacCmdEvent = NULL;
//
// In response to our attempt at launching a Command Prompt session,
// we signal the appropriate status event
//
HANDLE m_RequestSacCmdSuccessEvent = NULL;
HANDLE m_RequestSacCmdFailureEvent = NULL;
//
// the Command Prompt session exe
//
#define SAC_CMD_SCRAPER_PATH TEXT("sacsess.exe")
#define SETREGISTRYDW( constVal, keyHandle1, keyHandle2, keyName, val, size ) \
val = constVal ; \
if( RegSetValueEx( keyHandle2, keyName, 0, REG_DWORD, (LPBYTE)&val, size \
) != ERROR_SUCCESS ) \
{ \
if( keyHandle1 ) { \
RegCloseKey( keyHandle1 ); \
} \
RegCloseKey( keyHandle2 ); \
return ( FALSE ); \
}
#define REG_CONSOLE_KEY L".DEFAULT\\Console"
//Add other FAREAST languages
#define JAP_CODEPAGE 932
#define CHS_CODEPAGE 936
#define KOR_CODEPAGE 949
#define CHT_CODEPAGE 950
#define JAP_FONTSIZE 786432
#define CHT_FONTSIZE 917504
#define KOR_FONTSIZE 917504
#define CHS_FONTSIZE 917504
BOOL
CreateClient(
DWORD* pdwPid
);
BOOL
CreateSessionProcess(
DWORD* dwProcessId,
HANDLE* hProcess
);
BOOL
SetServiceStartType(
IN PWSTR RegKey,
IN DWORD StartType
)
/*++
Routine Description:
Arguments:
None.
Return Value:
true - success
false, otherwise
--*/
{
DWORD rc;
HKEY hKey;
//
// Open the service configuration key
//
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
RegKey,
0,
KEY_WRITE,
&hKey );
if( rc == NO_ERROR ) {
rc = RegSetValueEx(
hKey,
TEXT("Start"),
0,
REG_DWORD,
(LPBYTE)&StartType,
sizeof(DWORD)
);
RegCloseKey( hKey );
}
//
// Success
//
return rc == NO_ERROR ? TRUE : FALSE;
}
BOOL
InitSacCmd(
VOID
)
/*++
Routine Description:
This routine initializes the relationship between the SACDRV and this service.
We register an event with the SACDRV so that when a 'cmd' command is executed
in the EMS, the event is fired and we launch a sac cmd session.
Arguments:
none
Return Value:
TRUE - if SacCmd was initialized successfully
otherwise, FALSE
--*/
{
BOOL bStatus;
//
// Initialize the our SAC Cmd Info
//
do {
//
// These events use the auto-reset mechanism since they are used as syncronization events
//
m_RequestSacCmdEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if (m_RequestSacCmdEvent == NULL) {
bStatus = FALSE;
break;
}
m_RequestSacCmdSuccessEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if (m_RequestSacCmdSuccessEvent == NULL) {
bStatus = FALSE;
break;
}
m_RequestSacCmdFailureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if (m_RequestSacCmdFailureEvent == NULL) {
bStatus = FALSE;
break;
}
//
// Reset the service start type to Manual. By doing this,
// we enable the scenario where the system boots with headless
// enabled and then the user disables headless. In this scenario,
// the service will not auto start next boot. This works because
// the SAC driver moves the service start type from manual to auto,
// if and only if the start type is manual.
//
// Note: we do this before we register with the SAC so we are sure
// it happens
//
bStatus = SetServiceStartType(
L"System\\CurrentControlSet\\Services\\sacsvr",
SERVICE_DEMAND_START
);
if (! bStatus) {
SvcDebugOut("Failed to set service start type\n", bStatus);
break;
} else {
SvcDebugOut("Succeded to set service start type\n", bStatus);
}
//
// Send the SAC Driver the event handles and the pointer to our
// communication buffer
//
bStatus = SacRegisterCmdEvent(
&m_SacDriverHandle,
m_RequestSacCmdEvent,
m_RequestSacCmdSuccessEvent,
m_RequestSacCmdFailureEvent
);
if (! bStatus) {
SvcDebugOut("Failed registration\n", bStatus);
} else {
SvcDebugOut("Succeeded registration\n", bStatus);
}
} while ( FALSE );
//
// clean up, if necessary
//
if (!bStatus) {
if (m_RequestSacCmdEvent != NULL) {
CloseHandle(m_RequestSacCmdEvent);
m_RequestSacCmdEvent = NULL;
}
if (m_RequestSacCmdSuccessEvent != NULL) {
CloseHandle(m_RequestSacCmdSuccessEvent);
m_RequestSacCmdSuccessEvent = NULL;
}
if (m_RequestSacCmdFailureEvent != NULL) {
CloseHandle(m_RequestSacCmdFailureEvent);
m_RequestSacCmdFailureEvent = NULL;
}
}
return bStatus;
}
BOOL
ShutdownSacCmd(
void
)
/*++
Routine Description:
This routine removes the relationship between the SACDRV and this service.
Arguments:
none
Return Value:
TRUE - if SacCmd was initialized successfully
otherwise, FALSE
--*/
{
BOOL Status;
//
// default status
//
Status = TRUE;
//
// Send the SAC Driver notification to remove the event handles
// and the pointer to our communication buffer
//
if (! SacUnRegisterCmdEvent(&m_SacDriverHandle)) {
Status = FALSE;
}
return Status;
}
VOID
CompleteSacRequest(
BOOLEAN Status
)
/*++
Routine Description:
This routine notifies the SAC driver about the status
of the attempt to launc the SAC session.
Arguments:
Status - TRUE if the session was successfully launched,
FALSE otherwise
Return Value:
None
--*/
{
//
// Fire the event corresponding to the request completion status
//
if (Status == TRUE) {
SetEvent(m_RequestSacCmdSuccessEvent);
} else {
SetEvent(m_RequestSacCmdFailureEvent);
}
}
BOOL
ListenerThread(
VOID
)
/*++
Routine Description:
This routine waits around for a "lauch a SAC session"
event message from the SAC driver.
Arguments:
None
Return Value:
Status
--*/
{
HANDLE eventArray[ 1 ];
DWORD dwWaitRet = 0;
DWORD dwPid = 0;
BOOL bContinue;
BOOL bStatus;
//
// setup the event array
//
enum {
SAC_CMD_LAUNCH_EVENT = WAIT_OBJECT_0
};
eventArray[ 0 ] = m_RequestSacCmdEvent;
//
// While we want to continue, service events
//
bStatus = TRUE;
bContinue = TRUE;
while ( bContinue ) {
dwWaitRet = WaitForMultipleObjects (
sizeof(eventArray)/sizeof(HANDLE),
eventArray,
FALSE,
INFINITE
);
switch (dwWaitRet) {
case SAC_CMD_LAUNCH_EVENT:
//
// Attempt to launch the command console process
//
if ( !CreateClient( &dwPid ) ) {
//
// Notify the SAC driver that we failed to
// launch the SAC session
//
CompleteSacRequest( FALSE );
bStatus = FALSE;
break;
}
//
// Notify the SAC driver that we successfully
// launched the SAC session
//
CompleteSacRequest(TRUE);
break;
default:
bContinue = FALSE;
bStatus = FALSE;
break;
}
}
return( bStatus );
}
BOOL
CreateClient(
OUT DWORD *pdwPid
)
/*++
Routine Description:
This routine launches the SAC session
Arguments:
pdwPid - the PID of the newly created SAC session process
Return Value:
Status
--*/
{
BOOL bRetVal;
BOOL bSuccess;
DWORD dwProcessId;
HANDLE hProcess;
DWORD dwExitCode;
//
// default: we failed to create the process
//
bRetVal = FALSE;
hProcess = NULL;
do {
//
// Create the Command Console session process
//
bSuccess = CreateSessionProcess(
&dwProcessId,
&hProcess
);
if ( !bSuccess ) {
break;
}
if ( hProcess == NULL ) {
break;
}
//
// Send back PID to caller
//
*pdwPid = dwProcessId;
//
// Check if the process has really started. It may not have started properly
// in the following cases and yet the createprocess return code
// will not say it
//
// 1. Could not launch process on the desktop because of lack of perms or
// heap memory. Doing GetExitCodeProcess immediate may not help always.
//
GetExitCodeProcess( hProcess, &dwExitCode );
//
// Make sure the process is still active before we declare victory
//
if (dwExitCode != STILL_ACTIVE ) {
break;
}
//
// We successfully created the process
//
bRetVal = TRUE;
} while ( FALSE );
//
// We are done with the process handle
//
if (hProcess) {
CloseHandle( hProcess );
}
return(bRetVal);
}
PTCHAR
GetPathOfTheExecutable(
VOID
)
/*++
Routine Description:
Find out where the SAC session executable is located.
Arguments:
NONE
Return Value:
Failure: NULL
SUCCESS: pointer to path (caller must free)
--*/
{
TCHAR SystemDir[MAX_PATH+1];
PTCHAR pBuffer;
ULONG length;
//
// default: we didnt create a new path
//
pBuffer = NULL;
do {
//
// get the system path
//
length = GetSystemDirectoryW(SystemDir, MAX_PATH+1);
if (length == 0) {
break;
}
//
// compute the length
//
length += 1; // backslash
length += lstrlen(SAC_CMD_SCRAPER_PATH);
length += 1; // NULL termination
//
// allocate our new path
//
pBuffer = malloc(length * sizeof(WCHAR));
if (pBuffer == NULL) {
break;
}
//
// create the path
//
wnsprintf(
pBuffer,
length,
TEXT("%s\\%s"),
SystemDir,
SAC_CMD_SCRAPER_PATH
);
} while ( FALSE );
return pBuffer;
}
void
FillProcessStartupInfo(
STARTUPINFO *si
)
/*++
Routine Description:
Populate the process startup info structure for the
SAC session process.
Arguments:
si - the startup info
Return Value:
None
--*/
{
ASSERT( si != NULL );
ZeroMemory(si, sizeof(STARTUPINFO));
si->cb = sizeof(STARTUPINFO);
si->wShowWindow = SW_SHOW;
return;
}
BOOL
CreateSessionProcess(
OUT DWORD *dwProcessId,
OUT HANDLE *hProcess
)
/*++
Routine Description:
This routine does the real work to launch the SAC session process.
Arguments:
dwProcessId - the PID of the SAC session process
Return Value:
TRUE - the process was created successfully,
FALSE - otherwise
--*/
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
PTCHAR pCmdBuf;
BOOL dwStatus;
PWCHAR SessionPath = SAC_CMD_SCRAPER_PATH;
do {
//
// get the pathname to the SAC session exe
//
pCmdBuf = GetPathOfTheExecutable();
if (pCmdBuf == NULL) {
dwStatus = FALSE;
break;
}
//
//
//
FillProcessStartupInfo( &si );
//
//
//
dwStatus = CreateProcess(
pCmdBuf,
SessionPath,
NULL,
NULL,
TRUE,
CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi
);
//
// release our SAC session path
//
free(pCmdBuf);
if ( !dwStatus ) {
break;
}
//
//
//
*hProcess = pi.hProcess;
CloseHandle( pi.hThread );
*dwProcessId = pi.dwProcessId;
} while ( FALSE );
return( dwStatus );
}
BOOL
FormSACSessKeyForCmd(
LPWSTR *lpszKey
)
/*++
Routine Description:
This routine forms the reg key used to specify the console
fonts for the sacsess.exe app.
See comments for HandleJapSpecificRegKeys
Mem allocation by this function.
To be deleted by the caller.
(based on telnet's FormTlntSessKeyForCmd)
Arguments:
lpszKey - on success, contains the key name
Return Value:
TRUE - We completed successfully
FALSE - otherwise
--*/
{
WCHAR szPathName[MAX_PATH+1];
WCHAR session_path[MAX_PATH*2];
LPTSTR pSlash;
wint_t ch;
LPTSTR pBackSlash;
DWORD length_required;
//
//
//
if( !GetModuleFileName( NULL, szPathName, MAX_PATH+1 ) )
{
return ( FALSE );
}
szPathName[MAX_PATH] = UNICODE_NULL;
//
// Nuke the trailing "sacsvr.exe"
//
pSlash = wcsrchr( szPathName, L'\\' );
if( pSlash == NULL )
{
return ( FALSE );
}
else
{
*pSlash = L'\0';
}
//
// Replace all '\\' with '_' This format is required for the console to
// interpret the key.
//
ch = L'\\';
pBackSlash = NULL;
while ( 1 )
{
pBackSlash = wcschr( szPathName, ch );
if( pBackSlash == NULL )
{
break;
}
else
{
*pBackSlash = L'_';
}
}
//
//
//
_snwprintf(session_path, MAX_PATH*2 - 1, L"%s_sacsess.exe", szPathName);
session_path[MAX_PATH*2 - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
length_required = (DWORD)(wcslen( REG_CONSOLE_KEY ) + wcslen( session_path ) + 2);
*lpszKey = malloc(length_required * sizeof(WCHAR));
if( *lpszKey == NULL )
{
return( FALSE );
}
//
//
//
_snwprintf(*lpszKey, length_required - 1, L"%s\\%s", REG_CONSOLE_KEY, session_path );
(*lpszKey)[length_required - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
return ( TRUE );
}
BOOL
HandleFarEastSpecificRegKeys(
VOID
)
/*++
Routine Description:
If Japanese codepage, then we need to verify 3 registry settings for
console fonts:
HKEY_USERS\.DEFAULT\Console\FaceName :REG_SZ:‚l‚r ƒSƒVƒbƒN
where the FaceName is "MS gothic" written in Japanese full widthKana
HKEY_USERS\.DEFAULT\Console\FontFamily:REG_DWORD:0x36
HKEY_USERS\.DEFAULT\Console\C:_SFU_Telnet_sacsess.exe\FontFamily:REG_DWORD: 0x36
where the "C:" part is the actual path to SFU installation
(based on telnet's HandleFarEastSpecificRegKeys)
Arguments:
None
Return Value:
TRUE - We completed successfully
FALSE - otherwise
--*/
{
HKEY hk;
DWORD dwFontSize = 0;
const TCHAR szJAPFaceName[] = { 0xFF2D ,0xFF33 ,L' ' ,0x30B4 ,0x30B7 ,0x30C3 ,0x30AF ,L'\0' };
const TCHAR szCHTFaceName[] = { 0x7D30 ,0x660E ,0x9AD4 ,L'\0'};
const TCHAR szKORFaceName[] = { 0xAD74 ,0xB9BC ,0xCCB4 ,L'\0'};
const TCHAR szCHSFaceName[] = { 0x65B0 ,0x5B8B ,0x4F53 ,L'\0' };
TCHAR szFaceNameDef[256];
DWORD dwCodePage = GetACP();
DWORD dwFaceNameSize = 0;
DWORD dwFontFamily = 54;
DWORD dwFontWeight = 400;
DWORD dwHistoryNoDup = 0;
DWORD dwSize = 0;
switch (dwCodePage)
{
case JAP_CODEPAGE:
_tcscpy(szFaceNameDef, szJAPFaceName); //On JAP, set the FaceName to "MS Gothic"
dwFontSize = JAP_FONTSIZE;
break;
case CHT_CODEPAGE:
_tcscpy(szFaceNameDef, szCHTFaceName); //On CHT, set the FaceName to "MingLiU"
dwFontSize = CHT_FONTSIZE;
break;
case KOR_CODEPAGE:
_tcscpy(szFaceNameDef, szKORFaceName);//On KOR, set the FaceName to "GulimChe"
dwFontSize = KOR_FONTSIZE;
break;
case CHS_CODEPAGE:
_tcscpy(szFaceNameDef, szCHSFaceName);//On CHS, set the FaceName to "NSimSun"
dwFontSize = CHS_FONTSIZE;
break;
default:
_tcscpy(szFaceNameDef,L"\0");
break;
}
dwFaceNameSize = (DWORD)(( _tcslen( szFaceNameDef ) + 1 ) * sizeof( TCHAR ));
if( !RegOpenKeyEx( HKEY_USERS, REG_CONSOLE_KEY, 0, KEY_SET_VALUE, &hk ) )
{
DWORD dwVal;
LPWSTR lpszKey;
HKEY hk2;
RegSetValueEx(
hk,
L"FaceName",
0,
REG_SZ,
(LPBYTE) szFaceNameDef,
dwFaceNameSize
);
dwSize = sizeof( DWORD );
SETREGISTRYDW( dwFontFamily, NULL, hk, L"FontFamily", dwVal,dwSize );
lpszKey = NULL;
if ( !FormSACSessKeyForCmd( &lpszKey ) ) {
RegCloseKey( hk );
return( FALSE );
}
hk2 = NULL;
if ( RegCreateKey( HKEY_USERS, lpszKey, &hk2 ) ) {
free(lpszKey);
return( FALSE );
}
free(lpszKey);
dwSize = sizeof( DWORD );
SETREGISTRYDW( dwFontFamily, hk, hk2, L"FontFamily", dwVal, dwSize);
SETREGISTRYDW( dwCodePage, hk, hk2, L"CodePage", dwVal, dwSize );
SETREGISTRYDW( dwFontSize, hk, hk2, L"FontSize", dwVal, dwSize);
SETREGISTRYDW( dwFontWeight, hk, hk2, L"FontWeight", dwVal, dwSize );
SETREGISTRYDW( dwHistoryNoDup, hk, hk2, L"HistoryNoDup", dwVal, dwSize );
RegSetValueEx(
hk2,
L"FaceName",
0,
REG_SZ,
(LPBYTE) szFaceNameDef,
dwFaceNameSize
);
RegCloseKey( hk2 );
RegCloseKey( hk );
return( TRUE );
}
return ( FALSE );
}
BOOL
InitializeGlobalObjects(
VOID
)
/*++
Routine Description:
This routine performs init of the global settings
needed by the service or will be needed by the session.
Arguments:
None
Return Value:
TRUE - We completed successfully
FALSE - otherwise
--*/
{
DWORD dwCodePage;
BOOL bStatus;
do {
//
// notify the SAC driver that we are ready to launch sessions
//
bStatus = InitSacCmd();
if (! bStatus) {
SvcDebugOut("RUN: Failed SAC init: %x\n", bStatus);
break;
}
//
// make sure we have the Console fonts set up properly for
// far-east builds. We need to do this, or when we call
// ReadConsoleOutput in sacsess, we will get back a malformed
// screen frame buffer - it will not have properly constructed
// double width jpn chars, for instance.
//
dwCodePage = GetACP();
if ( dwCodePage == JAP_CODEPAGE ||
dwCodePage == CHS_CODEPAGE ||
dwCodePage == CHT_CODEPAGE ||
dwCodePage == KOR_CODEPAGE ) {
//
// Fareast code page
//
bStatus = HandleFarEastSpecificRegKeys();
if( !bStatus )
{
SvcDebugOut("RUN: Failed to handle FES init: %x\n", bStatus);
break;
}
}
} while ( FALSE );
return bStatus;
}
BOOL
Run(
VOID
)
/*++
Routine Description:
This routine registers the service with the SAC driver
and waits for messages from the SAC driver to launch SAC sessions.
Arguments:
None
Return Value:
TRUE - We completed successfully
FALSE - otherwise
--*/
{
BOOL Status;
do {
//
//
//
Status = InitializeGlobalObjects();
if (! Status) {
SvcDebugOut("RUN: Failed init of global objects: %x\n", Status);
break;
}
//
//
//
Status = ListenerThread();
if (! Status) {
SvcDebugOut("RUN: Failed Listener: %x\n", Status);
break;
}
} while (FALSE);
return Status;
}
BOOL
Stop(
VOID
)
/*++
Routine Description:
Shutdown the service, which in this case implies
that we unregister with the SAC driver so it knows
we aren't listening anymore.
Arguments:
None
Return Value:
TRUE - We completed successfully
FALSE - otherwise
--*/
{
BOOL Status;
Status = ShutdownSacCmd();
return Status;
}