2 ******************************************************************************
4 * @file consoleprocess_unix.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
8 * @see The GNU Public License (GPL) Version 3
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "consoleprocess.h"
31 #include <QtCore/QCoreApplication>
32 #include <QtCore/QDir>
33 #include <QtCore/QSettings>
34 #include <QtCore/QTemporaryFile>
36 #include <QtNetwork/QLocalSocket>
39 #include <sys/types.h>
44 using namespace Utils
;
46 ConsoleProcess::ConsoleProcess(QObject
*parent
) :
53 connect(&m_stubServer
, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
55 m_process
.setProcessChannelMode(QProcess::ForwardedChannels
);
56 connect(&m_process
, SIGNAL(finished(int, QProcess::ExitStatus
)),
60 ConsoleProcess::~ConsoleProcess()
65 bool ConsoleProcess::start(const QString
&program
, const QStringList
&args
)
71 const QString err
= stubServerListen();
73 emit
processError(msgCommChannelFailed(err
));
77 if (!environment().isEmpty()) {
78 m_tempFile
= new QTemporaryFile();
79 if (!m_tempFile
->open()) {
81 emit
processError(msgCannotCreateTempFile(m_tempFile
->errorString()));
86 foreach(const QString
&var
, environment()) {
87 m_tempFile
->write(var
.toLocal8Bit());
88 m_tempFile
->write("", 1);
93 QStringList xtermArgs
= terminalEmulator(m_settings
).split(QLatin1Char(' ')); // FIXME: quoting
96 << (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub"))
98 << (QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub"))
100 << modeOption(m_mode
)
101 << m_stubServer
.fullServerName()
102 << msgPromptToClose()
103 << workingDirectory()
104 << (m_tempFile
? m_tempFile
->fileName() : 0)
107 QString xterm
= xtermArgs
.takeFirst();
108 m_process
.start(xterm
, xtermArgs
);
109 if (!m_process
.waitForStarted()) {
110 stubServerShutdown();
111 emit
processError(tr("Cannot start the terminal emulator '%1'.").arg(xterm
));
116 m_executable
= program
;
117 emit
wrapperStarted();
121 void ConsoleProcess::stop()
126 stubServerShutdown();
128 m_process
.terminate();
129 if (!m_process
.waitForFinished(1000)) {
132 m_process
.waitForFinished();
135 bool ConsoleProcess::isRunning() const
137 return m_process
.state() != QProcess::NotRunning
;
140 QString
ConsoleProcess::stubServerListen()
142 // We need to put the socket in a private directory, as some systems simply do not
143 // check the file permissions of sockets.
150 return msgCannotCreateTempFile(tf
.errorString());
152 stubFifoDir
= QFile::encodeName(tf
.fileName());
154 // By now the temp file was deleted again
155 m_stubServerDir
= QFile::encodeName(stubFifoDir
);
156 if (!::mkdir(m_stubServerDir
.constData(), 0700)) {
159 if (errno
!= EEXIST
) {
160 return msgCannotCreateTempDir(stubFifoDir
, QString::fromLocal8Bit(strerror(errno
)));
163 const QString stubServer
= stubFifoDir
+ "/stub-socket";
164 if (!m_stubServer
.listen(stubServer
)) {
165 ::rmdir(m_stubServerDir
.constData());
166 return tr("Cannot create socket '%1': %2").arg(stubServer
, m_stubServer
.errorString());
171 void ConsoleProcess::stubServerShutdown()
175 if (m_stubServer
.isListening()) {
176 m_stubServer
.close();
177 ::rmdir(m_stubServerDir
.constData());
181 void ConsoleProcess::stubConnectionAvailable()
183 m_stubSocket
= m_stubServer
.nextPendingConnection();
184 connect(m_stubSocket
, SIGNAL(readyRead()), SLOT(readStubOutput()));
187 static QString
errorMsg(int code
)
189 return QString::fromLocal8Bit(strerror(code
));
192 void ConsoleProcess::readStubOutput()
194 while (m_stubSocket
->canReadLine()) {
195 QByteArray out
= m_stubSocket
->readLine();
197 if (out
.startsWith("err:chdir ")) {
198 emit
processError(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out
.mid(10).toInt())));
199 } else if (out
.startsWith("err:exec ")) {
200 emit
processError(msgCannotExecute(m_executable
, errorMsg(out
.mid(9).toInt())));
201 } else if (out
.startsWith("pid ")) {
202 // Will not need it any more
206 m_appPid
= out
.mid(4).toInt();
207 emit
processStarted();
208 } else if (out
.startsWith("exit ")) {
209 m_appStatus
= QProcess::NormalExit
;
210 m_appCode
= out
.mid(5).toInt();
212 emit
processStopped();
213 } else if (out
.startsWith("crash ")) {
214 m_appStatus
= QProcess::CrashExit
;
215 m_appCode
= out
.mid(6).toInt();
217 emit
processStopped();
219 emit
processError(msgUnexpectedOutput());
220 m_process
.terminate();
226 void ConsoleProcess::stubExited()
228 // The stub exit might get noticed before we read the error status.
229 if (m_stubSocket
&& m_stubSocket
->state() == QLocalSocket::ConnectedState
) {
230 m_stubSocket
->waitForDisconnected();
232 stubServerShutdown();
236 m_appStatus
= QProcess::CrashExit
;
239 emit
processStopped(); // Maybe it actually did not, but keep state consistent
241 emit
wrapperStopped();
244 QString
ConsoleProcess::defaultTerminalEmulator()
246 // FIXME: enable this once runInTerminal works nicely
247 #if 0 // def Q_OS_MAC
248 return QDir::cleanPath(QCoreApplication::applicationDirPath()
249 + QLatin1String("/../Resources/runInTerminal.command"));
252 return QLatin1String("xterm");
257 QString
ConsoleProcess::terminalEmulator(const QSettings
*settings
)
259 const QString dflt
= defaultTerminalEmulator() + QLatin1String(" -e");
264 return settings
->value(QLatin1String("General/TerminalEmulator"), dflt
).toString();
267 void ConsoleProcess::setTerminalEmulator(QSettings
*settings
, const QString
&term
)
269 return settings
->setValue(QLatin1String("General/TerminalEmulator"), term
);