diff options
author | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2009-02-20 12:33:16 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2009-02-27 18:04:36 +0100 |
commit | bbaf7893cc75feba518ecd868aa005545b1f5c58 (patch) | |
tree | af626ecc471c88f8fc7e6b325a4c04d1419d08a4 /src/libs/utils/consoleprocess_unix.cpp | |
parent | d35e19060111382ac8c86c99731a0b7f860782c0 (diff) | |
download | qt-creator-bbaf7893cc75feba518ecd868aa005545b1f5c58.tar.gz |
"debug in terminal" feature.
includes complete refactoring of ConsoleProcess.
Diffstat (limited to 'src/libs/utils/consoleprocess_unix.cpp')
-rw-r--r-- | src/libs/utils/consoleprocess_unix.cpp | 171 |
1 files changed, 135 insertions, 36 deletions
diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp index 3470925376..6100823778 100644 --- a/src/libs/utils/consoleprocess_unix.cpp +++ b/src/libs/utils/consoleprocess_unix.cpp @@ -29,57 +29,78 @@ #include "consoleprocess.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QTemporaryFile> + +#include <QtNetwork/QLocalSocket> + +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + using namespace Core::Utils; ConsoleProcess::ConsoleProcess(QObject *parent) : QObject(parent) { - m_isRunning = false; -} + m_debug = false; + m_appPid = 0; + m_stubSocket = 0; -ConsoleProcess::~ConsoleProcess() -{ + connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable())); + + m_process.setProcessChannelMode(QProcess::ForwardedChannels); + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(stubExited())); } -static QString shellEscape(const QString &in) +ConsoleProcess::~ConsoleProcess() { - QString out = in; - out.replace('\'', "'\''"); - out.prepend('\''); - out.append('\''); - return out; + stop(); } bool ConsoleProcess::start(const QString &program, const QStringList &args) { - if (m_process.state() != QProcess::NotRunning) + if (isRunning()) return false; - QString shellArgs; - shellArgs += QLatin1String("cd "); - shellArgs += shellEscape(workingDirectory()); - shellArgs += QLatin1Char(';'); - shellArgs += shellEscape(program); - foreach (const QString &arg, args) { - shellArgs += QLatin1Char(' '); - shellArgs += shellEscape(arg); - } - shellArgs += QLatin1String("; echo; echo \"Press enter to close this window\"; read DUMMY"); - m_process.setEnvironment(environment()); + QString err = stubServerListen(); + if (!err.isEmpty()) { + emit processError(tr("Cannot set up comm channel: %1").arg(err)); + return false; + } - connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(processFinished(int, QProcess::ExitStatus))); + QStringList xtermArgs; + xtermArgs << "-e" << (QCoreApplication::applicationDirPath() + "/qtcreator_process_stub") + << (m_debug ? "debug" : "exec") + << m_stubServer.fullServerName() + << tr("Press <RETURN> to close this window...") + << workingDirectory() << environment() << "" + << program << args; - m_process.start(QLatin1String("xterm"), QStringList() << QLatin1String("-e") << "/bin/sh" << "-c" << shellArgs); - if (!m_process.waitForStarted()) + m_process.start(QLatin1String("xterm"), xtermArgs); + if (!m_process.waitForStarted()) { + stubServerShutdown(); + emit processError(tr("Cannot start console emulator xterm.")); return false; - emit processStarted(); + } + m_executable = program; + emit wrapperStarted(); return true; } -void ConsoleProcess::processFinished(int, QProcess::ExitStatus) +void ConsoleProcess::stop() { - emit processStopped(); + if (!isRunning()) + return; + stubServerShutdown(); + m_appPid = 0; + m_process.terminate(); + if (!m_process.waitForFinished(1000)) + m_process.kill(); + m_process.waitForFinished(); } bool ConsoleProcess::isRunning() const @@ -87,19 +108,97 @@ bool ConsoleProcess::isRunning() const return m_process.state() != QProcess::NotRunning; } -void ConsoleProcess::stop() +QString ConsoleProcess::stubServerListen() { - m_process.terminate(); - m_process.waitForFinished(); + // We need to put the socket in a private directory, as some systems simply do not + // check the file permissions of sockets. + QString stubFifoDir; + forever { + { + QTemporaryFile tf; + if (!tf.open()) + return tr("Cannot create temporary file: %2").arg(tf.errorString()); + stubFifoDir = QFile::encodeName(tf.fileName()); + } + // By now the temp file was deleted again + m_stubServerDir = QFile::encodeName(stubFifoDir); + if (!::mkdir(m_stubServerDir.constData(), 0700)) + break; + if (errno != EEXIST) + return tr("Cannot create temporary directory %1: %2").arg(stubFifoDir, strerror(errno)); + } + QString stubServer = stubFifoDir + "/stub-socket"; + if (!m_stubServer.listen(stubServer)) { + ::rmdir(m_stubServerDir.constData()); + return tr("Cannot create socket %1: %2").arg(stubServer, m_stubServer.errorString()); + } + return QString(); +} + +void ConsoleProcess::stubServerShutdown() +{ + delete m_stubSocket; + m_stubSocket = 0; + if (m_stubServer.isListening()) { + m_stubServer.close(); + ::rmdir(m_stubServerDir.constData()); + } +} + +void ConsoleProcess::stubConnectionAvailable() +{ + m_stubSocket = m_stubServer.nextPendingConnection(); + connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput())); } -qint64 ConsoleProcess::applicationPID() const +static QString errorMsg(int code) { - return m_process.pid(); + return QString::fromLocal8Bit(strerror(code)); } -int ConsoleProcess::exitCode() const +void ConsoleProcess::readStubOutput() { - return m_process.exitCode(); + while (m_stubSocket->canReadLine()) { + QByteArray out = m_stubSocket->readLine(); + out.chop(1); // \n + if (out.startsWith("err:chdir ")) { + emit processError(tr("Cannot change to working directory %1: %2") + .arg(workingDirectory(), errorMsg(out.mid(10).toInt()))); + } else if (out.startsWith("err:exec ")) { + emit processError(tr("Cannot execute %1: %2") + .arg(m_executable, errorMsg(out.mid(9).toInt()))); + } else if (out.startsWith("pid ")) { + m_appPid = out.mid(4).toInt(); + emit processStarted(); + } else if (out.startsWith("exit ")) { + m_appStatus = QProcess::NormalExit; + m_appCode = out.mid(5).toInt(); + m_appPid = 0; + emit processStopped(); + } else if (out.startsWith("crash ")) { + m_appStatus = QProcess::CrashExit; + m_appCode = out.mid(6).toInt(); + m_appPid = 0; + emit processStopped(); + } else { + emit processError(tr("Unexpected output from helper program.")); + m_process.terminate(); + break; + } + } } +void ConsoleProcess::stubExited() +{ + // The stub exit might get noticed before we read the error status. + if (m_stubSocket && m_stubSocket->state() == QLocalSocket::ConnectedState) + m_stubSocket->waitForDisconnected(); + stubServerShutdown(); + if (m_appPid) { + m_appStatus = QProcess::CrashExit; + m_appCode = -1; + m_appPid = 0; + emit processStopped(); // Maybe it actually did not, but keep state consistent + } + emit wrapperStopped(); +} |