File indexing completed on 2024-05-12 15:43:56
0001 /* 0002 SPDX-FileCopyrightText: 2020 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kpackagejob.h" 0008 0009 #include <knewstuffcore_debug.h> 0010 0011 #include <KLocalizedString> 0012 0013 #include <KPackage/Package> 0014 #include <KPackage/PackageLoader> 0015 #include <KPackage/PackageStructure> 0016 0017 #include <QCoreApplication> 0018 #include <QRunnable> 0019 #include <QStandardPaths> 0020 #include <QThreadPool> 0021 #include <QTimer> 0022 0023 using namespace KNSCore; 0024 0025 enum Operation { 0026 UnknownOperation, 0027 InstallOperation, 0028 UpdateOperation, 0029 UninstallOperation, 0030 }; 0031 class KPackageTask; 0032 class KNSCore::KPackageJobPrivate 0033 { 0034 public: 0035 KPackageJobPrivate() 0036 { 0037 } 0038 0039 QString package; 0040 QString packageRoot; 0041 QString serviceType; 0042 Operation operation{UnknownOperation}; 0043 0044 KPackageTask *runnable{nullptr}; 0045 }; 0046 0047 class KPackageTask : public QObject, public QRunnable 0048 { 0049 Q_OBJECT 0050 public: 0051 QString package; 0052 QString packageRoot; 0053 QString serviceType; 0054 Operation operation{UnknownOperation}; 0055 0056 explicit KPackageTask(QObject *parent = nullptr) 0057 : QObject(parent) 0058 , QRunnable() 0059 { 0060 // We'll handle our own deletion - otherwise we may end up deleted 0061 // before things have been read out that we need to have read 0062 // As this has to be set before QThreadPool runs things, we need to do so here 0063 setAutoDelete(false); 0064 }; 0065 ~KPackageTask() override 0066 { 0067 } 0068 void run() override 0069 { 0070 qCDebug(KNEWSTUFFCORE) << "Attempting to perform an installation operation of type" << operation << "on the package" << package << "of type" 0071 << serviceType << "in the package root" << packageRoot; 0072 int errorlevel{0}; 0073 QString errordescription; 0074 // PackageStructure instances are managed internally by KPackage, never delete them 0075 KPackage::PackageStructure *structure = KPackage::PackageLoader::self()->loadPackageStructure(serviceType); 0076 if (structure) { 0077 qCDebug(KNEWSTUFFCORE) << "Service type understood"; 0078 installer.reset(new KPackage::Package(structure)); 0079 if (installer->hasValidStructure()) { 0080 qCDebug(KNEWSTUFFCORE) << "Installer successfully created and has a valid structure"; 0081 switch (operation) { 0082 case InstallOperation: 0083 job.reset(installer->install(package, packageRoot)); 0084 break; 0085 case UpdateOperation: 0086 job.reset(installer->update(package, packageRoot)); 0087 break; 0088 case UninstallOperation: 0089 job.reset(installer->uninstall(package, packageRoot)); 0090 break; 0091 case UnknownOperation: 0092 default: 0093 // This should really not be happening, can't create one of these without going through one 0094 // of the functions below, so how'd you get it in this state? 0095 break; 0096 }; 0097 if (job) { 0098 qCDebug(KNEWSTUFFCORE) << "Created job, now let's wait for it to do its thing..."; 0099 job->setAutoDelete(false); 0100 QEventLoop loop; 0101 connect( 0102 job.get(), 0103 &KJob::result, 0104 this, 0105 [&loop, &errordescription](KJob *job) { // clazy:exclude=lambda-in-connect 0106 errordescription = job->errorText(); 0107 loop.exit(job->error()); 0108 }, 0109 Qt::BlockingQueuedConnection); 0110 errorlevel = loop.exec(); 0111 } else { 0112 errorlevel = 3; 0113 errordescription = i18n( 0114 "Failed to create a job for the package management task. This is usually because the package is invalid. We attempted to operate on " 0115 "the package %1", 0116 package); 0117 } 0118 } else { 0119 errorlevel = 2; 0120 errordescription = 0121 i18n("Could not create a package installer for the service type %1: The installer does not have a valid structure", serviceType); 0122 } 0123 } else { 0124 errorlevel = 1; 0125 errordescription = i18n("The service type %1 was not understood by the KPackage installer", serviceType); 0126 } 0127 if (errorlevel > 0) { 0128 Q_EMIT error(errorlevel, errordescription); 0129 } 0130 Q_EMIT result(); 0131 } 0132 Q_SIGNAL void result(); 0133 Q_SIGNAL void error(int errorCode, const QString &errorText); 0134 0135 private: 0136 QScopedPointer<KPackage::Package> installer; 0137 QScopedPointer<KJob> job; 0138 }; 0139 0140 KPackageJob::KPackageJob(QObject *parent) 0141 : KJob(parent) 0142 , d(new KPackageJobPrivate) 0143 { 0144 } 0145 0146 KPackageJob::~KPackageJob() = default; 0147 0148 void KPackageJob::start() 0149 { 0150 if (d->runnable) { 0151 // refuse to start the task more than once 0152 return; 0153 } 0154 d->runnable = new KPackageTask(this); 0155 d->runnable->package = d->package; 0156 d->runnable->packageRoot = d->packageRoot; 0157 d->runnable->serviceType = d->serviceType; 0158 d->runnable->operation = d->operation; 0159 connect( 0160 d->runnable, 0161 &KPackageTask::error, 0162 this, 0163 [this](int errorCode, const QString &errorText) { 0164 setError(errorCode); 0165 setErrorText(errorText); 0166 }, 0167 Qt::QueuedConnection); 0168 connect( 0169 d->runnable, 0170 &KPackageTask::result, 0171 this, 0172 [this]() { 0173 emitResult(); 0174 }, 0175 Qt::QueuedConnection); 0176 QThreadPool::globalInstance()->start(d->runnable); 0177 } 0178 0179 KNSCore::KPackageJob *KNSCore::KPackageJob::install(const QString &sourcePackage, const QString &packageRoot, const QString &serviceType) 0180 { 0181 KPackageJob *job = new KPackageJob(); 0182 job->d->package = sourcePackage; 0183 job->d->packageRoot = packageRoot; 0184 job->d->serviceType = serviceType; 0185 job->d->operation = InstallOperation; 0186 QTimer::singleShot(0, job, &KPackageJob::start); 0187 return job; 0188 } 0189 0190 KPackageJob *KPackageJob::update(const QString &sourcePackage, const QString &packageRoot, const QString &serviceType) 0191 { 0192 KPackageJob *job = new KPackageJob(); 0193 job->d->package = sourcePackage; 0194 job->d->packageRoot = packageRoot; 0195 job->d->serviceType = serviceType; 0196 job->d->operation = UpdateOperation; 0197 QTimer::singleShot(0, job, &KPackageJob::start); 0198 return job; 0199 } 0200 0201 KPackageJob *KPackageJob::uninstall(const QString &packageName, const QString &packageRoot, const QString &serviceType) 0202 { 0203 KPackageJob *job = new KPackageJob(); 0204 job->d->package = packageName; 0205 job->d->packageRoot = packageRoot; 0206 job->d->serviceType = serviceType; 0207 job->d->operation = UninstallOperation; 0208 QTimer::singleShot(0, job, &KPackageJob::start); 0209 return job; 0210 } 0211 0212 #include "kpackagejob.moc" 0213 #include "moc_kpackagejob.cpp"