• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KPty

kptydevice.cpp

Go to the documentation of this file.
00001 /*
00002 
00003    This file is part of the KDE libraries
00004    Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
00005    Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org>
00006      Author Adriaan de Groot <groot@kde.org>
00007 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021    Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "kptydevice.h"
00025 #include "kpty_p.h"
00026 
00027 #include <config.h>
00028 #include <config-pty.h>
00029 
00030 #include <QSocketNotifier>
00031 
00032 #include <klocale.h>
00033 
00034 #include <unistd.h>
00035 #include <errno.h>
00036 #include <signal.h>
00037 #include <termios.h>
00038 #include <fcntl.h>
00039 #include <sys/ioctl.h>
00040 #ifdef HAVE_SYS_FILIO_H
00041 # include <sys/filio.h>
00042 #endif
00043 #ifdef HAVE_SYS_TIME_H
00044 # include <sys/time.h>
00045 #endif
00046 
00047 #if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
00048   // "the other end's output queue size" - kinda braindead, huh?
00049 # define PTY_BYTES_AVAILABLE TIOCOUTQ
00050 #elif defined(TIOCINQ)
00051   // "our end's input queue size"
00052 # define PTY_BYTES_AVAILABLE TIOCINQ
00053 #else
00054   // likewise. more generic ioctl (theoretically)
00055 # define PTY_BYTES_AVAILABLE FIONREAD
00056 #endif
00057 
00058 #define KMAXINT ((int)(~0U >> 1))
00059 
00061 // Helper. Remove when QRingBuffer becomes public. //
00063 
00064 #include <QtCore/qbytearray.h>
00065 #include <QtCore/qlinkedlist.h>
00066 
00067 #define CHUNKSIZE 4096
00068 
00069 class KRingBuffer
00070 {
00071 public:
00072     KRingBuffer()
00073     {
00074         clear();
00075     }
00076 
00077     void clear()
00078     {
00079         buffers.clear();
00080         QByteArray tmp;
00081         tmp.resize(CHUNKSIZE);
00082         buffers << tmp;
00083         head = tail = 0;
00084         totalSize = 0;
00085     }
00086 
00087     inline bool isEmpty() const
00088     {
00089         return buffers.count() == 1 && !tail;
00090     }
00091 
00092     inline int size() const
00093     {
00094         return totalSize;
00095     }
00096 
00097     inline int readSize() const
00098     {
00099         return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
00100     }
00101 
00102     inline const char *readPointer() const
00103     {
00104         Q_ASSERT(totalSize > 0);
00105         return buffers.first().constData() + head;
00106     }
00107 
00108     void free(int bytes)
00109     {
00110         totalSize -= bytes;
00111         Q_ASSERT(totalSize >= 0);
00112 
00113         forever {
00114             int nbs = readSize();
00115 
00116             if (bytes < nbs) {
00117                 head += bytes;
00118                 if (head == tail && buffers.count() == 1) {
00119                     buffers.first().resize(CHUNKSIZE);
00120                     head = tail = 0;
00121                 }
00122                 break;
00123             }
00124 
00125             bytes -= nbs;
00126             if (buffers.count() == 1) {
00127                 buffers.first().resize(CHUNKSIZE);
00128                 head = tail = 0;
00129                 break;
00130             }
00131 
00132             buffers.removeFirst();
00133             head = 0;
00134         }
00135     }
00136 
00137     char *reserve(int bytes)
00138     {
00139         totalSize += bytes;
00140 
00141         char *ptr;
00142         if (tail + bytes <= buffers.last().size()) {
00143             ptr = buffers.last().data() + tail;
00144             tail += bytes;
00145         } else {
00146             buffers.last().resize(tail);
00147             QByteArray tmp;
00148             tmp.resize(qMax(CHUNKSIZE, bytes));
00149             ptr = tmp.data();
00150             buffers << tmp;
00151             tail = bytes;
00152         }
00153         return ptr;
00154     }
00155 
00156     // release a trailing part of the last reservation
00157     inline void unreserve(int bytes)
00158     {
00159         totalSize -= bytes;
00160         tail -= bytes;
00161     }
00162 
00163     inline void write(const char *data, int len)
00164     {
00165         memcpy(reserve(len), data, len);
00166     }
00167 
00168     // Find the first occurrence of c and return the index after it.
00169     // If c is not found until maxLength, maxLength is returned, provided
00170     // it is smaller than the buffer size. Otherwise -1 is returned.
00171     int indexAfter(char c, int maxLength = KMAXINT) const
00172     {
00173         int index = 0;
00174         int start = head;
00175         QLinkedList<QByteArray>::ConstIterator it = buffers.begin();
00176         forever {
00177             if (!maxLength)
00178                 return index;
00179             if (index == size())
00180                 return -1;
00181             const QByteArray &buf = *it;
00182             ++it;
00183             int len = qMin((it == buffers.end() ? tail : buf.size()) - start,
00184                            maxLength);
00185             const char *ptr = buf.data() + start;
00186             if (const char *rptr = (const char *)memchr(ptr, c, len))
00187                 return index + (rptr - ptr) + 1;
00188             index += len;
00189             maxLength -= len;
00190             start = 0;
00191         }
00192     }
00193 
00194     inline int lineSize(int maxLength = KMAXINT) const
00195     {
00196         return indexAfter('\n', maxLength);
00197     }
00198 
00199     inline bool canReadLine() const
00200     {
00201         return lineSize() != -1;
00202     }
00203 
00204     int read(char *data, int maxLength)
00205     {
00206         int bytesToRead = qMin(size(), maxLength);
00207         int readSoFar = 0;
00208         while (readSoFar < bytesToRead) {
00209             const char *ptr = readPointer();
00210             int bs = qMin(bytesToRead - readSoFar, readSize());
00211             memcpy(data + readSoFar, ptr, bs);
00212             readSoFar += bs;
00213             free(bs);
00214         }
00215         return readSoFar;
00216     }
00217 
00218     int readLine(char *data, int maxLength)
00219     {
00220         return read(data, lineSize(qMin(maxLength, size())));
00221     }
00222 
00223 private:
00224     QLinkedList<QByteArray> buffers;
00225     int head, tail;
00226     int totalSize;
00227 };
00228 
00230 // private data //
00232 
00233 // Lifted from Qt. I don't think they would mind. ;)
00234 // Re-lift again from Qt whenever a proper replacement for pthread_once appears
00235 static void qt_ignore_sigpipe()
00236 {
00237     static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
00238     if (atom.testAndSetRelaxed(0, 1)) {
00239         struct sigaction noaction;
00240         memset(&noaction, 0, sizeof(noaction));
00241         noaction.sa_handler = SIG_IGN;
00242         sigaction(SIGPIPE, &noaction, 0);
00243     }
00244 }
00245 
00246 #define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR)
00247 
00248 struct KPtyDevicePrivate : public KPtyPrivate {
00249     Q_DECLARE_PUBLIC(KPtyDevice)
00250 
00251     KPtyDevicePrivate(KPty* parent) :
00252         KPtyPrivate(parent),
00253         emittedReadyRead(false), emittedBytesWritten(false),
00254         readNotifier(0), writeNotifier(0)
00255     {
00256     }
00257 
00258     bool _k_canRead();
00259     bool _k_canWrite();
00260 
00261     bool doWait(int msecs, bool reading);
00262     void finishOpen(QIODevice::OpenMode mode);
00263 
00264     bool emittedReadyRead;
00265     bool emittedBytesWritten;
00266     QSocketNotifier *readNotifier;
00267     QSocketNotifier *writeNotifier;
00268     KRingBuffer readBuffer;
00269     KRingBuffer writeBuffer;
00270 };
00271 
00272 bool KPtyDevicePrivate::_k_canRead()
00273 {
00274     Q_Q(KPtyDevice);
00275     qint64 readBytes = 0;
00276 
00277 #ifdef Q_OS_IRIX // this should use a config define, but how to check it?
00278     size_t available;
00279 #else
00280     int available;
00281 #endif
00282     if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *) &available)) {
00283 #ifdef Q_OS_SOLARIS
00284         // A Pty is a STREAMS module, and those can be activated
00285         // with 0 bytes available. This happens either when ^C is
00286         // pressed, or when an application does an explicit write(a,b,0)
00287         // which happens in experiments fairly often. When 0 bytes are
00288         // available, you must read those 0 bytes to clear the STREAMS
00289         // module, but we don't want to hit the !readBytes case further down.
00290         if (!available) {
00291             char c;
00292             // Read the 0-byte STREAMS message
00293             NO_INTR(readBytes, read(q->masterFd(), &c, 0));
00294             // Should return 0 bytes read; -1 is error
00295             if (readBytes < 0) {
00296                 readNotifier->setEnabled(false);
00297                 emit q->readEof();
00298                 return false;
00299             }
00300             return true;
00301         }
00302 #endif
00303 
00304         char *ptr = readBuffer.reserve(available);
00305 #ifdef Q_OS_SOLARIS
00306         // Even if available > 0, it is possible for read()
00307         // to return 0 on Solaris, due to 0-byte writes in the stream.
00308         // Ignore them and keep reading until we hit *some* data.
00309         // In Solaris it is possible to have 15 bytes available
00310         // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads.
00311         // Because the stream is set to O_NONBLOCK in finishOpen(),
00312         // an EOF read will return -1.
00313         readBytes = 0;
00314         while (!readBytes)
00315 #endif
00316         // Useless block braces except in Solaris
00317         {
00318           NO_INTR(readBytes, read(q->masterFd(), ptr, available));
00319         }
00320         if (readBytes < 0) {
00321             readBuffer.unreserve(available);
00322             q->setErrorString(i18n("Error reading from PTY"));
00323             return false;
00324         }
00325         readBuffer.unreserve(available - readBytes); // *should* be a no-op
00326     }
00327 
00328     if (!readBytes) {
00329         readNotifier->setEnabled(false);
00330         emit q->readEof();
00331         return false;
00332     } else {
00333         if (!emittedReadyRead) {
00334             emittedReadyRead = true;
00335             emit q->readyRead();
00336             emittedReadyRead = false;
00337         }
00338         return true;
00339     }
00340 }
00341 
00342 bool KPtyDevicePrivate::_k_canWrite()
00343 {
00344     Q_Q(KPtyDevice);
00345 
00346     writeNotifier->setEnabled(false);
00347     if (writeBuffer.isEmpty())
00348         return false;
00349 
00350     qt_ignore_sigpipe();
00351     int wroteBytes;
00352     NO_INTR(wroteBytes,
00353             write(q->masterFd(),
00354                   writeBuffer.readPointer(), writeBuffer.readSize()));
00355     if (wroteBytes < 0) {
00356         q->setErrorString(i18n("Error writing to PTY"));
00357         return false;
00358     }
00359     writeBuffer.free(wroteBytes);
00360 
00361     if (!emittedBytesWritten) {
00362         emittedBytesWritten = true;
00363         emit q->bytesWritten(wroteBytes);
00364         emittedBytesWritten = false;
00365     }
00366 
00367     if (!writeBuffer.isEmpty())
00368         writeNotifier->setEnabled(true);
00369     return true;
00370 }
00371 
00372 #ifndef timeradd
00373 // Lifted from GLIBC
00374 # define timeradd(a, b, result) \
00375     do { \
00376         (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
00377         (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
00378         if ((result)->tv_usec >= 1000000) { \
00379             ++(result)->tv_sec; \
00380             (result)->tv_usec -= 1000000; \
00381         } \
00382     } while (0)
00383 # define timersub(a, b, result) \
00384     do { \
00385         (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
00386         (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
00387         if ((result)->tv_usec < 0) { \
00388             --(result)->tv_sec; \
00389             (result)->tv_usec += 1000000; \
00390         } \
00391     } while (0)
00392 #endif
00393 
00394 bool KPtyDevicePrivate::doWait(int msecs, bool reading)
00395 {
00396     Q_Q(KPtyDevice);
00397 #ifndef __linux__
00398     struct timeval etv;
00399 #endif
00400     struct timeval tv, *tvp;
00401 
00402     if (msecs < 0)
00403         tvp = 0;
00404     else {
00405         tv.tv_sec = msecs / 1000;
00406         tv.tv_usec = (msecs % 1000) * 1000;
00407 #ifndef __linux__
00408         gettimeofday(&etv, 0);
00409         timeradd(&tv, &etv, &etv);
00410 #endif
00411         tvp = &tv;
00412     }
00413 
00414     while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
00415         fd_set rfds;
00416         fd_set wfds;
00417 
00418         FD_ZERO(&rfds);
00419         FD_ZERO(&wfds);
00420 
00421         if (readNotifier->isEnabled())
00422             FD_SET(q->masterFd(), &rfds);
00423         if (!writeBuffer.isEmpty())
00424             FD_SET(q->masterFd(), &wfds);
00425 
00426 #ifndef __linux__
00427         if (tvp) {
00428             gettimeofday(&tv, 0);
00429             timersub(&etv, &tv, &tv);
00430             if (tv.tv_sec < 0)
00431                 tv.tv_sec = tv.tv_usec = 0;
00432         }
00433 #endif
00434 
00435         switch (select(q->masterFd() + 1, &rfds, &wfds, 0, tvp)) {
00436         case -1:
00437             if (errno == EINTR)
00438                 break;
00439             return false;
00440         case 0:
00441             q->setErrorString(i18n("PTY operation timed out"));
00442             return false;
00443         default:
00444             if (FD_ISSET(q->masterFd(), &rfds)) {
00445                 bool canRead = _k_canRead();
00446                 if (reading && canRead)
00447                     return true;
00448             }
00449             if (FD_ISSET(q->masterFd(), &wfds)) {
00450                 bool canWrite = _k_canWrite();
00451                 if (!reading)
00452                     return canWrite;
00453             }
00454             break;
00455         }
00456     }
00457     return false;
00458 }
00459 
00460 void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
00461 {
00462     Q_Q(KPtyDevice);
00463 
00464     q->QIODevice::open(mode);
00465     fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
00466     readBuffer.clear();
00467     readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
00468     writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
00469     QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead()));
00470     QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite()));
00471     readNotifier->setEnabled(true);
00472 }
00473 
00475 // public member functions //
00477 
00478 KPtyDevice::KPtyDevice(QObject *parent) :
00479     QIODevice(parent),
00480     KPty(new KPtyDevicePrivate(this))
00481 {
00482 }
00483 
00484 KPtyDevice::~KPtyDevice()
00485 {
00486     close();
00487 }
00488 
00489 bool KPtyDevice::open(OpenMode mode)
00490 {
00491     Q_D(KPtyDevice);
00492 
00493     if (masterFd() >= 0)
00494         return true;
00495 
00496     if (!KPty::open()) {
00497         setErrorString(i18n("Error opening PTY"));
00498         return false;
00499     }
00500 
00501     d->finishOpen(mode);
00502 
00503     return true;
00504 }
00505 
00506 bool KPtyDevice::open(int fd, OpenMode mode)
00507 {
00508     Q_D(KPtyDevice);
00509 
00510     if (!KPty::open(fd)) {
00511         setErrorString(i18n("Error opening PTY"));
00512         return false;
00513     }
00514 
00515     d->finishOpen(mode);
00516 
00517     return true;
00518 }
00519 
00520 void KPtyDevice::close()
00521 {
00522     Q_D(KPtyDevice);
00523 
00524     if (masterFd() < 0)
00525         return;
00526 
00527     delete d->readNotifier;
00528     delete d->writeNotifier;
00529 
00530     QIODevice::close();
00531 
00532     KPty::close();
00533 }
00534 
00535 bool KPtyDevice::isSequential() const
00536 {
00537     return true;
00538 }
00539 
00540 bool KPtyDevice::canReadLine() const
00541 {
00542     Q_D(const KPtyDevice);
00543     return QIODevice::canReadLine() || d->readBuffer.canReadLine();
00544 }
00545 
00546 bool KPtyDevice::atEnd() const
00547 {
00548     Q_D(const KPtyDevice);
00549     return QIODevice::atEnd() && d->readBuffer.isEmpty();
00550 }
00551 
00552 qint64 KPtyDevice::bytesAvailable() const
00553 {
00554     Q_D(const KPtyDevice);
00555     return QIODevice::bytesAvailable() + d->readBuffer.size();
00556 }
00557 
00558 qint64 KPtyDevice::bytesToWrite() const
00559 {
00560     Q_D(const KPtyDevice);
00561     return d->writeBuffer.size();
00562 }
00563 
00564 bool KPtyDevice::waitForReadyRead(int msecs)
00565 {
00566     Q_D(KPtyDevice);
00567     return d->doWait(msecs, true);
00568 }
00569 
00570 bool KPtyDevice::waitForBytesWritten(int msecs)
00571 {
00572     Q_D(KPtyDevice);
00573     return d->doWait(msecs, false);
00574 }
00575 
00576 void KPtyDevice::setSuspended(bool suspended)
00577 {
00578     Q_D(KPtyDevice);
00579     d->readNotifier->setEnabled(!suspended);
00580 }
00581 
00582 bool KPtyDevice::isSuspended() const
00583 {
00584     Q_D(const KPtyDevice);
00585     return !d->readNotifier->isEnabled();
00586 }
00587 
00588 // protected
00589 qint64 KPtyDevice::readData(char *data, qint64 maxlen)
00590 {
00591     Q_D(KPtyDevice);
00592     return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT));
00593 }
00594 
00595 // protected
00596 qint64 KPtyDevice::readLineData(char *data, qint64 maxlen)
00597 {
00598     Q_D(KPtyDevice);
00599     return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT));
00600 }
00601 
00602 // protected
00603 qint64 KPtyDevice::writeData(const char *data, qint64 len)
00604 {
00605     Q_D(KPtyDevice);
00606     Q_ASSERT(len <= KMAXINT);
00607 
00608     d->writeBuffer.write(data, len);
00609     d->writeNotifier->setEnabled(true);
00610     return len;
00611 }
00612 
00613 #include "kptydevice.moc"

KPty

Skip menu "KPty"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal