--- /dev/null
+/* $Id: output.c,v 1.1 2003/03/31 22:28:59 hyperion Exp $
+ *
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS system libraries
+ * FILE: lib/kernel32/debug/debugger.c
+ * PURPOSE: OutputDebugString()
+ * PROGRAMMER: KJK::Hyperion <noog@libero.it>
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <k32.h>
+
+/* FUNCTIONS *****************************************************************/
+
+/* Open or create the mutex used to communicate with the debug monitor */
+HANDLE K32CreateDBMonMutex(void)
+{
+ static SID_IDENTIFIER_AUTHORITY siaNTAuth = SECURITY_NT_AUTHORITY;
+ static SID_IDENTIFIER_AUTHORITY siaWorldAuth = SECURITY_WORLD_SID_AUTHORITY;
+
+ HANDLE hMutex;
+
+ /* SIDs to be used in the DACL */
+ PSID psidSystem = NULL;
+ PSID psidAdministrators = NULL;
+ PSID psidEveryone = NULL;
+
+ /* buffer for the DACL */
+ PVOID pDaclBuf = NULL;
+
+ /*
+ minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
+ headers. We'll add the size of SIDs when we'll know it
+ */
+ SIZE_T nDaclBufSize =
+ sizeof(ACL) +
+ (
+ sizeof(ACCESS_ALLOWED_ACE) -
+ sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart)
+ ) * 3;
+
+ /* security descriptor of the mutex */
+ SECURITY_DESCRIPTOR sdMutexSecurity;
+
+ /* attributes of the mutex object we'll create */
+ SECURITY_ATTRIBUTES saMutexAttribs =
+ {
+ sizeof(saMutexAttribs),
+ &sdMutexSecurity,
+ TRUE
+ };
+
+ NTSTATUS nErrCode;
+
+ /* first, try to open the mutex */
+ hMutex = OpenMutexW
+ (
+ SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
+ TRUE,
+ L"DBWinMutex"
+ );
+
+ if(hMutex != NULL)
+ {
+ /* success */
+ return hMutex;
+ }
+ /* error other than the mutex not being found */
+ else if(GetLastError() != ERROR_FILE_NOT_FOUND)
+ {
+ /* failure */
+ return NULL;
+ }
+
+ /* if the mutex doesn't exist, create it */
+#if 0 /* please uncomment when GCC supports SEH */
+ __try
+ {
+#else
+#define __leave goto l_Cleanup
+#endif
+ /* first, set up the mutex security */
+ /* allocate the NT AUTHORITY\SYSTEM SID */
+ nErrCode = RtlAllocateAndInitializeSid
+ (
+ &siaNTAuth,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &psidSystem
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* allocate the BUILTIN\Administrators SID */
+ nErrCode = RtlAllocateAndInitializeSid
+ (
+ &siaNTAuth,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &psidAdministrators
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* allocate the Everyone SID */
+ nErrCode = RtlAllocateAndInitializeSid
+ (
+ &siaWorldAuth,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &psidEveryone
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* allocate space for the SIDs too */
+ nDaclBufSize += RtlLengthSid(psidSystem);
+ nDaclBufSize += RtlLengthSid(psidAdministrators);
+ nDaclBufSize += RtlLengthSid(psidEveryone);
+
+ /* allocate the buffer for the DACL */
+ pDaclBuf = GlobalAlloc(GMEM_FIXED, nDaclBufSize);
+
+ /* failure */
+ if(pDaclBuf == NULL) __leave;
+
+ /* create the DACL */
+ nErrCode = RtlCreateAcl(pDaclBuf, nDaclBufSize, ACL_REVISION);
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* grant the minimum required access to Everyone */
+ nErrCode = RtlAddAccessAllowedAce
+ (
+ pDaclBuf,
+ ACL_REVISION,
+ SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
+ psidEveryone
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* grant full access to BUILTIN\Administrators */
+ nErrCode = RtlAddAccessAllowedAce
+ (
+ pDaclBuf,
+ ACL_REVISION,
+ MUTANT_ALL_ACCESS,
+ psidAdministrators
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* grant full access to NT AUTHORITY\SYSTEM */
+ nErrCode = RtlAddAccessAllowedAce
+ (
+ pDaclBuf,
+ ACL_REVISION,
+ MUTANT_ALL_ACCESS,
+ psidSystem
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* create the security descriptor */
+ nErrCode = RtlCreateSecurityDescriptor
+ (
+ &sdMutexSecurity,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* set the descriptor's DACL to the ACL we created */
+ nErrCode = RtlSetDaclSecurityDescriptor
+ (
+ &sdMutexSecurity,
+ TRUE,
+ pDaclBuf,
+ FALSE
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) __leave;
+
+ /* create the mutex */
+ hMutex = CreateMutexW(&saMutexAttribs, FALSE, L"DBWinMutex");
+#if 0
+ }
+ __finally
+ {
+#else
+l_Cleanup:
+#endif
+ /* free the buffers */
+ if(pDaclBuf) GlobalFree(pDaclBuf);
+ if(psidEveryone) RtlFreeSid(psidEveryone);
+ if(psidAdministrators) RtlFreeSid(psidAdministrators);
+ if(psidSystem) RtlFreeSid(psidSystem);
+#if 0
+ }
+#endif
+
+ return hMutex;
+}
+
+VOID WINAPI OutputDebugStringA(LPCSTR _OutputString)
+{
+#if 0
+/* FIXME: this will be pointless until GCC does SEH */
+ __try
+ {
+ ULONG_PTR a_nArgs[2];
+
+ a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
+ a_nArgs[1] = (ULONG_PTR)_OutputString;
+
+ /* send the string to the user-mode debugger */
+ RaiseException(0x40010006, 0, 2, a_nArgs);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+#endif
+ /*
+ no user-mode debugger: try the systemwide debug message monitor, or the
+ kernel debugger as a last resort
+ */
+
+ /* mutex used to synchronize invocations of OutputDebugString */
+ static HANDLE s_hDBMonMutex = NULL;
+ /* true if we already attempted to open/create the mutex */
+ static BOOL s_bDBMonMutexTriedOpen = FALSE;
+
+ /* local copy of the mutex handle */
+ HANDLE hDBMonMutex = s_hDBMonMutex;
+ /* handle to the Section of the shared buffer */
+ HANDLE hDBMonBuffer = NULL;
+ /*
+ pointer to the mapped view of the shared buffer. It consist of the current
+ process id followed by the message string
+ */
+ struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
+ /*
+ event: signaled by the debug message monitor when OutputDebugString can write
+ to the shared buffer
+ */
+ HANDLE hDBMonBufferReady = NULL;
+ /*
+ event: to be signaled by OutputDebugString when it's done writing to the
+ shared buffer
+ */
+ HANDLE hDBMonDataReady = NULL;
+
+ /* mutex not opened, and no previous attempts to open/create it */
+ if(hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
+ {
+ /* open/create the mutex */
+ hDBMonMutex = K32CreateDBMonMutex();
+ /* store the handle */
+ s_hDBMonMutex = hDBMonMutex;
+ }
+
+#if 0
+ __try
+ {
+#endif
+ /* opening the mutex failed */
+ if(hDBMonMutex == NULL)
+ {
+ /* remember next time */
+ s_bDBMonMutexTriedOpen = TRUE;
+ }
+ /* opening the mutex succeeded */
+ else
+ {
+ do
+ {
+ /* synchronize with other invocations of OutputDebugString */
+ WaitForSingleObject(hDBMonMutex, INFINITE);
+
+ /* buffer of the system-wide debug message monitor */
+ hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
+
+ /* couldn't open the buffer: send the string to the kernel debugger */
+ if(hDBMonBuffer == NULL) break;
+
+ /* map the buffer */
+ pDBMonBuffer = MapViewOfFile
+ (
+ hDBMonBuffer,
+ SECTION_MAP_READ | SECTION_MAP_WRITE,
+ 0,
+ 0,
+ 0
+ );
+
+ /* couldn't map the buffer: send the string to the kernel debugger */
+ if(pDBMonBuffer == NULL) break;
+
+ /* open the event signaling that the buffer can be accessed */
+ hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
+
+ /* couldn't open the event: send the string to the kernel debugger */
+ if(hDBMonBufferReady == NULL) break;
+
+ /* open the event to be signaled when the buffer has been filled */
+ hDBMonDataReady =
+ OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
+ }
+ while(0);
+
+ /*
+ we couldn't connect to the system-wide debug message monitor: send the
+ string to the kernel debugger
+ */
+ if(hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
+ }
+
+#if 0
+ __try
+#else
+ do
+#endif
+ {
+ /* size of the current output block */
+ SIZE_T nRoundLen;
+ /* size of the remainder of the string */
+ SIZE_T nOutputStringLen;
+
+ for
+ (
+ /* output the whole string */
+ nOutputStringLen = strlen(_OutputString);
+ /* repeat until the string has been fully output */
+ nOutputStringLen > 0;
+ /* move to the next block */
+ _OutputString += nRoundLen, nOutputStringLen -= nRoundLen
+ )
+ {
+ /*
+ we're connected to the debug monitor: write the current block to the
+ shared buffer
+ */
+ if(hDBMonDataReady)
+ {
+ /*
+ wait a maximum of 10 seconds for the debug monitor to finish processing
+ the shared buffer
+ */
+ if(WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
+ {
+ /* timeout or failure: give up */
+ break;
+ }
+
+ /* write the process id into the buffer */
+ pDBMonBuffer->ProcessId = GetCurrentProcessId();
+
+ /* write only as many bytes as they fit in the buffer */
+ if(nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
+ nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
+ else
+ nRoundLen = nOutputStringLen;
+
+ /* copy the current block into the buffer */
+ memcpy(pDBMonBuffer->Buffer, _OutputString, nOutputStringLen);
+
+ /* null-terminate the current block */
+ pDBMonBuffer->Buffer[nOutputStringLen] = 0;
+
+ /* signal that the data contains meaningful data and can be read */
+ SetEvent(hDBMonDataReady);
+ }
+ /* else, send the current block to the kernel debugger */
+ else
+ {
+ /* output in blocks of 512 characters */
+ CHAR a_cBuffer[512];
+
+ /* write a maximum of 511 bytes */
+ if(nOutputStringLen > (sizeof(a_cBuffer) - 1))
+ nRoundLen = sizeof(a_cBuffer) - 1;
+ else
+ nRoundLen = nOutputStringLen;
+
+ /* copy the current block */
+ memcpy(a_cBuffer, _OutputString, nRoundLen);
+
+ /* null-terminate the current block */
+ a_cBuffer[nRoundLen] = 0;
+
+ /* send the current block to the kernel debugger */
+ DbgPrint("%s", a_cBuffer);
+ }
+ }
+ }
+#if 0
+ /* ignore access violations and let other exceptions fall through */
+ __except
+ (
+ (GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
+ EXCEPTION_EXECUTE_HANDLER :
+ EXCEPTION_CONTINUE_SEARCH
+ )
+ {
+ /* string copied verbatim from Microsoft's kernel32.dll */
+ DbgPrint("\nOutputDebugString faulted during output\n");
+ }
+#else
+ while(0);
+#endif
+
+#if 0
+ }
+ __finally
+ {
+#endif
+ /* close all the still open resources */
+ if(hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
+ if(pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
+ if(hDBMonBuffer) CloseHandle(hDBMonBuffer);
+ if(hDBMonDataReady) CloseHandle(hDBMonDataReady);
+
+ /* leave the critical section */
+ ReleaseMutex(hDBMonMutex);
+#if 0
+ }
+ }
+#endif
+}
+
+VOID WINAPI OutputDebugStringW(LPCWSTR _OutputString)
+{
+ UNICODE_STRING wstrOut;
+ ANSI_STRING strOut;
+ NTSTATUS nErrCode;
+
+ /* convert the string in ANSI */
+ RtlInitUnicodeString(&wstrOut, _OutputString);
+ nErrCode = RtlUnicodeStringToAnsiString(&strOut, &wstrOut, TRUE);
+
+ if(!NT_SUCCESS(nErrCode))
+ {
+ /*
+ Microsoft's kernel32.dll always prints something, even in case the conversion
+ fails
+ */
+ OutputDebugStringA("");
+ }
+ else
+ {
+ /* output the converted string */
+ OutputDebugStringA(strOut.Buffer);
+
+ /* free the converted string */
+ RtlFreeAnsiString(&strOut);
+ }
+}
+
+/* EOF */