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 <KPluginMetaData> 0008 #include <QApplication> 0009 #include <QCborMap> 0010 #include <QCborValue> 0011 #include <QCommandLineParser> 0012 #include <QFileInfo> 0013 #include <QJsonDocument> 0014 #include <QLocalSocket> 0015 #include <QMetaMethod> 0016 0017 #include "helper.h" 0018 #include "purpose_external_process_debug.h" 0019 #include <purpose/configuration.h> 0020 #include <purpose/job.h> 0021 0022 static QString pluginType; 0023 static KPluginMetaData md; 0024 0025 class Communication : public QObject 0026 { 0027 Q_OBJECT 0028 public: 0029 Communication(const QString &serverName) 0030 { 0031 int pcIdx = metaObject()->indexOfSlot("propertyChanged()"); 0032 Q_ASSERT(pcIdx >= 0); 0033 const QMetaMethod propertyChangedMethod = metaObject()->method(pcIdx); 0034 0035 m_socket.setServerName(serverName); 0036 m_socket.connectToServer(QIODevice::ReadWrite); 0037 connect(&m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(error())); 0038 0039 bool b = m_socket.waitForConnected(); 0040 Q_ASSERT(b); 0041 0042 b = m_socket.waitForReadyRead(); 0043 Q_ASSERT(b); 0044 0045 Q_ASSERT(m_socket.canReadLine()); 0046 QByteArray byteLine = m_socket.readLine(); 0047 byteLine.chop(1); // Drop \n 0048 const qint64 bytes = byteLine.toLongLong(); 0049 // QByteArray and QJsonDocument::from* can only handle int size. 0050 // If the payload is bigger we are screwed. 0051 Q_ASSERT(bytes <= std::numeric_limits<int>::max()); 0052 0053 QByteArray dataArray; 0054 dataArray.resize(bytes); 0055 int pos = 0; 0056 bool couldRead = false; 0057 while ((pos < bytes) && (couldRead = (m_socket.bytesAvailable() || m_socket.waitForReadyRead()))) { 0058 pos += m_socket.read(dataArray.data() + pos, qMin(m_socket.bytesAvailable(), bytes - pos)); 0059 } 0060 Q_ASSERT(couldRead); // false if we hit a timeout before read-end. 0061 Q_ASSERT(pos == bytes); 0062 0063 Purpose::Configuration config(QCborValue::fromCbor(dataArray).toMap().toJsonObject(), pluginType, md); 0064 config.setUseSeparateProcess(false); 0065 0066 Q_ASSERT(config.isReady()); 0067 0068 m_job = config.createJob(); 0069 m_job->start(); 0070 0071 const QMetaObject *m = m_job->metaObject(); 0072 for (int i = 0, c = m->propertyCount(); i < c; ++i) { 0073 QMetaProperty prop = m->property(i); 0074 if (prop.hasNotifySignal() && prop.isReadable()) { 0075 connect(m_job, prop.notifySignal(), this, propertyChangedMethod, Qt::UniqueConnection); 0076 } 0077 } 0078 } 0079 0080 private Q_SLOTS: 0081 void error() 0082 { 0083 qCWarning(PURPOSE_EXTERNAL_PROCESS_LOG) << "socket error:" << m_socket.error(); 0084 } 0085 0086 void propertyChanged() 0087 { 0088 const int idx = senderSignalIndex(); 0089 0090 const QMetaObject *m = m_job->metaObject(); 0091 QJsonObject toSend; 0092 for (int i = 0, c = m->propertyCount(); i < c; ++i) { 0093 QMetaProperty prop = m->property(i); 0094 if (prop.notifySignalIndex() == idx) { 0095 toSend[QString::fromLatin1(prop.name())] = fromVariant(prop.read(m_job)); 0096 } 0097 } 0098 send(toSend); 0099 } 0100 0101 static QJsonValue fromVariant(const QVariant &var) 0102 { 0103 if (var.canConvert<QJsonObject>()) { 0104 return var.toJsonObject(); 0105 } else { 0106 return QJsonValue::fromVariant(var); 0107 } 0108 } 0109 0110 private: 0111 void send(const QJsonObject &object) 0112 { 0113 const QByteArray data = QJsonDocument(object).toJson(QJsonDocument::Compact) + '\n'; 0114 // qCDebug(PURPOSE_EXTERNAL_PROCESS_LOG) << "sending..." << data; 0115 m_socket.write(data); 0116 } 0117 0118 Purpose::Job *m_job = nullptr; 0119 QLocalSocket m_socket; 0120 }; 0121 0122 int main(int argc, char **argv) 0123 { 0124 #pragma message("warning: make QGuiApplication, consider QCoreApplication?") 0125 QApplication app(argc, argv); 0126 0127 QString serverName; 0128 0129 { 0130 QCommandLineParser parser; 0131 parser.addOption(QCommandLineOption(QStringLiteral("server"), QStringLiteral("Named socket to connect to"), QStringLiteral("name"))); 0132 parser.addOption(QCommandLineOption(QStringLiteral("pluginPath"), QStringLiteral("Chosen plugin"), QStringLiteral("path"))); 0133 parser.addOption(QCommandLineOption(QStringLiteral("pluginType"), QStringLiteral("Plugin type name "), QStringLiteral("path"))); 0134 parser.addHelpOption(); 0135 0136 parser.process(app); 0137 0138 serverName = parser.value(QStringLiteral("server")); 0139 pluginType = parser.value(QStringLiteral("pluginType")); 0140 0141 const QString path = parser.value(QStringLiteral("pluginPath")); 0142 if (path.endsWith(QLatin1String("/metadata.json"))) { 0143 QFileInfo fi(path); 0144 md = Purpose::createMetaData(path); 0145 } else { 0146 md = KPluginMetaData(path); 0147 Q_ASSERT(md.isValid()); 0148 } 0149 } 0150 0151 Communication c(serverName); 0152 0153 return app.exec(); 0154 } 0155 0156 #include "purposeprocess_main.moc"