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

KPty

  • sources
  • kde-4.12
  • kdelibs
  • kpty
kptydevice.cpp
Go to the documentation of this file.
1 /*
2 
3  This file is part of the KDE libraries
4  Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
5  Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org>
6  Author Adriaan de Groot <groot@kde.org>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 
24 #include "kptydevice.h"
25 #include "kpty_p.h"
26 
27 #include <config.h>
28 #include <config-pty.h>
29 
30 #include <QSocketNotifier>
31 
32 #include <klocale.h>
33 
34 #include <unistd.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <termios.h>
38 #include <fcntl.h>
39 #include <sys/ioctl.h>
40 #ifdef HAVE_SYS_FILIO_H
41 # include <sys/filio.h>
42 #endif
43 #ifdef HAVE_SYS_TIME_H
44 # include <sys/time.h>
45 #endif
46 
47 #if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
48  // "the other end's output queue size" - kinda braindead, huh?
49 # define PTY_BYTES_AVAILABLE TIOCOUTQ
50 #elif defined(TIOCINQ)
51  // "our end's input queue size"
52 # define PTY_BYTES_AVAILABLE TIOCINQ
53 #else
54  // likewise. more generic ioctl (theoretically)
55 # define PTY_BYTES_AVAILABLE FIONREAD
56 #endif
57 
58 #define KMAXINT ((int)(~0U >> 1))
59 
61 // Helper. Remove when QRingBuffer becomes public. //
63 
64 #include <QtCore/qbytearray.h>
65 #include <QtCore/qlinkedlist.h>
66 
67 #define CHUNKSIZE 4096
68 
69 class KRingBuffer
70 {
71 public:
72  KRingBuffer()
73  {
74  clear();
75  }
76 
77  void clear()
78  {
79  buffers.clear();
80  QByteArray tmp;
81  tmp.resize(CHUNKSIZE);
82  buffers << tmp;
83  head = tail = 0;
84  totalSize = 0;
85  }
86 
87  inline bool isEmpty() const
88  {
89  return buffers.count() == 1 && !tail;
90  }
91 
92  inline int size() const
93  {
94  return totalSize;
95  }
96 
97  inline int readSize() const
98  {
99  return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
100  }
101 
102  inline const char *readPointer() const
103  {
104  Q_ASSERT(totalSize > 0);
105  return buffers.first().constData() + head;
106  }
107 
108  void free(int bytes)
109  {
110  totalSize -= bytes;
111  Q_ASSERT(totalSize >= 0);
112 
113  forever {
114  int nbs = readSize();
115 
116  if (bytes < nbs) {
117  head += bytes;
118  if (head == tail && buffers.count() == 1) {
119  buffers.first().resize(CHUNKSIZE);
120  head = tail = 0;
121  }
122  break;
123  }
124 
125  bytes -= nbs;
126  if (buffers.count() == 1) {
127  buffers.first().resize(CHUNKSIZE);
128  head = tail = 0;
129  break;
130  }
131 
132  buffers.removeFirst();
133  head = 0;
134  }
135  }
136 
137  char *reserve(int bytes)
138  {
139  totalSize += bytes;
140 
141  char *ptr;
142  if (tail + bytes <= buffers.last().size()) {
143  ptr = buffers.last().data() + tail;
144  tail += bytes;
145  } else {
146  buffers.last().resize(tail);
147  QByteArray tmp;
148  tmp.resize(qMax(CHUNKSIZE, bytes));
149  ptr = tmp.data();
150  buffers << tmp;
151  tail = bytes;
152  }
153  return ptr;
154  }
155 
156  // release a trailing part of the last reservation
157  inline void unreserve(int bytes)
158  {
159  totalSize -= bytes;
160  tail -= bytes;
161  }
162 
163  inline void write(const char *data, int len)
164  {
165  memcpy(reserve(len), data, len);
166  }
167 
168  // Find the first occurrence of c and return the index after it.
169  // If c is not found until maxLength, maxLength is returned, provided
170  // it is smaller than the buffer size. Otherwise -1 is returned.
171  int indexAfter(char c, int maxLength = KMAXINT) const
172  {
173  int index = 0;
174  int start = head;
175  QLinkedList<QByteArray>::ConstIterator it = buffers.begin();
176  forever {
177  if (!maxLength)
178  return index;
179  if (index == size())
180  return -1;
181  const QByteArray &buf = *it;
182  ++it;
183  int len = qMin((it == buffers.end() ? tail : buf.size()) - start,
184  maxLength);
185  const char *ptr = buf.data() + start;
186  if (const char *rptr = (const char *)memchr(ptr, c, len))
187  return index + (rptr - ptr) + 1;
188  index += len;
189  maxLength -= len;
190  start = 0;
191  }
192  }
193 
194  inline int lineSize(int maxLength = KMAXINT) const
195  {
196  return indexAfter('\n', maxLength);
197  }
198 
199  inline bool canReadLine() const
200  {
201  return lineSize() != -1;
202  }
203 
204  int read(char *data, int maxLength)
205  {
206  int bytesToRead = qMin(size(), maxLength);
207  int readSoFar = 0;
208  while (readSoFar < bytesToRead) {
209  const char *ptr = readPointer();
210  int bs = qMin(bytesToRead - readSoFar, readSize());
211  memcpy(data + readSoFar, ptr, bs);
212  readSoFar += bs;
213  free(bs);
214  }
215  return readSoFar;
216  }
217 
218  int readLine(char *data, int maxLength)
219  {
220  return read(data, lineSize(qMin(maxLength, size())));
221  }
222 
223 private:
224  QLinkedList<QByteArray> buffers;
225  int head, tail;
226  int totalSize;
227 };
228 
230 // private data //
232 
233 // Lifted from Qt. I don't think they would mind. ;)
234 // Re-lift again from Qt whenever a proper replacement for pthread_once appears
235 static void qt_ignore_sigpipe()
236 {
237  static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
238  if (atom.testAndSetRelaxed(0, 1)) {
239  struct sigaction noaction;
240  memset(&noaction, 0, sizeof(noaction));
241  noaction.sa_handler = SIG_IGN;
242  sigaction(SIGPIPE, &noaction, 0);
243  }
244 }
245 
246 #define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR)
247 
248 struct KPtyDevicePrivate : public KPtyPrivate {
249  Q_DECLARE_PUBLIC(KPtyDevice)
250 
251  KPtyDevicePrivate(KPty* parent) :
252  KPtyPrivate(parent),
253  emittedReadyRead(false), emittedBytesWritten(false),
254  readNotifier(0), writeNotifier(0)
255  {
256  }
257 
258  bool _k_canRead();
259  bool _k_canWrite();
260 
261  bool doWait(int msecs, bool reading);
262  void finishOpen(QIODevice::OpenMode mode);
263 
264  bool emittedReadyRead;
265  bool emittedBytesWritten;
266  QSocketNotifier *readNotifier;
267  QSocketNotifier *writeNotifier;
268  KRingBuffer readBuffer;
269  KRingBuffer writeBuffer;
270 };
271 
272 bool KPtyDevicePrivate::_k_canRead()
273 {
274  Q_Q(KPtyDevice);
275  qint64 readBytes = 0;
276 
277 #ifdef Q_OS_IRIX // this should use a config define, but how to check it?
278  size_t available;
279 #else
280  int available;
281 #endif
282  if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *) &available)) {
283 #ifdef Q_OS_SOLARIS
284  // A Pty is a STREAMS module, and those can be activated
285  // with 0 bytes available. This happens either when ^C is
286  // pressed, or when an application does an explicit write(a,b,0)
287  // which happens in experiments fairly often. When 0 bytes are
288  // available, you must read those 0 bytes to clear the STREAMS
289  // module, but we don't want to hit the !readBytes case further down.
290  if (!available) {
291  char c;
292  // Read the 0-byte STREAMS message
293  NO_INTR(readBytes, read(q->masterFd(), &c, 0));
294  // Should return 0 bytes read; -1 is error
295  if (readBytes < 0) {
296  readNotifier->setEnabled(false);
297  emit q->readEof();
298  return false;
299  }
300  return true;
301  }
302 #endif
303 
304  char *ptr = readBuffer.reserve(available);
305 #ifdef Q_OS_SOLARIS
306  // Even if available > 0, it is possible for read()
307  // to return 0 on Solaris, due to 0-byte writes in the stream.
308  // Ignore them and keep reading until we hit *some* data.
309  // In Solaris it is possible to have 15 bytes available
310  // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads.
311  // Because the stream is set to O_NONBLOCK in finishOpen(),
312  // an EOF read will return -1.
313  readBytes = 0;
314  while (!readBytes)
315 #endif
316  // Useless block braces except in Solaris
317  {
318  NO_INTR(readBytes, read(q->masterFd(), ptr, available));
319  }
320  if (readBytes < 0) {
321  readBuffer.unreserve(available);
322  q->setErrorString(i18n("Error reading from PTY"));
323  return false;
324  }
325  readBuffer.unreserve(available - readBytes); // *should* be a no-op
326  }
327 
328  if (!readBytes) {
329  readNotifier->setEnabled(false);
330  emit q->readEof();
331  return false;
332  } else {
333  if (!emittedReadyRead) {
334  emittedReadyRead = true;
335  emit q->readyRead();
336  emittedReadyRead = false;
337  }
338  return true;
339  }
340 }
341 
342 bool KPtyDevicePrivate::_k_canWrite()
343 {
344  Q_Q(KPtyDevice);
345 
346  writeNotifier->setEnabled(false);
347  if (writeBuffer.isEmpty())
348  return false;
349 
350  qt_ignore_sigpipe();
351  int wroteBytes;
352  NO_INTR(wroteBytes,
353  write(q->masterFd(),
354  writeBuffer.readPointer(), writeBuffer.readSize()));
355  if (wroteBytes < 0) {
356  q->setErrorString(i18n("Error writing to PTY"));
357  return false;
358  }
359  writeBuffer.free(wroteBytes);
360 
361  if (!emittedBytesWritten) {
362  emittedBytesWritten = true;
363  emit q->bytesWritten(wroteBytes);
364  emittedBytesWritten = false;
365  }
366 
367  if (!writeBuffer.isEmpty())
368  writeNotifier->setEnabled(true);
369  return true;
370 }
371 
372 #ifndef timeradd
373 // Lifted from GLIBC
374 # define timeradd(a, b, result) \
375  do { \
376  (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
377  (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
378  if ((result)->tv_usec >= 1000000) { \
379  ++(result)->tv_sec; \
380  (result)->tv_usec -= 1000000; \
381  } \
382  } while (0)
383 # define timersub(a, b, result) \
384  do { \
385  (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
386  (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
387  if ((result)->tv_usec < 0) { \
388  --(result)->tv_sec; \
389  (result)->tv_usec += 1000000; \
390  } \
391  } while (0)
392 #endif
393 
394 bool KPtyDevicePrivate::doWait(int msecs, bool reading)
395 {
396  Q_Q(KPtyDevice);
397 #ifndef __linux__
398  struct timeval etv;
399 #endif
400  struct timeval tv, *tvp;
401 
402  if (msecs < 0)
403  tvp = 0;
404  else {
405  tv.tv_sec = msecs / 1000;
406  tv.tv_usec = (msecs % 1000) * 1000;
407 #ifndef __linux__
408  gettimeofday(&etv, 0);
409  timeradd(&tv, &etv, &etv);
410 #endif
411  tvp = &tv;
412  }
413 
414  while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
415  fd_set rfds;
416  fd_set wfds;
417 
418  FD_ZERO(&rfds);
419  FD_ZERO(&wfds);
420 
421  if (readNotifier->isEnabled())
422  FD_SET(q->masterFd(), &rfds);
423  if (!writeBuffer.isEmpty())
424  FD_SET(q->masterFd(), &wfds);
425 
426 #ifndef __linux__
427  if (tvp) {
428  gettimeofday(&tv, 0);
429  timersub(&etv, &tv, &tv);
430  if (tv.tv_sec < 0)
431  tv.tv_sec = tv.tv_usec = 0;
432  }
433 #endif
434 
435  switch (select(q->masterFd() + 1, &rfds, &wfds, 0, tvp)) {
436  case -1:
437  if (errno == EINTR)
438  break;
439  return false;
440  case 0:
441  q->setErrorString(i18n("PTY operation timed out"));
442  return false;
443  default:
444  if (FD_ISSET(q->masterFd(), &rfds)) {
445  bool canRead = _k_canRead();
446  if (reading && canRead)
447  return true;
448  }
449  if (FD_ISSET(q->masterFd(), &wfds)) {
450  bool canWrite = _k_canWrite();
451  if (!reading)
452  return canWrite;
453  }
454  break;
455  }
456  }
457  return false;
458 }
459 
460 void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
461 {
462  Q_Q(KPtyDevice);
463 
464  q->QIODevice::open(mode);
465  fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
466  readBuffer.clear();
467  readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
468  writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
469  QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead()));
470  QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite()));
471  readNotifier->setEnabled(true);
472 }
473 
475 // public member functions //
477 
478 KPtyDevice::KPtyDevice(QObject *parent) :
479  QIODevice(parent),
480  KPty(new KPtyDevicePrivate(this))
481 {
482 }
483 
484 KPtyDevice::~KPtyDevice()
485 {
486  close();
487 }
488 
489 bool KPtyDevice::open(OpenMode mode)
490 {
491  Q_D(KPtyDevice);
492 
493  if (masterFd() >= 0)
494  return true;
495 
496  if (!KPty::open()) {
497  setErrorString(i18n("Error opening PTY"));
498  return false;
499  }
500 
501  d->finishOpen(mode);
502 
503  return true;
504 }
505 
506 bool KPtyDevice::open(int fd, OpenMode mode)
507 {
508  Q_D(KPtyDevice);
509 
510  if (!KPty::open(fd)) {
511  setErrorString(i18n("Error opening PTY"));
512  return false;
513  }
514 
515  d->finishOpen(mode);
516 
517  return true;
518 }
519 
520 void KPtyDevice::close()
521 {
522  Q_D(KPtyDevice);
523 
524  if (masterFd() < 0)
525  return;
526 
527  delete d->readNotifier;
528  delete d->writeNotifier;
529 
530  QIODevice::close();
531 
532  KPty::close();
533 }
534 
535 bool KPtyDevice::isSequential() const
536 {
537  return true;
538 }
539 
540 bool KPtyDevice::canReadLine() const
541 {
542  Q_D(const KPtyDevice);
543  return QIODevice::canReadLine() || d->readBuffer.canReadLine();
544 }
545 
546 bool KPtyDevice::atEnd() const
547 {
548  Q_D(const KPtyDevice);
549  return QIODevice::atEnd() && d->readBuffer.isEmpty();
550 }
551 
552 qint64 KPtyDevice::bytesAvailable() const
553 {
554  Q_D(const KPtyDevice);
555  return QIODevice::bytesAvailable() + d->readBuffer.size();
556 }
557 
558 qint64 KPtyDevice::bytesToWrite() const
559 {
560  Q_D(const KPtyDevice);
561  return d->writeBuffer.size();
562 }
563 
564 bool KPtyDevice::waitForReadyRead(int msecs)
565 {
566  Q_D(KPtyDevice);
567  return d->doWait(msecs, true);
568 }
569 
570 bool KPtyDevice::waitForBytesWritten(int msecs)
571 {
572  Q_D(KPtyDevice);
573  return d->doWait(msecs, false);
574 }
575 
576 void KPtyDevice::setSuspended(bool suspended)
577 {
578  Q_D(KPtyDevice);
579  d->readNotifier->setEnabled(!suspended);
580 }
581 
582 bool KPtyDevice::isSuspended() const
583 {
584  Q_D(const KPtyDevice);
585  return !d->readNotifier->isEnabled();
586 }
587 
588 // protected
589 qint64 KPtyDevice::readData(char *data, qint64 maxlen)
590 {
591  Q_D(KPtyDevice);
592  return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT));
593 }
594 
595 // protected
596 qint64 KPtyDevice::readLineData(char *data, qint64 maxlen)
597 {
598  Q_D(KPtyDevice);
599  return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT));
600 }
601 
602 // protected
603 qint64 KPtyDevice::writeData(const char *data, qint64 len)
604 {
605  Q_D(KPtyDevice);
606  Q_ASSERT(len <= KMAXINT);
607 
608  d->writeBuffer.write(data, len);
609  d->writeNotifier->setEnabled(true);
610  return len;
611 }
612 
613 #include "kptydevice.moc"
i18n
QString i18n(const char *text)
qint64
KPty::masterFd
int masterFd() const
Definition: kpty.cpp:696
KPtyDevice::~KPtyDevice
virtual ~KPtyDevice()
Destructor:
Definition: kptydevice.cpp:484
KPtyDevice::isSequential
virtual bool isSequential() const
Definition: kptydevice.cpp:535
KPtyDevice::atEnd
bool atEnd() const
Definition: kptydevice.cpp:546
KPtyDevice::bytesAvailable
qint64 bytesAvailable() const
Definition: kptydevice.cpp:552
CHUNKSIZE
#define CHUNKSIZE
Definition: kptydevice.cpp:67
timeradd
#define timeradd(a, b, result)
Definition: kptydevice.cpp:374
timersub
#define timersub(a, b, result)
Definition: kptydevice.cpp:383
QObject
klocale.h
KPty::open
bool open()
Create a pty master/slave pair.
Definition: kpty.cpp:194
qt_ignore_sigpipe
static void qt_ignore_sigpipe()
Definition: kptydevice.cpp:235
KPtyDevice::readLineData
virtual qint64 readLineData(char *data, qint64 maxSize)
Definition: kptydevice.cpp:596
KPtyDevice::close
virtual void close()
Close the pty master/slave pair.
Definition: kptydevice.cpp:520
KPtyDevice::setSuspended
void setSuspended(bool suspended)
Sets whether the KPtyDevice monitors the pty for incoming data.
Definition: kptydevice.cpp:576
KPtyDevice::readData
virtual qint64 readData(char *data, qint64 maxSize)
Definition: kptydevice.cpp:589
NO_INTR
#define NO_INTR(ret, func)
Definition: kptydevice.cpp:246
KPtyDevice::waitForReadyRead
bool waitForReadyRead(int msecs=-1)
Definition: kptydevice.cpp:564
KPtyDevice::writeData
virtual qint64 writeData(const char *data, qint64 maxSize)
Definition: kptydevice.cpp:603
KPtyDevice::bytesToWrite
qint64 bytesToWrite() const
Definition: kptydevice.cpp:558
PTY_BYTES_AVAILABLE
#define PTY_BYTES_AVAILABLE
Definition: kptydevice.cpp:49
KPtyPrivate
Definition: kpty_p.h:30
KPtyDevice::isSuspended
bool isSuspended() const
Returns true if the KPtyDevice is not monitoring the pty for incoming data.
Definition: kptydevice.cpp:582
KPty::close
void close()
Close the pty master/slave pair.
Definition: kpty.cpp:446
kptydevice.h
KPtyDevice::KPtyDevice
KPtyDevice(QObject *parent=0)
Constructor.
Definition: kptydevice.cpp:478
KPtyPrivate::KPtyPrivate
KPtyPrivate(KPty *parent)
Definition: kpty.cpp:156
KPtyDevice::canReadLine
bool canReadLine() const
Definition: kptydevice.cpp:540
KPtyDevice
Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
Definition: kptydevice.h:38
KMAXINT
#define KMAXINT
Definition: kptydevice.cpp:58
KPty
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY...
Definition: kpty.h:33
QIODevice
kpty_p.h
KPtyDevice::waitForBytesWritten
bool waitForBytesWritten(int msecs=-1)
Definition: kptydevice.cpp:570
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:56 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KPty

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal