bool CRemoteCacheLink::EnsureCommandPipeOpen() { AutoLocker lock(m_critSec); if(m_hCommandPipe != INVALID_HANDLE_VALUE) { return true; } m_hCommandPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file if (m_hCommandPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY) { // TSVNCache is running but is busy connecting a different client. // Do not give up immediately but wait for a few milliseconds until // the server has created the next pipe instance if (WaitNamedPipe(GetCacheCommandPipeName(), 50)) { m_hCommandPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file } } if (m_hCommandPipe != INVALID_HANDLE_VALUE) { // The pipe connected; change to message-read mode. DWORD dwMode; dwMode = PIPE_READMODE_MESSAGE; if(!SetNamedPipeHandleState( m_hCommandPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL)) // don't set maximum time { ATLTRACE("SetNamedPipeHandleState failed"); CloseHandle(m_hCommandPipe); m_hCommandPipe = INVALID_HANDLE_VALUE; return false; } return true; } return false; }
void CSettings::HandleRestart() { int restart = ISettingsPropPage::Restart_None; restart |= m_pMainPage->GetRestart(); restart |= m_pOverlayPage->GetRestart(); restart |= m_pOverlaysPage->GetRestart(); restart |= m_pOverlayHandlersPage->GetRestart(); restart |= m_pProxyPage->GetRestart(); restart |= m_pProgsDiffPage->GetRestart(); restart |= m_pProgsMergePage->GetRestart(); restart |= m_pProgsAlternativeEditor->GetRestart(); restart |= m_pLookAndFeelPage->GetRestart(); restart |= m_pDialogsPage->GetRestart(); restart |= m_pColorsPage->GetRestart(); restart |= m_pColorsPage2->GetRestart(); restart |= m_pColorsPage3->GetRestart(); restart |= m_pSavedPage->GetRestart(); restart |= m_pHooksPage->GetRestart(); restart |= m_pBugTraqPage->GetRestart(); restart |= m_pTBlamePage->GetRestart(); restart |= m_pGitConfig->GetRestart(); restart |= m_pGitRemote->GetRestart(); restart |= m_pBugTraqPage->GetRestart(); restart |= m_pExtMenu->GetRestart(); restart |= m_pAdvanced->GetRestart(); if (restart & ISettingsPropPage::Restart_System) { DWORD_PTR res = 0; ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_ABORTIFHUNG, 20, &res); CMessageBox::Show(NULL, IDS_SETTINGS_RESTARTSYSTEM, IDS_APPNAME, MB_ICONINFORMATION); } if (restart & ISettingsPropPage::Restart_Cache) { DWORD_PTR res = 0; ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_ABORTIFHUNG, 20, &res); // tell the cache to refresh everything HANDLE hPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file if (hPipe != INVALID_HANDLE_VALUE) { // The pipe connected; change to message-read mode. DWORD dwMode; dwMode = PIPE_READMODE_MESSAGE; if (SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL)) // don't set maximum time { DWORD cbWritten; TGITCacheCommand cmd; SecureZeroMemory(&cmd, sizeof(TGITCacheCommand)); cmd.command = TGITCACHECOMMAND_REFRESHALL; BOOL fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O if (! fSuccess || sizeof(cmd) != cbWritten) { DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } if (hPipe != INVALID_HANDLE_VALUE) { // now tell the cache we don't need it's command thread anymore DWORD cbWritten; TGITCacheCommand cmd; SecureZeroMemory(&cmd, sizeof(TGITCacheCommand)); cmd.command = TGITCACHECOMMAND_END; WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } } else { ATLTRACE("SetNamedPipeHandleState failed"); CloseHandle(hPipe); } } } }
void CCacheDlg::RemoveFromCache(const CString& path) { // if we use the external cache, we tell the cache directly that something // has changed, without the detour via the shell. HANDLE hPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing nullptr, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes nullptr); // no template file if (hPipe != INVALID_HANDLE_VALUE) { // The pipe connected; change to message-read mode. DWORD dwMode; dwMode = PIPE_READMODE_MESSAGE; if(SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL)) // don't set maximum time { DWORD cbWritten; TGITCacheCommand cmd; cmd.command = TGITCACHECOMMAND_CRAWL; wcsncpy_s(cmd.path, path, MAX_PATH); BOOL fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O if (! fSuccess || sizeof(cmd) != cbWritten) { DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } if (hPipe != INVALID_HANDLE_VALUE) { // now tell the cache we don't need it's command thread anymore DWORD cbWritten2; TGITCacheCommand cmd2; cmd2.command = TGITCACHECOMMAND_END; WriteFile( hPipe, // handle to pipe &cmd2, // buffer to write from sizeof(cmd2), // number of bytes to write &cbWritten2, // number of bytes written NULL); // not overlapped I/O DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } } else { ATLTRACE("SetNamedPipeHandleState failed"); CloseHandle(hPipe); } } }
void CShellUpdater::UpdateShell() { // Tell the shell extension to purge its cache CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Setting cache invalidation event %I64u\n", GetTickCount64()); SetEvent(m_hInvalidationEvent); // We use the SVN 'notify' call-back to add items to the list // Because this might call-back more than once per file (for example, when committing) // it's possible that there may be duplicates in the list. // There's no point asking the shell to do more than it has to, so we remove the duplicates before // passing the list on m_pathsForUpdating.RemoveDuplicates(); // if we use the external cache, we tell the cache directly that something // has changed, without the detour via the shell. CAutoFile hPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file if (!hPipe) return; // The pipe connected; change to message-read mode. DWORD dwMode = PIPE_READMODE_MESSAGE; if(SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL) == 0) // don't set maximum time { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": SetNamedPipeHandleState failed"); return; } for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Cache Item Update for %s (%I64u)\n", m_pathsForUpdating[nPath].GetDirectory().GetWinPath(), GetTickCount64()); if (!m_pathsForUpdating[nPath].IsDirectory()) { // send notifications to the shell for changed files - folders are updated by the cache itself. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, m_pathsForUpdating[nPath].GetWinPath(), NULL); } TSVNCacheCommand cmd; cmd.command = TSVNCACHECOMMAND_CRAWL; wcsncpy_s(cmd.path, m_pathsForUpdating[nPath].GetDirectory().GetWinPath(), _countof(cmd.path)-1); DWORD cbWritten; BOOL fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O if (! fSuccess || sizeof(cmd) != cbWritten) { DisconnectNamedPipe(hPipe); return; } } // now tell the cache we don't need it's command thread anymore TSVNCacheCommand cmd; cmd.command = TSVNCACHECOMMAND_END; DWORD cbWritten; WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O DisconnectNamedPipe(hPipe); }
bool SendCacheCommand(BYTE command, const WCHAR * path /* = NULL */) { int retrycount = 2; CAutoFile hPipe; do { hPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file retrycount--; if (!hPipe) Sleep(10); } while ((!hPipe) && (retrycount)); if (!hPipe) { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Could not connect to pipe\n"); return false; } // The pipe connected; change to message-read mode. DWORD dwMode = PIPE_READMODE_MESSAGE; if (SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL)) // don't set maximum time { DWORD cbWritten; TSVNCacheCommand cmd; SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand)); cmd.command = command; if (path) _tcsncpy_s(cmd.path, path, _TRUNCATE); retrycount = 2; BOOL fSuccess = FALSE; do { fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O retrycount--; if (! fSuccess || sizeof(cmd) != cbWritten) Sleep(10); } while ((retrycount) && (! fSuccess || sizeof(cmd) != cbWritten)); if (! fSuccess || sizeof(cmd) != cbWritten) { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Could not write to pipe\n"); DisconnectNamedPipe(hPipe); return false; } // now tell the cache we don't need it's command thread anymore SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand)); cmd.command = TSVNCACHECOMMAND_END; WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O DisconnectNamedPipe(hPipe); } else { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": SetNamedPipeHandleState failed"); return false; } return true; }
DWORD WINAPI CommandWaitThread(LPVOID lpvParam) { ATLTRACE("CommandWaitThread started\n"); bool * bRun = (bool *)lpvParam; // The main loop creates an instance of the named pipe and // then waits for a client to connect to it. When the client // connects, a thread is created to handle communications // with that client, and the loop is repeated. DWORD dwThreadId; BOOL fConnected; CAutoFile hPipe; while (*bRun) { hPipe = CreateNamedPipe( GetCacheCommandPipeName(), PIPE_ACCESS_DUPLEX, // read/write access PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // max. instances BUFSIZE, // output buffer size BUFSIZE, // input buffer size NMPWAIT_USE_DEFAULT_WAIT, // client time-out NULL); // NULL DACL if (!hPipe) { //OutputDebugStringA("TSVNCache: CreatePipe failed\n"); //DebugOutputLastError(); if (*bRun) Sleep(200); continue; // never leave the thread! } // Wait for the client to connect; if it succeeds, // the function returns a nonzero value. If the function returns // zero, GetLastError returns ERROR_PIPE_CONNECTED. fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); if (fConnected) { // Create a thread for this client. CAutoGeneralHandle hCommandThread = CreateThread( NULL, // no security attribute 0, // default stack size CommandThread, (HANDLE) hPipe, // thread parameter 0, // not suspended &dwThreadId); // returns thread ID if (!hCommandThread) { //OutputDebugStringA("TSVNCache: Could not create Command thread\n"); //DebugOutputLastError(); DisconnectNamedPipe(hPipe); hPipe.CloseHandle(); // since we're now closing this thread, we also have to close the whole application! // otherwise the thread is dead, but the app is still running, refusing new instances // but no pipe will be available anymore. PostMessage(hWnd, WM_CLOSE, 0, 0); return 1; } // detach the handle, since we passed it to the thread hPipe.Detach(); } else { // The client could not connect, so close the pipe. //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n"); //DebugOutputLastError(); hPipe.CloseHandle(); if (*bRun) Sleep(200); continue; // don't end the thread! } } ATLTRACE("CommandWait thread exited\n"); return 0; }
bool CRemoteCacheLink::EnsureCommandPipeOpen() { AutoLocker lock(m_critSec); return InternalEnsurePipeOpen (m_hCommandPipe, GetCacheCommandPipeName()); }
bool SendCacheCommand(BYTE command, const WCHAR* path /* = nullptr */) { CAutoFile hPipe; CString pipeName = GetCacheCommandPipeName(); for (int retry = 0; retry < 2; ++retry) { if (retry > 0) WaitNamedPipe(pipeName, 50); hPipe = CreateFile( pipeName, // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing nullptr, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes nullptr); // no template file if (!hPipe && GetLastError() == ERROR_PIPE_BUSY) { // TGitCache is running but is busy connecting a different client. // Do not give up immediately but wait for a few milliseconds until // the server has created the next pipe instance continue; } break; } if (!hPipe) { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Could not connect to pipe\n"); return false; } // The pipe connected; change to message-read mode. DWORD dwMode = PIPE_READMODE_MESSAGE; if (SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode nullptr, // don't set maximum bytes nullptr)) // don't set maximum time { DWORD cbWritten; TGITCacheCommand cmd = { 0 }; cmd.command = command; if (path) wcsncpy_s(cmd.path, path, _TRUNCATE); int retrycount = 2; BOOL fSuccess = FALSE; do { fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written nullptr); // not overlapped I/O retrycount--; if (! fSuccess || sizeof(cmd) != cbWritten) Sleep(10); } while ((retrycount) && (! fSuccess || sizeof(cmd) != cbWritten)); if (! fSuccess || sizeof(cmd) != cbWritten) { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Could not write to pipe\n"); DisconnectNamedPipe(hPipe); return false; } // now tell the cache we don't need it's command thread anymore SecureZeroMemory(&cmd, sizeof(TGITCacheCommand)); cmd.command = TGITCACHECOMMAND_END; WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written nullptr); // not overlapped I/O DisconnectNamedPipe(hPipe); } else { CTraceToOutputDebugString::Instance()(__FUNCTION__ ": SetNamedPipeHandleState failed"); return false; } return true; }
void CShellUpdater::UpdateShell() { // Tell the shell extension to purge its cache ATLTRACE("Setting cache invalidation event %d\n", GetTickCount()); SetEvent(m_hInvalidationEvent); // We use the SVN 'notify' call-back to add items to the list // Because this might call-back more than once per file (for example, when committing) // it's possible that there may be duplicates in the list. // There's no point asking the shell to do more than it has to, so we remove the duplicates before // passing the list on m_pathsForUpdating.RemoveDuplicates(); // if we use the external cache, we tell the cache directly that something // has changed, without the detour via the shell. HANDLE hPipe = CreateFile( GetCacheCommandPipeName(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file if (hPipe != INVALID_HANDLE_VALUE) { // The pipe connected; change to message-read mode. DWORD dwMode; dwMode = PIPE_READMODE_MESSAGE; if(SetNamedPipeHandleState( hPipe, // pipe handle &dwMode, // new pipe mode NULL, // don't set maximum bytes NULL)) // don't set maximum time { CTGitPath path; for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++) { path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_pathsForUpdating[nPath].GetWinPathString()); ATLTRACE(_T("Cache Item Update for %s (%d)\n"), path.GetWinPathString(), GetTickCount()); if (!path.IsDirectory()) { // send notifications to the shell for changed files - folders are updated by the cache itself. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.GetWinPath(), NULL); } DWORD cbWritten; TSVNCacheCommand cmd; cmd.command = TSVNCACHECOMMAND_CRAWL; wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH); BOOL fSuccess = WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O if (! fSuccess || sizeof(cmd) != cbWritten) { DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; break; } } if (hPipe != INVALID_HANDLE_VALUE) { // now tell the cache we don't need it's command thread anymore DWORD cbWritten; TSVNCacheCommand cmd; cmd.command = TSVNCACHECOMMAND_END; WriteFile( hPipe, // handle to pipe &cmd, // buffer to write from sizeof(cmd), // number of bytes to write &cbWritten, // number of bytes written NULL); // not overlapped I/O DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } } else { ATLTRACE("SetNamedPipeHandleState failed"); CloseHandle(hPipe); } } }