Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / runtime / kioslave / sftp / process.cpp
blobb9c668d2ce6bee875f7b3b6728d8b7b2c892b5e8
1 /* vi: ts=8 sts=4 sw=4
4 * This file is part of the KDE project, module kdesu.
5 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
6 *
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
15 * terminal programs.
18 #include <config-runtime.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <termios.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <sys/socket.h>
36 #if defined(__SVR4) && defined(sun)
37 #include <stropts.h>
38 #include <sys/stream.h>
39 #endif
41 #ifdef HAVE_SYS_SELECT_H
42 #include <sys/select.h> // Needed on some systems.
43 #endif
45 #include <QtCore/QBool>
46 #include <QFile>
48 #include <kdebug.h>
49 #include <kstandarddirs.h>
51 #include "process.h"
53 MyPtyProcess::MyPtyProcess()
55 m_bTerminal = false;
56 m_bErase = false;
57 m_pPTY = 0L;
58 m_Pid = -1;
62 int MyPtyProcess::init()
64 delete m_pPTY;
65 m_pPTY = new KPty();
66 if (!m_pPTY->open())
68 kError(PTYPROC) << k_lineinfo << "Master setup failed.\n" << endl;
69 return -1;
71 m_stdoutBuf.resize(0);
72 m_stderrBuf.resize(0);
73 m_ptyBuf.resize(0);
74 return 0;
78 MyPtyProcess::~MyPtyProcess()
80 delete m_pPTY;
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
87 * one time.
91 QByteArray MyPtyProcess::readLineFrom(int fd, QByteArray& inbuf, bool block)
93 int pos;
94 QByteArray ret;
96 if (!inbuf.isEmpty())
98 pos = inbuf.indexOf('\n');
99 if (pos == -1)
101 ret = inbuf;
102 inbuf.resize(0);
103 } else
105 ret = inbuf.left(pos);
106 inbuf = inbuf.mid(pos+1);
108 return ret;
112 int flags = fcntl(fd, F_GETFL);
113 if (flags < 0)
115 kError(PTYPROC) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
116 return ret;
118 if (block)
119 flags &= ~O_NONBLOCK;
120 else
121 flags |= O_NONBLOCK;
122 if (fcntl(fd, F_SETFL, flags) < 0)
124 kError(PTYPROC) << k_lineinfo << "fcntl(F_SETFL): " << perror << "\n";
125 return ret;
128 int nbytes;
129 char buf[256];
130 while (1)
132 nbytes = read(fd, buf, 255);
133 if (nbytes == -1)
135 if (errno == EINTR)
136 continue;
137 else break;
139 if (nbytes == 0)
140 break; // eof
142 buf[nbytes] = '\000';
143 inbuf += buf;
145 pos = inbuf.indexOf('\n');
146 if (pos == -1)
148 ret = inbuf;
149 inbuf.resize(0);
150 } else
152 ret = inbuf.left(pos);
153 inbuf = inbuf.mid(pos+1);
155 break;
159 return ret;
162 void MyPtyProcess::writeLine(QByteArray line, bool addnl)
164 if (!line.isEmpty())
165 write(fd(), line, line.length());
166 if (addnl)
167 write(fd(), "\n", 1);
170 void MyPtyProcess::unreadLineFrom(QByteArray inbuf, QByteArray line, bool addnl)
172 if (addnl)
173 line += '\n';
174 if (!line.isEmpty())
175 inbuf.prepend(line);
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 ;
187 if (init() < 0)
188 return -1;
190 // Open the pty slave before forking. See SetupTTY()
191 int slave = open(m_pPTY->ttyName(), O_RDWR);
192 if (slave < 0)
194 kError(PTYPROC) << k_lineinfo << "Could not open slave pty.\n";
195 return -1;
198 // Also create a socket pair to connect to standard in/out.
199 // This will allow use to bypass the terminal.
200 int inout[2];
201 int err[2];
202 int ok = 1;
203 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, inout) >= 0;
204 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err ) >= 0;
205 if( !ok ) {
206 kDebug(PTYPROC) << "Could not create socket";
207 close(slave);
208 return -1;
210 m_stdinout = inout[0];
211 m_err = err[0];
213 if ((m_Pid = fork()) == -1)
215 kError(PTYPROC) << k_lineinfo << "fork(): " << perror << "\n";
216 return -1;
219 // Parent
220 if (m_Pid)
222 close(slave);
223 close(inout[1]);
224 close(err[1]);
225 return 0;
228 // Child
230 ok = 1;
231 ok &= dup2(inout[1], STDIN_FILENO) >= 0;
232 ok &= dup2(inout[1], STDOUT_FILENO) >= 0;
233 ok &= dup2(err[1], STDERR_FILENO) >= 0;
235 if( !ok )
237 kError(PTYPROC) << "dup of socket descriptor failed" << endl;
238 _exit(1);
241 close(inout[1]);
242 close(inout[0]);
243 close(err[1]);
244 close(err[0]);
246 if (setupTTY() < 0)
247 _exit(1);
249 // From now on, terminal output goes through the tty.
250 QByteArray path;
251 if (command.contains('/'))
252 path = command;
253 else
255 QString file = KStandardDirs::findExe(command);
256 if (file.isEmpty())
258 kError(PTYPROC) << k_lineinfo << command << " not found\n";
259 _exit(1);
261 path = QFile::encodeName(file);
264 int i;
265 const char * argp[32];
266 argp[0] = path;
267 QCStringList::Iterator it;
268 for (i=1, it=args.begin(); it!=args.end() && i<31; it++) {
269 argp[i++] = *it;
270 kDebug(PTYPROC) << *it;
272 argp[i] = 0L;
273 execv(path, (char * const *)argp);
274 kError(PTYPROC) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
275 _exit(1);
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);
292 if (slave < 0)
294 kError(PTYPROC) << k_lineinfo << "Could not open slave tty.\n";
295 return -1;
298 struct termios tio;
299 struct timeval tv;
300 while (1)
302 if (tcgetattr(slave, &tio) < 0)
304 kError(PTYPROC) << k_lineinfo << "tcgetattr(): " << perror << "\n";
305 close(slave);
306 return -1;
308 if (tio.c_lflag & ECHO)
310 kDebug(PTYPROC) << k_lineinfo << "Echo mode still on.";
311 // sleep 1/10 sec
312 tv.tv_sec = 0; tv.tv_usec = 100000;
313 select(slave, 0L, 0L, 0L, &tv);
314 continue;
316 break;
318 close(slave);
319 return 0;
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
331 * matches `m_Exit'.
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;
340 struct timeval tv;
342 fd_set fds;
344 while (1)
346 int _fd = fd();
347 tv.tv_sec = 1; tv.tv_usec = 0;
348 FD_ZERO(&fds);
349 if (_fd >= 0)
350 FD_SET(_fd, &fds);
351 ret = select(_fd+1, &fds, 0L, 0L, &tv);
352 if (ret == -1)
354 if (errno == EINTR) continue;
355 else
357 kError(PTYPROC) << k_lineinfo << "select(): " << perror << "\n";
358 return -1;
362 if (ret)
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);
369 if (m_bTerminal)
371 fputs(line, stdout);
372 fputc('\n', stdout);
374 line = readLine(false);
378 // Check if the process is still alive
379 ret = waitpid(m_Pid, &state, WNOHANG);
380 if (ret < 0)
382 if (errno == ECHILD)
383 retval = 0;
384 else
385 kError(PTYPROC) << k_lineinfo << "waitpid(): " << perror << "\n";
386 break;
388 if (ret == m_Pid)
390 if (WIFEXITED(state))
391 retval = WEXITSTATUS(state);
392 break;
396 return -retval;
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);
419 m_pPTY->setCTty();
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";
427 return -1;
429 tio.c_oflag &= ~OPOST;
430 if (m_pPTY->tcSetAttr(&tio) < 0)
432 kError(PTYPROC) << k_lineinfo << "tcsetattr(): " << perror << "\n";
433 return -1;
436 return 0;