Pty.cpp 8.02 KB
Newer Older
1
2
/*
    This file is part of Konsole, an X terminal.
3
    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/
20

21
22
23
// Own
#include "Pty.h"

24
// System
25
#include <termios.h>
26
#include <signal.h>
Robert Knight's avatar
Robert Knight committed
27

28
// Qt
Dirk Mueller's avatar
Dirk Mueller committed
29
#include <QtCore/QStringList>
30
#include <qplatformdefs.h>
31

32
// KDE
Laurent Montel's avatar
Laurent Montel committed
33
#include <QDebug>
34
#include <KPtyDevice>
35

36
using Konsole::Pty;
37

38
39
Pty::Pty(int masterFd, QObject* aParent)
    : KPtyProcess(masterFd, aParent)
40
41
42
43
{
    init();
}

44
45
Pty::Pty(QObject* aParent)
    : KPtyProcess(aParent)
46
47
48
49
50
51
52
53
54
55
56
57
{
    init();
}

void Pty::init()
{
    _windowColumns = 0;
    _windowLines   = 0;
    _eraseChar     = 0;
    _xonXoff       = true;
    _utf8          = true;

58
59
60
61
62
63
64
    setEraseChar(_eraseChar);
    setFlowControlEnabled(_xonXoff);
    setUtf8Mode(_utf8);

    setWindowSize(_windowColumns, _windowLines);

    setUseUtmp(true);
65
    setPtyChannels(KPtyProcess::AllChannels);
66

67
    connect(pty(), &KPtyDevice::readyRead , this , &Konsole::Pty::dataReceived);
68
69
70
71
72
73
}

Pty::~Pty()
{
}

74
void Pty::sendData(const QByteArray& data)
75
{
76
    if (data.isEmpty())
77
78
        return;

79
    if (!pty()->write(data.constData(), data.length())) {
Laurent Montel's avatar
Laurent Montel committed
80
        qWarning() << "Could not send input data to terminal process.";
81
82
83
84
85
86
87
88
89
90
        return;
    }
}

void Pty::dataReceived()
{
    QByteArray data = pty()->readAll();
    emit receivedData(data.constData(), data.count());
}

91
void Pty::setWindowSize(int columns, int lines)
92
{
93
    _windowColumns = columns;
Jekyll Wu's avatar
Jekyll Wu committed
94
    _windowLines = lines;
95

Jekyll Wu's avatar
Jekyll Wu committed
96
    if (pty()->masterFd() >= 0)
97
        pty()->setWinSize(lines, columns);
98
}
99

100
101
QSize Pty::windowSize() const
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
102
    return QSize(_windowColumns, _windowLines);
103
}
104

Robert Knight's avatar
   
Robert Knight committed
105
void Pty::setFlowControlEnabled(bool enable)
106
{
Jekyll Wu's avatar
Jekyll Wu committed
107
    _xonXoff = enable;
108

Kurt Hindenburg's avatar
Kurt Hindenburg committed
109
    if (pty()->masterFd() >= 0) {
Jekyll Wu's avatar
Jekyll Wu committed
110
111
        struct ::termios ttmode;
        pty()->tcGetAttr(&ttmode);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
112
        if (enable)
Jekyll Wu's avatar
Jekyll Wu committed
113
            ttmode.c_iflag |= (IXOFF | IXON);
114
115
116
        else
            ttmode.c_iflag &= ~(IXOFF | IXON);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
117
        if (!pty()->tcSetAttr(&ttmode))
Laurent Montel's avatar
Laurent Montel committed
118
            qWarning() << "Unable to set terminal attributes.";
Jekyll Wu's avatar
Jekyll Wu committed
119
    }
120
}
121

Robert Knight's avatar
   
Robert Knight committed
122
123
bool Pty::flowControlEnabled() const
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
124
    if (pty()->masterFd() >= 0) {
Robert Knight's avatar
   
Robert Knight committed
125
126
127
128
        struct ::termios ttmode;
        pty()->tcGetAttr(&ttmode);
        return ttmode.c_iflag & IXOFF &&
               ttmode.c_iflag & IXON;
Jekyll Wu's avatar
Jekyll Wu committed
129
    } else {
Laurent Montel's avatar
Laurent Montel committed
130
        qWarning() << "Unable to get flow control status, terminal not connected.";
Jekyll Wu's avatar
Jekyll Wu committed
131
        return _xonXoff;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
132
    }
Robert Knight's avatar
   
Robert Knight committed
133
}
134

135
void Pty::setUtf8Mode(bool enable)
Waldo Bastian's avatar
Waldo Bastian committed
136
{
137
#if defined(IUTF8) // XXX not a reasonable place to check it.
Jekyll Wu's avatar
Jekyll Wu committed
138
    _utf8 = enable;
139

Kurt Hindenburg's avatar
Kurt Hindenburg committed
140
    if (pty()->masterFd() >= 0) {
Jekyll Wu's avatar
Jekyll Wu committed
141
142
        struct ::termios ttmode;
        pty()->tcGetAttr(&ttmode);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
143
        if (enable)
Jekyll Wu's avatar
Jekyll Wu committed
144
            ttmode.c_iflag |= IUTF8;
145
146
147
        else
            ttmode.c_iflag &= ~IUTF8;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
148
        if (!pty()->tcSetAttr(&ttmode))
Laurent Montel's avatar
Laurent Montel committed
149
            qWarning() << "Unable to set terminal attributes.";
Jekyll Wu's avatar
Jekyll Wu committed
150
    }
151
152
#else
    Q_UNUSED(enable);
153
#endif
Waldo Bastian's avatar
Waldo Bastian committed
154
155
}

156
void Pty::setEraseChar(char eChar)
157
{
158
    _eraseChar = eChar;
Jekyll Wu's avatar
Jekyll Wu committed
159

Kurt Hindenburg's avatar
Kurt Hindenburg committed
160
    if (pty()->masterFd() >= 0) {
Jekyll Wu's avatar
Jekyll Wu committed
161
162
        struct ::termios ttmode;
        pty()->tcGetAttr(&ttmode);
163
        ttmode.c_cc[VERASE] = eChar;
Jekyll Wu's avatar
Jekyll Wu committed
164

Jekyll Wu's avatar
Jekyll Wu committed
165
        if (!pty()->tcSetAttr(&ttmode))
Laurent Montel's avatar
Laurent Montel committed
166
            qWarning() << "Unable to set terminal attributes.";
Jekyll Wu's avatar
Jekyll Wu committed
167
    }
168
169
}

170
char Pty::eraseChar() const
171
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
172
    if (pty()->masterFd() >= 0) {
173
174
175
        struct ::termios ttyAttributes;
        pty()->tcGetAttr(&ttyAttributes);
        return ttyAttributes.c_cc[VERASE];
Jekyll Wu's avatar
Jekyll Wu committed
176
    } else {
Laurent Montel's avatar
Laurent Montel committed
177
        qWarning() << "Unable to get erase char attribute, terminal not connected.";
Jekyll Wu's avatar
Jekyll Wu committed
178
        return _eraseChar;
179
    }
180
181
}

182
183
void Pty::setInitialWorkingDirectory(const QString& dir)
{
184
185
186
187
    QString pwd = dir;

    // remove trailing slash in the path when appropriate
    // example: /usr/share/icons/ ==> /usr/share/icons
Kurt Hindenburg's avatar
Kurt Hindenburg committed
188
    if (pwd.length() > 1 && pwd.endsWith(QLatin1Char('/'))) {
189
        pwd.chop(1);
190
191
192
    }

    setWorkingDirectory(pwd);
Jekyll Wu's avatar
Jekyll Wu committed
193
194

    // setting PWD to "." will cause problem for bash & zsh
195
196
    if (pwd != ".")
        setEnv("PWD", pwd);
197
198
}

199
void Pty::addEnvironmentVariables(const QStringList& environmentVariables)
200
{
201
202
    bool isTermEnvAdded = false;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
203
    foreach(const QString& pair, environmentVariables) {
204
        // split on the first '=' character
205
        const int separator = pair.indexOf('=');
206

207
208
209
        if (separator >= 0) {
            QString variable = pair.left(separator);
            QString value = pair.mid(separator + 1);
210

Kurt Hindenburg's avatar
Kurt Hindenburg committed
211
            setEnv(variable, value);
212

Kurt Hindenburg's avatar
Kurt Hindenburg committed
213
            if (variable == "TERM") {
214
215
                isTermEnvAdded = true;
            }
216
217
        }
    }
218
219

    // extra safeguard to make sure $TERM is always set
Kurt Hindenburg's avatar
Kurt Hindenburg committed
220
    if (!isTermEnvAdded) {
221
222
        setEnv("TERM", "xterm");
    }
223
224
}

225
int Pty::start(const QString& programName,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
226
               const QStringList& programArguments,
227
               const QStringList& environmentList)
228
{
Jekyll Wu's avatar
Jekyll Wu committed
229
230
    clearProgram();

Kurt Hindenburg's avatar
Kurt Hindenburg committed
231
    // For historical reasons, the first argument in programArguments is the
Jekyll Wu's avatar
Jekyll Wu committed
232
233
234
    // name of the program to execute, so create a list consisting of all
    // but the first argument to pass to setProgram()
    Q_ASSERT(programArguments.count() >= 1);
235
    setProgram(programName, programArguments.mid(1));
Jekyll Wu's avatar
Jekyll Wu committed
236

237
    addEnvironmentVariables(environmentList);
Jekyll Wu's avatar
Jekyll Wu committed
238
239
240
241
242
243
244
245
246
247
248
249

    // unless the LANGUAGE environment variable has been set explicitly
    // set it to a null string
    // this fixes the problem where KCatalog sets the LANGUAGE environment
    // variable during the application's startup to something which
    // differs from LANG,LC_* etc. and causes programs run from
    // the terminal to display messages in the wrong language
    //
    // this can happen if LANG contains a language which KDE
    // does not have a translation for
    //
    // BR:149300
Kurt Hindenburg's avatar
Kurt Hindenburg committed
250
    setEnv("LANGUAGE", QString(), false /* do not overwrite existing value if any */);
