File indexing completed on 2024-04-21 03:56:41

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org>
0004     SPDX-FileCopyrightText: 2010 KDE e.V. <kde-ev-board@kde.org>
0005     SPDX-FileContributor: 2010 Adriaan de Groot <groot@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kptydevice.h"
0011 #include "kpty_p.h"
0012 
0013 #include <config-pty.h>
0014 
0015 #include <QSocketNotifier>
0016 
0017 #include <KLocalizedString>
0018 
0019 #include <cerrno>
0020 #include <fcntl.h>
0021 #include <signal.h>
0022 #include <sys/ioctl.h>
0023 #include <termios.h>
0024 #include <unistd.h>
0025 #if HAVE_SYS_FILIO_H
0026 #include <sys/filio.h>
0027 #endif
0028 #if HAVE_SYS_TIME_H
0029 #include <sys/time.h>
0030 #endif
0031 
0032 #if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
0033 // "the other end's output queue size" -- that is is our end's input
0034 #define PTY_BYTES_AVAILABLE TIOCOUTQ
0035 #elif defined(TIOCINQ)
0036 // "our end's input queue size"
0037 #define PTY_BYTES_AVAILABLE TIOCINQ
0038 #else
0039 // likewise. more generic ioctl (theoretically)
0040 #define PTY_BYTES_AVAILABLE FIONREAD
0041 #endif
0042 
0043 #define KMAXINT ((int)(~0U >> 1))
0044 
0045 /////////////////////////////////////////////////////
0046 // Helper. Remove when QRingBuffer becomes public. //
0047 /////////////////////////////////////////////////////
0048 
0049 #include <QByteArray>
0050 #include <QList>
0051 
0052 #define CHUNKSIZE 4096
0053 
0054 class KRingBuffer
0055 {
0056 public:
0057     KRingBuffer()
0058     {
0059         clear();
0060     }
0061 
0062     void clear()
0063     {
0064         buffers.clear();
0065         QByteArray tmp;
0066         tmp.resize(CHUNKSIZE);
0067         buffers << tmp;
0068         head = tail = 0;
0069         totalSize = 0;
0070     }
0071 
0072     inline bool isEmpty() const
0073     {
0074         return buffers.count() == 1 && !tail;
0075     }
0076 
0077     inline int size() const
0078     {
0079         return totalSize;
0080     }
0081 
0082     inline int readSize() const
0083     {
0084         return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
0085     }
0086 
0087     inline const char *readPointer() const
0088     {
0089         Q_ASSERT(totalSize > 0);
0090         return buffers.first().constData() + head;
0091     }
0092 
0093     void free(int bytes)
0094     {
0095         totalSize -= bytes;
0096         Q_ASSERT(totalSize >= 0);
0097 
0098         for (;;) {
0099             int nbs = readSize();
0100 
0101             if (bytes < nbs) {
0102                 head += bytes;
0103                 if (head == tail && buffers.count() == 1) {
0104                     buffers.first().resize(CHUNKSIZE);
0105                     head = tail = 0;
0106                 }
0107                 break;
0108             }
0109 
0110             bytes -= nbs;
0111             if (buffers.count() == 1) {
0112                 buffers.first().resize(CHUNKSIZE);
0113                 head = tail = 0;
0114                 break;
0115             }
0116 
0117             buffers.removeFirst();
0118             head = 0;
0119         }
0120     }
0121 
0122     char *reserve(int bytes)
0123     {
0124         totalSize += bytes;
0125 
0126         char *ptr;
0127         if (tail + bytes <= buffers.last().size()) {
0128             ptr = buffers.last().data() + tail;
0129             tail += bytes;
0130         } else {
0131             buffers.last().resize(tail);
0132             QByteArray tmp;
0133             tmp.resize(qMax(CHUNKSIZE, bytes));
0134             ptr = tmp.data();
0135             buffers << tmp;
0136             tail = bytes;
0137         }
0138         return ptr;
0139     }
0140 
0141     // release a trailing part of the last reservation
0142     inline void unreserve(int bytes)
0143     {
0144         totalSize -= bytes;
0145         tail -= bytes;
0146     }
0147 
0148     inline void write(const char *data, int len)
0149     {
0150         memcpy(reserve(len), data, len);
0151     }
0152 
0153     // Find the first occurrence of c and return the index after it.
0154     // If c is not found until maxLength, maxLength is returned, provided
0155     // it is smaller than the buffer size. Otherwise -1 is returned.
0156     int indexAfter(char c, int maxLength = KMAXINT) const
0157     {
0158         int index = 0;
0159         int start = head;
0160         QList<QByteArray>::ConstIterator it = buffers.begin();
0161         for (;;) {
0162             if (!maxLength) {
0163                 return index;
0164             }
0165             if (index == size()) {
0166                 return -1;
0167             }
0168             const QByteArray &buf = *it;
0169             ++it;
0170             int len = qMin((it == buffers.end() ? tail : buf.size()) - start, maxLength);
0171             const char *ptr = buf.data() + start;
0172             if (const char *rptr = (const char *)memchr(ptr, c, len)) {
0173                 return index + (rptr - ptr) + 1;
0174             }
0175             index += len;
0176             maxLength -= len;
0177             start = 0;
0178         }
0179     }
0180 
0181     inline int lineSize(int maxLength = KMAXINT) const
0182     {
0183         return indexAfter('\n', maxLength);
0184     }
0185 
0186     inline bool canReadLine() const
0187     {
0188         return lineSize() != -1;
0189     }
0190 
0191     int read(char *data, int maxLength)
0192     {
0193         int bytesToRead = qMin(size(), maxLength);
0194         int readSoFar = 0;
0195         while (readSoFar < bytesToRead) {
0196             const char *ptr = readPointer();
0197             int bs = qMin(bytesToRead - readSoFar, readSize());
0198             memcpy(data + readSoFar, ptr, bs);
0199             readSoFar += bs;
0200             free(bs);
0201         }
0202         return readSoFar;
0203     }
0204 
0205     int readLine(char *data, int maxLength)
0206     {
0207         return read(data, lineSize(qMin(maxLength, size())));
0208     }
0209 
0210 private:
0211     QList<QByteArray> buffers;
0212     int head, tail;
0213     int totalSize;
0214 };
0215 
0216 //////////////////
0217 // private data //
0218 //////////////////
0219 
0220 // Lifted from Qt. I don't think they would mind. ;)
0221 // Re-lift again from Qt whenever a proper replacement for pthread_once appears
0222 static void qt_ignore_sigpipe()
0223 {
0224     static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
0225     if (atom.testAndSetRelaxed(0, 1)) {
0226         struct sigaction noaction;
0227         memset(&noaction, 0, sizeof(noaction));
0228         noaction.sa_handler = SIG_IGN;
0229         sigaction(SIGPIPE, &noaction, nullptr);
0230     }
0231 }
0232 
0233 /* clang-format off */
0234 #define NO_INTR(ret, func) \
0235     do { \
0236         ret = func; \
0237     } while (ret < 0 && errno == EINTR)
0238 /* clang-format on */
0239 
0240 class KPtyDevicePrivate : public KPtyPrivate
0241 {
0242     Q_DECLARE_PUBLIC(KPtyDevice)
0243 public:
0244     KPtyDevicePrivate(KPty *parent)
0245         : KPtyPrivate(parent)
0246         , emittedReadyRead(false)
0247         , emittedBytesWritten(false)
0248         , readNotifier(nullptr)
0249         , writeNotifier(nullptr)
0250     {
0251     }
0252 
0253     bool _k_canRead();
0254     bool _k_canWrite();
0255 
0256     bool doWait(int msecs, bool reading);
0257     void finishOpen(QIODevice::OpenMode mode);
0258 
0259     bool emittedReadyRead;
0260     bool emittedBytesWritten;
0261     QSocketNotifier *readNotifier;
0262     QSocketNotifier *writeNotifier;
0263     KRingBuffer readBuffer;
0264     KRingBuffer writeBuffer;
0265 };
0266 
0267 bool KPtyDevicePrivate::_k_canRead()
0268 {
0269     Q_Q(KPtyDevice);
0270     qint64 readBytes = 0;
0271 
0272     int available;
0273     if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *)&available)) {
0274         char *ptr = readBuffer.reserve(available);
0275         NO_INTR(readBytes, read(q->masterFd(), ptr, available));
0276         if (readBytes < 0) {
0277             readBuffer.unreserve(available);
0278             q->setErrorString(i18n("Error reading from PTY"));
0279             return false;
0280         }
0281         readBuffer.unreserve(available - readBytes); // *should* be a no-op
0282     }
0283 
0284     if (!readBytes) {
0285         readNotifier->setEnabled(false);
0286         Q_EMIT q->readEof();
0287         return false;
0288     } else {
0289         if (!emittedReadyRead) {
0290             emittedReadyRead = true;
0291             Q_EMIT q->readyRead();
0292             emittedReadyRead = false;
0293         }
0294         return true;
0295     }
0296 }
0297 
0298 bool KPtyDevicePrivate::_k_canWrite()
0299 {
0300     Q_Q(KPtyDevice);
0301 
0302     writeNotifier->setEnabled(false);
0303     if (writeBuffer.isEmpty()) {
0304         return false;
0305     }
0306 
0307     qt_ignore_sigpipe();
0308     int wroteBytes;
0309     NO_INTR(wroteBytes, write(q->masterFd(), writeBuffer.readPointer(), writeBuffer.readSize()));
0310     if (wroteBytes < 0) {
0311         q->setErrorString(i18n("Error writing to PTY"));
0312         return false;
0313     }
0314     writeBuffer.free(wroteBytes);
0315 
0316     if (!emittedBytesWritten) {
0317         emittedBytesWritten = true;
0318         Q_EMIT q->bytesWritten(wroteBytes);
0319         emittedBytesWritten = false;
0320     }
0321 
0322     if (!writeBuffer.isEmpty()) {
0323         writeNotifier->setEnabled(true);
0324     }
0325     return true;
0326 }
0327 
0328 #ifndef timeradd
0329 // Lifted from GLIBC
0330 /* clang-format off */
0331 #define timeradd(a, b, result) \
0332     do { \
0333         (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
0334         (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
0335         if ((result)->tv_usec >= 1000000) { \
0336             ++(result)->tv_sec; \
0337             (result)->tv_usec -= 1000000; \
0338         } \
0339     } while (0)
0340 
0341 #define timersub(a, b, result) \
0342     do { \
0343         (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
0344         (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
0345         if ((result)->tv_usec < 0) { \
0346             --(result)->tv_sec; \
0347             (result)->tv_usec += 1000000; \
0348         } \
0349     } while (0)
0350 #endif
0351 /* clang-format on */
0352 
0353 bool KPtyDevicePrivate::doWait(int msecs, bool reading)
0354 {
0355     Q_Q(KPtyDevice);
0356 #ifndef Q_OS_LINUX
0357     struct timeval etv;
0358 #endif
0359     struct timeval tv;
0360     struct timeval *tvp;
0361 
0362     if (msecs < 0) {
0363         tvp = nullptr;
0364     } else {
0365         tv.tv_sec = msecs / 1000;
0366         tv.tv_usec = (msecs % 1000) * 1000;
0367 #ifndef Q_OS_LINUX
0368         gettimeofday(&etv, nullptr);
0369         timeradd(&tv, &etv, &etv);
0370 #endif
0371         tvp = &tv;
0372     }
0373 
0374     while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
0375         fd_set rfds;
0376         fd_set wfds;
0377 
0378         FD_ZERO(&rfds);
0379         FD_ZERO(&wfds);
0380 
0381         if (readNotifier->isEnabled()) {
0382             FD_SET(q->masterFd(), &rfds);
0383         }
0384         if (!writeBuffer.isEmpty()) {
0385             FD_SET(q->masterFd(), &wfds);
0386         }
0387 
0388 #ifndef Q_OS_LINUX
0389         if (tvp) {
0390             gettimeofday(&tv, nullptr);
0391             timersub(&etv, &tv, &tv);
0392             if (tv.tv_sec < 0) {
0393                 tv.tv_sec = tv.tv_usec = 0;
0394             }
0395         }
0396 #endif
0397 
0398         switch (select(q->masterFd() + 1, &rfds, &wfds, nullptr, tvp)) {
0399         case -1:
0400             if (errno == EINTR) {
0401                 break;
0402             }
0403             return false;
0404         case 0:
0405             q->setErrorString(i18n("PTY operation timed out"));
0406             return false;
0407         default:
0408             if (FD_ISSET(q->masterFd(), &rfds)) {
0409                 bool canRead = _k_canRead();
0410                 if (reading && canRead) {
0411                     return true;
0412                 }
0413             }
0414             if (FD_ISSET(q->masterFd(), &wfds)) {
0415                 bool canWrite = _k_canWrite();
0416                 if (!reading) {
0417                     return canWrite;
0418                 }
0419             }
0420             break;
0421         }
0422     }
0423     return false;
0424 }
0425 
0426 void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
0427 {
0428     Q_Q(KPtyDevice);
0429 
0430     q->QIODevice::open(mode);
0431     fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
0432     readBuffer.clear();
0433     readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
0434     writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
0435     QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead()));
0436     QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite()));
0437     readNotifier->setEnabled(true);
0438 }
0439 
0440 /////////////////////////////
0441 // public member functions //
0442 /////////////////////////////
0443 
0444 KPtyDevice::KPtyDevice(QObject *parent)
0445     : QIODevice(parent)
0446     , KPty(new KPtyDevicePrivate(this))
0447 {
0448 }
0449 
0450 KPtyDevice::~KPtyDevice()
0451 {
0452     close();
0453 }
0454 
0455 bool KPtyDevice::open(OpenMode mode)
0456 {
0457     Q_D(KPtyDevice);
0458 
0459     if (masterFd() >= 0) {
0460         return true;
0461     }
0462 
0463     if (!KPty::open()) {
0464         setErrorString(i18n("Error opening PTY"));
0465         return false;
0466     }
0467 
0468     d->finishOpen(mode);
0469 
0470     return true;
0471 }
0472 
0473 bool KPtyDevice::open(int fd, OpenMode mode)
0474 {
0475     Q_D(KPtyDevice);
0476 
0477     if (!KPty::open(fd)) {
0478         setErrorString(i18n("Error opening PTY"));
0479         return false;
0480     }
0481 
0482     d->finishOpen(mode);
0483 
0484     return true;
0485 }
0486 
0487 void KPtyDevice::close()
0488 {
0489     Q_D(KPtyDevice);
0490 
0491     if (masterFd() < 0) {
0492         return;
0493     }
0494 
0495     delete d->readNotifier;
0496     delete d->writeNotifier;
0497 
0498     QIODevice::close();
0499 
0500     KPty::close();
0501 }
0502 
0503 bool KPtyDevice::isSequential() const
0504 {
0505     return true;
0506 }
0507 
0508 bool KPtyDevice::canReadLine() const
0509 {
0510     Q_D(const KPtyDevice);
0511     return QIODevice::canReadLine() || d->readBuffer.canReadLine();
0512 }
0513 
0514 bool KPtyDevice::atEnd() const
0515 {
0516     Q_D(const KPtyDevice);
0517     return QIODevice::atEnd() && d->readBuffer.isEmpty();
0518 }
0519 
0520 qint64 KPtyDevice::bytesAvailable() const
0521 {
0522     Q_D(const KPtyDevice);
0523     return QIODevice::bytesAvailable() + d->readBuffer.size();
0524 }
0525 
0526 qint64 KPtyDevice::bytesToWrite() const
0527 {
0528     Q_D(const KPtyDevice);
0529     return d->writeBuffer.size();
0530 }
0531 
0532 bool KPtyDevice::waitForReadyRead(int msecs)
0533 {
0534     Q_D(KPtyDevice);
0535     return d->doWait(msecs, true);
0536 }
0537 
0538 bool KPtyDevice::waitForBytesWritten(int msecs)
0539 {
0540     Q_D(KPtyDevice);
0541     return d->doWait(msecs, false);
0542 }
0543 
0544 void KPtyDevice::setSuspended(bool suspended)
0545 {
0546     Q_D(KPtyDevice);
0547     d->readNotifier->setEnabled(!suspended);
0548 }
0549 
0550 bool KPtyDevice::isSuspended() const
0551 {
0552     Q_D(const KPtyDevice);
0553     return !d->readNotifier->isEnabled();
0554 }
0555 
0556 // protected
0557 qint64 KPtyDevice::readData(char *data, qint64 maxlen)
0558 {
0559     Q_D(KPtyDevice);
0560     return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT));
0561 }
0562 
0563 // protected
0564 qint64 KPtyDevice::readLineData(char *data, qint64 maxlen)
0565 {
0566     Q_D(KPtyDevice);
0567     return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT));
0568 }
0569 
0570 // protected
0571 qint64 KPtyDevice::writeData(const char *data, qint64 len)
0572 {
0573     Q_D(KPtyDevice);
0574     Q_ASSERT(len <= KMAXINT);
0575 
0576     d->writeBuffer.write(data, len);
0577     d->writeNotifier->setEnabled(true);
0578     return len;
0579 }
0580 
0581 #include "moc_kptydevice.cpp"