desktop/source/app/app.cxx | 7 ++++++- desktop/source/app/cmdlineargs.hxx | 7 +++++++ desktop/source/app/dispatchwatcher.cxx | 13 ++++++++++++- desktop/source/app/dispatchwatcher.hxx | 20 +++++++++++++++++++- desktop/source/app/officeipcthread.cxx | 12 +++++------- desktop/source/app/officeipcthread.hxx | 6 ++++-- 6 files changed, 53 insertions(+), 12 deletions(-)
New commits: commit 454d8db90e0f5062def3c83930b2c7e88541fed9 Author: Justin Luth <jl...@mail.com> AuthorDate: Thu Jun 26 10:12:20 2025 -0400 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Thu Jul 3 11:47:52 2025 +0200 tdf#148275: EXIT_FAILURE if any cmdline operation fails While by no means comprehensive, this patch fulfills the bug request by returning error status 1 (instead of 0) if REQUEST_CONVERSION (and some others) fail for various reasons. instdir/program/soffice --convert-to junk non-existant-file.ods echo "status is $?" -EXIT_FAILURE b/c non-existant-file.ods not found -EXIT_FAILURE b/c junk is not a valid filter -EXIT_FAILURE if xStorable->storeToURL raises an exception Change-Id: Ic1d25034fa152cdc4933ce3cca06def204f764a6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187075 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187278 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/desktop/source/app/app.cxx b/desktop/source/app/app.cxx index 2461a05fa145..adc254da541e 100644 --- a/desktop/source/app/app.cxx +++ b/desktop/source/app/app.cxx @@ -1788,7 +1788,7 @@ int Desktop::doShutdown() return EXITHELPER_NORMAL_RESTART; } - return EXIT_SUCCESS; + return rCmdLineArgs.GetAllSucceeded() ? EXIT_SUCCESS : EXIT_FAILURE; } IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool ) @@ -2215,8 +2215,13 @@ void Desktop::OpenClients() } #endif // Process request + DispatchRequestFlags eFlags = DispatchRequestFlags::NONE; + aRequest.mpFlags = &eFlags; if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) ) { + if (eFlags & DispatchRequestFlags::WithError) + Desktop::GetCommandLineArgs().SetAllSucceeded(false); + // Don't do anything if we have successfully called terminate at desktop: return; } diff --git a/desktop/source/app/cmdlineargs.hxx b/desktop/source/app/cmdlineargs.hxx index d5d94abd67f2..2f533e728dd0 100644 --- a/desktop/source/app/cmdlineargs.hxx +++ b/desktop/source/app/cmdlineargs.hxx @@ -122,6 +122,9 @@ class CommandLineArgs void setHeadless() { m_headless = true; } + bool GetAllSucceeded() const { return m_bAllSucceeded; } + void SetAllSucceeded(bool bSet) { m_bAllSucceeded = bSet; } + private: void ParseCommandLine_Impl( Supplier& supplier ); void InitParamValues(); @@ -182,6 +185,10 @@ class CommandLineArgs std::vector< OUString > m_infilter; OUString m_language; OUString m_pidfile; + + // If any argument fails, this may be set to false so that the app exits with EXIT_FAILURE. + // NOTE: only items of interest are setting this to false - can be extended to more cases... + bool m_bAllSucceeded = true; }; } diff --git a/desktop/source/app/dispatchwatcher.cxx b/desktop/source/app/dispatchwatcher.cxx index faf830ffae3c..39bd64db53f7 100644 --- a/desktop/source/app/dispatchwatcher.cxx +++ b/desktop/source/app/dispatchwatcher.cxx @@ -334,7 +334,9 @@ DispatchWatcher::~DispatchWatcher() } -bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate ) +bool DispatchWatcher::executeDispatchRequests( + const std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate, + DispatchRequestFlags* pFlags) { Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() ); @@ -636,6 +638,8 @@ bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest if (aFilter.isEmpty()) { std::cerr << "Error: no export filter" << std::endl; + if (pFlags) + *pFlags |= DispatchRequestFlags::WithError; } else { @@ -720,6 +724,8 @@ bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest if (!rException.Message.isEmpty()) std::cerr << " (" << rException.Message << ")"; std::cerr << std::endl; + if (pFlags) + *pFlags |= DispatchRequestFlags::WithError; } if (fileForCat && fileForCat->IsValid()) @@ -768,6 +774,8 @@ bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest else { std::cerr << "Error: source file could not be loaded" << std::endl; + if (pFlags) + *pFlags |= DispatchRequestFlags::WithError; } // remove the document @@ -815,6 +823,9 @@ bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest } } + if (pFlags) + *pFlags |= DispatchRequestFlags::Finished; + bool bEmpty = (m_nRequestCount == 0); // No more asynchronous requests? diff --git a/desktop/source/app/dispatchwatcher.hxx b/desktop/source/app/dispatchwatcher.hxx index 9a970cf82dce..a6b53a74d2a1 100644 --- a/desktop/source/app/dispatchwatcher.hxx +++ b/desktop/source/app/dispatchwatcher.hxx @@ -28,6 +28,23 @@ namespace desktop { +enum class DispatchRequestFlags +{ + NONE = 0x0000, + Finished = 0x0001, + WithError = 0x0002, // one or more requests reported an error condition +}; + +} + +namespace o3tl +{ + template<> struct typed_flags<desktop::DispatchRequestFlags> : is_typed_flags<desktop::DispatchRequestFlags, 0x3> {}; +} + +namespace desktop +{ + /* Class for controls dispatching of command URL through office command line. There are "dangerous" command URLs, that can result in a running office without UI. To prevent @@ -74,7 +91,8 @@ class DispatchWatcher : public ::cppu::WeakImplHelper< css::frame::XDispatchResu virtual void SAL_CALL dispatchFinished( const css::frame::DispatchResultEvent& aEvent ) override; // execute new dispatch request - bool executeDispatchRequests( const std::vector<DispatchRequest>& aDispatches, bool bNoTerminate ); + bool executeDispatchRequests(const std::vector<DispatchRequest>& aDispatches, + bool bNoTerminate, DispatchRequestFlags* pFlags); private: diff --git a/desktop/source/app/officeipcthread.cxx b/desktop/source/app/officeipcthread.cxx index 4029546fd666..6a18a6e6fb48 100644 --- a/desktop/source/app/officeipcthread.cxx +++ b/desktop/source/app/officeipcthread.cxx @@ -26,7 +26,6 @@ #include <app.hxx> #include "officeipcthread.hxx" #include "cmdlineargs.hxx" -#include "dispatchwatcher.hxx" #include <com/sun/star/frame/TerminationVetoException.hpp> #include <salhelper/thread.hxx> #include <sal/log.hxx> @@ -970,8 +969,8 @@ bool IpcThread::process(OString const & arguments, bool * waitProcessed) { aCmdLineArgs->getCwdUrl())); m_handler->cProcessed.reset(); pRequest->pcProcessed = &m_handler->cProcessed; - m_handler->mbSuccess = false; - pRequest->mpbSuccess = &m_handler->mbSuccess; + m_handler->mFlags = DispatchRequestFlags::NONE; + pRequest->mpFlags = &m_handler->mFlags; // Print requests are not dependent on the --invisible cmdline argument as they are // loaded with the "hidden" flag! So they are always checked. @@ -1175,7 +1174,7 @@ void PipeIpcThread::execute() if (waitProcessed) { m_handler->cProcessed.wait(); - bSuccess = m_handler->mbSuccess; + bSuccess = static_cast<bool>(m_handler->mFlags & DispatchRequestFlags::Finished); } if (bSuccess) { @@ -1344,9 +1343,8 @@ bool RequestHandler::ExecuteCmdLineRequests( aGuard.clear(); // Execute dispatch requests - bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate); - if (aRequest.mpbSuccess) - *aRequest.mpbSuccess = true; // signal that we have actually succeeded + bShutdown + = dispatchWatcher->executeDispatchRequests(aTempList, noTerminate, aRequest.mpFlags); } return bShutdown; diff --git a/desktop/source/app/officeipcthread.hxx b/desktop/source/app/officeipcthread.hxx index fc95b42668bc..3f7f4c00d602 100644 --- a/desktop/source/app/officeipcthread.hxx +++ b/desktop/source/app/officeipcthread.hxx @@ -34,6 +34,8 @@ #include <osl/conditn.hxx> #include <optional> +#include "dispatchwatcher.hxx" + namespace desktop { @@ -63,7 +65,7 @@ struct ProcessDocumentsRequest OUString aStartListParams; std::vector< OUString > aInFilter; ::osl::Condition *pcProcessed; // pointer condition to be set when the request has been processed - bool* mpbSuccess = nullptr; // pointer to boolean receiving if the processing was successful + DispatchRequestFlags* mpFlags = nullptr; // pointer to DispatchRequestFlags receiving if the processing was successful bool bTextCat; // boolean flag indicating whether to dump text content to console bool bScriptCat; // boolean flag indicating whether to dump script content to console }; @@ -92,7 +94,7 @@ class RequestHandler: public salhelper::SimpleReferenceObject /* condition to be set when the request has been processed */ ::osl::Condition cProcessed; /* receives if the processing was successful (may be false e.g. when shutting down) */ - bool mbSuccess = false; + DispatchRequestFlags mFlags = DispatchRequestFlags::NONE; /* condition to be set when the main event loop is ready otherwise an error dialogs event loop could eat away