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
18 #include <config-runtime.h>
29 #include <sys/types.h>
33 #include <sys/resource.h>
34 #include <sys/socket.h>
36 #if defined(__SVR4) && defined(sun)
38 #include <sys/stream.h>
41 #ifdef HAVE_SYS_SELECT_H
42 #include <sys/select.h> // Needed on some systems.
45 #include <QtCore/QBool>
49 #include <kstandarddirs.h>
53 MyPtyProcess::MyPtyProcess()
62 int MyPtyProcess::init()
68 kError(PTYPROC
) << k_lineinfo
<< "Master setup failed.\n" << endl
;
71 m_stdoutBuf
.resize(0);
72 m_stderrBuf
.resize(0);
78 MyPtyProcess::~MyPtyProcess()
85 * Read one line of input. The terminal is in canonical mode, so you always
86 * read a line at at time, but it's possible to receive multiple lines in
91 QByteArray
MyPtyProcess::readLineFrom(int fd
, QByteArray
& inbuf
, bool block
)
98 pos
= inbuf
.indexOf('\n');
105 ret
= inbuf
.left(pos
);
106 inbuf
= inbuf
.mid(pos
+1);
112 int flags
= fcntl(fd
, F_GETFL
);
115 kError(PTYPROC
) << k_lineinfo
<< "fcntl(F_GETFL): " << perror
<< "\n";
119 flags
&= ~O_NONBLOCK
;
122 if (fcntl(fd
, F_SETFL
, flags
) < 0)
124 kError(PTYPROC
) << k_lineinfo
<< "fcntl(F_SETFL): " << perror
<< "\n";
132 nbytes
= read(fd
, buf
, 255);
142 buf
[nbytes
] = '\000';
145 pos
= inbuf
.indexOf('\n');
152 ret
= inbuf
.left(pos
);
153 inbuf
= inbuf
.mid(pos
+1);
162 void MyPtyProcess::writeLine(QByteArray line
, bool addnl
)
165 write(fd(), line
, line
.length());
167 write(fd(), "\n", 1);
170 void MyPtyProcess::unreadLineFrom(QByteArray inbuf
, QByteArray line
, bool addnl
)
180 * Fork and execute the command. This returns in the parent.
183 int MyPtyProcess::exec(QByteArray command
, QCStringList args
)
185 kDebug(PTYPROC
) << "MyPtyProcess::exec(): " << command
;// << ", args = " << args ;
190 // Open the pty slave before forking. See SetupTTY()
191 int slave
= open(m_pPTY
->ttyName(), O_RDWR
);
194 kError(PTYPROC
) << k_lineinfo
<< "Could not open slave pty.\n";
198 // Also create a socket pair to connect to standard in/out.
199 // This will allow use to bypass the terminal.
203 ok
&= socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) >= 0;
204 ok
&= socketpair(AF_UNIX
, SOCK_STREAM
, 0, err
) >= 0;
206 kDebug(PTYPROC
) << "Could not create socket";
210 m_stdinout
= inout
[0];
213 if ((m_Pid
= fork()) == -1)
215 kError(PTYPROC
) << k_lineinfo
<< "fork(): " << perror
<< "\n";
231 ok
&= dup2(inout
[1], STDIN_FILENO
) >= 0;
232 ok
&= dup2(inout
[1], STDOUT_FILENO
) >= 0;
233 ok
&= dup2(err
[1], STDERR_FILENO
) >= 0;
237 kError(PTYPROC
) << "dup of socket descriptor failed" << endl
;
249 // From now on, terminal output goes through the tty.
251 if (command
.contains('/'))
255 QString file
= KStandardDirs::findExe(command
);
258 kError(PTYPROC
) << k_lineinfo
<< command
<< " not found\n";
261 path
= QFile::encodeName(file
);
265 const char * argp
[32];
267 QCStringList::Iterator it
;
268 for (i
=1, it
=args
.begin(); it
!=args
.end() && i
<31; it
++) {
270 kDebug(PTYPROC
) << *it
;
273 execv(path
, (char * const *)argp
);
274 kError(PTYPROC
) << k_lineinfo
<< "execv(\"" << path
<< "\"): " << perror
<< "\n";
276 return -1; // Shut up compiler. Never reached.
280 * Wait until the terminal is set into no echo mode. At least one su
281 * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
282 * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
283 * taking the password with it. So we wait until no echo mode is set
284 * before writing the password.
285 * Note that this is done on the slave fd. While Linux allows tcgetattr() on
286 * the master side, Solaris doesn't.
289 int MyPtyProcess::WaitSlave()
291 int slave
= open(m_pPTY
->ttyName(), O_RDWR
);
294 kError(PTYPROC
) << k_lineinfo
<< "Could not open slave tty.\n";
302 if (tcgetattr(slave
, &tio
) < 0)
304 kError(PTYPROC
) << k_lineinfo
<< "tcgetattr(): " << perror
<< "\n";
308 if (tio
.c_lflag
& ECHO
)
310 kDebug(PTYPROC
) << k_lineinfo
<< "Echo mode still on.";
312 tv
.tv_sec
= 0; tv
.tv_usec
= 100000;
313 select(slave
, 0L, 0L, 0L, &tv
);
323 int MyPtyProcess::enableLocalEcho(bool enable
)
325 return m_pPTY
->setEcho(enable
) ? 0 : -1;
330 * Copy output to stdout until the child process exists, or a line of output
332 * We have to use waitpid() to test for exit. Merely waiting for EOF on the
333 * pty does not work, because the target process may have children still
334 * attached to the terminal.
337 int MyPtyProcess::waitForChild()
339 int ret
, state
, retval
= 1;
347 tv
.tv_sec
= 1; tv
.tv_usec
= 0;
351 ret
= select(_fd
+1, &fds
, 0L, 0L, &tv
);
354 if (errno
== EINTR
) continue;
357 kError(PTYPROC
) << k_lineinfo
<< "select(): " << perror
<< "\n";
364 QByteArray line
= readLine(false);
365 while (!line
.isNull())
367 if (!m_Exit
.isEmpty() && !qstrnicmp(line
, m_Exit
, m_Exit
.length()))
368 kill(m_Pid
, SIGTERM
);
374 line
= readLine(false);
378 // Check if the process is still alive
379 ret
= waitpid(m_Pid
, &state
, WNOHANG
);
385 kError(PTYPROC
) << k_lineinfo
<< "waitpid(): " << perror
<< "\n";
390 if (WIFEXITED(state
))
391 retval
= WEXITSTATUS(state
);
400 * SetupTTY: Creates a new session. The filedescriptor "fd" should be
401 * connected to the tty. It is closed after the tty is reopened to make it
402 * our controlling terminal. This way the tty is always opened at least once
403 * so we'll never get EIO when reading from it.
406 int MyPtyProcess::setupTTY()
408 // Reset signal handlers
409 for (int sig
= 1; sig
< NSIG
; sig
++)
410 signal(sig
, SIG_DFL
);
411 signal(SIGHUP
, SIG_IGN
);
413 // Close all file handles
414 // struct rlimit rlp;
415 // getrlimit(RLIMIT_NOFILE, &rlp);
416 // for (int i = 0; i < (int)rlp.rlim_cur; i++)
417 // if (i != fd) close(i);
421 // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
422 // translated to '\r\n'.
423 struct ::termios tio
;
424 if (m_pPTY
->tcGetAttr(&tio
) < 0)
426 kError(PTYPROC
) << k_lineinfo
<< "tcgetattr(): " << perror
<< "\n";
429 tio
.c_oflag
&= ~OPOST
;
430 if (m_pPTY
->tcSetAttr(&tio
) < 0)
432 kError(PTYPROC
) << k_lineinfo
<< "tcsetattr(): " << perror
<< "\n";