LP-311 Remove basic/advanced stabilization tab auto-switch (autotune/txpid lock issues)
[librepilot.git] / ground / gcs / src / libs / utils / consoleprocess_unix.cpp
blobfb7165046bce2c64acd636410245b2f6ac0e555e
1 /**
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.
7 * @brief
8 * @see The GNU Public License (GPL) Version 3
9 * @defgroup
10 * @{
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
22 * for more details.
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>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <unistd.h>
44 using namespace Utils;
46 ConsoleProcess::ConsoleProcess(QObject *parent) :
47 QObject(parent),
48 m_mode(Run),
49 m_appPid(0),
50 m_stubSocket(0),
51 m_settings(0)
53 connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
55 m_process.setProcessChannelMode(QProcess::ForwardedChannels);
56 connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
57 SLOT(stubExited()));
60 ConsoleProcess::~ConsoleProcess()
62 stop();
65 bool ConsoleProcess::start(const QString &program, const QStringList &args)
67 if (isRunning()) {
68 return false;
71 const QString err = stubServerListen();
72 if (!err.isEmpty()) {
73 emit processError(msgCommChannelFailed(err));
74 return false;
77 if (!environment().isEmpty()) {
78 m_tempFile = new QTemporaryFile();
79 if (!m_tempFile->open()) {
80 stubServerShutdown();
81 emit processError(msgCannotCreateTempFile(m_tempFile->errorString()));
82 delete m_tempFile;
83 m_tempFile = 0;
84 return false;
86 foreach(const QString &var, environment()) {
87 m_tempFile->write(var.toLocal8Bit());
88 m_tempFile->write("", 1);
90 m_tempFile->flush();
93 QStringList xtermArgs = terminalEmulator(m_settings).split(QLatin1Char(' ')); // FIXME: quoting
94 xtermArgs
95 #ifdef Q_OS_MAC
96 << (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub"))
97 #else
98 << (QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub"))
99 #endif
100 << modeOption(m_mode)
101 << m_stubServer.fullServerName()
102 << msgPromptToClose()
103 << workingDirectory()
104 << (m_tempFile ? m_tempFile->fileName() : 0)
105 << program << args;
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));
112 delete m_tempFile;
113 m_tempFile = 0;
114 return false;
116 m_executable = program;
117 emit wrapperStarted();
118 return true;
121 void ConsoleProcess::stop()
123 if (!isRunning()) {
124 return;
126 stubServerShutdown();
127 m_appPid = 0;
128 m_process.terminate();
129 if (!m_process.waitForFinished(1000)) {
130 m_process.kill();
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.
144 QString stubFifoDir;
146 forever {
148 QTemporaryFile tf;
149 if (!tf.open()) {
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)) {
157 break;
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());
168 return QString();
171 void ConsoleProcess::stubServerShutdown()
173 delete m_stubSocket;
174 m_stubSocket = 0;
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();
196 out.chop(1); // \n
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
203 delete m_tempFile;
204 m_tempFile = 0;
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();
211 m_appPid = 0;
212 emit processStopped();
213 } else if (out.startsWith("crash ")) {
214 m_appStatus = QProcess::CrashExit;
215 m_appCode = out.mid(6).toInt();
216 m_appPid = 0;
217 emit processStopped();
218 } else {
219 emit processError(msgUnexpectedOutput());
220 m_process.terminate();
221 break;
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();
233 delete m_tempFile;
234 m_tempFile = 0;
235 if (m_appPid) {
236 m_appStatus = QProcess::CrashExit;
237 m_appCode = -1;
238 m_appPid = 0;
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"));
251 #else
252 return QLatin1String("xterm");
254 #endif
257 QString ConsoleProcess::terminalEmulator(const QSettings *settings)
259 const QString dflt = defaultTerminalEmulator() + QLatin1String(" -e");
261 if (!settings) {
262 return dflt;
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);