summaryrefslogtreecommitdiff
path: root/src/libs/utils/consoleprocess_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/consoleprocess_unix.cpp')
-rw-r--r--src/libs/utils/consoleprocess_unix.cpp171
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();
+}