diff options
Diffstat (limited to 'src/libs/installer/packagemanagercore_p.cpp')
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 210 |
1 files changed, 165 insertions, 45 deletions
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 3cc44a7e5..2e5e3e369 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -43,10 +43,13 @@ #include "adminauthorization.h" #include "binaryformat.h" #include "component.h" +#include "scriptengine.h" #include "componentmodel.h" #include "errors.h" #include "fileutils.h" #include "fsengineclient.h" +#include "globals.h" +#include "graph.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "progresscoordinator.h" @@ -58,7 +61,6 @@ #include "kdupdaterfiledownloaderfactory.h" #include "kdupdaterupdatesourcesinfo.h" #include "kdupdaterupdateoperationfactory.h" -#include "kdupdaterupdatefinder.h" #include <productkeycheck.h> @@ -94,7 +96,8 @@ public: { if (!m_operation) return; - qDebug() << QString::fromLatin1("%1 operation: %2").arg(state, m_operation->name()); + qDebug() << QString::fromLatin1("%1 %2 operation: %3").arg(state, m_operation->value( + QLatin1String("component")).toString(), m_operation->name()); qDebug() << QString::fromLatin1("\t- arguments: %1").arg(m_operation->arguments() .join(QLatin1String(", "))); } @@ -201,6 +204,7 @@ static void deferredRename(const QString &oldName, const QString &newName, bool PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) : m_updateFinder(0) + , m_updaterApplication(new DummyConfigurationInterface) , m_FSEngineClientHandler(0) , m_core(core) , m_repoMetaInfoJob(0) @@ -208,18 +212,22 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_componentsToInstallCalculated(false) + , m_componentScriptEngine(0) + , m_controlScriptEngine(0) , m_proxyFactory(0) , m_defaultModel(0) , m_updaterModel(0) + , m_guiObject(0) { } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const OperationList &performedOperations) : m_updateFinder(0) + , m_updaterApplication(new DummyConfigurationInterface) , m_FSEngineClientHandler(initFSEngineClientHandler()) , m_status(PackageManagerCore::Unfinished) - , m_forceRestart(false) + , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) , m_completeUninstall(false) @@ -233,9 +241,12 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_updateSourcesAdded(false) , m_magicBinaryMarker(magicInstallerMaker) , m_componentsToInstallCalculated(false) + , m_componentScriptEngine(0) + , m_controlScriptEngine(0) , m_proxyFactory(0) , m_defaultModel(0) , m_updaterModel(0) + , m_guiObject(0) { connect(this, SIGNAL(installationStarted()), m_core, SIGNAL(installationStarted())); connect(this, SIGNAL(installationFinished()), m_core, SIGNAL(installationFinished())); @@ -262,6 +273,9 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate() delete m_defaultModel; delete m_updaterModel; + + // at the moment the tabcontroller deletes the m_gui, this needs to be changed in the future + // delete m_gui; } /*! @@ -373,7 +387,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c std::sort(m_rootComponents.begin(), m_rootComponents.end(), Component::SortingPriorityGreaterThan()); } catch (const Error &error) { clearAllComponentLists(); - emit m_core->finishAllComponentsReset(); + emit m_core->finishAllComponentsReset(QList<QInstaller::Component*>()); setStatus(PackageManagerCore::Failure, error.message()); // TODO: make sure we remove all message boxes inside the library at some point. @@ -384,6 +398,32 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c return true; } +void PackageManagerCorePrivate::cleanUpComponentEnvironment() +{ + // clean up already downloaded data, don't reset registered archives in offline installer case + if (QInstallerCreator::BinaryFormatEngineHandler::instance() && !m_core->isInstaller()) + QInstallerCreator::BinaryFormatEngineHandler::instance()->resetRegisteredArchives(); + + // there could be still some references to already deleted components, + // so we need to remove the current component script engine + delete m_componentScriptEngine; + m_componentScriptEngine = 0; +} + +ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const +{ + if (!m_componentScriptEngine) + m_componentScriptEngine = new ScriptEngine(m_core); + return m_componentScriptEngine; +} + +ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const +{ + if (!m_controlScriptEngine) + m_controlScriptEngine = new ScriptEngine(m_core); + return m_controlScriptEngine; +} + void PackageManagerCorePrivate::clearAllComponentLists() { qDeleteAll(m_rootComponents); @@ -396,6 +436,8 @@ void PackageManagerCorePrivate::clearAllComponentLists() delete list.at(i).second; m_componentsToReplaceAllMode.clear(); m_componentsToInstallCalculated = false; + + cleanUpComponentEnvironment(); } void PackageManagerCorePrivate::clearUpdaterComponentLists() @@ -420,16 +462,18 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists() m_componentsToReplaceUpdaterMode.clear(); m_componentsToInstallCalculated = false; + + cleanUpComponentEnvironment(); } -QList<Component *> &PackageManagerCorePrivate::replacementDependencyComponents(RunMode mode) +QList<Component *> &PackageManagerCorePrivate::replacementDependencyComponents() { - return mode == AllMode ? m_rootDependencyReplacements : m_updaterDependencyReplacements; + return (!isUpdater()) ? m_rootDependencyReplacements : m_updaterDependencyReplacements; } -QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::componentsToReplace(RunMode mode) +QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::componentsToReplace() { - return mode == AllMode ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode; + return (!isUpdater()) ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode; } void PackageManagerCorePrivate::clearComponentsToInstall() @@ -453,7 +497,7 @@ bool PackageManagerCorePrivate::appendComponentsToInstall(const QList<Component relevantComponentForAutoDependOn = m_updaterComponents + m_updaterComponentsDeps; else { foreach (QInstaller::Component *component, m_rootComponents) - relevantComponentForAutoDependOn += component->childComponents(true, AllMode); + relevantComponentForAutoDependOn += component->childComponents(Component::Descendants); } QList<Component*> notAppendedComponents; // for example components with unresolved dependencies @@ -557,13 +601,13 @@ QString PackageManagerCorePrivate::installReason(Component *component) void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms) { - if (!ProductKeyCheck::instance()->hasValidKey()) { + if (!ProductKeyCheck::instance(m_core)->hasValidKey()) { if (m_core->isInstaller()) { - setStatus(PackageManagerCore::Failure, ProductKeyCheck::instance()->lastErrorString()); + setStatus(PackageManagerCore::Failure, ProductKeyCheck::instance(m_core)->lastErrorString()); } else { MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("ProductKeyCheckError"), ProductKeyCheck::instance()->lastErrorString(), - ProductKeyCheck::instance()->maintainanceToolDetailErrorNotice(), QMessageBox::Ok); + QLatin1String("ProductKeyCheckError"), ProductKeyCheck::instance(m_core)->lastErrorString(), + ProductKeyCheck::instance(m_core)->maintainanceToolDetailErrorNotice(), QMessageBox::Ok); } } @@ -617,7 +661,7 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms } if (!m_repoMetaInfoJob) { - m_repoMetaInfoJob = new GetRepositoriesMetaInfoJob(this); + m_repoMetaInfoJob = new GetRepositoriesMetaInfoJob(m_core); m_repoMetaInfoJob->setAutoDelete(false); connect(m_repoMetaInfoJob, SIGNAL(infoMessage(KDJob*, QString)), this, SLOT(infoMessage(KDJob*, QString))); @@ -769,13 +813,22 @@ static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefaul void PackageManagerCorePrivate::writeMaintenanceConfigFiles() { + bool gainedAdminRights = false; // write current state (variables) to the uninstaller ini file const QString iniPath = targetDir() + QLatin1Char('/') + m_data.settings().uninstallerIniFile(); + { + QFile tmp(iniPath); // force gaining admin rights in case we haven't done already and we need it + if (!tmp.open(QIODevice::ReadWrite) || !tmp.isWritable()) { + if (!m_FSEngineClientHandler->isActive()) // check if nobody did it before... + gainedAdminRights = m_core->gainAdminRights(); + } + tmp.close(); + } QVariantHash variables; QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat); foreach (const QString &key, m_data.keys()) { - if (key != scRunProgramDescription && key != scRunProgram) + if (key != scRunProgramDescription && key != scRunProgram && key != scRunProgramArguments) variables.insert(key, m_data.value(key)); } cfg.setValue(QLatin1String("Variables"), variables); @@ -787,6 +840,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() cfg.sync(); if (cfg.status() != QSettingsWrapper::NoError) { + if (gainedAdminRights) + m_core->dropAdminRights(); const QString reason = cfg.status() == QSettingsWrapper::AccessError ? tr("Access error") : tr("Format error"); throw Error(tr("Could not write installer configuration to %1: %2").arg(iniPath, reason)); @@ -828,6 +883,9 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() writer.writeEndElement(); writer.writeEndElement(); } + + if (gainedAdminRights) + m_core->dropAdminRights(); } void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir) @@ -1065,7 +1123,25 @@ void PackageManagerCorePrivate::writeUninstallerBinaryData(QIODevice *output, QF const qint64 dataBlockStart = output->pos(); QVector<Range<qint64> >resourceSegments; - foreach (const Range<qint64> &segment, layout.metadataResourceSegments) { + QVector<Range<qint64> >existingResourceSegments = layout.metadataResourceSegments; + + const QString newDefaultResource = m_core->value(QString::fromLatin1("DefaultResourceReplacement")); + if (!newDefaultResource.isEmpty()) { + QFile file(newDefaultResource); + if (file.open(QIODevice::ReadOnly)) { + resourceSegments.append(Range<qint64>::fromStartAndLength(output->pos(), file.size())); + appendData(output, &file, file.size()); + existingResourceSegments.remove(0); + + file.remove(); // clear all possible leftovers + m_core->setValue(QString::fromLatin1("DefaultResourceReplacement"), QString()); + } else { + qWarning() << QString::fromLatin1("Could not replace default resource with '%1'.") + .arg(newDefaultResource); + } + } + + foreach (const Range<qint64> &segment, existingResourceSegments) { input->seek(segment.start()); resourceSegments.append(Range<qint64>::fromStartAndLength(output->pos(), segment.length())); appendData(output, input, segment.length()); @@ -1127,8 +1203,6 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio performedOperations.append(takeOwnedOperation(op)); } - writeMaintenanceConfigFiles(); - #ifdef Q_OS_MAC // if it is a bundle, we need some stuff in it... const QString sourceAppDirPath = QCoreApplication::applicationDirPath(); @@ -1235,7 +1309,7 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio bool newBinaryWritten = false; bool replacementExists = false; - const QString installerBaseBinary = m_core->replaceVariables(m_installerBaseBinaryUnreplaced); + const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced); if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) { qDebug() << "Got a replacement installer base binary:" << installerBaseBinary; @@ -1243,8 +1317,8 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio try { openForRead(&replacementBinary, replacementBinary.fileName()); writeUninstallerBinary(&replacementBinary, replacementBinary.size(), true); + qDebug() << "Wrote the binary with the new replacement."; - m_forceRestart = true; newBinaryWritten = true; replacementExists = true; } catch (const Error &error) { @@ -1254,10 +1328,17 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio if (!replacementBinary.remove()) { // Is there anything more sensible we can do with this error? I think not. It's not serious // enough for throwing / aborting the process. - qDebug() << QString::fromLatin1("Could not remove installer base binary (%1) after updating " + qDebug() << QString::fromLatin1("Could not remove installer base binary '%1' after updating " "the uninstaller: %2").arg(installerBaseBinary, replacementBinary.errorString()); + } else { + qDebug() << QString::fromLatin1("Removed installer base binary '%1' after updating the " + "uninstaller/ maintenance tool.").arg(installerBaseBinary); } m_installerBaseBinaryUnreplaced.clear(); + } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) { + qWarning() << QString::fromLatin1("The current uninstaller/ maintenance tool could not be " + "updated. '%1' does not exist. Please fix the 'setInstallerBaseBinary(<temp_installer_base_" + "binary_path>)' call in your script.").arg(installerBaseBinary); } QFile input; @@ -1301,9 +1382,13 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio #endif } + performedOperations = sortOperationsBasedOnComponentDependencies(performedOperations); + m_core->setValue(QLatin1String("installedOperationAreSorted"), QLatin1String("true")); + try { KDSaveFile file(dataFile + QLatin1String(".new")); openForWrite(&file, file.fileName()); + writeUninstallerBinaryData(&file, &input, performedOperations, layout); appendInt64(&file, MagicCookieDat); file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup @@ -1328,10 +1413,11 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio appendInt64(&file, MagicCookie); } input.close(); + writeMaintenanceConfigFiles(); deferredRename(dataFile + QLatin1String(".new"), dataFile, false); if (newBinaryWritten) { - const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()); + const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()) && m_needsHardRestart; deferredRename(uninstallerName() + QLatin1String(".new"), uninstallerName(), restart); qDebug() << "Maintenance tool restart:" << (restart ? "true." : "false."); } @@ -1584,8 +1670,14 @@ bool PackageManagerCorePrivate::runPackageUpdater() OperationList nonRevertedOperations; QHash<QString, Component *> componentsByName; + // order the operations in the right component dependency order + // next loop will save the needed operations in reverse order for uninstallation + OperationList performedOperationsOld = m_performedOperationsOld; + if (m_core->value(QLatin1String("installedOperationAreSorted")) != QLatin1String("true")) + performedOperationsOld = sortOperationsBasedOnComponentDependencies(m_performedOperationsOld); + // build a list of undo operations based on the checked state of the component - foreach (Operation *operation, m_performedOperationsOld) { + foreach (Operation *operation, performedOperationsOld) { const QString &name = operation->value(QLatin1String("component")).toString(); Component *component = componentsByName.value(name, 0); if (!component) @@ -1636,6 +1728,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() continue; } + // uninstallation should be in reverse order so prepend it here undoOperations.prepend(operation); updateAdminRights |= operation->value(QLatin1String("admin")).toBool(); } @@ -1837,7 +1930,6 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr // Remember that the operation was performed, that allows us to undo it if a following operation // fails or if this operation failed but still needs an undo call to cleanup. addPerformed(operation); - operation->setValue(QLatin1String("component"), component->name()); } if (becameAdmin) @@ -1847,7 +1939,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr throw Error(operation->errorString()); if (component->value(scEssential, scFalse) == scTrue) - m_forceRestart = true; + m_needsHardRestart = true; } registerPathesForUninstallation(component->pathesForUninstallation(), component->name()); @@ -1990,25 +2082,21 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera const QString componentName = undoOperation->value(QLatin1String("component")).toString(); if (!componentName.isEmpty()) { - while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) { - const QMessageBox::StandardButton button = - MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("installationErrorWithRetry"), tr("Installer Error"), - tr("Error during uninstallation process:\n%1").arg(undoOperation->errorString()), - QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Retry); - - if (button == QMessageBox::Retry) { - ok = performOperationThreaded(undoOperation, Undo); - } else if (button == QMessageBox::Ignore) { - ignoreError = true; - } - } - } - - if (!componentName.isEmpty()) { + while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) { + const QMessageBox::StandardButton button = + MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), + QLatin1String("installationErrorWithRetry"), tr("Installer Error"), + tr("Error during uninstallation process:\n%1").arg(undoOperation->errorString()), + QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Retry); + + if (button == QMessageBox::Retry) + ok = performOperationThreaded(undoOperation, Undo); + else if (button == QMessageBox::Ignore) + ignoreError = true; + } Component *component = m_core->componentByName(componentName); if (!component) - component = componentsToReplace(m_core->runMode()).value(componentName).second; + component = componentsToReplace().value(componentName).second; if (component) { component->setUninstalled(); packages.removePackage(component->name()); @@ -2041,7 +2129,6 @@ PackagesList PackageManagerCorePrivate::remotePackages() m_updateFinder = new KDUpdater::UpdateFinder(&m_updaterApplication); m_updateFinder->setAutoDelete(false); - m_updateFinder->setUpdateType(KDUpdater::PackageUpdate | KDUpdater::NewPackage); m_updateFinder->run(); if (m_updateFinder->updates().isEmpty()) { @@ -2285,8 +2372,8 @@ bool PackageManagerCorePrivate::appendComponentsToUninstall(const QList<Componen foreach (Component *c, installedComponents) { const QString replaces = c->value(scReplaces); - QStringList possibleNames = replaces.split(scCommaRegExp, QString::SkipEmptyParts); - possibleNames.append(c->name()); + const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(), + QString::SkipEmptyParts) << c->name(); foreach (const QString &possibleName, possibleNames) autoDependencies.removeAll(possibleName); } @@ -2333,6 +2420,39 @@ void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *con } } +OperationList PackageManagerCorePrivate::sortOperationsBasedOnComponentDependencies(const OperationList &operationList) +{ + OperationList sortedOperations; + QHash<QString, OperationList> componentOperationHash; + + // sort component unrelated operations to the beginning + foreach (Operation *operation, operationList) { + const QString componentName = operation->value(QLatin1String("component")).toString(); + if (componentName.isEmpty()) + sortedOperations.append(operation); + else { + OperationList componentOperationList = componentOperationHash.value(componentName); + componentOperationList.append(operation); + componentOperationHash.insert(operation->value(QLatin1String("component")).toString(), + componentOperationList); + } + } + + // create the complete component graph + Graph<QString> componentGraph; + const QRegExp dash(QLatin1String("-.*")); + foreach (const Component* componentNode, m_core->availableComponents()) { + componentGraph.addNode(componentNode->name()); + const QStringList dependencies = componentNode->dependencies().replaceInStrings(dash,QString()); + componentGraph.addEdges(componentNode->name(), dependencies); + } + + foreach (const QString &componentName, componentGraph.sort()) + sortedOperations.append(componentOperationHash.value(componentName)); + + return sortedOperations; +} + void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &invokableMethodName) { QObject *obj = QObject::sender(); |