4 * This file is part of the KDE project, module kdesu.
5 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
7 * This file contains code from TEShell.C of the KDE konsole.
8 * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
10 * This is free software; you can use this library under the GNU Library
11 * General Public License, version 2. See the file "COPYING.LIB" for the
12 * exact licensing terms.
14 * process.cpp: Functionality to build a front end to password asking
20 #include <config-runtime.h>
34 #include <sys/types.h>
38 #include <sys/resource.h>
39 #include <sys/socket.h>
41 #if defined(__SVR4) && defined(sun)
43 #include <sys/stream.h>
46 #ifdef HAVE_SYS_SELECT_H
47 #include <sys/select.h> // Needed on some systems.
50 #include <QtCore/QBool>
54 #include <kstandarddirs.h>
56 MyPtyProcess::MyPtyProcess()
65 int MyPtyProcess::init()
72 kError(PTYPROC
) << k_lineinfo
<< "Master setup failed.\n" << endl
;
76 m_pPTY
= new KProcess();
77 m_pPTY
->setOutputChannelMode(KProcess::MergedChannels
);
80 m_stdoutBuf
.resize(0);
81 m_stderrBuf
.resize(0);
87 MyPtyProcess::~MyPtyProcess()
94 * Read one line of input. The terminal is in canonical mode, so you always
95 * read a line at at time, but it's possible to receive multiple lines in
100 QByteArray
MyPtyProcess::readLineFrom(int fd
, QByteArray
& inbuf
, bool block
)
105 if (!inbuf
.isEmpty())
107 pos
= inbuf
.indexOf('\n');
114 ret
= inbuf
.left(pos
);
116 if( ret
.endsWith('\r') )
121 inbuf
= inbuf
.mid(pos
+1);
128 if( m_pPTY
->waitForReadyRead(60000) )
130 inbuf
= m_pPTY
->readAll();
131 pos
= inbuf
.indexOf('\n');
138 ret
= inbuf
.left(pos
);
139 if( ret
.endsWith('\r') )
143 inbuf
= inbuf
.mid(pos
+1);
148 int flags
= fcntl(fd
, F_GETFL
);
151 kError(PTYPROC
) << k_lineinfo
<< "fcntl(F_GETFL): " << perror
<< "\n";
155 flags
&= ~O_NONBLOCK
;
158 if (fcntl(fd
, F_SETFL
, flags
) < 0)
160 kError(PTYPROC
) << k_lineinfo
<< "fcntl(F_SETFL): " << perror
<< "\n";
168 nbytes
= read(fd
, buf
, 255);
178 buf
[nbytes
] = '\000';
181 pos
= inbuf
.indexOf('\n');
188 ret
= inbuf
.left(pos
);
189 inbuf
= inbuf
.mid(pos
+1);
199 void MyPtyProcess::writeLine(QByteArray line
, bool addnl
)
203 write(fd(), line
, line
.length());
209 write(fd(), "\n", 1);
211 m_pPTY
->write("\r\n");
215 void MyPtyProcess::unreadLineFrom(QByteArray inbuf
, QByteArray line
, bool addnl
)
225 * Fork and execute the command. This returns in the parent.
228 int MyPtyProcess::exec(QByteArray command
, QCStringList args
)
230 kDebug(PTYPROC
) << "MyPtyProcess::exec(): " << command
;// << ", args = " << args ;
235 for(int i
= 0; i
< args
.size(); ++i
)
239 m_pPTY
->setProgram(command
, ar
);
243 // Open the pty slave before forking. See SetupTTY()
244 int slave
= open(m_pPTY
->ttyName(), O_RDWR
);
247 kError(PTYPROC
) << k_lineinfo
<< "Could not open slave pty.\n";
251 // Also create a socket pair to connect to standard in/out.
252 // This will allow use to bypass the terminal.
256 ok
&= socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) >= 0;
257 ok
&= socketpair(AF_UNIX
, SOCK_STREAM
, 0, err
) >= 0;
259 kDebug(PTYPROC
) << "Could not create socket";
263 m_stdinout
= inout
[0];
266 if ((m_Pid
= fork()) == -1)
268 kError(PTYPROC
) << k_lineinfo
<< "fork(): " << perror
<< "\n";
284 ok
&= dup2(inout
[1], STDIN_FILENO
) >= 0;
285 ok
&= dup2(inout
[1], STDOUT_FILENO
) >= 0;
286 ok
&= dup2(err
[1], STDERR_FILENO
) >= 0;
290 kError(PTYPROC
) << "dup of socket descriptor failed" << endl
;
302 // From now on, terminal output goes through the tty.
304 if (command
.contains('/'))
308 QString file
= KStandardDirs::findExe(command
);
311 kError(PTYPROC
) << k_lineinfo
<< command
<< " not found\n";
314 path
= QFile::encodeName(file
);
318 const char * argp
[32];
320 QCStringList::Iterator it
;
321 for (i
=1, it
=args
.begin(); it
!=args
.end() && i
<31; it
++) {
323 kDebug(PTYPROC
) << *it
;
326 execv(path
, (char * const *)argp
);
327 kError(PTYPROC
) << k_lineinfo
<< "execv(\"" << path
<< "\"): " << perror
<< "\n";
329 return -1; // Shut up compiler. Never reached.
334 * Wait until the terminal is set into no echo mode. At least one su
335 * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
336 * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
337 * taking the password with it. So we wait until no echo mode is set
338 * before writing the password.
339 * Note that this is done on the slave fd. While Linux allows tcgetattr() on
340 * the master side, Solaris doesn't.
343 int MyPtyProcess::WaitSlave()
346 int slave
= open(m_pPTY
->ttyName(), O_RDWR
);
349 kError(PTYPROC
) << k_lineinfo
<< "Could not open slave tty.\n";
357 if (tcgetattr(slave
, &tio
) < 0)
359 kError(PTYPROC
) << k_lineinfo
<< "tcgetattr(): " << perror
<< "\n";
363 if (tio
.c_lflag
& ECHO
)
365 kDebug(PTYPROC
) << k_lineinfo
<< "Echo mode still on.";
367 tv
.tv_sec
= 0; tv
.tv_usec
= 100000;
368 select(slave
, 0L, 0L, 0L, &tv
);
379 int MyPtyProcess::enableLocalEcho(bool enable
)
384 return m_pPTY
->setEcho(enable
) ? 0 : -1;
390 * Copy output to stdout until the child process exists, or a line of output
392 * We have to use waitpid() to test for exit. Merely waiting for EOF on the
393 * pty does not work, because the target process may have children still
394 * attached to the terminal.
397 int MyPtyProcess::waitForChild()
402 int ret
, state
, retval
= 1;
410 tv
.tv_sec
= 1; tv
.tv_usec
= 0;
414 ret
= select(_fd
+1, &fds
, 0L, 0L, &tv
);
417 if (errno
== EINTR
) continue;
420 kError(PTYPROC
) << k_lineinfo
<< "select(): " << perror
<< "\n";
427 QByteArray line
= readLine(false);
428 while (!line
.isNull())
430 if (!m_Exit
.isEmpty() && !qstrnicmp(line
, m_Exit
, m_Exit
.length()))
431 kill(m_Pid
, SIGTERM
);
437 line
= readLine(false);
441 // Check if the process is still alive
442 ret
= waitpid(m_Pid
, &state
, WNOHANG
);
448 kError(PTYPROC
) << k_lineinfo
<< "waitpid(): " << perror
<< "\n";
453 if (WIFEXITED(state
))
454 retval
= WEXITSTATUS(state
);
464 * SetupTTY: Creates a new session. The filedescriptor "fd" should be
465 * connected to the tty. It is closed after the tty is reopened to make it
466 * our controlling terminal. This way the tty is always opened at least once
467 * so we'll never get EIO when reading from it.
470 int MyPtyProcess::setupTTY()
473 // Reset signal handlers
474 for (int sig
= 1; sig
< NSIG
; sig
++)
475 signal(sig
, SIG_DFL
);
476 signal(SIGHUP
, SIG_IGN
);
478 // Close all file handles
479 // struct rlimit rlp;
480 // getrlimit(RLIMIT_NOFILE, &rlp);
481 // for (int i = 0; i < (int)rlp.rlim_cur; i++)
482 // if (i != fd) close(i);
486 // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
487 // translated to '\r\n'.
488 struct ::termios tio
;
489 if (m_pPTY
->tcGetAttr(&tio
) < 0)
491 kError(PTYPROC
) << k_lineinfo
<< "tcgetattr(): " << perror
<< "\n";
494 tio
.c_oflag
&= ~OPOST
;
495 if (m_pPTY
->tcSetAttr(&tio
) < 0)
497 kError(PTYPROC
) << k_lineinfo
<< "tcsetattr(): " << perror
<< "\n";