/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// Receives packets from the pipe until a packet ends in a NUL character (that // will not be included in the returned string) or it cannot read anything (due // to error or closed pipe, in which case an empty string will be returned to // signal failure):
OString readStringFromPipe(osl::StreamPipe const & pipe) { for (OStringBuffer str;;) { char buf[1024];
sal_Int32 n = pipe.recv(buf, std::size(buf)); if (n <= 0) {
SAL_INFO("desktop.app", "read empty string"); return""_ostr;
} bool end = false; if (buf[n - 1] == '\0') {
end = true;
--n;
}
str.append(buf, n); //TODO: how does OStringBuffer.append handle overflow? if (end) { auto s = str.makeStringAndClear();
SAL_INFO("desktop.app", "read <" << s << ">"); return s;
}
}
}
// 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 << "'");
// 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 );
// The RequestHandlerController implementation is a bookkeeper for all pending requests // that were created by the RequestHandler. The requests are waiting to be processed by // our framework loadComponentFromURL function (e.g. open/print request). // During shutdown the framework is asking RequestHandlerController about pending requests. // If there are pending requests framework has to stop the shutdown process. It is waiting // for these requests because framework is not able to handle shutdown and open a document // concurrently.
// XTerminateListener void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
{ // Desktop ask about pending request through our office ipc pipe. We have to // be sure that no pending request is waiting because framework is not able to // handle shutdown and open a document concurrently.
if ( RequestHandler::AreRequestsPending() ) throw TerminationVetoException();
RequestHandler::SetDowning();
}
void DbusIpcThread::execute()
{
assert(m_handler != nullptr);
m_handler->cReady.wait(); for (;;) {
{
osl::MutexGuard g(RequestHandler::GetMutex()); if (m_handler->mState == RequestHandler::State::Downing) { break;
}
} if (!dbus_connection_read_write(connection_.connection, -1)) { break;
} for (;;) {
DbusMessageHolder msg(
dbus_connection_pop_message(connection_.connection)); if (msg.message == nullptr) { break;
} if (!dbus_message_is_method_call(
msg.message, "org.libreoffice.LibreOfficeIpcIfc0", "Execute"))
{
SAL_INFO("desktop.app", "unknown DBus message ignored"); continue;
}
DBusMessageIter it; if (!dbus_message_iter_init(msg.message, &it)) {
SAL_WARN( "desktop.app", "DBus message without argument ignored"); continue;
} if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
SAL_WARN( "desktop.app", "DBus message with non-string argument ignored"); continue;
} charconst * argstr;
dbus_message_iter_get_basic(&it, &argstr); bool waitProcessed = false;
{
osl::MutexGuard g(RequestHandler::GetMutex()); if (!process(argstr, &waitProcessed)) { continue;
}
} if (waitProcessed) {
m_handler->cProcessed.wait();
}
DbusMessageHolder repl(dbus_message_new_method_return(msg.message)); if (repl.message == nullptr) {
SAL_WARN( "desktop.app", "dbus_message_new_method_return failed"); continue;
}
dbus_uint32_t serial = 0; if (!dbus_connection_send(
connection_.connection, repl.message, &serial)) {
SAL_WARN("desktop.app", "dbus_connection_send failed"); continue;
}
dbus_connection_flush(connection_.connection);
}
}
}
void DbusIpcThread::close() {
assert(connection_.connection != nullptr); // Make dbus_connection_read_write fall out of internal poll call blocking // on POLLIN: int fd; if (!dbus_connection_get_socket(connection_.connection, &fd)) {
SAL_WARN("desktop.app", "dbus_connection_get_socket failed"); return;
} if (shutdown(fd, SHUT_RD) == -1) { autoconst e = errno;
SAL_WARN("desktop.app", "shutdown failed with errno " << e);
}
}
void RequestHandler::SetDowning()
{ // We have the order to block all incoming requests. Framework // wants to shutdown and we have to make sure that no loading/printing // requests are executed anymore.
::osl::MutexGuard aGuard( GetMutex() );
if ( pGlobal.is() )
pGlobal->mState = State::Downing;
}
void RequestHandler::EnableRequests()
{ // switch between just queueing the requests and executing them
::osl::MutexGuard aGuard( GetMutex() );
// 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() ) return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode ); do
{
osl::Security security;
// Try to create pipe if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
{ // Pipe created
nPipeMode = PIPEMODE_CREATED;
} elseif( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
{
osl::StreamPipe aStreamPipe(pipe.getHandle()); if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
{ // Pipe connected to first office
nPipeMode = PIPEMODE_CONNECTED;
} else
{ // Pipe connection failed (other office exited or crashed)
std::this_thread::sleep_for( std::chrono::milliseconds(500) );
}
} else
{
oslPipeError eReason = pipe.getError(); if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError)) return RequestHandler::IPC_STATUS_PIPE_ERROR;
// Wait for second office to be ready
std::this_thread::sleep_for( std::chrono::milliseconds(10) );
}
} while ( nPipeMode == PIPEMODE_DONTKNOW );
if ( nPipeMode == PIPEMODE_CREATED )
{ // Seems we are the one and only, so create listening thread
*thread = new PipeIpcThread(std::move(pipe)); return RequestHandler::IPC_STATUS_OK;
} else
{ // Seems another office is running. Pipe arguments to it and self terminate
osl::StreamPipe aStreamPipe(pipe.getHandle());
OStringBuffer aArguments(ARGUMENT_PREFIX);
OUString cwdUrl; if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
addArgument(aArguments, '1', cwdUrl)))
{
aArguments.append('0');
}
sal_uInt32 nCount = rtl_getAppCommandArgCount(); for( sal_uInt32 i=0; i < nCount; i++ )
{
rtl_getAppCommandArg( i, &aUserInstallPath.pData ); if (!addArgument(aArguments, ',', aUserInstallPath)) { return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
}
}
aArguments.append('\0'); // finally, write the string onto the pipe
SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
sal_Int32 n = aStreamPipe.write(
aArguments.getStr(), aArguments.getLength()); if (n != aArguments.getLength()) {
SAL_INFO("desktop.app", "short write: " << n); return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
}
if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
{ // something went wrong return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
}
if ( aCmdLineArgs->IsQuickstart() )
{ // we have to use application event, because we have to start quickstart service in main thread!!
ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::QuickStart);
ImplPostForeignAppEvent( pAppEvent );
}
// Print requests are not dependent on the --invisible cmdline argument as they are // loaded with the "hidden" flag! So they are always checked.
pRequest->aPrintList = aCmdLineArgs->GetPrintList();
bDocRequestSent |= !pRequest->aPrintList.empty();
pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
pRequest->aConversionList = aCmdLineArgs->GetConversionList();
pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
pRequest->aStartListParams = aCmdLineArgs->GetStartListParams();
pRequest->aInFilter = aCmdLineArgs->GetInFilter();
pRequest->bTextCat = aCmdLineArgs->IsTextCat();
pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
bDocRequestSent |= !pRequest->aConversionList.empty();
if ( !rCurrentCmdLineArgs.IsInvisible() )
{ // Read cmdline args that can open/create documents. As they would open a window // they are only allowed if the "--invisible" is currently not used!
pRequest->aOpenList = aCmdLineArgs->GetOpenList();
bDocRequestSent |= !pRequest->aOpenList.empty();
pRequest->aViewList = aCmdLineArgs->GetViewList();
bDocRequestSent |= !pRequest->aViewList.empty();
pRequest->aStartList = aCmdLineArgs->GetStartList();
bDocRequestSent |= !pRequest->aStartList.empty();
pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
bDocRequestSent |= !pRequest->aForceOpenList.empty();
pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
bDocRequestSent |= !pRequest->aForceNewList.empty();
// Special command line args to create an empty document for a given module
// #i18338# (lo) // we only do this if no document was specified on the command line, // since this would be inconsistent with the behaviour of // the first process, see OpenClients() (call to OpenDefault()) in app.cxx if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
{
SvtModuleOptions aOpt;
SvtModuleOptions::EFactory eFactory = SvtModuleOptions::EFactory::WRITER; if ( aCmdLineArgs->IsWriter() )
eFactory = SvtModuleOptions::EFactory::WRITER; elseif ( aCmdLineArgs->IsCalc() )
eFactory = SvtModuleOptions::EFactory::CALC; elseif ( aCmdLineArgs->IsDraw() )
eFactory = SvtModuleOptions::EFactory::DRAW; elseif ( aCmdLineArgs->IsImpress() )
eFactory = SvtModuleOptions::EFactory::IMPRESS; elseif ( aCmdLineArgs->IsBase() )
eFactory = SvtModuleOptions::EFactory::DATABASE; elseif ( aCmdLineArgs->IsMath() )
eFactory = SvtModuleOptions::EFactory::MATH; elseif ( aCmdLineArgs->IsGlobal() )
eFactory = SvtModuleOptions::EFactory::WRITERGLOBAL; elseif ( aCmdLineArgs->IsWeb() )
eFactory = SvtModuleOptions::EFactory::WRITERWEB;
if ( bDocRequestSent )
{ // Send requests to dispatch watcher if we have at least one. The receiver // is responsible to delete the request after processing it. if ( aCmdLineArgs->HasModuleParam() )
{
SvtModuleOptions aOpt;
// Support command line parameters to start a module (as preselection) if (aCmdLineArgs->IsWriter() && aOpt.IsWriterInstalled())
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER ); elseif (aCmdLineArgs->IsCalc() && aOpt.IsCalcInstalled())
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC ); elseif (aCmdLineArgs->IsImpress() && aOpt.IsImpressInstalled())
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS ); elseif (aCmdLineArgs->IsDraw() && aOpt.IsDrawInstalled())
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
}
ImplPostProcessDocumentsEvent( std::move(pRequest) );
} else
{ // delete not used request again
pRequest.reset();
} if (aCmdLineArgs->IsEmpty())
{ // no document was sent, just bring Office to front
ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Appear);
ImplPostForeignAppEvent( pAppEvent );
}
}
*waitProcessed = bDocRequestSent; returntrue;
}
if( nError == osl_Pipe_E_None )
{ // if we receive a request while the office is displaying some dialog or error during // bootstrap, that dialogs event loop might get events that are dispatched by this thread // we have to wait for cReady to be set by the real main loop. // only requests that don't dispatch events may be processed before cReady is set.
m_handler->cReady.wait();
// we might have decided to shutdown while we were sleeping if (!RequestHandler::pGlobal.is()) return;
// only lock the mutex when processing starts, otherwise we deadlock when the office goes // down during wait
osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
if (m_handler->mState == RequestHandler::State::Downing)
{ break;
}
// notify client we're ready to process its args:
SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
std::size_t n = aStreamPipe.write(
SEND_ARGUMENTS, std::size(SEND_ARGUMENTS)); // incl. terminating NUL if (n != std::size(SEND_ARGUMENTS)) {
SAL_WARN("desktop.app", "short write: " << n); continue;
}
if ( pGlobal.is() )
{ if( ! pGlobal->AreRequestsEnabled() )
{ // Either starting, or downing - do not process the request, just try to bring Office to front
ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::Type::Appear);
ImplPostForeignAppEvent(pAppEvent); return bShutdown;
}
pGlobal->mnPendingRequests += aDispatchList.size(); if ( !pGlobal->mpDispatchWatcher.is() )
{
pGlobal->mpDispatchWatcher = new DispatchWatcher;
}
rtl::Reference<DispatchWatcher> dispatchWatcher(
pGlobal->mpDispatchWatcher);
// copy for execute
std::vector<DispatchWatcher::DispatchRequest> aTempList;
aTempList.swap( aDispatchList );
aGuard.clear();
// Execute dispatch requests
bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate); if (aRequest.mpbSuccess)
*aRequest.mpbSuccess = true; // signal that we have actually succeeded
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.