desktop/inc/dp_misc.h | 5 - desktop/source/app/officeipcthread.cxx | 64 ++++----------- desktop/source/deployment/misc/dp_misc.cxx | 117 +++++++++++------------------ 3 files changed, 68 insertions(+), 118 deletions(-)
New commits: commit 522e9816589e0cddcc0ad9f2b83a33f02796ac11 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Jul 23 17:17:43 2025 +0200 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Thu Jul 24 00:15:51 2025 +0200 Reimplement office_is_running to avoid executable name check Comparing the executable name is fragile; a different character case could break it. Create a flag set in PipeIpcThread::execute, which would signal its operation. That would reliably resolve i#82778, which was the reason for that check. Change-Id: Ia1baede5f34cd480ce8cfc2d1710d40927af1139 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188244 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/desktop/inc/dp_misc.h b/desktop/inc/dp_misc.h index d1a04b21b717..43749711714a 100644 --- a/desktop/inc/dp_misc.h +++ b/desktop/inc/dp_misc.h @@ -79,6 +79,8 @@ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString makeURLAppendSysPathSegment( DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateRandomPipeId(); DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateOfficePipeId(); +DESKTOP_DEPLOYMENTMISC_DLLPUBLIC void setOfficeIpcThreadRunning(bool bRunning); + class AbortChannel; DESKTOP_DEPLOYMENTMISC_DLLPUBLIC @@ -87,7 +89,7 @@ css::uno::Reference< css::uno::XInterface> resolveUnoURL( css::uno::Reference< css::uno::XComponentContext> const & xLocalContext, AbortChannel const * abortChannel = nullptr ); - +// Check if office is running on this system (not necessarily in this process!) DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool office_is_running(); diff --git a/desktop/source/app/officeipcthread.cxx b/desktop/source/app/officeipcthread.cxx index f0a414f984a2..76dbec575e4d 100644 --- a/desktop/source/app/officeipcthread.cxx +++ b/desktop/source/app/officeipcthread.cxx @@ -45,6 +45,7 @@ #include <rtl/process.h> #include <o3tl/string_view.hxx> #include <comphelper/diagnose_ex.hxx> +#include <comphelper/scopeguard.hxx> #include <cassert> #include <cstdlib> @@ -1087,6 +1088,8 @@ bool IpcThread::process(OString const & arguments, bool * waitProcessed) { void PipeIpcThread::execute() { assert(m_handler != nullptr); + dp_misc::setOfficeIpcThreadRunning(true); + comphelper::ScopeGuard resetThreadRunning([]() { dp_misc::setOfficeIpcThreadRunning(false); }); do { osl::StreamPipe aStreamPipe; diff --git a/desktop/source/deployment/misc/dp_misc.cxx b/desktop/source/deployment/misc/dp_misc.cxx index 2b083910751e..e0de3cba3f38 100644 --- a/desktop/source/deployment/misc/dp_misc.cxx +++ b/desktop/source/deployment/misc/dp_misc.cxx @@ -44,6 +44,7 @@ #include <com/sun/star/deployment/ExtensionManager.hpp> #include <com/sun/star/lang/DisposedException.hpp> #include <com/sun/star/task/OfficeRestartManager.hpp> +#include <atomic> #include <memory> #include <string_view> #include <thread> @@ -51,11 +52,6 @@ #include <comphelper/processfactory.hxx> #include <salhelper/linkhelper.hxx> -#ifdef _WIN32 -#include <prewin.h> -#include <postwin.h> -#endif - using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -189,11 +185,10 @@ bool needToSyncRepository(std::u16string_view name) folder, file); } +// True when IPC thread is running in this process. Set in PipeIpcThread::execute. Used to avoid +// attempts to open the pipe from office_is_running, which could deadlock. +std::atomic<bool> s_bOfficeIpcThreadRunning = false; -} // anon namespace - - -namespace { OUString encodeForRcFile( std::u16string_view str ) { // escape $\{} (=> rtl bootstrap files) @@ -214,7 +209,7 @@ OUString encodeForRcFile( std::u16string_view str ) } return buf.makeStringAndClear(); } -} +} // anon namespace OUString makeURL( std::u16string_view baseURL, OUString const & relPath_ ) @@ -335,33 +330,13 @@ bool office_is_running() #if defined EMSCRIPTEN return true; #else - //We need to check if we run within the office process. Then we must not use the pipe, because - //this could cause a deadlock. This is actually a workaround for i82778 - OUString sFile; - oslProcessError err = osl_getExecutableFile(& sFile.pData); - if (osl_Process_E_None == err) - { - if ( -#if defined MACOSX - sFile.endsWith("/soffice") -#elif defined UNIX | defined _WIN32 - sFile.endsWith("/soffice.bin") -#else -#error "Unsupported platform" -#endif - - ) - return true; - } - else - { - OSL_FAIL("NOT osl_Process_E_None "); - //if osl_getExecutableFile failed then we take the risk of creating a pipe - } - return existsOfficePipe(); + // i#82778: We need to check if we run within the office process. Then we must not use the pipe, + // because this could cause a deadlock (if called from IPC thread). + return s_bOfficeIpcThreadRunning || existsOfficePipe(); #endif } +void setOfficeIpcThreadRunning(bool bRunning) { s_bOfficeIpcThreadRunning = bRunning; } oslProcess raiseProcess( OUString const & appURL, Sequence<OUString> const & args ) commit 2b1b4e9f1fb0e24eb4b7d7604154c1c231de0e43 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Jul 23 16:54:41 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Thu Jul 24 00:15:44 2025 +0200 Deduplicate code generating IPC pipe id Make the function in dp_misc available from PipeIpcThread. Change-Id: I3d4beb3810a270656ea2873545b2c77f6c75486d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188228 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/desktop/inc/dp_misc.h b/desktop/inc/dp_misc.h index f9ac5687e46a..d1a04b21b717 100644 --- a/desktop/inc/dp_misc.h +++ b/desktop/inc/dp_misc.h @@ -77,6 +77,7 @@ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString makeURLAppendSysPathSegment( DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateRandomPipeId(); +DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateOfficePipeId(); class AbortChannel; diff --git a/desktop/source/app/officeipcthread.cxx b/desktop/source/app/officeipcthread.cxx index 6a18a6e6fb48..f0a414f984a2 100644 --- a/desktop/source/app/officeipcthread.cxx +++ b/desktop/source/app/officeipcthread.cxx @@ -24,6 +24,7 @@ #include <config_feature_desktop.h> #include <app.hxx> +#include <dp_misc.h> #include "officeipcthread.hxx" #include "cmdlineargs.hxx" #include <com/sun/star/frame/TerminationVetoException.hpp> @@ -43,6 +44,7 @@ #include <osl/file.hxx> #include <rtl/process.h> #include <o3tl/string_view.hxx> +#include <comphelper/diagnose_ex.hxx> #include <cassert> #include <cstdlib> @@ -233,36 +235,6 @@ bool addArgument(OStringBuffer &rArguments, char prefix, rtl::Reference< RequestHandler > RequestHandler::pGlobal; -// Turns a string in aMsg such as file:///home/foo/.libreoffice/3 -// Into a hex string of well known length ff132a86... -static OUString CreateMD5FromString( const OUString& aMsg ) -{ - SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'"); - - rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); - if ( handle ) - { - const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr()); - sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode ); - sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle ); - std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]); - - rtl_digest_init( handle, pData, nSize ); - rtl_digest_update( handle, pData, nSize ); - rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen ); - rtl_digest_destroy( handle ); - - // Create hex-value string from the MD5 value to keep the string size minimal - OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 ); - for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ ) - aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 ); - - return aBuffer.makeStringAndClear(); - } - - return OUString(); -} - namespace { class ProcessEventsClass_Impl @@ -739,26 +711,21 @@ RequestHandler::Status PipeIpcThread::enable(rtl::Reference<IpcThread> * thread) { assert(thread != nullptr); - // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve - // this information from a unotools implementation. - OUString aUserInstallPath; - ::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath ); - if (aLocateResult != utl::Bootstrap::PATH_EXISTS - && aLocateResult != utl::Bootstrap::PATH_VALID) - { - return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; - } - // Try to determine if we are the first office or not! This should prevent multiple // access to the user directory ! // First we try to create our pipe if this fails we try to connect. We have to do this // in a loop because the other office can crash or shutdown between createPipe // and connectPipe!! - auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath); - - // Check result to create a hash code from the user install path - if ( aUserInstallPathHashCode.isEmpty() ) + OUString aPipeIdent; + try + { + aPipeIdent = dp_misc::generateOfficePipeId(); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("desktop.app"); return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code! + } osl::Pipe pipe; enum PipeMode @@ -769,7 +736,6 @@ RequestHandler::Status PipeIpcThread::enable(rtl::Reference<IpcThread> * thread) }; PipeMode nPipeMode = PIPEMODE_DONTKNOW; - OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode ); do { osl::Security security; @@ -825,10 +791,11 @@ RequestHandler::Status PipeIpcThread::enable(rtl::Reference<IpcThread> * thread) aArguments.append('0'); } sal_uInt32 nCount = rtl_getAppCommandArgCount(); + OUString arg; for( sal_uInt32 i=0; i < nCount; i++ ) { - rtl_getAppCommandArg( i, &aUserInstallPath.pData ); - if (!addArgument(aArguments, ',', aUserInstallPath)) { + rtl_getAppCommandArg(i, &arg.pData); + if (!addArgument(aArguments, ',', arg)) { return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; } } diff --git a/desktop/source/deployment/misc/dp_misc.cxx b/desktop/source/deployment/misc/dp_misc.cxx index 5064aa76e0c9..2b083910751e 100644 --- a/desktop/source/deployment/misc/dp_misc.cxx +++ b/desktop/source/deployment/misc/dp_misc.cxx @@ -77,42 +77,6 @@ std::shared_ptr<rtl::Bootstrap> & UnoRc() #if !defined EMSCRIPTEN -OUString generateOfficePipeId() -{ - OUString userPath; - ::utl::Bootstrap::PathStatus aLocateResult = - ::utl::Bootstrap::locateUserInstallation( userPath ); - if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS && - aLocateResult != ::utl::Bootstrap::PATH_VALID) - { - throw Exception(u"Extension Manager: Could not obtain path for UserInstallation."_ustr, nullptr); - } - - rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); - if (!digest) { - throw RuntimeException(u"cannot get digest rtl_Digest_AlgorithmMD5!"_ustr, nullptr ); - } - - sal_uInt8 const * data = - reinterpret_cast<sal_uInt8 const *>(userPath.getStr()); - std::size_t size = userPath.getLength() * sizeof (sal_Unicode); - sal_uInt32 md5_key_len = rtl_digest_queryLength( digest ); - std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] ); - - rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) ); - rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) ); - rtl_digest_get( digest, md5_buf.get(), md5_key_len ); - rtl_digest_destroy( digest ); - - // create hex-value string from the MD5 value to keep - // the string size minimal - OUStringBuffer buf( "SingleOfficeIPC_" ); - for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) { - buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 ); - } - return buf.makeStringAndClear(); -} - bool existsOfficePipe() { static const OUString OfficePipeId = generateOfficePipeId(); @@ -327,6 +291,44 @@ OUString expandUnoRcUrl( OUString const & url ) } } +OUString generateOfficePipeId() +{ + // The name of the named pipe is created with the hashcode of the user installation directory + // (without /user). We have to retrieve this information from a unotools implementation. + + OUString userPath; + ::utl::Bootstrap::PathStatus aLocateResult = + ::utl::Bootstrap::locateUserInstallation( userPath ); + if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS && + aLocateResult != ::utl::Bootstrap::PATH_VALID) + { + throw Exception(u"Extension Manager: Could not obtain path for UserInstallation."_ustr, nullptr); + } + + rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); + if (!digest) { + throw RuntimeException(u"cannot get digest rtl_Digest_AlgorithmMD5!"_ustr, nullptr ); + } + + sal_uInt8 const * data = + reinterpret_cast<sal_uInt8 const *>(userPath.getStr()); + std::size_t size = userPath.getLength() * sizeof (sal_Unicode); + sal_uInt32 md5_key_len = rtl_digest_queryLength( digest ); + std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] ); + + rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) ); + rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) ); + rtl_digest_get( digest, md5_buf.get(), md5_key_len ); + rtl_digest_destroy( digest ); + + // create hex-value string from the MD5 value to keep + // the string size minimal + OUStringBuffer buf( "SingleOfficeIPC_" ); + for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) { + buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 ); + } + return buf.makeStringAndClear(); +} bool office_is_running() {