Jekyll Wu's avatar
Jekyll Wu committed
251
252

    KProcess::start();
253

254
255
256
    if (waitForStarted()) {
        return 0;
    } else {
Jekyll Wu's avatar
Jekyll Wu committed
257
        return -1;
258
    }
259
260
}

261
void Pty::setWriteable(bool writeable)
262
{
263
264
    QT_STATBUF sbuf;
    if (QT_STAT(pty()->ttyName(), &sbuf) == 0) {
265
        if (writeable) {
266
            if (::chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP) < 0) {
Laurent Montel's avatar
Laurent Montel committed
267
                qWarning() << "Could not set writeable on "<<pty()->ttyName();
268
269
            }
        } else {
270
            if (::chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
Laurent Montel's avatar
Laurent Montel committed
271
                qWarning() << "Could not unset writeable on "<<pty()->ttyName();
272
            }
273
        }
274
275
    } else {
        qWarning() << "Could not stat "<<pty()->ttyName();
276
    }
277
278
}

279
280
281
282
283
void Pty::closePty()
{
    pty()->close();
}

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
void Pty::sendEof()
{
    if (pty()->masterFd() < 0) {
        qWarning() << "Unable to get eof char attribute, terminal not connected.";
        return;
    }
    struct ::termios ttyAttributes;
    pty()->tcGetAttr(&ttyAttributes);
    char eofChar = ttyAttributes.c_cc[VEOF];
    if (!pty()->write(QByteArray(1, eofChar))) {
        qWarning() << "Unable to send EOF";
    }

    pty()->waitForBytesWritten();
}

300
301
int Pty::foregroundProcessGroup() const
{
302
    int foregroundPid = tcgetpgrp(pty()->masterFd());
303

304
305
    if (foregroundPid != -1) {
        return foregroundPid;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
306
    }
307
308
309
310

    return 0;
}

311
312
void Pty::setupChildProcess()
{
313
    KPtyProcess::setupChildProcess();
Jekyll Wu's avatar
Jekyll Wu committed
314

315
    // reset all signal handlers
Kurt Hindenburg's avatar
Kurt Hindenburg committed
316
    // this ensures that terminal applications respond to
317
318
319
320
321
322
    // signals generated via key sequences such as Ctrl+C
    // (which sends SIGINT)
    struct sigaction action;
    sigemptyset(&action.sa_mask);
    action.sa_handler = SIG_DFL;
    action.sa_flags = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
323
    for (int signal = 1; signal < NSIG; signal++)
Kurt Hindenburg's avatar
/s/0L/0    
Kurt Hindenburg committed
324
        sigaction(signal, &action, 0);
325
326
}