1/*
2 * This file is a part of QTerminal - http://gitorious.org/qterminal
3 *
4 * This file was un-linked from KDE and modified
5 * by Maxim Bourmistrov <maxim@unixconn.com>
6 *
7 */
8
9/*
10
11 This file is part of the KDE libraries
12 Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
13 Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org>
14 Author Adriaan de Groot <groot@kde.org>
15
16 This library is free software; you can redistribute it and/or
17 modify it under the terms of the GNU Library General Public
18 License as published by the Free Software Foundation; either
19 version 2 of the License, or (at your option) any later version.
20
21 This library is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 Library General Public License for more details.
25
26 You should have received a copy of the GNU Library General Public License
27 along with this library; see the file COPYING.LIB. If not, write to
28 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 Boston, MA 02110-1301, USA.
30*/
31
32#include "kptydevice.h"
33#include "kpty_p.h"
34
35#include <QSocketNotifier>
36
37#include <unistd.h>
38#include <errno.h>
39#include <signal.h>
40#include <termios.h>
41#include <fcntl.h>
42#include <sys/ioctl.h>
43#ifdef HAVE_SYS_FILIO_H
44# include <sys/filio.h>
45#endif
46#ifdef HAVE_SYS_TIME_H
47# include <sys/time.h>
48#endif
49
50#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
51 // "the other end's output queue size" - kinda braindead, huh?
52# define PTY_BYTES_AVAILABLE TIOCOUTQ
53#elif defined(TIOCINQ)
54 // "our end's input queue size"
55# define PTY_BYTES_AVAILABLE TIOCINQ
56#else
57 // likewise. more generic ioctl (theoretically)
58# define PTY_BYTES_AVAILABLE FIONREAD
59#endif
60
61
62
63
64//////////////////
65// private data //
66//////////////////
67
68// Lifted from Qt. I don't think they would mind. ;)
69// Re-lift again from Qt whenever a proper replacement for pthread_once appears
70static void qt_ignore_sigpipe()
71{
72 static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
73 if (atom.testAndSetRelaxed(0, 1)) {
74 struct sigaction noaction;
75 memset(&noaction, 0, sizeof(noaction));
76 noaction.sa_handler = SIG_IGN;
77 sigaction(SIGPIPE, &noaction, 0);
78 }
79}
80
81#define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR)
82
83bool KPtyDevicePrivate::_k_canRead()
84{
85 Q_Q(KPtyDevice);
86 qint64 readBytes = 0;
87
88#ifdef Q_OS_IRIX // this should use a config define, but how to check it?
89 size_t available;
90#else
91 int available;
92#endif
93 if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *) &available)) {
94#ifdef Q_OS_SOLARIS
95 // A Pty is a STREAMS module, and those can be activated
96 // with 0 bytes available. This happens either when ^C is
97 // pressed, or when an application does an explicit write(a,b,0)
98 // which happens in experiments fairly often. When 0 bytes are
99 // available, you must read those 0 bytes to clear the STREAMS
100 // module, but we don't want to hit the !readBytes case further down.
101 if (!available) {
102 char c;
103 // Read the 0-byte STREAMS message
104 NO_INTR(readBytes, read(q->masterFd(), &c, 0));
105 // Should return 0 bytes read; -1 is error
106 if (readBytes < 0) {
107 readNotifier->setEnabled(false);
108 emit q->readEof();
109 return false;
110 }
111 return true;
112 }
113#endif
114
115 char *ptr = readBuffer.reserve(available);
116#ifdef Q_OS_SOLARIS
117 // Even if available > 0, it is possible for read()
118 // to return 0 on Solaris, due to 0-byte writes in the stream.
119 // Ignore them and keep reading until we hit *some* data.
120 // In Solaris it is possible to have 15 bytes available
121 // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads.
122 // Because the stream is set to O_NONBLOCK in finishOpen(),
123 // an EOF read will return -1.
124 readBytes = 0;
125 while (!readBytes)
126#endif
127 // Useless block braces except in Solaris
128 {
129 NO_INTR(readBytes, read(q->masterFd(), ptr, available));
130 }
131 if (readBytes < 0) {
132 readBuffer.unreserve(available);
133 q->setErrorString(QLatin1String("Error reading from PTY"));
134 return false;
135 }
136 readBuffer.unreserve(available - readBytes); // *should* be a no-op
137 }
138
139 if (!readBytes) {
140 readNotifier->setEnabled(false);
141 emit q->readEof();
142 return false;
143 } else {
144 if (!emittedReadyRead) {
145 emittedReadyRead = true;
146 emit q->readyRead();
147 emittedReadyRead = false;
148 }
149 return true;
150 }
151}
152
153bool KPtyDevicePrivate::_k_canWrite()
154{
155 Q_Q(KPtyDevice);
156
157 writeNotifier->setEnabled(false);
158 if (writeBuffer.isEmpty())
159 return false;
160
161 qt_ignore_sigpipe();
162 int wroteBytes;
163 NO_INTR(wroteBytes,
164 write(q->masterFd(),
165 writeBuffer.readPointer(), writeBuffer.readSize()));
166 if (wroteBytes < 0) {
167 q->setErrorString(QLatin1String("Error writing to PTY"));
168 return false;
169 }
170 writeBuffer.free(wroteBytes);
171
172 if (!emittedBytesWritten) {
173 emittedBytesWritten = true;
174 emit q->bytesWritten(wroteBytes);
175 emittedBytesWritten = false;
176 }
177
178 if (!writeBuffer.isEmpty())
179 writeNotifier->setEnabled(true);
180 return true;
181}
182
183#ifndef timeradd
184// Lifted from GLIBC
185# define timeradd(a, b, result) \
186 do { \
187 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
188 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
189 if ((result)->tv_usec >= 1000000) { \
190 ++(result)->tv_sec; \
191 (result)->tv_usec -= 1000000; \
192 } \
193 } while (0)
194# define timersub(a, b, result) \
195 do { \
196 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
197 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
198 if ((result)->tv_usec < 0) { \
199 --(result)->tv_sec; \
200 (result)->tv_usec += 1000000; \
201 } \
202 } while (0)
203#endif
204
205bool KPtyDevicePrivate::doWait(int msecs, bool reading)
206{
207 Q_Q(KPtyDevice);
208#ifndef __linux__
209 struct timeval etv;
210#endif
211 struct timeval tv, *tvp;
212
213 if (msecs < 0)
214 tvp = 0;
215 else {
216 tv.tv_sec = msecs / 1000;
217 tv.tv_usec = (msecs % 1000) * 1000;
218#ifndef __linux__
219 gettimeofday(&etv, 0);
220 timeradd(&tv, &etv, &etv);
221#endif
222 tvp = &tv;
223 }
224
225 while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
226 fd_set rfds;
227 fd_set wfds;
228
229 FD_ZERO(&rfds);
230 FD_ZERO(&wfds);
231
232 if (readNotifier->isEnabled())
233 FD_SET(q->masterFd(), &rfds);
234 if (!writeBuffer.isEmpty())
235 FD_SET(q->masterFd(), &wfds);
236
237#ifndef __linux__
238 if (tvp) {
239 gettimeofday(&tv, 0);
240 timersub(&etv, &tv, &tv);
241 if (tv.tv_sec < 0)
242 tv.tv_sec = tv.tv_usec = 0;
243 }
244#endif
245
246 switch (select(q->masterFd() + 1, &rfds, &wfds, 0, tvp)) {
247 case -1:
248 if (errno == EINTR)
249 break;
250 return false;
251 case 0:
252 q->setErrorString(QLatin1String("PTY operation timed out"));
253 return false;
254 default:
255 if (FD_ISSET(q->masterFd(), &rfds)) {
256 bool canRead = _k_canRead();
257 if (reading && canRead)
258 return true;
259 }
260 if (FD_ISSET(q->masterFd(), &wfds)) {
261 bool canWrite = _k_canWrite();
262 if (!reading)
263 return canWrite;
264 }
265 break;
266 }
267 }
268 return false;
269}
270
271void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
272{
273 Q_Q(KPtyDevice);
274
275 q->QIODevice::open(mode);
276 fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
277 readBuffer.clear();
278 readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
279 writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
280 QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead()));
281 QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite()));
282 readNotifier->setEnabled(true);
283}
284
285/////////////////////////////
286// public member functions //
287/////////////////////////////
288
289KPtyDevice::KPtyDevice(QObject *parent) :
290 QIODevice(parent),
291 KPty(new KPtyDevicePrivate(this))
292{
293}
294
295KPtyDevice::~KPtyDevice()
296{
297 close();
298}
299
300bool KPtyDevice::open(OpenMode mode)
301{
302 Q_D(KPtyDevice);
303
304 if (masterFd() >= 0)
305 return true;
306
307 if (!KPty::open()) {
308 setErrorString(QLatin1String("Error opening PTY"));
309 return false;
310 }
311
312 d->finishOpen(mode);
313
314 return true;
315}
316
317bool KPtyDevice::open(int fd, OpenMode mode)
318{
319 Q_D(KPtyDevice);
320
321 if (!KPty::open(fd)) {
322 setErrorString(QLatin1String("Error opening PTY"));
323 return false;
324 }
325
326 d->finishOpen(mode);
327
328 return true;
329}
330
331void KPtyDevice::close()
332{
333 Q_D(KPtyDevice);
334
335 if (masterFd() < 0)
336 return;
337
338 delete d->readNotifier;
339 delete d->writeNotifier;
340
341 QIODevice::close();
342
343 KPty::close();
344}
345
346bool KPtyDevice::isSequential() const
347{
348 return true;
349}
350
351bool KPtyDevice::canReadLine() const
352{
353 Q_D(const KPtyDevice);
354 return QIODevice::canReadLine() || d->readBuffer.canReadLine();
355}
356
357bool KPtyDevice::atEnd() const
358{
359 Q_D(const KPtyDevice);
360 return QIODevice::atEnd() && d->readBuffer.isEmpty();
361}
362
363qint64 KPtyDevice::bytesAvailable() const
364{
365 Q_D(const KPtyDevice);
366 return QIODevice::bytesAvailable() + d->readBuffer.size();
367}
368
369qint64 KPtyDevice::bytesToWrite() const
370{
371 Q_D(const KPtyDevice);
372 return d->writeBuffer.size();
373}
374
375bool KPtyDevice::waitForReadyRead(int msecs)
376{
377 Q_D(KPtyDevice);
378 return d->doWait(msecs, true);
379}
380
381bool KPtyDevice::waitForBytesWritten(int msecs)
382{
383 Q_D(KPtyDevice);
384 return d->doWait(msecs, false);
385}
386
387void KPtyDevice::setSuspended(bool suspended)
388{
389 Q_D(KPtyDevice);
390 d->readNotifier->setEnabled(!suspended);
391}
392
393bool KPtyDevice::isSuspended() const
394{
395 Q_D(const KPtyDevice);
396 return !d->readNotifier->isEnabled();
397}
398
399// protected
400qint64 KPtyDevice::readData(char *data, qint64 maxlen)
401{
402 Q_D(KPtyDevice);
403 return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT));
404}
405
406// protected
407qint64 KPtyDevice::readLineData(char *data, qint64 maxlen)
408{
409 Q_D(KPtyDevice);
410 return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT));
411}
412
413// protected
414qint64 KPtyDevice::writeData(const char *data, qint64 len)
415{
416 Q_D(KPtyDevice);
417 Q_ASSERT(len <= KMAXINT);
418
419 d->writeBuffer.write(data, len);
420 d->writeNotifier->setEnabled(true);
421 return len;
422}
423