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>
This commit is contained in:
parent
147a38faa6
commit
e3363fd945
@ -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__)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user