//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: client.c // // History: // Abolade Gbadegesin July-25-1995 Created // // Client struct routines and I/O routines for tracing dll //============================================================================ #include #include #include #include #include #include #include "trace.h" // // assumes server is locked for writing // DWORD TraceCreateClient( LPTRACE_CLIENT *lplpclient ) { DWORD dwErr; LPTRACE_CLIENT lpclient; lpclient = HeapAlloc(GetProcessHeap(), 0, sizeof(TRACE_CLIENT)); if (lpclient == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } // // initialize fields in the client structure // lpclient->TC_ClientID = MAX_CLIENT_COUNT; lpclient->TC_Flags = 0; lpclient->TC_File = NULL; lpclient->TC_Console = NULL; lpclient->TC_ConfigKey = NULL; lpclient->TC_ConfigEvent = NULL; lpclient->TC_MaxFileSize = DEF_MAXFILESIZE; ZeroMemory(lpclient->TC_ClientNameA, MAX_CLIENTNAME_LENGTH * sizeof(CHAR)); ZeroMemory(lpclient->TC_ClientNameW, MAX_CLIENTNAME_LENGTH * sizeof(WCHAR)); lstrcpy(lpclient->TC_FileDir, DEF_FILEDIRECTORY); #ifdef UNICODE wcstombs( lpclient->TC_FileDirA, lpclient->TC_FileDirW, lstrlenW(lpclient->TC_FileDirW) + 1 ); #else mbstowcs( lpclient->TC_FileDirW, lpclient->TC_FileDirA, lstrlenA(lpclient->TC_FileDirA) + 1 ); #endif __try { TRACE_STARTUP_LOCKING(lpclient); dwErr = NO_ERROR; } __except(EXCEPTION_EXECUTE_HANDLER) { dwErr = GetExceptionCode(); HeapFree(GetProcessHeap(), 0, lpclient); lpclient = NULL; } InterlockedExchangePointer(lplpclient, lpclient); return dwErr; } // // assumes server is locked for writing and client is locked for writing // DWORD TraceDeleteClient( LPTRACE_SERVER lpserver, LPTRACE_CLIENT *lplpclient ) { LPTRACE_CLIENT lpclient; if (lplpclient == NULL || *lplpclient == NULL) { return ERROR_INVALID_PARAMETER; } lpclient = *lplpclient; InterlockedExchangePointer(lplpclient, NULL); InterlockedExchange(lpserver->TS_FlagsCache + lpclient->TC_ClientID, 0); TRACE_CLEANUP_LOCKING(lpclient); // // closing this key will cause the event to be signalled // however, we hold the lock on the table so the server thread // will be blocked until the cleanup completes // if (lpclient->TC_ConfigKey != NULL) { RegCloseKey(lpclient->TC_ConfigKey); } if (lpclient->TC_ConfigEvent != NULL) { CloseHandle(lpclient->TC_ConfigEvent); } if (TRACE_CLIENT_USES_CONSOLE(lpclient)) { TraceCloseClientConsole(lpserver, lpclient); } if (TRACE_CLIENT_USES_FILE(lpclient)) { TraceCloseClientFile(lpclient); } HeapFree(GetProcessHeap(), 0, lpclient); return 0; } // // assumes server is locked for reading or for writing // LPTRACE_CLIENT TraceFindClient( LPTRACE_SERVER lpserver, LPCTSTR lpszClient ) { DWORD dwClient; LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend; lplpcstart = lpserver->TS_ClientTable; lplpcend = lplpcstart + MAX_CLIENT_COUNT; for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc != NULL && lstrcmp((*lplpc)->TC_ClientName, lpszClient) == 0) { break; } } return (lplpc < lplpcend) ? *lplpc : NULL; } // // assumes that the server is locked for writing, // and that the client is locked for writing // also assumes the client is not already a console client // DWORD TraceOpenClientConsole(LPTRACE_SERVER lpserver, LPTRACE_CLIENT lpclient) { DWORD dwErr; COORD screen; HANDLE hConsole; // // if all console tracing is disabled, do nothing // if ((lpserver->TS_Flags & TRACEFLAGS_USECONSOLE) == 0) { return 0; } // // create the console if it isn't already created // if (lpserver->TS_Console == NULL) { COORD screen; // // allocate a console and set the buffer size // AllocConsole(); lpserver->TS_Console = GetStdHandle(STD_INPUT_HANDLE); if (lpserver->TS_Console == INVALID_HANDLE_VALUE ) return GetLastError(); } // // allocate a console for this client // hConsole = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); if (hConsole == INVALID_HANDLE_VALUE) { return GetLastError(); } // // set the buffer to the standard size // and save the console buffer handle // screen.X = DEF_SCREENBUF_WIDTH; screen.Y = DEF_SCREENBUF_HEIGHT; SetConsoleScreenBufferSize(hConsole, screen); lpclient->TC_Console = hConsole; // // see if there was a previous console client; // if not, set this new one's screen buffer to be // the active screen buffer // if (lpserver->TS_ConsoleOwner == MAX_CLIENT_COUNT) { TraceUpdateConsoleOwner(lpserver, 1); } return 0; } // // assumes that the server is locked for writing, // and that the client is locked for writing // also assumes the client is already a console client // DWORD TraceCloseClientConsole( LPTRACE_SERVER lpserver, LPTRACE_CLIENT lpclient ) { HANDLE hConsole; // // if all console tracing is disabled, do nothing // if ((lpserver->TS_Flags & TRACEFLAGS_USECONSOLE) == 0) { return 0; } // // close the client's screen buffer and associated handles // if (lpclient->TC_Console != NULL) { CloseHandle(lpclient->TC_Console); lpclient->TC_Console = NULL; } // // if the client owned the screen, find another owner // if (lpserver->TS_ConsoleOwner == lpclient->TC_ClientID) { TraceUpdateConsoleOwner(lpserver, 1); } // // if no owner was found, free the server's console // if (lpserver->TS_ConsoleOwner == MAX_CLIENT_COUNT || lpserver->TS_ConsoleOwner == lpclient->TC_ClientID) { lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT; CloseHandle(lpserver->TS_Console); lpserver->TS_Console = NULL; FreeConsole(); } return 0; } // // assumes that the server is locked for reading or writing // and that the client is locked for writing // DWORD TraceCreateClientFile( LPTRACE_CLIENT lpclient ) { DWORD dwErr; HANDLE hFile; LPOVERLAPPED lpovl; TCHAR szFilename[MAX_PATH]; // // create the directory in case it doesn't exist // CreateDirectory(lpclient->TC_FileDir, NULL); // // figure out the file name // lstrcpy(szFilename, lpclient->TC_FileDir); lstrcat(szFilename, STR_DIRSEP); lstrcat(szFilename, lpclient->TC_ClientName); lstrcat(szFilename, STR_LOGEXT); // // open the file, disabling write sharing // hFile = CreateFile( szFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { return GetLastError(); } SetFilePointer(hFile, 0, NULL, FILE_END); lpclient->TC_File = hFile; return 0; } // // assumes that the server is locked for reading or writing // and that the client is locked for writing // DWORD TraceMoveClientFile( LPTRACE_CLIENT lpclient ) { TCHAR szDestname[MAX_PATH], szSrcname[MAX_PATH]; lstrcpy(szSrcname, lpclient->TC_FileDir); lstrcat(szSrcname, STR_DIRSEP); lstrcat(szSrcname, lpclient->TC_ClientName); lstrcpy(szDestname, szSrcname); lstrcat(szSrcname, STR_LOGEXT); lstrcat(szDestname, STR_OLDEXT); // // close the file handle if it is open // TraceCloseClientFile(lpclient); // // do the move // MoveFileEx( szSrcname, szDestname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED ); // // re-open the log file // return TraceCreateClientFile(lpclient); } // // assumes that the server is locked for reading or writing // and that the client is locked for writing // DWORD TraceCloseClientFile( LPTRACE_CLIENT lpclient ) { if (lpclient->TC_File != NULL) { CloseHandle(lpclient->TC_File); lpclient->TC_File = NULL; } return 0; } // // assumes that the server is locked for reading or writing // and that the client is locked for reading // DWORD TraceWriteOutput( LPTRACE_SERVER lpserver, LPTRACE_CLIENT lpclient, DWORD dwFlags, LPCTSTR lpszOutput ) { BOOL bSuccess; DWORD dwFileMask, dwConsoleMask; DWORD dwErr, dwFileSize, dwBytesToWrite, dwBytesWritten, dwChars; dwBytesWritten = 0; dwBytesToWrite = lstrlen(lpszOutput) * sizeof(TCHAR); dwFileMask = dwConsoleMask = 1; // // if the client uses output masking, compute the mask for this message // if (dwFlags & TRACE_USE_MASK) { dwFileMask = (dwFlags & lpclient->TC_FileMask); dwConsoleMask = (dwFlags & lpclient->TC_ConsoleMask); } if (TRACE_CLIENT_USES_FILE(lpclient) && dwFileMask != 0 && lpclient->TC_File != NULL) { // // check the size of the file to see if it needs renaming // dwFileSize = GetFileSize(lpclient->TC_File, NULL); if ((dwFileSize + dwBytesToWrite) > lpclient->TC_MaxFileSize) { TRACE_READ_TO_WRITELOCK(lpclient); // // move the existing file over and start with an empty one // dwErr = TraceMoveClientFile(lpclient); if (dwErr!=NO_ERROR) return dwErr; dwFileSize = 0; TRACE_WRITE_TO_READLOCK(lpclient); } // // perform the write operation // bSuccess = WriteFile( lpclient->TC_File, lpszOutput, dwBytesToWrite, &dwBytesWritten, NULL ); } if (TRACE_CLIENT_USES_CONSOLE(lpclient) && dwConsoleMask != 0 && lpclient->TC_Console != NULL) { // // write to the console directly; this is less costly // than writing to a file, which is fortunate since we // cannot use completion ports with console handles // dwChars = dwBytesToWrite / sizeof(TCHAR); bSuccess = WriteConsole( lpclient->TC_Console, lpszOutput, dwChars, &dwChars, NULL ); } return dwBytesWritten; } //---------------------------------------------------------------------------- // Function: TraceDumpLine // // Parameters: // LPTRACE_CLIENT lpclient pointer to client struct for caller // LPBYTE lpbBytes address of buffer to dump // DWORD dwLine length of line in bytes // DWORD dwGroup size of byte groupings // BOOL bPrefixAddr if TRUE, prefix lines with addresses // LPBYTE lpbPrefix address with which to prefix lines // LPTSTR lpszPrefix optional string with which to prefix lines //---------------------------------------------------------------------------- DWORD TraceDumpLine( LPTRACE_SERVER lpserver, LPTRACE_CLIENT lpclient, DWORD dwFlags, LPBYTE lpbBytes, DWORD dwLine, DWORD dwGroup, BOOL bPrefixAddr, LPBYTE lpbPrefix, LPCTSTR lpszPrefix ) { INT offset; LPTSTR lpszHex, lpszAscii; TCHAR szBuffer[256] = TEXT("\r\n"); TCHAR szAscii[BYTES_PER_DUMPLINE + 2] = TEXT(""); TCHAR szHex[(3 * BYTES_PER_DUMPLINE) + 1] = TEXT(""); TCHAR szDigits[] = TEXT("0123456789ABCDEF"); // // prepend prefix string if necessary // if (lpszPrefix != NULL) { lstrcat(szBuffer, lpszPrefix); } // // prepend address if needed // if (bPrefixAddr) { LPTSTR lpsz; ULONG_PTR ulpAddress = (ULONG_PTR) lpbPrefix; ULONG i; // // each line prints out a hex-digit // with the most-significant digit leftmost in the string // prepend address to lpsz[1]..lpsz[2*sizeof(ULONG_PTR)] // lpsz = szBuffer + lstrlen(szBuffer); for (i=0; i<2*sizeof(ULONG_PTR); i++) { lpsz[2*sizeof(ULONG_PTR)-i] = szDigits[ulpAddress & 0x0F]; ulpAddress >>= 4; } lpsz[2*sizeof(ULONG_PTR) + 1] = TEXT(':'); lpsz[2*sizeof(ULONG_PTR) + 2] = TEXT(' '); lpsz[2*sizeof(ULONG_PTR) + 3] = TEXT('\0'); } lpszHex = szHex; lpszAscii = szAscii; // // rather than test the size of the grouping every time through // a loop, have a loop for each group size // switch(dwGroup) { // // single byte groupings // case 1: { while (dwLine >= sizeof(BYTE)) { // // print hex digits // *lpszHex++ = szDigits[*lpbBytes / 16]; *lpszHex++ = szDigits[*lpbBytes % 16]; *lpszHex++ = TEXT(' '); // // print ascii characters // *lpszAscii++ = (*lpbBytes >= 0x20 && *lpbBytes < 0x80) ? *lpbBytes : TEXT('.'); ++lpbBytes; --dwLine; } break; } // // word-sized groupings // case 2: { WORD wBytes; BYTE loByte, hiByte; // // should already be aligned on a word boundary // while (dwLine >= sizeof(WORD)) { wBytes = *(LPWORD)lpbBytes; loByte = LOBYTE(wBytes); hiByte = HIBYTE(wBytes); // print hex digits *lpszHex++ = szDigits[hiByte / 16]; *lpszHex++ = szDigits[hiByte % 16]; *lpszHex++ = szDigits[loByte / 16]; *lpszHex++ = szDigits[loByte % 16]; *lpszHex++ = TEXT(' '); // print ascii characters *lpszAscii++ = (hiByte >= 0x20 && hiByte < 0x80) ? hiByte : TEXT('.'); *lpszAscii++ = (loByte >= 0x20 && loByte < 0x80) ? loByte : TEXT('.'); dwLine -= sizeof(WORD); lpbBytes += sizeof(WORD); } break; } // // double-word sized groupings // case 4: { DWORD dwBytes; BYTE loloByte, lohiByte, hiloByte, hihiByte; // // should already be aligned on a double-word boundary // while (dwLine >= sizeof(DWORD)) { dwBytes = *(LPDWORD)lpbBytes; hihiByte = HIBYTE(HIWORD(dwBytes)); lohiByte = LOBYTE(HIWORD(dwBytes)); hiloByte = HIBYTE(LOWORD(dwBytes)); loloByte = LOBYTE(LOWORD(dwBytes)); // print hex digits *lpszHex++ = szDigits[hihiByte / 16]; *lpszHex++ = szDigits[hihiByte % 16]; *lpszHex++ = szDigits[lohiByte / 16]; *lpszHex++ = szDigits[lohiByte % 16]; *lpszHex++ = szDigits[hiloByte / 16]; *lpszHex++ = szDigits[hiloByte % 16]; *lpszHex++ = szDigits[loloByte / 16]; *lpszHex++ = szDigits[loloByte % 16]; *lpszHex++ = TEXT(' '); // print ascii characters *lpszAscii++ = (hihiByte >= 0x20 && hihiByte < 0x80) ? hihiByte : TEXT('.'); *lpszAscii++ = (lohiByte >= 0x20 && lohiByte < 0x80) ? lohiByte : TEXT('.'); *lpszAscii++ = (hiloByte >= 0x20 && hiloByte < 0x80) ? hiloByte : TEXT('.'); *lpszAscii++ = (loloByte >= 0x20 && loloByte < 0x80) ? loloByte : TEXT('.'); // on to the next double-word dwLine -= sizeof(DWORD); lpbBytes += sizeof(DWORD); } break; } default: break; } *lpszHex = *lpszAscii = TEXT('\0'); lstrcat(szBuffer, szHex); lstrcat(szBuffer, TEXT("|")); lstrcat(szBuffer, szAscii); lstrcat(szBuffer, TEXT("|")); return TraceWriteOutput(lpserver, lpclient, dwFlags, szBuffer); }