You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
11 KiB
447 lines
11 KiB
/*
|
|
** Copyright (C) 1999,2000 Toivo Pedaste <toivo@ucs.uwa.edu.au>
|
|
**
|
|
*/
|
|
|
|
/*
|
|
** 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 in a file called COPYING; if not, write to
|
|
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
** MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
** Bug reports and questions can be sent to kde-devel@kde.org
|
|
*/
|
|
|
|
|
|
#include "../config.h"
|
|
|
|
#include <qtimer.h>
|
|
#include <qregexp.h>
|
|
|
|
#include <kprocctrl.h>
|
|
#include <kpty.h>
|
|
#include <kdebug.h>
|
|
#include <kpassdlg.h>
|
|
|
|
#include <kpPty.h>
|
|
#include <kpackage.h>
|
|
#include <kpTerm.h>
|
|
#include <options.h>
|
|
#include <utils.h>
|
|
|
|
#define SHPROMPT "# "
|
|
const int TIMEOUT = -3;
|
|
const int PASSWORD = -2;
|
|
const int PROMPT = -1;
|
|
|
|
extern Opts *opts;
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
kpKProcIO::kpKProcIO ( QTextCodec *_codec)
|
|
: KProcIO(_codec)
|
|
{
|
|
}
|
|
|
|
kpKProcIO::~kpKProcIO()
|
|
{
|
|
}
|
|
|
|
bool kpKProcIO::sstart (RunMode runmode)
|
|
{
|
|
|
|
connect (this, SIGNAL (receivedStdout (KProcess *, char *, int)),
|
|
this, SLOT (received (KProcess *, char *, int)));
|
|
|
|
|
|
connect (this, SIGNAL (wroteStdin(KProcess *)),
|
|
this, SLOT (sent (KProcess *)));
|
|
|
|
return KProcess::start (runmode,( KProcess::Communication) ( KProcess::Stdin | KProcess::Stdout));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
kpPty::kpPty() : QObject()
|
|
{
|
|
pty = new kpKProcIO();
|
|
pty->setUsePty(KProcess::All, false);
|
|
|
|
connect(pty, SIGNAL(readReady(KProcIO *)), this,
|
|
SLOT(readLines()));
|
|
connect(pty, SIGNAL(processExited(KProcess *)), this,
|
|
SLOT(done()));
|
|
pty->pty()->setWinSize(0,80);
|
|
tm = new QTimer(this);
|
|
connect(tm, SIGNAL(timeout()), this, SLOT(slotTimeout()));
|
|
|
|
eventLoop = FALSE;
|
|
inSession = FALSE;
|
|
pUnterm = FALSE;
|
|
loginSession = FALSE;
|
|
|
|
codec = QTextCodec::codecForLocale();
|
|
QMap<QString, QCString> passwords;
|
|
}
|
|
|
|
|
|
kpPty::~kpPty()
|
|
{
|
|
}
|
|
|
|
void kpPty::startSu()
|
|
{
|
|
kdDebug() << "startSu()\n";
|
|
pty->setEnvironment("PS1", SHPROMPT);
|
|
#if defined(__FreeBSD__) || defined(__bsdi__)
|
|
(*pty) << "su";
|
|
#else
|
|
(*pty) << "su" << "-s" << "/bin/sh";
|
|
#endif
|
|
|
|
}
|
|
|
|
void kpPty::startSudo()
|
|
{
|
|
kdDebug() << "startSudo()\n";
|
|
pty->setEnvironment("PS1", SHPROMPT);
|
|
(*pty) << "sudo" << "-p" << "Password: " << "/bin/sh";
|
|
}
|
|
|
|
void kpPty::startSsh()
|
|
{
|
|
kdDebug() << "startSsh()\n";
|
|
(*pty) << "/usr/bin/ssh" << "-t" << "-l" << "root";
|
|
if (hostName.isEmpty()) {
|
|
(*pty) << "-o" << "StrictHostKeyChecking=no" << "localhost";
|
|
} else {
|
|
(*pty) << hostName;
|
|
}
|
|
(*pty) << "env PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin PS1='" SHPROMPT "' sh";
|
|
}
|
|
|
|
bool kpPty::needSession(bool needRoot)
|
|
{
|
|
return (!hostName.isEmpty() || needRoot);
|
|
}
|
|
|
|
bool kpPty::startSession(bool needRoot)
|
|
{
|
|
bool interact = FALSE; // Have interacted with user, prevents loops
|
|
bool passwordTried = FALSE; // Have tried the current save password, so need to put up dialog
|
|
pUnterm = FALSE;
|
|
kdDebug() << "kpPty::startSession\n";
|
|
if (!inSession && needSession(needRoot)) {
|
|
// Assume !needRoot actions are simple executables
|
|
kdDebug() << "kpPty::startSession TRUE\n";
|
|
loginSession = TRUE;
|
|
int ret;
|
|
QString s = "echo START=$?\n";
|
|
|
|
FULL_RESTART:
|
|
interact = FALSE;
|
|
retList.clear();
|
|
pty->resetAll();
|
|
|
|
QString passMsg;
|
|
kdDebug() << "privCmd=" << opts->privCmd << "\n";
|
|
if (opts->privCmd == Opts::SSHcmd || !hostName.isEmpty()) {
|
|
passMsg = i18n("The action you requested uses ssh. Please enter the password or pass phrase.\n");
|
|
startSsh();
|
|
} else if (opts->privCmd == Opts::SUcmd) {
|
|
passMsg = i18n("The action you requested needs root privileges. Please enter root's password.\n");
|
|
startSu();
|
|
} else if (opts->privCmd == Opts::SUDOcmd) {
|
|
passMsg = i18n("The action you requested needs root privileges. Please enter your SUDO password.\n");
|
|
startSudo();
|
|
}
|
|
pty->sstart(KProcess::NotifyOnExit);
|
|
|
|
RESTART:
|
|
tm->start(6*1000, TRUE);
|
|
eventLoop = TRUE;
|
|
kdDebug() << "Loopst\n";
|
|
kapp->enter_loop();
|
|
kdDebug() << "Loopfn Result=" << Result << "\n";
|
|
tm->stop();
|
|
if (Result == TIMEOUT) { // timeout
|
|
interact = TRUE;
|
|
// kdDebug() << "Line=" << retList.last() << "\n";
|
|
kpstart->addText(retList);
|
|
kpstart->run("", i18n("Login Problem: Please login manually"));
|
|
|
|
ret = kpstart->exec();
|
|
kdDebug() << "Sret=" << ret << "\n";
|
|
if (ret) {
|
|
inSession = FALSE;
|
|
} else {
|
|
inSession = TRUE;
|
|
}
|
|
} else if (Result == PASSWORD) { // We got a password prompt
|
|
QCString pass;
|
|
int res;
|
|
interact = TRUE;
|
|
// kdDebug() << "H=" << hostName << " PH=" << passwords[hostName] << " PT=" << passwordTried <<"\n";
|
|
if (passwords[hostName] != 0 && !passwordTried) {
|
|
pass = passwords[hostName];
|
|
res = 1;
|
|
} else {
|
|
kdDebug() << "Passwd=" << retList.last() << "\n";
|
|
QString msg = passMsg;
|
|
// kdDebug() << "privCmd=" << opts->privCmd << " host=" << hostName.isEmpty() << "\n";
|
|
if (opts->privCmd == Opts::SSHcmd || !hostName.isEmpty()) {
|
|
msg += retList.last();
|
|
}
|
|
int keep = 1;
|
|
res = KPasswordDialog::getPassword(pass,msg,&keep);
|
|
// kdDebug() << "Pass=" << pass << " Keep=" << keep << " Res=" << res << "\n";
|
|
if (keep) {
|
|
passwords[hostName] = pass;
|
|
} else {
|
|
passwords.remove(hostName);
|
|
}
|
|
}
|
|
pty->writeStdin(pass.append("\n"), false);
|
|
passwordTried = TRUE;
|
|
if (res) {
|
|
retList.clear();
|
|
goto RESTART;
|
|
} else {
|
|
inSession = FALSE;
|
|
}
|
|
} else if (Result == PROMPT) { // Got Prompt
|
|
inSession = TRUE;
|
|
kdDebug() << "kpPty::startSession TRUE\n";
|
|
} else { // process return code
|
|
pty->writeStdin(QCString("\04"), false); // SU doesn't listen to ^C
|
|
if (interact) {
|
|
goto FULL_RESTART;
|
|
} else {
|
|
QString errMsg = retList.join(" ");
|
|
KpMsgE(errMsg, TRUE);
|
|
inSession = FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
kdDebug() << "kpPty::startSession Not needed\n";
|
|
}
|
|
|
|
loginSession = FALSE;
|
|
if (!inSession)
|
|
close();
|
|
|
|
return inSession;
|
|
}
|
|
|
|
void kpPty::breakUpCmd(const QString &cmd)
|
|
{
|
|
kdDebug() << " kpPty::run CMD=\""<< cmd <<"\" pty = " << pty << endl;
|
|
|
|
bool quote = FALSE;
|
|
QString s;
|
|
QStringList cl = QStringList::split(" ", cmd);
|
|
|
|
for ( QStringList::Iterator it = cl.begin(); it != cl.end(); ++it ) {
|
|
int lastPt = (*it).length() - 1;
|
|
if ((*it)[0] == '\'') { // Start of quoted string
|
|
s = *it;
|
|
if ((*it)[lastPt] == '\'') { // Also End of quoted string
|
|
s.replace("'","");
|
|
(*pty) << s;
|
|
quote = FALSE;
|
|
} else {
|
|
s += " ";
|
|
quote = TRUE;
|
|
}
|
|
} else if ((*it)[lastPt] == '\'') { // End of quoted string
|
|
s += *it;
|
|
s.replace("'","");
|
|
(*pty) << s;
|
|
quote = FALSE;
|
|
} else if (quote) {
|
|
s += *it;
|
|
s += " ";
|
|
} else {
|
|
(*pty) << (*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
QStringList kpPty::run(const QString &cmd, bool inLoop, bool needRoot)
|
|
{
|
|
Result = 0;
|
|
|
|
pUnterm = FALSE;
|
|
|
|
if (!inSession && !needSession(needRoot)) {
|
|
// Assume !needRoot actions are simple executables
|
|
pty->resetAll();
|
|
breakUpCmd(cmd);
|
|
pty->setEnvironment("TERM", "dumb");
|
|
if (!pty->sstart(KProcess::NotifyOnExit)) {
|
|
kdDebug() << " kpPty::run execute=0\n";
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (startSession(needRoot)) {
|
|
kdDebug() << "CMDroot='"<< cmd <<"'\n";
|
|
QString s = cmd + ";echo RESULT=$?";
|
|
pty->writeStdin(s);
|
|
kdDebug() << " kpPty::run session\n";
|
|
} else {
|
|
kdDebug() << " kpPty::run other=0\n";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
retList.clear();
|
|
|
|
if (inLoop) {
|
|
eventLoop = TRUE;
|
|
kapp->enter_loop();
|
|
|
|
return retList;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void kpPty::close() {
|
|
// kdDebug() << "kpPty::close\n";
|
|
|
|
pty->closeAll();
|
|
while(pty->isRunning()) {
|
|
KProcessController::theKProcessController->waitForProcessExit(1);
|
|
}
|
|
inSession = false;
|
|
}
|
|
|
|
void kpPty::finish(int ret)
|
|
{
|
|
kdDebug() << "kpPty::finish " << ret << "\n";
|
|
|
|
QStringList::Iterator l;
|
|
Result = ret;
|
|
|
|
if (ret == PROMPT) { // Called program executed in session
|
|
if (!retList.empty()) {
|
|
l = retList.fromLast();
|
|
if ((*l).right(2) == SHPROMPT) {
|
|
retList.remove(l); // Remove prompt
|
|
}
|
|
}
|
|
|
|
if (!retList.empty()) {
|
|
int p;
|
|
l = retList.fromLast();
|
|
if ((p = (*l).find("RESULT=")) >= 0) {
|
|
ret = (*l).mid(p+7).toInt(0,10);
|
|
retList.remove(l); // Remove return code
|
|
} else {
|
|
ret = 666;
|
|
}
|
|
}
|
|
|
|
if (!retList.empty()) {
|
|
l = retList.begin();
|
|
if ( l != retList.end()) {
|
|
if ((*l).find("RESULT=") >= 0) {
|
|
retList.remove(l); // Remove command at start
|
|
}
|
|
}
|
|
}
|
|
}
|
|
emit result(retList,ret);
|
|
|
|
|
|
if (eventLoop) {
|
|
eventLoop = FALSE;
|
|
kapp->exit_loop();
|
|
}
|
|
}
|
|
|
|
void kpPty::readLines()
|
|
{
|
|
bool unterm = FALSE;
|
|
|
|
QString stext;
|
|
while(pty->readln(stext, false, &unterm) >= 0)
|
|
{
|
|
stext = codec->toUnicode(stext.ascii(), stext.length());
|
|
emit textIn(stext, !unterm);
|
|
// kdDebug() << "[" << pUnterm << "-" << unterm << "-" << stext << ">\n";
|
|
if (pUnterm) {
|
|
QStringList::Iterator lst = retList.fromLast();
|
|
if (lst != retList.end())
|
|
{
|
|
stext = *lst + stext;
|
|
retList.remove(lst);
|
|
}
|
|
}
|
|
int i;
|
|
if (!unterm)
|
|
{
|
|
while (stext.endsWith("\r")) {
|
|
stext.truncate(stext.length()-1);
|
|
}
|
|
|
|
i = stext.findRev('\r');
|
|
if (i > -1) {
|
|
stext = stext.mid(i+1);
|
|
}
|
|
}
|
|
|
|
pUnterm = unterm;
|
|
|
|
retList << stext;
|
|
// kdDebug() << "++" << stext << "\n";
|
|
if (stext.right(2) == SHPROMPT) { // Shell prompt
|
|
emit textIn("\r \n", false);
|
|
finish(PROMPT);
|
|
} else if (loginSession) {
|
|
QRegExp rx( "^[^:]+:[\\s]*$"); // Password prompt
|
|
if (rx.search(retList.last()) >= 0) {
|
|
kdDebug() << loginSession << " " <<retList.last()<< " Match password p\n";
|
|
finish(PASSWORD);
|
|
}
|
|
}
|
|
}
|
|
pty->ackRead();
|
|
}
|
|
|
|
void kpPty::keyOut(char ch)
|
|
{
|
|
QCString s(2);
|
|
s[0] = ch;
|
|
s[1] = '\0';
|
|
pty->writeStdin(s, false);
|
|
}
|
|
|
|
void kpPty::done()
|
|
{
|
|
int ret = pty->exitStatus();
|
|
QString stext;
|
|
|
|
//kdDebug() << "Done (" << ret << ")" << endl;
|
|
|
|
finish(ret);
|
|
}
|
|
|
|
void kpPty::slotTimeout()
|
|
{
|
|
kdDebug() << "Timeout..............\n";
|
|
finish(TIMEOUT);
|
|
}
|
|
#include "kpPty.moc"
|