10#include "kptydevice.h"
13#include <config-pty.h>
15#include <QSocketNotifier>
17#include <KLocalizedString>
32#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
34#define PTY_BYTES_AVAILABLE TIOCOUTQ
37#define PTY_BYTES_AVAILABLE TIOCINQ
40#define PTY_BYTES_AVAILABLE FIONREAD
43#define KMAXINT ((int)(~0U >> 1))
72 inline bool isEmpty()
const
74 return buffers.
count() == 1 && !tail;
77 inline int size()
const
82 inline int readSize()
const
84 return (buffers.
count() == 1 ? tail : buffers.
first().size()) - head;
87 inline const char *readPointer()
const
89 Q_ASSERT(totalSize > 0);
90 return buffers.
first().constData() + head;
96 Q_ASSERT(totalSize >= 0);
103 if (head == tail && buffers.
count() == 1) {
104 buffers.
first().resize(CHUNKSIZE);
111 if (buffers.
count() == 1) {
112 buffers.
first().resize(CHUNKSIZE);
122 char *reserve(
int bytes)
127 if (tail + bytes <= buffers.
last().size()) {
128 ptr = buffers.
last().data() + tail;
131 buffers.
last().resize(tail);
133 tmp.
resize(qMax(CHUNKSIZE, bytes));
142 inline void unreserve(
int bytes)
148 inline void write(
const char *data,
int len)
150 memcpy(reserve(len), data, len);
156 int indexAfter(
char c,
int maxLength = KMAXINT)
const
165 if (index == size()) {
170 int len = qMin((it == buffers.
end() ? tail : buf.
size()) -
start, maxLength);
172 if (
const char *rptr = (
const char *)memchr(ptr, c, len)) {
173 return index + (rptr - ptr) + 1;
181 inline int lineSize(
int maxLength = KMAXINT)
const
183 return indexAfter(
'\n', maxLength);
186 inline bool canReadLine()
const
188 return lineSize() != -1;
191 int read(
char *data,
int maxLength)
193 int bytesToRead = qMin(size(), maxLength);
195 while (readSoFar < bytesToRead) {
196 const char *ptr = readPointer();
197 int bs = qMin(bytesToRead - readSoFar, readSize());
198 memcpy(data + readSoFar, ptr, bs);
205 int readLine(
char *data,
int maxLength)
207 return read(data, lineSize(qMin(maxLength, size())));
222static void qt_ignore_sigpipe()
224 static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
225 if (atom.testAndSetRelaxed(0, 1)) {
226 struct sigaction noaction;
227 memset(&noaction, 0,
sizeof(noaction));
228 noaction.sa_handler = SIG_IGN;
229 sigaction(SIGPIPE, &noaction,
nullptr);
234#define NO_INTR(ret, func) \
237 } while (ret < 0 && errno == EINTR)
240class KPtyDevicePrivate :
public KPtyPrivate
244 KPtyDevicePrivate(
KPty *parent)
245 : KPtyPrivate(parent)
246 , emittedReadyRead(false)
247 , emittedBytesWritten(false)
248 , readNotifier(nullptr)
249 , writeNotifier(nullptr)
256 bool doWait(
int msecs,
bool reading);
259 bool emittedReadyRead;
260 bool emittedBytesWritten;
263 KRingBuffer readBuffer;
264 KRingBuffer writeBuffer;
267bool KPtyDevicePrivate::_k_canRead()
270 qint64 readBytes = 0;
273 if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (
char *)&available)) {
274 char *ptr = readBuffer.reserve(available);
275 NO_INTR(readBytes,
read(q->masterFd(), ptr, available));
277 readBuffer.unreserve(available);
278 q->setErrorString(
i18n(
"Error reading from PTY"));
281 readBuffer.unreserve(available - readBytes);
289 if (!emittedReadyRead) {
290 emittedReadyRead =
true;
291 Q_EMIT q->readyRead();
292 emittedReadyRead =
false;
298bool KPtyDevicePrivate::_k_canWrite()
303 if (writeBuffer.isEmpty()) {
309 NO_INTR(wroteBytes, write(q->masterFd(), writeBuffer.readPointer(), writeBuffer.readSize()));
310 if (wroteBytes < 0) {
311 q->setErrorString(
i18n(
"Error writing to PTY"));
314 writeBuffer.free(wroteBytes);
316 if (!emittedBytesWritten) {
317 emittedBytesWritten =
true;
318 Q_EMIT q->bytesWritten(wroteBytes);
319 emittedBytesWritten =
false;
322 if (!writeBuffer.isEmpty()) {
331#define timeradd(a, b, result) \
333 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
334 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
335 if ((result)->tv_usec >= 1000000) { \
336 ++(result)->tv_sec; \
337 (result)->tv_usec -= 1000000; \
341#define timersub(a, b, result) \
343 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
344 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
345 if ((result)->tv_usec < 0) { \
346 --(result)->tv_sec; \
347 (result)->tv_usec += 1000000; \
353bool KPtyDevicePrivate::doWait(
int msecs,
bool reading)
365 tv.tv_sec = msecs / 1000;
366 tv.tv_usec = (msecs % 1000) * 1000;
368 gettimeofday(&etv,
nullptr);
369 timeradd(&tv, &etv, &etv);
374 while (reading ? readNotifier->
isEnabled() : !writeBuffer.isEmpty()) {
382 FD_SET(q->masterFd(), &rfds);
384 if (!writeBuffer.isEmpty()) {
385 FD_SET(q->masterFd(), &wfds);
390 gettimeofday(&tv,
nullptr);
391 timersub(&etv, &tv, &tv);
393 tv.tv_sec = tv.tv_usec = 0;
398 switch (select(q->masterFd() + 1, &rfds, &wfds,
nullptr, tvp)) {
400 if (errno == EINTR) {
405 q->setErrorString(
i18n(
"PTY operation timed out"));
408 if (FD_ISSET(q->masterFd(), &rfds)) {
409 bool canRead = _k_canRead();
410 if (reading && canRead) {
414 if (FD_ISSET(q->masterFd(), &wfds)) {
415 bool canWrite = _k_canWrite();
430 q->QIODevice::open(mode);
431 fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
450 ,
KPty(new KPtyDevicePrivate(this))
499 delete d->readNotifier;
500 delete d->writeNotifier;
533 return d->writeBuffer.size();
536bool KPtyDevice::waitForReadyRead(
int msecs)
539 return d->doWait(msecs,
true);
542bool KPtyDevice::waitForBytesWritten(
int msecs)
545 return d->doWait(msecs,
false);
551 d->readNotifier->setEnabled(!suspended);
557 return !d->readNotifier->isEnabled();
561qint64 KPtyDevice::readData(
char *data, qint64 maxlen)
564 return d->readBuffer.read(data, (
int)qMin<qint64>(maxlen, KMAXINT));
568qint64 KPtyDevice::readLineData(
char *data, qint64 maxlen)
571 return d->readBuffer.readLine(data, (
int)qMin<qint64>(maxlen, KMAXINT));
575qint64 KPtyDevice::writeData(
const char *data, qint64 len)
578 Q_ASSERT(len <= KMAXINT);
580 d->writeBuffer.write(data, len);
581 d->writeNotifier->setEnabled(
true);
585#include "moc_kptydevice.cpp"
Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
void setSuspended(bool suspended)
Sets whether the KPtyDevice monitors the pty for incoming data.
qint64 bytesAvailable() const override
bool isSequential() const override
bool canReadLine() const override
KPtyDevice(QObject *parent=nullptr)
Constructor.
qint64 bytesToWrite() const override
bool isSuspended() const
Returns true if the KPtyDevice is not monitoring the pty for incoming data.
void close() override
Close the pty master/slave pair.
bool atEnd() const override
~KPtyDevice() override
Destructor:
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY,...
bool open()
Create a pty master/slave pair.
void close()
Close the pty master/slave pair.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
QVariant read(const QByteArray &data, int versionOverride=0)
void resize(qsizetype newSize, char c)
qsizetype size() const const
virtual bool atEnd() const const
virtual qint64 bytesAvailable() const const
virtual bool canReadLine() const const
void setErrorString(const QString &str)
qsizetype count() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
bool isEnabled() const const
void setEnabled(bool enable)