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"