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