File indexing completed on 2024-04-28 16:51:32

0001 /*
0002     SPDX-FileCopyrightText: 2017 Kai Uwe Broulik <kde@privat.broulik.de>
0003     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 #include "connection.h"
0009 #include <QCoreApplication>
0010 #include <QJsonDocument>
0011 #include <QSocketNotifier>
0012 
0013 #include <QDebug>
0014 #include <poll.h>
0015 #include <unistd.h>
0016 
0017 Connection::Connection()
0018     : QObject()
0019 {
0020     // Make really sure no one but us, who uses the correct format, prints to stdout
0021     int newStdout = dup(STDOUT_FILENO);
0022     // redirect it to stderr so it's not just swallowed
0023     dup2(STDERR_FILENO, STDOUT_FILENO);
0024     m_stdOut.open(newStdout, QIODevice::WriteOnly);
0025 
0026     m_stdIn.open(STDIN_FILENO, QIODevice::ReadOnly | QIODevice::Unbuffered);
0027 
0028     auto notifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);
0029     connect(notifier, &QSocketNotifier::activated, this, &Connection::readData);
0030 }
0031 
0032 void Connection::sendData(const QJsonObject &data)
0033 {
0034     const QByteArray rawData = QJsonDocument(data).toJson(QJsonDocument::Compact);
0035     // note, don't use QDataStream as we need to control the binary format used
0036     quint32 len = rawData.count();
0037     m_stdOut.write((char *)&len, sizeof(len));
0038     m_stdOut.write(rawData);
0039     m_stdOut.flush();
0040 }
0041 
0042 Connection *Connection::self()
0043 {
0044     static Connection *s = nullptr;
0045     if (!s) {
0046         s = new Connection();
0047     }
0048     return s;
0049 }
0050 
0051 void Connection::readData()
0052 {
0053     /* Qt does not recognize POLLHUP as an error and
0054      * as that flag never gets cleared, we enter an
0055      * infinite busy loop polling STDIN.
0056      * So we need to check for this condition ourselves
0057      * and exit. */
0058 
0059     struct pollfd poll_stdin = {};
0060     poll_stdin.fd = STDIN_FILENO;
0061     poll_stdin.events = POLLHUP;
0062     poll_stdin.revents = 0;
0063 
0064     if (poll(&poll_stdin, 1, 0) != 0) {
0065         // STDIN has HUP/ERR/NVAL condition
0066         qApp->exit(0);
0067         return;
0068     }
0069 
0070     m_stdIn.startTransaction();
0071     quint32 length = 0;
0072     auto rc = m_stdIn.read((char *)(&length), sizeof(quint32));
0073     if (rc == -1) {
0074         m_stdIn.rollbackTransaction();
0075         return;
0076     }
0077 
0078     QByteArray data = m_stdIn.read(length);
0079     if (data.length() != int(length)) {
0080         m_stdIn.rollbackTransaction();
0081         return;
0082     }
0083 
0084     if (data.isEmpty()) {
0085         m_stdIn.rollbackTransaction();
0086         return;
0087     }
0088 
0089     m_stdIn.commitTransaction();
0090     const QJsonObject json = QJsonDocument::fromJson(data).object();
0091     Q_EMIT dataReceived(json);
0092 }