From 3cd73f550256069c6756b0b01d717f1f42b83068 Mon Sep 17 00:00:00 2001 From: Ritt Konstantin Date: Tue, 21 Jun 2011 13:51:06 +0200 Subject: make QProcess really work on QNX a multi-threaded QNX Process can't fork properly so we call spawn() instead Merge-request: 1259 Reviewed-by: Harald Fernengel --- src/corelib/global/qglobal.h | 2 - src/corelib/io/qprocess.cpp | 10 +++ src/corelib/io/qprocess_p.h | 8 +- src/corelib/io/qprocess_unix.cpp | 183 ++++++++++++++++++++++++++++++++++----- 4 files changed, 179 insertions(+), 24 deletions(-) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 32b3e6b..08f9eb0 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2753,8 +2753,6 @@ QT_LICENSED_MODULE(DBus) # 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 70a70c2..4b689c5 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -751,12 +751,16 @@ QProcessPrivate::QProcessPrivate() sequenceNumber = 0; exitCode = 0; exitStatus = QProcess::NormalExit; +#ifndef Q_OS_QNX startupSocketNotifier = 0; +#endif deathNotifier = 0; notifier = 0; pipeWriter = 0; +#ifndef Q_OS_QNX childStartedPipe[0] = INVALID_Q_PIPE; childStartedPipe[1] = INVALID_Q_PIPE; +#endif deathPipe[0] = INVALID_Q_PIPE; deathPipe[1] = INVALID_Q_PIPE; exitCode = 0; @@ -825,11 +829,13 @@ void QProcessPrivate::cleanup() qDeleteInEventHandler(stdinChannel.notifier); stdinChannel.notifier = 0; } +#ifndef Q_OS_QNX if (startupSocketNotifier) { startupSocketNotifier->setEnabled(false); qDeleteInEventHandler(startupSocketNotifier); startupSocketNotifier = 0; } +#endif if (deathNotifier) { deathNotifier->setEnabled(false); qDeleteInEventHandler(deathNotifier); @@ -842,7 +848,9 @@ void QProcessPrivate::cleanup() destroyPipe(stdoutChannel.pipe); destroyPipe(stderrChannel.pipe); destroyPipe(stdinChannel.pipe); +#ifndef Q_OS_QNX destroyPipe(childStartedPipe); +#endif destroyPipe(deathPipe); #ifdef Q_OS_UNIX serial = 0; @@ -1077,8 +1085,10 @@ bool QProcessPrivate::_q_startupNotification() qDebug("QProcessPrivate::startupNotification()"); #endif +#ifndef Q_OS_QNX if (startupSocketNotifier) startupSocketNotifier->setEnabled(false); +#endif if (processStarted()) { q->setProcessState(QProcess::Running); emit q->started(); diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 9adb331..f24a7bc 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -288,11 +288,15 @@ public: QRingBuffer errorReadBuffer; QRingBuffer writeBuffer; +#ifndef Q_OS_QNX Q_PIPE childStartedPipe[2]; +#endif Q_PIPE deathPipe[2]; void destroyPipe(Q_PIPE pipe[2]); +#ifndef Q_OS_QNX QSocketNotifier *startupSocketNotifier; +#endif QSocketNotifier *deathNotifier; // the wonderful windows notifier @@ -301,8 +305,10 @@ public: QWinEventNotifier *processFinishedNotifier; void startProcess(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !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 07e3087..725e4c5 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -105,6 +105,10 @@ QT_END_NAMESPACE #include #include #include +#ifdef Q_OS_QNX +# include +#endif + QT_BEGIN_NAMESPACE @@ -521,16 +525,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 -} - #ifdef Q_OS_MAC Q_GLOBAL_STATIC(QMutex, cfbundleMutex); #endif @@ -550,15 +544,18 @@ void QProcessPrivate::startProcess() !createChannel(stdoutChannel) || !createChannel(stderrChannel)) return; +#if !defined(Q_OS_QNX) qt_create_pipe(childStartedPipe); +#endif qt_create_pipe(deathPipe); if (threadData->eventDispatcher) { +#if !defined(Q_OS_QNX) startupSocketNotifier = new QSocketNotifier(childStartedPipe[0], QSocketNotifier::Read, q); QObject::connect(startupSocketNotifier, SIGNAL(activated(int)), q, SLOT(_q_startupNotification())); - +#endif deathNotifier = new QSocketNotifier(deathPipe[0], QSocketNotifier::Read, q); QObject::connect(deathNotifier, SIGNAL(activated(int)), @@ -650,7 +647,11 @@ 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(); +#endif int lastForkErrno = errno; if (childPid != 0) { // Clean up duplicated memory. @@ -668,7 +669,7 @@ void QProcessPrivate::startProcess() 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); @@ -679,11 +680,13 @@ void QProcessPrivate::startProcess() return; } +#if !defined(Q_OS_QNX) // Start the child. if (childPid == 0) { 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. @@ -694,14 +697,15 @@ void QProcessPrivate::startProcess() // parent // close the ends we don't use and make all pipes non-blocking ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK); +#if !defined(Q_OS_QNX) qt_safe_close(childStartedPipe[1]); childStartedPipe[1] = -1; +#endif if (stdinChannel.pipe[0] != -1) { qt_safe_close(stdinChannel.pipe[0]); stdinChannel.pipe[0] = -1; } - if (stdinChannel.pipe[1] != -1) ::fcntl(stdinChannel.pipe[1], F_SETFL, ::fcntl(stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK); @@ -709,7 +713,6 @@ void QProcessPrivate::startProcess() qt_safe_close(stdoutChannel.pipe[1]); stdoutChannel.pipe[1] = -1; } - if (stdoutChannel.pipe[0] != -1) ::fcntl(stdoutChannel.pipe[0], F_SETFL, ::fcntl(stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK); @@ -721,6 +724,7 @@ void QProcessPrivate::startProcess() ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK); } +#if !defined(Q_OS_QNX) void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp) { ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored @@ -728,17 +732,17 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv Q_Q(QProcess); // copy the stdin socket (without closing on exec) - qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0); + qt_safe_dup2(stdinChannel.pipe[0], QT_FILENO(stdin), 0); // copy the stdout and stderr if asked to if (processChannelMode != QProcess::ForwardedChannels) { - qt_safe_dup2(stdoutChannel.pipe[1], fileno(stdout), 0); + qt_safe_dup2(stdoutChannel.pipe[1], QT_FILENO(stdout), 0); // merge stdout and stderr if asked to if (processChannelMode == QProcess::MergedChannels) { - qt_safe_dup2(fileno(stdout), fileno(stderr), 0); + qt_safe_dup2(QT_FILENO(stdout), QT_FILENO(stderr), 0); } else { - qt_safe_dup2(stderrChannel.pipe[1], fileno(stderr), 0); + qt_safe_dup2(stderrChannel.pipe[1], QT_FILENO(stderr), 0); } } @@ -807,6 +811,87 @@ bool QProcessPrivate::processStarted() return i <= 0; } +#else // Q_OS_QNX + +static pid_t doSpawn(int fd_count, int fd_map[], char **argv, char **envp, 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 + + 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)); + } + + return childPid; +} + +pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **envp) +{ + Q_Q(QProcess); + + 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; + } + + // enter the working directory + char *oldWorkingDir = 0; + char buff[PATH_MAX + 1]; + if (workingDir) { + oldWorkingDir = QT_GETCWD(buff, PATH_MAX + 1); + QT_CHDIR(workingDir); + } + + pid_t childPid = doSpawn(fd_count, fd_map, argv, envp, false); + + if (oldWorkingDir) + QT_CHDIR(oldWorkingDir); + + if (childPid != -1) { + q->setProcessState(QProcess::Running); + QMetaObject::invokeMethod(q, "_q_startupNotification", Qt::QueuedConnection); + } + + return childPid; +} + +bool QProcessPrivate::processStarted() +{ + return processState == QProcess::Running; +} + +bool QProcessPrivate::waitForStarted(int /*msecs*/) +{ + return processStarted(); +} +#endif // Q_OS_QNX + qint64 QProcessPrivate::bytesAvailableFromStdout() const { int nbytes = 0; @@ -924,6 +1009,7 @@ static int qt_timeout_value(int msecs, int elapsed) return timeout < 0 ? 0 : timeout; } +#if !defined(Q_OS_QNX) bool QProcessPrivate::waitForStarted(int msecs) { Q_Q(QProcess); @@ -951,6 +1037,7 @@ bool QProcessPrivate::waitForStarted(int msecs) #endif return startedEmitted; } +#endif // Q_OS_QNX bool QProcessPrivate::waitForReadyRead(int msecs) { @@ -972,8 +1059,10 @@ bool QProcessPrivate::waitForReadyRead(int msecs) int nfds = deathPipe[0]; FD_SET(deathPipe[0], &fdread); +#if !defined(Q_OS_QNX) if (processState == QProcess::Starting) add_fd(nfds, childStartedPipe[0], &fdread); +#endif if (stdoutChannel.pipe[0] != -1) add_fd(nfds, stdoutChannel.pipe[0], &fdread); @@ -994,10 +1083,12 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; } +#if !defined(Q_OS_QNX) if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { if (!_q_startupNotification()) return false; } +#endif bool readyReadEmitted = false; if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { @@ -1044,8 +1135,10 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) int nfds = deathPipe[0]; FD_SET(deathPipe[0], &fdread); +#if !defined(Q_OS_QNX) if (processState == QProcess::Starting) add_fd(nfds, childStartedPipe[0], &fdread); +#endif if (stdoutChannel.pipe[0] != -1) add_fd(nfds, stdoutChannel.pipe[0], &fdread); @@ -1068,10 +1161,12 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) return false; } +#if !defined(Q_OS_QNX) if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { if (!_q_startupNotification()) return false; } +#endif if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) return _q_canWrite(); @@ -1109,8 +1204,10 @@ bool QProcessPrivate::waitForFinished(int msecs) FD_ZERO(&fdread); FD_ZERO(&fdwrite); +#if !defined(Q_OS_QNX) if (processState == QProcess::Starting) add_fd(nfds, childStartedPipe[0], &fdread); +#endif if (stdoutChannel.pipe[0] != -1) add_fd(nfds, stdoutChannel.pipe[0], &fdread); @@ -1134,10 +1231,12 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; } +#if !defined(Q_OS_QNX) if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { if (!_q_startupNotification()) return false; } +#endif if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) _q_canWrite(); @@ -1199,6 +1298,47 @@ void QProcessPrivate::_q_notified() { } +#if defined(Q_OS_QNX) +bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) +{ + QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory); + + // enter the working directory + char *oldWorkingDir = 0; + char buff[PATH_MAX + 1]; + if (!encodedWorkingDirectory.isEmpty()) { + oldWorkingDir = QT_GETCWD(buff, PATH_MAX + 1); + QT_CHDIR(encodedWorkingDirectory.constData()); + } + + const int fd_count = 3; + int fd_map[fd_count] = { QT_FILENO(stdin), QT_FILENO(stdout), QT_FILENO(stderr) }; + + QList 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 raw_argv(new char*[argc + 1]); + for (int i = 0; i < argc; ++i) + raw_argv[i] = const_cast(enc_args.at(i).data()); + raw_argv[argc] = 0; + + char **envp = 0; // inherit environment + + pid_t childPid = doSpawn(fd_count, fd_map, raw_argv.data(), envp, true); + if (pid && childPid != -1) + *pid = childPid; + + if (oldWorkingDir) + QT_CHDIR(oldWorkingDir); + + return childPid != -1; +} + +#else + bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) { processManager()->start(); @@ -1212,7 +1352,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)); @@ -1224,7 +1364,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]); @@ -1312,6 +1452,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a qt_safe_close(pidPipe[0]); return success; } +#endif // Q_OS_QNX void QProcessPrivate::initializeProcessManager() { -- cgit v0.12