delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / kioslave / sftp / process.cpp
blob10689e22e090d9dd455b0ec6b4492baf9810ca99
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 "process.h"
20 #include <config-runtime.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <string.h>
30 #ifndef Q_WS_WIN
31 #include <termios.h>
32 #endif
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 #include <sys/socket.h>
41 #if defined(__SVR4) && defined(sun)
42 #include <stropts.h>
43 #include <sys/stream.h>
44 #endif
46 #ifdef HAVE_SYS_SELECT_H
47 #include <sys/select.h> // Needed on some systems.
48 #endif
50 #include <QtCore/QBool>
51 #include <QFile>
53 #include <kdebug.h>
54 #include <kstandarddirs.h>
56 MyPtyProcess::MyPtyProcess()
58 m_bTerminal = false;
59 m_bErase = false;
60 m_pPTY = 0L;
61 m_Pid = -1;
65 int MyPtyProcess::init()
67 delete m_pPTY;
68 #ifndef Q_WS_WIN
69 m_pPTY = new KPty();
70 if (!m_pPTY->open())
72 kError(PTYPROC) << k_lineinfo << "Master setup failed.\n" << endl;
73 return -1;
75 #else
76 m_pPTY = new KProcess();
77 m_pPTY->setOutputChannelMode(KProcess::MergedChannels);
78 #endif
80 m_stdoutBuf.resize(0);
81 m_stderrBuf.resize(0);
82 m_ptyBuf.resize(0);
83 return 0;
87 MyPtyProcess::~MyPtyProcess()
89 delete m_pPTY;
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
96 * one time.
100 QByteArray MyPtyProcess::readLineFrom(int fd, QByteArray& inbuf, bool block)
102 int pos;
103 QByteArray ret;
105 if (!inbuf.isEmpty())
107 pos = inbuf.indexOf('\n');
108 if (pos == -1)
110 ret = inbuf;
111 inbuf.resize(0);
112 } else
114 ret = inbuf.left(pos);
115 #ifdef Q_WS_WIN
116 if( ret.endsWith('\r') )
118 ret.chop(1);
120 #endif
121 inbuf = inbuf.mid(pos+1);
123 return ret;
127 #ifdef Q_WS_WIN
128 if( m_pPTY->waitForReadyRead(60000) )
130 inbuf = m_pPTY->readAll();
131 pos = inbuf.indexOf('\n');
132 if (pos == -1)
134 ret = inbuf;
135 inbuf.resize(0);
136 } else
138 ret = inbuf.left(pos);
139 if( ret.endsWith('\r') )
141 ret.chop(1);
143 inbuf = inbuf.mid(pos+1);
146 return ret;
147 #else
148 int flags = fcntl(fd, F_GETFL);
149 if (flags < 0)
151 kError(PTYPROC) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
152 return ret;
154 if (block)
155 flags &= ~O_NONBLOCK;
156 else
157 flags |= O_NONBLOCK;
158 if (fcntl(fd, F_SETFL, flags) < 0)
160 kError(PTYPROC) << k_lineinfo << "fcntl(F_SETFL): " << perror << "\n";
161 return ret;
164 int nbytes;
165 char buf[256];
166 while (1)
168 nbytes = read(fd, buf, 255);
169 if (nbytes == -1)
171 if (errno == EINTR)
172 continue;
173 else break;
175 if (nbytes == 0)
176 break; // eof
178 buf[nbytes] = '\000';
179 inbuf += buf;
181 pos = inbuf.indexOf('\n');
182 if (pos == -1)
184 ret = inbuf;
185 inbuf.resize(0);
186 } else
188 ret = inbuf.left(pos);
189 inbuf = inbuf.mid(pos+1);
191 break;
195 return ret;
196 #endif
199 void MyPtyProcess::writeLine(QByteArray line, bool addnl)
201 if (!line.isEmpty())
202 #ifndef Q_WS_WIN
203 write(fd(), line, line.length());
204 #else
205 m_pPTY->write(line);
206 #endif
207 if (addnl)
208 #ifndef Q_WS_WIN
209 write(fd(), "\n", 1);
210 #else
211 m_pPTY->write("\r\n");
212 #endif
215 void MyPtyProcess::unreadLineFrom(QByteArray inbuf, QByteArray line, bool addnl)
217 if (addnl)
218 line += '\n';
219 if (!line.isEmpty())
220 inbuf.prepend(line);
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 ;
231 if (init() < 0)
232 return -1;
233 #ifdef Q_WS_WIN
234 QStringList ar;
235 for(int i = 0; i < args.size(); ++i)
237 ar << args[i];
239 m_pPTY->setProgram(command, ar);
240 m_pPTY->start();
241 return 0;
242 #else
243 // Open the pty slave before forking. See SetupTTY()
244 int slave = open(m_pPTY->ttyName(), O_RDWR);
245 if (slave < 0)
247 kError(PTYPROC) << k_lineinfo << "Could not open slave pty.\n";
248 return -1;
251 // Also create a socket pair to connect to standard in/out.
252 // This will allow use to bypass the terminal.
253 int inout[2];
254 int err[2];
255 int ok = 1;
256 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, inout) >= 0;
257 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err ) >= 0;
258 if( !ok ) {
259 kDebug(PTYPROC) << "Could not create socket";
260 close(slave);
261 return -1;
263 m_stdinout = inout[0];
264 m_err = err[0];
266 if ((m_Pid = fork()) == -1)
268 kError(PTYPROC) << k_lineinfo << "fork(): " << perror << "\n";
269 return -1;
272 // Parent
273 if (m_Pid)
275 close(slave);
276 close(inout[1]);
277 close(err[1]);
278 return 0;
281 // Child
283 ok = 1;
284 ok &= dup2(inout[1], STDIN_FILENO) >= 0;
285 ok &= dup2(inout[1], STDOUT_FILENO) >= 0;
286 ok &= dup2(err[1], STDERR_FILENO) >= 0;
288 if( !ok )
290 kError(PTYPROC) << "dup of socket descriptor failed" << endl;
291 _exit(1);
294 close(inout[1]);
295 close(inout[0]);
296 close(err[1]);
297 close(err[0]);
299 if (setupTTY() < 0)
300 _exit(1);
302 // From now on, terminal output goes through the tty.
303 QByteArray path;
304 if (command.contains('/'))
305 path = command;
306 else
308 QString file = KStandardDirs::findExe(command);
309 if (file.isEmpty())
311 kError(PTYPROC) << k_lineinfo << command << " not found\n";
312 _exit(1);
314 path = QFile::encodeName(file);
317 int i;
318 const char * argp[32];
319 argp[0] = path;
320 QCStringList::Iterator it;
321 for (i=1, it=args.begin(); it!=args.end() && i<31; it++) {
322 argp[i++] = *it;
323 kDebug(PTYPROC) << *it;
325 argp[i] = 0L;
326 execv(path, (char * const *)argp);
327 kError(PTYPROC) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
328 _exit(1);
329 return -1; // Shut up compiler. Never reached.
330 #endif
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()
345 #ifndef Q_WS_WIN
346 int slave = open(m_pPTY->ttyName(), O_RDWR);
347 if (slave < 0)
349 kError(PTYPROC) << k_lineinfo << "Could not open slave tty.\n";
350 return -1;
353 struct termios tio;
354 struct timeval tv;
355 while (1)
357 if (tcgetattr(slave, &tio) < 0)
359 kError(PTYPROC) << k_lineinfo << "tcgetattr(): " << perror << "\n";
360 close(slave);
361 return -1;
363 if (tio.c_lflag & ECHO)
365 kDebug(PTYPROC) << k_lineinfo << "Echo mode still on.";
366 // sleep 1/10 sec
367 tv.tv_sec = 0; tv.tv_usec = 100000;
368 select(slave, 0L, 0L, 0L, &tv);
369 continue;
371 break;
373 close(slave);
374 #endif
375 return 0;
379 int MyPtyProcess::enableLocalEcho(bool enable)
381 #ifdef Q_WS_WIN
382 return -1;
383 #else
384 return m_pPTY->setEcho(enable) ? 0 : -1;
385 #endif
390 * Copy output to stdout until the child process exists, or a line of output
391 * matches `m_Exit'.
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()
399 #ifdef Q_WS_WIN
400 return -1;
401 #else
402 int ret, state, retval = 1;
403 struct timeval tv;
405 fd_set fds;
407 while (1)
409 int _fd = fd();
410 tv.tv_sec = 1; tv.tv_usec = 0;
411 FD_ZERO(&fds);
412 if (_fd >= 0)
413 FD_SET(_fd, &fds);
414 ret = select(_fd+1, &fds, 0L, 0L, &tv);
415 if (ret == -1)
417 if (errno == EINTR) continue;
418 else
420 kError(PTYPROC) << k_lineinfo << "select(): " << perror << "\n";
421 return -1;
425 if (ret)
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);
432 if (m_bTerminal)
434 fputs(line, stdout);
435 fputc('\n', stdout);
437 line = readLine(false);
441 // Check if the process is still alive
442 ret = waitpid(m_Pid, &state, WNOHANG);
443 if (ret < 0)
445 if (errno == ECHILD)
446 retval = 0;
447 else
448 kError(PTYPROC) << k_lineinfo << "waitpid(): " << perror << "\n";
449 break;
451 if (ret == m_Pid)
453 if (WIFEXITED(state))
454 retval = WEXITSTATUS(state);
455 break;
459 return -retval;
460 #endif
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()
472 #ifndef Q_WS_WIN
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);
484 m_pPTY->setCTty();
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";
492 return -1;
494 tio.c_oflag &= ~OPOST;
495 if (m_pPTY->tcSetAttr(&tio) < 0)
497 kError(PTYPROC) << k_lineinfo << "tcsetattr(): " << perror << "\n";
498 return -1;
500 #endif
501 return 0;