File indexing completed on 2024-04-28 04:00:30
0001 /* 0002 SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "processjob.h" 0008 #include "cmake-paths.h" 0009 #include "purpose_external_process_debug.h" 0010 #include <QCborValue> 0011 #include <QFile> 0012 #include <QFileInfo> 0013 #include <QJsonDocument> 0014 #include <QLibrary> 0015 #include <QMetaMethod> 0016 #include <QRandomGenerator> 0017 #include <QStandardPaths> 0018 0019 using namespace Purpose; 0020 0021 ProcessJob::ProcessJob(const QString &pluginPath, const QString &pluginType, const QJsonObject &data, QObject *parent) 0022 : Job(parent) 0023 , m_process(new QProcess(this)) 0024 , m_pluginPath(pluginPath) 0025 , m_pluginType(pluginType) 0026 , m_data(data) 0027 , m_localSocket(nullptr) 0028 { 0029 if (QLibrary::isLibrary(pluginPath)) { 0030 QString exec = QStandardPaths::findExecutable(QStringLiteral("purposeprocess"), QStringList(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF))); 0031 Q_ASSERT(!exec.isEmpty()); 0032 m_process->setProgram(exec); 0033 } else { 0034 Q_ASSERT(QFile::exists(pluginPath)); 0035 Q_ASSERT(QFileInfo(pluginPath).permission(QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser)); 0036 m_process->setProgram(pluginPath); 0037 } 0038 m_process->setProcessChannelMode(QProcess::ForwardedChannels); 0039 0040 connect(static_cast<QProcess *>(m_process), &QProcess::errorOccurred, this, [](QProcess::ProcessError error) { 0041 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "error!" << error; 0042 }); 0043 connect(static_cast<QProcess *>(m_process), &QProcess::stateChanged, this, &ProcessJob::processStateChanged); 0044 0045 m_socket.setMaxPendingConnections(1); 0046 m_socket.setSocketOptions(QLocalServer::UserAccessOption); 0047 bool b = m_socket.listen(QStringLiteral("randomname-%1").arg(QRandomGenerator::global()->generate())); 0048 Q_ASSERT(b); 0049 connect(&m_socket, &QLocalServer::newConnection, this, &ProcessJob::writeSocket); 0050 } 0051 0052 ProcessJob::~ProcessJob() 0053 { 0054 m_process->kill(); 0055 delete m_process; 0056 } 0057 0058 void ProcessJob::writeSocket() 0059 { 0060 m_localSocket = m_socket.nextPendingConnection(); 0061 connect(static_cast<QIODevice *>(m_localSocket), &QIODevice::readyRead, this, &ProcessJob::readSocket); 0062 0063 m_socket.removeServer(m_socket.serverName()); 0064 0065 const QByteArray data = QCborValue::fromJsonValue(m_data).toCbor(); 0066 m_localSocket->write(QByteArray::number(data.size()) + '\n'); 0067 const auto ret = m_localSocket->write(data); 0068 Q_ASSERT(ret == data.size()); 0069 m_localSocket->flush(); 0070 } 0071 0072 void ProcessJob::readSocket() 0073 { 0074 QJsonParseError error; 0075 while (m_localSocket && m_localSocket->canReadLine()) { 0076 const QByteArray json = m_localSocket->readLine(); 0077 0078 const QJsonObject object = QJsonDocument::fromJson(json, &error).object(); 0079 if (error.error != QJsonParseError::NoError) { 0080 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "error!" << error.errorString() << json; 0081 continue; 0082 } 0083 0084 for (auto it = object.constBegin(), itEnd = object.constEnd(); it != itEnd; ++it) { 0085 const QByteArray propName = it.key().toLatin1(); 0086 if (propName == "percent") { 0087 setPercent(it->toInt()); 0088 } else if (propName == "error") { 0089 setError(it->toInt()); 0090 } else if (propName == "errorText") { 0091 setErrorText(it->toString()); 0092 } else if (propName == "output") { 0093 setOutput(it->toObject()); 0094 } 0095 } 0096 } 0097 } 0098 0099 void ProcessJob::start() 0100 { 0101 m_process->setArguments( 0102 {QStringLiteral("--server"), m_socket.fullServerName(), QStringLiteral("--pluginType"), m_pluginType, QStringLiteral("--pluginPath"), m_pluginPath}); 0103 0104 qCDebug(PURPOSE_EXTERNAL_PROCESS_LOG) << "launching..." << m_process->program() << m_process->arguments().join(QLatin1Char(' ')).constData(); 0105 0106 m_process->start(); 0107 } 0108 0109 void Purpose::ProcessJob::processStateChanged(QProcess::ProcessState state) 0110 { 0111 if (state == QProcess::NotRunning) { 0112 Q_ASSERT(m_process->exitCode() != 0 || m_localSocket); 0113 if (m_process->exitCode() != 0) { 0114 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "process exited with code:" << m_process->exitCode(); 0115 } 0116 0117 do { 0118 readSocket(); 0119 } while (m_localSocket->waitForReadyRead()); 0120 emitResult(); 0121 } 0122 } 0123 0124 #include "moc_processjob.cpp"