diff options
Diffstat (limited to 'installerbuilder/libinstaller/elevatedexecuteoperation.cpp')
-rw-r--r-- | installerbuilder/libinstaller/elevatedexecuteoperation.cpp | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/elevatedexecuteoperation.cpp b/installerbuilder/libinstaller/elevatedexecuteoperation.cpp new file mode 100644 index 000000000..c255a28af --- /dev/null +++ b/installerbuilder/libinstaller/elevatedexecuteoperation.cpp @@ -0,0 +1,235 @@ +/************************************************************************** +** +** This file is part of Qt SDK** +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* +** +** Contact: Nokia Corporation qt-info@nokia.com** +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception version +** 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#include "elevatedexecuteoperation.h" + +#include "environment.h" +#include "fsengineclient.h" +#include "common/utils.h" + +#include <QThread> + +#include <QProcessEnvironment> + +using namespace QInstaller; + +class ElevatedExecuteOperation::Private +{ +public: + explicit Private( ElevatedExecuteOperation* qq ) + : q( qq ), process(0), showStandardError(false) + { + } + ~Private() + { + } + +private: + ElevatedExecuteOperation* const q; + +public: + void readProcessOutput(); + + QProcessWrapper* process; + bool showStandardError; +}; + +ElevatedExecuteOperation::ElevatedExecuteOperation() + : d( new Private( this ) ) +{ + // this operation has to "overwrite" the Execute operation from KDUpdater + setName( QLatin1String( "Execute" ) ); +} + +ElevatedExecuteOperation::~ElevatedExecuteOperation() +{ +} + +bool ElevatedExecuteOperation::performOperation() +{ + // This operation receives only one argument. It is the complete + // command line of the external program to execute. + QStringList args = arguments(); + if( args.isEmpty() ) + { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments in %1: %2 arguments given, at least 1 expected.").arg( name(), QString::number( args.count() ) ) ); + return false; + } + + QString workingDirectory; + QStringList filteredWorkingDirectoryArgs = args.filter(QLatin1String("workingdirectory="), Qt::CaseInsensitive); + if (!filteredWorkingDirectoryArgs.isEmpty()) { + QString workingDirectoryArgument = filteredWorkingDirectoryArgs.at(0); + workingDirectory = workingDirectoryArgument; + workingDirectory.replace(QLatin1String("workingdirectory="), QString(), Qt::CaseInsensitive); + args.removeAll(workingDirectoryArgument); + } + + + if ( args.last().endsWith( QLatin1String("showStandardError") ) ) { + d->showStandardError = true; + args.pop_back(); + } + + QList< int > allowedExitCodes; + + QRegExp re( QLatin1String( "^\\{((-?\\d+,)*-?\\d+)\\}$" ) ); + if( re.exactMatch( args.first() ) ) + { + const QStringList numbers = re.cap( 1 ).split( QLatin1Char( ',' ) ); + for( QStringList::const_iterator it = numbers.constBegin(); it != numbers.constEnd(); ++it ) + allowedExitCodes.push_back( it->toInt() ); + args.pop_front(); + } + else + { + allowedExitCodes.push_back( 0 ); + } + + const QString callstr = args.join( QLatin1String( " " ) ); + + // unix style: when there's an ampersand after the command, it's started detached + if( args.count() >= 2 && args.last() == QLatin1String( "&" ) ) + { + args.pop_back(); + const bool success = QProcess::startDetached( args.front(), args.mid( 1 ) ); + if ( !success ) { + setError( UserDefinedError ); + setErrorString( tr( "Execution failed: Could not start detached: \"%1\"" ).arg( callstr ) ); + } + return success; + } + + d->process = new QProcessWrapper(); + if (!workingDirectory.isEmpty()) { + d->process->setWorkingDirectory( workingDirectory ); + QInstaller::verbose() << " ElevatedExecuteOperation setWorkingDirectory: " << workingDirectory << std::endl; + } + + QProcessEnvironment penv; + //there is no way to serialize a QProcessEnvironment properly other than per mangled QStringList :/ (i.e. no other way to list all keys) + d->process->setEnvironment( KDUpdater::Environment::instance()->applyTo( penv ).toStringList() ); + + if (d->showStandardError) + d->process->setProcessChannelMode(QProcess::MergedChannels); + + connect(this, SIGNAL(cancelProcess()), d->process, SLOT(cancel())); + + //we still like the none blocking possibility to perform this operation without threads + QEventLoop loop; + if (QThread::currentThread() == qApp->thread()) { + QObject::connect( d->process, SIGNAL( finished( int, QProcess::ExitStatus ) ), &loop, SLOT( quit() ) ); + } + //readProcessOutput should only called from this current Thread -> Qt::DirectConnection + QObject::connect( d->process, SIGNAL( readyRead() ), this, SLOT( readProcessOutput() ), Qt::DirectConnection ); +#ifdef Q_OS_WIN + if (args.count() == 1) { + d->process->setNativeArguments( args.front() ); + QInstaller::verbose() << " ElevatedExecuteOperation setNativeArguments to start: " << args.front() << std::endl; + d->process->start(QString(), QStringList()); + } else +#endif + d->process->start( args.front(), args.mid( 1 ) ); + QInstaller::verbose() << args.front() << " started, arguments: " << QStringList(args.mid( 1 )).join(QLatin1String(" ")) << std::endl; + + bool success = false; + //we still like the none blocking possibility to perform this operation without threads + if (QThread::currentThread() == qApp->thread()) { + success = d->process->waitForStarted(); + } else { + success = d->process->waitForFinished(-1); + } + + bool returnValue = true; + if ( !success ) + { + setError( UserDefinedError ); + //TODO: pass errorString() through the wrapper */ + setErrorString( tr( "Execution failed: Could not start: \"%1\"" ).arg( callstr ) ); + returnValue = false; + } + + if (QThread::currentThread() == qApp->thread()) { + if (d->process->state() != QProcess::NotRunning) { + loop.exec(); + } + d->readProcessOutput(); + } + + setValue( QLatin1String( "ExitCode" ), d->process->exitCode() ); + + if ( d->process->exitStatus() == QProcess::CrashExit ) + { + setError( UserDefinedError ); + setErrorString( tr( "Execution failed (Crash): \"%1\"" ).arg( callstr ) ); + returnValue = false; + } + + if ( !allowedExitCodes.contains( d->process->exitCode() ) ) + { + setError( UserDefinedError ); + setErrorString( tr( "Execution failed (Unexpected exit code: %1): \"%2\"" ).arg( QString::number( d->process->exitCode() ), callstr ) ); + returnValue = false; + } + + Q_ASSERT(d->process); + d->process->deleteLater(); + d->process = 0; + + return returnValue; +} + +/*! + Cancels the ElevatedExecuteOperation. This methods tries to terminate the process + gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed. + */ +void ElevatedExecuteOperation::cancelOperation() +{ + emit cancelProcess(); +} + +void ElevatedExecuteOperation::Private::readProcessOutput() +{ + Q_ASSERT(process); + Q_ASSERT(QThread::currentThread() == process->thread()); + if (QThread::currentThread() != process->thread()) { + QInstaller::verbose() << Q_FUNC_INFO << QLatin1String(" can only be called from the same thread as the process is.") << std::endl; + } + const QByteArray output = process->readAll(); + if( !output.isEmpty() ) { + QInstaller::verbose() << QString::fromLocal8Bit( output ) << std::endl; + emit q->outputTextChanged( QString::fromLocal8Bit( output ) ); + } +} + +#include "moc_elevatedexecuteoperation.cpp" |