summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRafael Roquetto <rafael.roquetto@kdab.com>2012-02-15 16:52:43 +0100
committerQt by Nokia <qt-info@nokia.com>2012-02-17 08:46:40 +0100
commite3363fd945b3ab961fba720ee013533dd8ba2930 (patch)
tree2ef072dc27562e365eb28652d7cc51f455d12728 /src
parent147a38faa6afbe616a9e6a123c8dbb5b00e458ef (diff)
Enables QProcess back on QNX.
Because fork()/vfork() on QNX are not supported on multithreaded applications, QProcess had been disabled on this platform. The corresponding code has now been replaced with functions that wrap around spawn(). Change-Id: I46091b7d41f322a5cad07d17893aa929c84941ef Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/global/qglobal.h2
-rw-r--r--src/corelib/io/qprocess.cpp13
-rw-r--r--src/corelib/io/qprocess_p.h4
-rw-r--r--src/corelib/io/qprocess_unix.cpp159
4 files changed, 160 insertions, 18 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 22e73ede26..7330a49939 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -1751,8 +1751,6 @@ Q_CORE_EXPORT int qrand();
# define QT_NO_QWS_SHARE_FONTS
# define QT_NO_SYSTEMSEMAPHORE
# define QT_NO_SHAREDMEMORY
-// QNX currently doesn't support forking in a thread, so disable QProcess
-# define QT_NO_PROCESS
#endif
#if defined (__ELF__)
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 0a0097c887..1312e9b55f 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -520,6 +520,11 @@ void QProcessPrivate::Channel::clear()
setWorkingDirectory(). By default, processes are run in the
current working directory of the calling process.
+ \note On QNX, setting the working directory may cause all
+ application threads, with the exception of the QProcess caller
+ thread, to momentaneusly freeze, owing to a limitation in
+ the operating system.
+
\section1 Synchronous Process API
QProcess provides a set of functions which allow it to be used
@@ -1433,6 +1438,9 @@ QString QProcess::workingDirectory() const
process in this directory. The default behavior is to start the
process in the working directory of the calling process.
+ \note On QNX, this may cause all application threads to
+ momentaneusly freeze.
+
\sa workingDirectory(), start()
*/
void QProcess::setWorkingDirectory(const QString &dir)
@@ -1755,7 +1763,7 @@ void QProcess::setProcessState(ProcessState state)
exit().
\warning This function is called by QProcess on Unix and Mac OS X
- only. On Windows, it is not called.
+ only. On Windows and QNX, it is not called.
*/
void QProcess::setupChildProcess()
{
@@ -2149,6 +2157,9 @@ int QProcess::execute(const QString &program)
The process will be started in the directory \a workingDirectory.
+ \note On QNX, this may cause all application threads to
+ momentaneusly freeze.
+
If the function is successful then *\a pid is set to the process
identifier of the started process.
*/
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 236e716b34..311b5ce845 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -304,8 +304,10 @@ public:
#endif
void startProcess();
-#if defined(Q_OS_UNIX)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_QNX)
void execChild(const char *workingDirectory, char **path, char **argv, char **envp);
+#elif defined(Q_OS_QNX)
+ pid_t spawnChild(const char *workingDirectory, char **argv, char **envp);
#endif
bool processStarted();
void terminateProcess();
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 9e35978c77..2da2913c6f 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -48,6 +48,7 @@
#include "qstring.h"
#include <ctype.h>
+
/*
Returns a human readable representation of the first \a len
characters in \a data.
@@ -105,6 +106,10 @@ QT_END_NAMESPACE
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#ifdef Q_OS_QNX
+#include <spawn.h>
+#include <sys/neutrino.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -538,16 +543,6 @@ static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Hash &environm
return envp;
}
-// under QNX RTOS we have to use vfork() when multithreading
-inline pid_t qt_fork()
-{
-#if defined(Q_OS_QNX)
- return vfork();
-#else
- return fork();
-#endif
-}
-
void QProcessPrivate::startProcess()
{
Q_Q(QProcess);
@@ -664,8 +659,12 @@ void QProcessPrivate::startProcess()
// Start the process manager, and fork off the child process.
processManager()->lock();
- pid_t childPid = qt_fork();
+#if defined(Q_OS_QNX)
+ pid_t childPid = spawnChild(workingDirPtr, argv, envp);
+#else
+ pid_t childPid = fork();
int lastForkErrno = errno;
+#endif
if (childPid != 0) {
// Clean up duplicated memory.
free(dupProgramName);
@@ -679,10 +678,22 @@ void QProcessPrivate::startProcess()
delete [] envp;
delete [] path;
}
+
+ // This is not a valid check under QNX, because the semantics are
+ // different. While under other platforms where fork() may succeed and exec() can still fail,
+ // causing the childPid to hold a valid value (and thus evaluating the
+ // following if to false), and then signaling the error via
+ // childStartedPipe, under QNX on the other hand, spawn() return value will be assigned
+ // to childPid (which will be -1 in case of failure). This will force
+ // QProcess to cleanup, instead of signaling the error via
+ // childStartedPipe. Since it will invalidade the pipes, functions like
+ // QProcess::waitForStarted() will fail, for childStartedPipe will be
+ // '-1' and mess with the select() calls.
+#if !defined(Q_OS_QNX)
if (childPid < 0) {
// Cleanup, report error and return
#if defined (QPROCESS_DEBUG)
- qDebug("qt_fork failed: %s", qPrintable(qt_error_string(lastForkErrno)));
+ qDebug("fork failed: %s", qPrintable(qt_error_string(lastForkErrno)));
#endif
processManager()->unlock();
q->setProcessState(QProcess::NotRunning);
@@ -698,6 +709,7 @@ void QProcessPrivate::startProcess()
execChild(workingDirPtr, path, argv, envp);
::_exit(-1);
}
+#endif
// Register the child. In the mean time, we can get a SIGCHLD, so we need
// to keep the lock held to avoid a race to catch the child.
@@ -735,6 +747,87 @@ void QProcessPrivate::startProcess()
::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
}
+#if defined(Q_OS_QNX)
+static pid_t doSpawn(int fd_count, int fd_map[], char **argv, char **envp,
+ const char *workingDir, bool spawn_detached)
+{
+ // A multi threaded QNX Process can't fork so we call spawn() instead.
+
+ struct inheritance inherit;
+ memset(&inherit, 0, sizeof(inherit));
+ inherit.flags |= SPAWN_SETSID;
+ inherit.flags |= SPAWN_CHECK_SCRIPT;
+ if (spawn_detached)
+ inherit.flags |= SPAWN_NOZOMBIE;
+ inherit.flags |= SPAWN_SETSIGDEF;
+ sigaddset(&inherit.sigdefault, SIGPIPE); // reset the signal that we ignored
+
+ // enter the working directory
+ const char *oldWorkingDir = 0;
+ char buff[PATH_MAX + 1];
+
+ if (workingDir) {
+ //we need to freeze everyone in order to avoid race conditions with //chdir().
+ if (ThreadCtl(_NTO_TCTL_THREADS_HOLD, 0) == -1)
+ qWarning("ThreadCtl(): cannot hold threads: %s", qPrintable(qt_error_string(errno)));
+
+ oldWorkingDir = QT_GETCWD(buff, PATH_MAX + 1);
+ QT_CHDIR(workingDir);
+ }
+
+ pid_t childPid;
+ EINTR_LOOP(childPid, ::spawn(argv[0], fd_count, fd_map, &inherit, argv, envp));
+ if (childPid == -1) {
+ inherit.flags |= SPAWN_SEARCH_PATH;
+ EINTR_LOOP(childPid, ::spawn(argv[0], fd_count, fd_map, &inherit, argv, envp));
+ }
+
+ if (oldWorkingDir) {
+ QT_CHDIR(oldWorkingDir);
+
+ if (ThreadCtl(_NTO_TCTL_THREADS_CONT, 0) == -1)
+ qFatal("ThreadCtl(): cannot resume threads: %s", qPrintable(qt_error_string(errno)));
+ }
+
+ return childPid;
+}
+
+pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **envp)
+{
+ const int fd_count = 3;
+ int fd_map[fd_count];
+ switch (processChannelMode) {
+ case QProcess::ForwardedChannels:
+ fd_map[0] = stdinChannel.pipe[0];
+ fd_map[1] = QT_FILENO(stdout);
+ fd_map[2] = QT_FILENO(stderr);
+ break;
+ case QProcess::MergedChannels:
+ fd_map[0] = stdinChannel.pipe[0];
+ fd_map[1] = stdoutChannel.pipe[1];
+ fd_map[2] = stdoutChannel.pipe[1];
+ break;
+ case QProcess::SeparateChannels:
+ fd_map[0] = stdinChannel.pipe[0];
+ fd_map[1] = stdoutChannel.pipe[1];
+ fd_map[2] = stderrChannel.pipe[1];
+ break;
+ }
+
+ pid_t childPid = doSpawn(fd_count, fd_map, argv, envp, workingDir, false);
+
+ if (childPid == -1) {
+ QString error = qt_error_string(errno);
+ qt_safe_write(childStartedPipe[1], error.data(), error.length() * sizeof(QChar));
+ qt_safe_close(childStartedPipe[1]);
+ childStartedPipe[1] = -1;
+ }
+
+ return childPid;
+}
+
+#else
+
void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp)
{
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
@@ -797,6 +890,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
qt_safe_close(childStartedPipe[1]);
childStartedPipe[1] = -1;
}
+#endif
bool QProcessPrivate::processStarted()
{
@@ -1213,6 +1307,42 @@ void QProcessPrivate::_q_notified()
{
}
+#if defined(Q_OS_QNX)
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+ const int fd_count = 3;
+ int fd_map[fd_count] = { QT_FILENO(stdin), QT_FILENO(stdout), QT_FILENO(stderr) };
+
+ QList<QByteArray> enc_args;
+ enc_args.append(QFile::encodeName(program));
+ for (int i = 0; i < arguments.size(); ++i)
+ enc_args.append(arguments.at(i).toLocal8Bit());
+
+ const int argc = enc_args.size();
+ QScopedArrayPointer<char*> raw_argv(new char*[argc + 1]);
+ for (int i = 0; i < argc; ++i)
+ raw_argv[i] = const_cast<char *>(enc_args.at(i).data());
+ raw_argv[argc] = 0;
+
+ char **envp = 0; // inherit environment
+
+ // Encode the working directory if it's non-empty, otherwise just pass 0.
+ const char *workingDirPtr = 0;
+ QByteArray encodedWorkingDirectory;
+ if (!workingDirectory.isEmpty()) {
+ encodedWorkingDirectory = QFile::encodeName(workingDirectory);
+ workingDirPtr = encodedWorkingDirectory.constData();
+ }
+
+ pid_t childPid = doSpawn(fd_count, fd_map, raw_argv.data(), envp, workingDirPtr, true);
+ if (pid && childPid != -1)
+ *pid = childPid;
+
+ return childPid != -1;
+}
+
+#else
+
bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
{
processManager()->start();
@@ -1226,7 +1356,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
int pidPipe[2];
qt_safe_pipe(pidPipe);
- pid_t childPid = qt_fork();
+ pid_t childPid = fork();
if (childPid == 0) {
struct sigaction noaction;
memset(&noaction, 0, sizeof(noaction));
@@ -1238,7 +1368,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
qt_safe_close(startedPipe[0]);
qt_safe_close(pidPipe[0]);
- pid_t doubleForkPid = qt_fork();
+ pid_t doubleForkPid = fork();
if (doubleForkPid == 0) {
qt_safe_close(pidPipe[1]);
@@ -1326,6 +1456,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
qt_safe_close(pidPipe[0]);
return success;
}
+#endif
void QProcessPrivate::initializeProcessManager()
{