Purpose

processjob.cpp
1/*
2 SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "processjob.h"
8#include "cmake-paths.h"
9#include "purpose_external_process_debug.h"
10#include <QCborValue>
11#include <QFile>
12#include <QFileInfo>
13#include <QJsonDocument>
14#include <QLibrary>
15#include <QMetaMethod>
16#include <QRandomGenerator>
17#include <QStandardPaths>
18
19using namespace Purpose;
20
21ProcessJob::ProcessJob(const QString &pluginPath, const QString &pluginType, const QJsonObject &data, QObject *parent)
22 : Job(parent)
23 , m_process(new QProcess(this))
24 , m_pluginPath(pluginPath)
25 , m_pluginType(pluginType)
26 , m_data(data)
27 , m_localSocket(nullptr)
28{
29 if (QLibrary::isLibrary(pluginPath)) {
30 QString exec = QStandardPaths::findExecutable(QStringLiteral("purposeprocess"), QStringList(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF)));
31 Q_ASSERT(!exec.isEmpty());
32 m_process->setProgram(exec);
33 } else {
34 Q_ASSERT(QFile::exists(pluginPath));
35 Q_ASSERT(QFileInfo(pluginPath).permission(QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser));
36 m_process->setProgram(pluginPath);
37 }
38 m_process->setProcessChannelMode(QProcess::ForwardedChannels);
39
40 connect(static_cast<QProcess *>(m_process), &QProcess::errorOccurred, this, [](QProcess::ProcessError error) {
41 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "error!" << error;
42 });
43 connect(static_cast<QProcess *>(m_process), &QProcess::stateChanged, this, &ProcessJob::processStateChanged);
44
45 m_socket.setMaxPendingConnections(1);
46 m_socket.setSocketOptions(QLocalServer::UserAccessOption);
47 bool b = m_socket.listen(QStringLiteral("randomname-%1").arg(QRandomGenerator::global()->generate()));
48 Q_ASSERT(b);
49 connect(&m_socket, &QLocalServer::newConnection, this, &ProcessJob::writeSocket);
50}
51
52ProcessJob::~ProcessJob()
53{
54 m_process->kill();
55 delete m_process;
56}
57
58void ProcessJob::writeSocket()
59{
60 m_localSocket = m_socket.nextPendingConnection();
61 connect(static_cast<QIODevice *>(m_localSocket), &QIODevice::readyRead, this, &ProcessJob::readSocket);
62
63 m_socket.removeServer(m_socket.serverName());
64
66 m_localSocket->write(QByteArray::number(data.size()) + '\n');
67 const auto ret = m_localSocket->write(data);
68 Q_ASSERT(ret == data.size());
69 m_localSocket->flush();
70}
71
72void ProcessJob::readSocket()
73{
75 while (m_localSocket && m_localSocket->canReadLine()) {
76 const QByteArray json = m_localSocket->readLine();
77
78 const QJsonObject object = QJsonDocument::fromJson(json, &error).object();
79 if (error.error != QJsonParseError::NoError) {
80 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "error!" << error.errorString() << json;
81 continue;
82 }
83
84 for (auto it = object.constBegin(), itEnd = object.constEnd(); it != itEnd; ++it) {
85 const QByteArray propName = it.key().toLatin1();
86 if (propName == "percent") {
87 setPercent(it->toInt());
88 } else if (propName == "error") {
89 setError(it->toInt());
90 } else if (propName == "errorText") {
91 setErrorText(it->toString());
92 } else if (propName == "output") {
93 setOutput(it->toObject());
94 }
95 }
96 }
97}
98
99void ProcessJob::start()
100{
101 m_process->setArguments(
102 {QStringLiteral("--server"), m_socket.fullServerName(), QStringLiteral("--pluginType"), m_pluginType, QStringLiteral("--pluginPath"), m_pluginPath});
103
104 qCDebug(PURPOSE_EXTERNAL_PROCESS_LOG) << "launching..." << m_process->program() << m_process->arguments().join(QLatin1Char(' ')).constData();
105
106 m_process->start();
107}
108
109void Purpose::ProcessJob::processStateChanged(QProcess::ProcessState state)
110{
111 if (state == QProcess::NotRunning) {
112 Q_ASSERT(m_process->exitCode() != 0 || m_localSocket);
113 if (m_process->exitCode() != 0) {
114 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "process exited with code:" << m_process->exitCode();
115 }
116
117 do {
118 readSocket();
119 } while (m_localSocket->waitForReadyRead());
120 emitResult();
121 }
122}
123
124#include "moc_processjob.cpp"
void setErrorText(const QString &errorText)
int error() const
void setPercent(unsigned long percentage)
void setError(int errorCode)
Job that will actually perform the sharing.
Definition job.h:34
QJsonObject data
Represents the data the job will have available to perform its task.
Definition job.h:39
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QByteArray number(double n, char format, int precision)
QCborValue fromJsonValue(const QJsonValue &v)
QByteArray toCbor(EncodingOptions opt) const const
bool exists() const const
void readyRead()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
qsizetype size() const const
bool isLibrary(const QString &fileName)
QString fullServerName() const const
void newConnection()
virtual QLocalSocket * nextPendingConnection()
bool removeServer(const QString &name)
QString serverName() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void errorOccurred(QProcess::ProcessError error)
void stateChanged(QProcess::ProcessState newState)
QRandomGenerator * global()
QString findExecutable(const QString &executableName, const QStringList &paths)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.