summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qprocess_unix.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/corelib/io/qprocess_unix.cpp
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'src/corelib/io/qprocess_unix.cpp')
-rw-r--r--src/corelib/io/qprocess_unix.cpp1380
1 files changed, 1380 insertions, 0 deletions
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
new file mode 100644
index 0000000..37173c8
--- /dev/null
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -0,0 +1,1380 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QPROCESS_DEBUG
+#include "qdebug.h"
+
+#ifndef QT_NO_PROCESS
+
+#if defined QPROCESS_DEBUG
+#include "qstring.h"
+#include <ctype.h>
+
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+QT_BEGIN_NAMESPACE
+static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(c)) {
+ out += c;
+ } else switch (c) {
+ case '\n': out += "\\n"; break;
+ case '\r': out += "\\r"; break;
+ case '\t': out += "\\t"; break;
+ default:
+ QString tmp;
+ tmp.sprintf("\\%o", c);
+ out += tmp.toLatin1();
+ }
+ }
+
+ if (len < maxSize)
+ out += "...";
+
+ return out;
+}
+QT_END_NAMESPACE
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qprocess.h"
+#include "qprocess_p.h"
+
+#ifdef Q_OS_MAC
+#include <private/qcore_mac_p.h>
+#endif
+
+#include <private/qcoreapplication_p.h>
+#include <private/qthread_p.h>
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qmutex.h>
+#include <qsemaphore.h>
+#include <qsocketnotifier.h>
+#include <qthread.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_INTEGRITY
+static inline char *strdup(const char *data)
+{
+ return qstrdup(data);
+}
+#endif
+
+static qint64 qt_native_read(int fd, char *data, qint64 maxlen)
+{
+ qint64 ret = 0;
+ do {
+ ret = ::read(fd, data, maxlen);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+static qint64 qt_native_write(int fd, const char *data, qint64 len)
+{
+ qint64 ret = 0;
+ do {
+ ret = ::write(fd, data, len);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+static void qt_native_close(int fd)
+{
+ int ret;
+ do {
+ ret = ::close(fd);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ int ret;
+ do {
+ ret = ::sigaction(signum, act, oldact);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_dup2(int oldfd, int newfd)
+{
+ int ret;
+ do {
+ ret = ::dup2(oldfd, newfd);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_chdir(const char *path)
+{
+ int ret;
+ do {
+ ret = ::chdir(path);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execve(const char *filename, char *const argv[],
+ char *const envp[])
+{
+ int ret;
+ do {
+ ret = ::execve(filename, argv, envp);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execv(const char *path, char *const argv[])
+{
+ int ret;
+ do {
+ ret = ::execv(path, argv);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execvp(const char *file, char *const argv[])
+{
+ int ret;
+ do {
+ ret = ::execvp(file, argv);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static int qt_qprocess_deadChild_pipe[2];
+static void (*qt_sa_old_sigchld_handler)(int) = 0;
+static void qt_sa_sigchld_handler(int signum)
+{
+ qt_native_write(qt_qprocess_deadChild_pipe[1], "", 1);
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "*** SIGCHLD\n");
+#endif
+
+ if (qt_sa_old_sigchld_handler && qt_sa_old_sigchld_handler != SIG_IGN)
+ qt_sa_old_sigchld_handler(signum);
+}
+
+struct QProcessInfo {
+ QProcess *process;
+ int deathPipe;
+ int exitResult;
+ pid_t pid;
+ int serialNumber;
+};
+
+class QProcessManager : public QThread
+{
+ Q_OBJECT
+public:
+ QProcessManager();
+ ~QProcessManager();
+
+ void run();
+ void catchDeadChildren();
+ void add(pid_t pid, QProcess *process);
+ void remove(QProcess *process);
+ void lock();
+ void unlock();
+
+private:
+ QMutex mutex;
+ QMap<int, QProcessInfo *> children;
+};
+
+Q_GLOBAL_STATIC(QProcessManager, processManager)
+
+QProcessManager::QProcessManager()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::QProcessManager()";
+#endif
+ // initialize the dead child pipe and make it non-blocking. in the
+ // extremely unlikely event that the pipe fills up, we do not under any
+ // circumstances want to block.
+ ::pipe(qt_qprocess_deadChild_pipe);
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFD, FD_CLOEXEC);
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFL,
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_GETFL) | O_NONBLOCK);
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFL,
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_GETFL) | O_NONBLOCK);
+
+ // set up the SIGCHLD handler, which writes a single byte to the dead
+ // child pipe every time a child dies.
+ struct sigaction oldAction;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = qt_sa_sigchld_handler;
+ action.sa_flags = SA_NOCLDSTOP;
+ qt_native_sigaction(SIGCHLD, &action, &oldAction);
+ if (oldAction.sa_handler != qt_sa_sigchld_handler)
+ qt_sa_old_sigchld_handler = oldAction.sa_handler;
+}
+
+QProcessManager::~QProcessManager()
+{
+ // notify the thread that we're shutting down.
+ qt_native_write(qt_qprocess_deadChild_pipe[1], "@", 1);
+ qt_native_close(qt_qprocess_deadChild_pipe[1]);
+ wait();
+
+ // on certain unixes, closing the reading end of the pipe will cause
+ // select in run() to block forever, rather than return with EBADF.
+ qt_native_close(qt_qprocess_deadChild_pipe[0]);
+
+ qt_qprocess_deadChild_pipe[0] = -1;
+ qt_qprocess_deadChild_pipe[1] = -1;
+
+ qDeleteAll(children.values());
+ children.clear();
+
+ struct sigaction oldAction;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = qt_sa_old_sigchld_handler;
+ action.sa_flags = SA_NOCLDSTOP;
+ qt_native_sigaction(SIGCHLD, &action, &oldAction);
+ if (oldAction.sa_handler != qt_sa_sigchld_handler) {
+ qt_native_sigaction(SIGCHLD, &oldAction, 0);
+ }
+}
+
+void QProcessManager::run()
+{
+ forever {
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(qt_qprocess_deadChild_pipe[0], &readset);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::run() waiting for children to die";
+#endif
+
+ // block forever, or until activity is detected on the dead child
+ // pipe. the only other peers are the SIGCHLD signal handler, and the
+ // QProcessManager destructor.
+ int nselect = select(qt_qprocess_deadChild_pipe[0] + 1, &readset, 0, 0, 0);
+ if (nselect < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ // empty only one byte from the pipe, even though several SIGCHLD
+ // signals may have been delivered in the meantime, to avoid race
+ // conditions.
+ char c;
+ if (qt_native_read(qt_qprocess_deadChild_pipe[0], &c, 1) < 0 || c == '@')
+ break;
+
+ // catch any and all children that we can.
+ catchDeadChildren();
+ }
+}
+
+void QProcessManager::catchDeadChildren()
+{
+ QMutexLocker locker(&mutex);
+
+ // try to catch all children whose pid we have registered, and whose
+ // deathPipe is still valid (i.e, we have not already notified it).
+ QMap<int, QProcessInfo *>::Iterator it = children.begin();
+ while (it != children.end()) {
+ // notify all children that they may have died. they need to run
+ // waitpid() in their own thread.
+ QProcessInfo *info = it.value();
+ qt_native_write(info->deathPipe, "", 1);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::run() sending death notice to" << info->process;
+#endif
+ ++it;
+ }
+}
+
+static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+void QProcessManager::add(pid_t pid, QProcess *process)
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::add() adding pid" << pid << "process" << process;
+#endif
+
+ // insert a new info structure for this process
+ QProcessInfo *info = new QProcessInfo;
+ info->process = process;
+ info->deathPipe = process->d_func()->deathPipe[1];
+ info->exitResult = 0;
+ info->pid = pid;
+
+ int serial = idCounter.fetchAndAddRelaxed(1);
+ process->d_func()->serial = serial;
+ children.insert(serial, info);
+}
+
+void QProcessManager::remove(QProcess *process)
+{
+ QMutexLocker locker(&mutex);
+
+ int serial = process->d_func()->serial;
+ QProcessInfo *info = children.value(serial);
+ if (!info)
+ return;
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::remove() removing pid" << info->pid << "process" << info->process;
+#endif
+
+ children.remove(serial);
+ delete info;
+}
+
+void QProcessManager::lock()
+{
+ mutex.lock();
+}
+
+void QProcessManager::unlock()
+{
+ mutex.unlock();
+}
+
+static void qt_create_pipe(int *pipe)
+{
+ if (pipe[0] != -1)
+ qt_native_close(pipe[0]);
+ if (pipe[1] != -1)
+ qt_native_close(pipe[1]);
+#ifdef Q_OS_IRIX
+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) == -1) {
+ qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s",
+ pipe, qPrintable(qt_error_string(errno)));
+ }
+#else
+ if (::pipe(pipe) != 0) {
+ qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s",
+ pipe, qPrintable(qt_error_string(errno)));
+ }
+#endif
+ ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC);
+}
+
+void QProcessPrivate::destroyPipe(int *pipe)
+{
+ if (pipe[1] != -1) {
+ qt_native_close(pipe[1]);
+ pipe[1] = -1;
+ }
+ if (pipe[0] != -1) {
+ qt_native_close(pipe[0]);
+ pipe[0] = -1;
+ }
+}
+
+/*
+ Create the pipes to a QProcessPrivate::Channel.
+
+ This function must be called in order: stdin, stdout, stderr
+*/
+bool QProcessPrivate::createChannel(Channel &channel)
+{
+ Q_Q(QProcess);
+
+ if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
+ channel.pipe[0] = -1;
+ channel.pipe[1] = -1;
+ return true;
+ }
+
+ if (channel.type == Channel::Normal) {
+ // we're piping this channel to our own process
+ qt_create_pipe(channel.pipe);
+
+ // create the socket notifiers
+ if (threadData->eventDispatcher) {
+ if (&channel == &stdinChannel) {
+ channel.notifier = new QSocketNotifier(channel.pipe[1],
+ QSocketNotifier::Write, q);
+ channel.notifier->setEnabled(false);
+ QObject::connect(channel.notifier, SIGNAL(activated(int)),
+ q, SLOT(_q_canWrite()));
+ } else {
+ channel.notifier = new QSocketNotifier(channel.pipe[0],
+ QSocketNotifier::Read, q);
+ const char *receiver;
+ if (&channel == &stdoutChannel)
+ receiver = SLOT(_q_canReadStandardOutput());
+ else
+ receiver = SLOT(_q_canReadStandardError());
+ QObject::connect(channel.notifier, SIGNAL(activated(int)),
+ q, receiver);
+ }
+ }
+
+ return true;
+ } else if (channel.type == Channel::Redirect) {
+ // we're redirecting the channel to/from a file
+ QByteArray fname = QFile::encodeName(channel.file);
+
+ if (&channel == &stdinChannel) {
+ // try to open in read-only mode
+ channel.pipe[1] = -1;
+ if ( (channel.pipe[0] = QT_OPEN(fname, O_RDONLY)) != -1)
+ return true; // success
+
+ q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
+ } else {
+ int mode = O_WRONLY | O_CREAT;
+ if (channel.append)
+ mode |= O_APPEND;
+ else
+ mode |= O_TRUNC;
+
+ channel.pipe[0] = -1;
+ if ( (channel.pipe[1] = QT_OPEN(fname, mode, 0666)) != -1)
+ return true; // success
+
+ q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
+ }
+
+ // could not open file
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+ cleanup();
+ return false;
+ } else {
+ Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
+
+ Channel *source;
+ Channel *sink;
+
+ if (channel.type == Channel::PipeSource) {
+ // we are the source
+ source = &channel;
+ sink = &channel.process->stdinChannel;
+
+ Q_ASSERT(source == &stdoutChannel);
+ Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
+ } else {
+ // we are the sink;
+ source = &channel.process->stdoutChannel;
+ sink = &channel;
+
+ Q_ASSERT(sink == &stdinChannel);
+ Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
+ }
+
+ if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) {
+ // already created, do nothing
+ return true;
+ } else {
+ Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE);
+ Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE);
+
+ Q_PIPE pipe[2] = { -1, -1 };
+ qt_create_pipe(pipe);
+ sink->pipe[0] = pipe[0];
+ source->pipe[1] = pipe[1];
+
+ return true;
+ }
+ }
+}
+
+static char **_q_dupEnvironment(const QStringList &environment, int *envc)
+{
+ // if LD_LIBRARY_PATH exists in the current environment, but
+ // not in the environment list passed by the programmer, then
+ // copy it over.
+#if defined(Q_OS_MAC)
+ static const char libraryPath[] = "DYLD_LIBRARY_PATH";
+#else
+ static const char libraryPath[] = "LD_LIBRARY_PATH";
+#endif
+ const QString libraryPathString = QLatin1String(libraryPath);
+ QStringList env = environment;
+ QStringList matches = env.filter(
+ QRegExp(QLatin1Char('^') + libraryPathString + QLatin1Char('=')));
+ const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath));
+ if (matches.isEmpty() && !envLibraryPath.isEmpty()) {
+ QString entry = libraryPathString;
+ entry += QLatin1Char('=');
+ entry += envLibraryPath;
+ env << libraryPathString + QLatin1Char('=') + envLibraryPath;
+ }
+
+ char **envp = new char *[env.count() + 1];
+ envp[env.count()] = 0;
+
+ for (int j = 0; j < env.count(); ++j) {
+ QString item = env.at(j);
+ envp[j] = ::strdup(item.toLocal8Bit().constData());
+ }
+
+ *envc = env.count();
+ 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
+
+void QProcessPrivate::startProcess()
+{
+ Q_Q(QProcess);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::startProcess()");
+#endif
+
+ processManager()->start();
+
+ // Initialize pipes
+ qt_create_pipe(childStartedPipe);
+ if (threadData->eventDispatcher) {
+ startupSocketNotifier = new QSocketNotifier(childStartedPipe[0],
+ QSocketNotifier::Read, q);
+ QObject::connect(startupSocketNotifier, SIGNAL(activated(int)),
+ q, SLOT(_q_startupNotification()));
+ }
+
+ qt_create_pipe(deathPipe);
+ ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC);
+ if (threadData->eventDispatcher) {
+ deathNotifier = new QSocketNotifier(deathPipe[0],
+ QSocketNotifier::Read, q);
+ QObject::connect(deathNotifier, SIGNAL(activated(int)),
+ q, SLOT(_q_processDied()));
+ }
+
+ if (!createChannel(stdinChannel) ||
+ !createChannel(stdoutChannel) ||
+ !createChannel(stderrChannel))
+ return;
+
+ // Start the process (platform dependent)
+ q->setProcessState(QProcess::Starting);
+
+ // Create argument list with right number of elements, and set the final
+ // one to 0.
+ char **argv = new char *[arguments.count() + 2];
+ argv[arguments.count() + 1] = 0;
+
+ // Encode the program name.
+ QByteArray encodedProgramName = QFile::encodeName(program);
+#ifdef Q_OS_MAC
+ // allow invoking of .app bundles on the Mac.
+ QFileInfo fileInfo(QString::fromUtf8(encodedProgramName.constData()));
+ if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) {
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
+ QCFString(fileInfo.absoluteFilePath()),
+ kCFURLPOSIXPathStyle, true);
+ {
+ // CFBundle is not reentrant, since CFBundleCreate might return a reference
+ // to a cached bundle object. Protect the bundle calls with a mutex lock.
+ QMutexLocker lock(cfbundleMutex());
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
+ url = CFBundleCopyExecutableURL(bundle);
+ }
+ if (url) {
+ QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ encodedProgramName += "/Contents/MacOS/" + static_cast<QString>(str).toUtf8();
+ }
+ }
+#endif
+
+ // Add the program name to the argument list.
+ char *dupProgramName = ::strdup(encodedProgramName.constData());
+ argv[0] = dupProgramName;
+
+ // Add every argument to the list
+ for (int i = 0; i < arguments.count(); ++i) {
+ QString arg = arguments.at(i);
+#ifdef Q_OS_MAC
+ // Mac OS X uses UTF8 for exec, regardless of the system locale.
+ argv[i + 1] = ::strdup(arg.toUtf8().constData());
+#else
+ argv[i + 1] = ::strdup(arg.toLocal8Bit().constData());
+#endif
+ }
+
+ // Duplicate the environment.
+ int envc = 0;
+ char **envp = _q_dupEnvironment(environment, &envc);
+
+ // 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();
+ }
+
+ // If the program does not specify a path, generate a list of possible
+ // locations for the binary using the PATH environment variable.
+ char **path = 0;
+ int pathc = 0;
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString pathEnv = QString::fromLocal8Bit(::getenv("PATH"));
+ if (!pathEnv.isEmpty()) {
+ QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
+ if (!pathEntries.isEmpty()) {
+ pathc = pathEntries.size();
+ path = new char *[pathc + 1];
+ path[pathc] = 0;
+
+ for (int k = 0; k < pathEntries.size(); ++k) {
+ QByteArray tmp = QFile::encodeName(pathEntries.at(k));
+ if (!tmp.endsWith('/')) tmp += '/';
+ tmp += encodedProgramName;
+ path[k] = ::strdup(tmp.constData());
+ }
+ }
+ }
+ }
+
+ // Start the process manager, and fork off the child process.
+ processManager()->lock();
+ pid_t childPid = qt_fork();
+ int lastForkErrno = errno;
+ if (childPid != 0) {
+ // Clean up duplicated memory.
+ free(dupProgramName);
+ for (int i = 1; i <= arguments.count(); ++i)
+ free(argv[i]);
+ for (int i = 0; i < envc; ++i)
+ free(envp[i]);
+ for (int i = 0; i < pathc; ++i)
+ free(path[i]);
+ delete [] argv;
+ delete [] envp;
+ delete [] path;
+ }
+ if (childPid < 0) {
+ // Cleanup, report error and return
+#if defined (QPROCESS_DEBUG)
+ qDebug("qt_fork failed: %s", qt_error_string(lastForkErrno));
+#endif
+ processManager()->unlock();
+ q->setProcessState(QProcess::NotRunning);
+ processError = QProcess::FailedToStart;
+ q->setErrorString(QProcess::tr("Resource error (fork failure): %1").arg(qt_error_string(lastForkErrno)));
+ emit q->error(processError);
+ cleanup();
+ return;
+ }
+
+ // Start the child.
+ if (childPid == 0) {
+ execChild(workingDirPtr, path, argv, envp);
+ ::_exit(-1);
+ }
+
+ // 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.
+ processManager()->add(childPid, q);
+ pid = Q_PID(childPid);
+ processManager()->unlock();
+
+ // 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);
+ qt_native_close(childStartedPipe[1]);
+ childStartedPipe[1] = -1;
+
+ if (stdinChannel.pipe[0] != -1) {
+ qt_native_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);
+
+ if (stdoutChannel.pipe[1] != -1) {
+ qt_native_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);
+
+ if (stderrChannel.pipe[1] != -1) {
+ qt_native_close(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = -1;
+ }
+ if (stderrChannel.pipe[0] != -1)
+ ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
+}
+
+void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp)
+{
+ ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
+
+ Q_Q(QProcess);
+
+ // copy the stdin socket
+ qt_native_dup2(stdinChannel.pipe[0], fileno(stdin));
+
+ // copy the stdout and stderr if asked to
+ if (processChannelMode != QProcess::ForwardedChannels) {
+ qt_native_dup2(stdoutChannel.pipe[1], fileno(stdout));
+
+ // merge stdout and stderr if asked to
+ if (processChannelMode == QProcess::MergedChannels) {
+ qt_native_dup2(fileno(stdout), fileno(stderr));
+ } else {
+ qt_native_dup2(stderrChannel.pipe[1], fileno(stderr));
+ }
+ }
+
+ // make sure this fd is closed if execvp() succeeds
+ qt_native_close(childStartedPipe[0]);
+ ::fcntl(childStartedPipe[1], F_SETFD, FD_CLOEXEC);
+
+ // enter the working directory
+ if (workingDir)
+ qt_native_chdir(workingDir);
+
+ // this is a virtual call, and it base behavior is to do nothing.
+ q->setupChildProcess();
+
+ // execute the process
+ if (environment.isEmpty()) {
+ qt_native_execvp(argv[0], argv);
+ } else {
+ if (path) {
+ char **arg = path;
+ while (*arg) {
+ argv[0] = *arg;
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() searching / starting %s\n", argv[0]);
+#endif
+ qt_native_execve(argv[0], argv, envp);
+ ++arg;
+ }
+ } else {
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
+#endif
+ qt_native_execve(argv[0], argv, envp);
+ }
+ }
+
+ // notify failure
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() failed, notifying parent process\n");
+#endif
+ qt_native_write(childStartedPipe[1], "", 1);
+ qt_native_close(childStartedPipe[1]);
+ childStartedPipe[1] = -1;
+}
+
+bool QProcessPrivate::processStarted()
+{
+ char c;
+ int i = qt_native_read(childStartedPipe[0], &c, 1);
+ if (startupSocketNotifier) {
+ startupSocketNotifier->setEnabled(false);
+ startupSocketNotifier->deleteLater();
+ startupSocketNotifier = 0;
+ }
+ qt_native_close(childStartedPipe[0]);
+ childStartedPipe[0] = -1;
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::processStarted() == %s", i <= 0 ? "true" : "false");
+#endif
+ return i <= 0;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStdout() const
+{
+ size_t nbytes = 0;
+ qint64 available = 0;
+ if (::ioctl(stdoutChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0)
+ available = (qint64) *((int *) &nbytes);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::bytesAvailableFromStdout() == %lld", available);
+#endif
+ return available;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStderr() const
+{
+ size_t nbytes = 0;
+ qint64 available = 0;
+ if (::ioctl(stderrChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0)
+ available = (qint64) *((int *) &nbytes);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::bytesAvailableFromStderr() == %lld", available);
+#endif
+ return available;
+}
+
+qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
+{
+ qint64 bytesRead = qt_native_read(stdoutChannel.pipe[0], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::readFromStdout(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);
+#endif
+ return bytesRead;
+}
+
+qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
+{
+ qint64 bytesRead = qt_native_read(stderrChannel.pipe[0], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::readFromStderr(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);
+#endif
+ return bytesRead;
+}
+
+static void qt_ignore_sigpipe()
+{
+ // Set to ignore SIGPIPE once only.
+ static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (atom.testAndSetRelaxed(0, 1)) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+ }
+}
+
+qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
+{
+ qt_ignore_sigpipe();
+
+ qint64 written = qt_native_write(stdinChannel.pipe[1], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, maxlen, 16).constData(), maxlen, written);
+#endif
+ return written;
+}
+
+void QProcessPrivate::terminateProcess()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::killProcess()");
+#endif
+ if (pid)
+ ::kill(pid_t(pid), SIGTERM);
+}
+
+void QProcessPrivate::killProcess()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::killProcess()");
+#endif
+ if (pid)
+ ::kill(pid_t(pid), SIGKILL);
+}
+
+static int qt_native_select(fd_set *fdread, fd_set *fdwrite, int timeout)
+{
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ int ret;
+ do {
+ ret = select(FD_SETSIZE, fdread, fdwrite, 0, timeout < 0 ? 0 : &tv);
+ } while (ret < 0 && (errno == EINTR));
+ return ret;
+}
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+bool QProcessPrivate::waitForStarted(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted(%d) waiting for child to start (fd = %d)", msecs,
+ childStartedPipe[0]);
+#endif
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(childStartedPipe[0], &fds);
+ int ret;
+ do {
+ ret = qt_native_select(&fds, 0, msecs);
+ } while (ret < 0 && errno == EINTR);
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted(%d) == false (timed out)", msecs);
+#endif
+ return false;
+ }
+
+ bool startedEmitted = _q_startupNotification();
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted() == %s", startedEmitted ? "true" : "false");
+#endif
+ return startedEmitted;
+}
+
+bool QProcessPrivate::waitForReadyRead(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForReadyRead(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ forever {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+
+ bool readyReadEmitted = false;
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) {
+ bool canRead = _q_canReadStandardOutput();
+ if (processChannel == QProcess::StandardOutput && canRead)
+ readyReadEmitted = true;
+ }
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) {
+ bool canRead = _q_canReadStandardError();
+ if (processChannel == QProcess::StandardError && canRead)
+ readyReadEmitted = true;
+ }
+ if (readyReadEmitted)
+ return true;
+
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ _q_canWrite();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return false;
+ }
+ }
+ return false;
+}
+
+bool QProcessPrivate::waitForBytesWritten(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForBytesWritten(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ while (!writeBuffer.isEmpty()) {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ return _q_canWrite();
+
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread))
+ _q_canReadStandardOutput();
+
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread))
+ _q_canReadStandardError();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool QProcessPrivate::waitForFinished(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ forever {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ if (processState == QProcess::Running)
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ _q_canWrite();
+
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread))
+ _q_canReadStandardOutput();
+
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread))
+ _q_canReadStandardError();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QProcessPrivate::waitForWrite(int msecs)
+{
+ fd_set fdwrite;
+ FD_ZERO(&fdwrite);
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int ret;
+ do {
+ ret = qt_native_select(0, &fdwrite, msecs < 0 ? 0 : msecs) == 1;
+ } while (ret < 0 && errno == EINTR);
+ return ret == 1;
+}
+
+void QProcessPrivate::findExitCode()
+{
+ Q_Q(QProcess);
+ processManager()->remove(q);
+}
+
+bool QProcessPrivate::waitForDeadChild()
+{
+ Q_Q(QProcess);
+
+ // read a byte from the death pipe
+ char c;
+ qt_native_read(deathPipe[0], &c, 1);
+
+ // check if our process is dead
+ int exitStatus;
+ pid_t waitResult = 0;
+ do {
+ waitResult = waitpid(pid_t(pid), &exitStatus, WNOHANG);
+ } while ((waitResult == -1 && errno == EINTR));
+ if (waitResult > 0) {
+ processManager()->remove(q);
+ crashed = !WIFEXITED(exitStatus);
+ exitCode = WEXITSTATUS(exitStatus);
+#if defined QPROCESS_DEBUG
+ qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode"
+ << exitCode << ", crashed?" << crashed;
+#endif
+ return true;
+ }
+#if defined QPROCESS_DEBUG
+ qDebug() << "QProcessPrivate::waitForDeadChild() not dead!";
+#endif
+ return false;
+}
+
+void QProcessPrivate::_q_notified()
+{
+}
+
+/*! \internal
+ */
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+ processManager()->start();
+
+ QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
+
+ // To catch the startup of the child
+ int startedPipe[2];
+ ::pipe(startedPipe);
+ // To communicate the pid of the child
+ int pidPipe[2];
+ ::pipe(pidPipe);
+
+ pid_t childPid = qt_fork();
+ if (childPid == 0) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ ::setsid();
+
+ qt_native_close(startedPipe[0]);
+ qt_native_close(pidPipe[0]);
+
+ pid_t doubleForkPid = qt_fork();
+ if (doubleForkPid == 0) {
+ ::fcntl(startedPipe[1], F_SETFD, FD_CLOEXEC);
+ qt_native_close(pidPipe[1]);
+
+ if (!encodedWorkingDirectory.isEmpty())
+ qt_native_chdir(encodedWorkingDirectory.constData());
+
+ char **argv = new char *[arguments.size() + 2];
+ for (int i = 0; i < arguments.size(); ++i) {
+#ifdef Q_OS_MAC
+ argv[i + 1] = ::strdup(arguments.at(i).toUtf8().constData());
+#else
+ argv[i + 1] = ::strdup(arguments.at(i).toLocal8Bit().constData());
+#endif
+ }
+ argv[arguments.size() + 1] = 0;
+
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString path = QString::fromLocal8Bit(::getenv("PATH"));
+ if (!path.isEmpty()) {
+ QStringList pathEntries = path.split(QLatin1Char(':'));
+ for (int k = 0; k < pathEntries.size(); ++k) {
+ QByteArray tmp = QFile::encodeName(pathEntries.at(k));
+ if (!tmp.endsWith('/')) tmp += '/';
+ tmp += QFile::encodeName(program);
+ argv[0] = tmp.data();
+ qt_native_execv(argv[0], argv);
+ }
+ }
+ } else {
+ QByteArray tmp = QFile::encodeName(program);
+ argv[0] = tmp.data();
+ qt_native_execv(argv[0], argv);
+ }
+
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ // '\1' means execv failed
+ char c = '\1';
+ qt_native_write(startedPipe[1], &c, 1);
+ qt_native_close(startedPipe[1]);
+ ::_exit(1);
+ } else if (doubleForkPid == -1) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ // '\2' means internal error
+ char c = '\2';
+ qt_native_write(startedPipe[1], &c, 1);
+ }
+
+ qt_native_close(startedPipe[1]);
+ qt_native_write(pidPipe[1], (const char *)&doubleForkPid, sizeof(pid_t));
+ qt_native_chdir("/");
+ ::_exit(1);
+ }
+
+ qt_native_close(startedPipe[1]);
+ qt_native_close(pidPipe[1]);
+
+ if (childPid == -1) {
+ qt_native_close(startedPipe[0]);
+ qt_native_close(pidPipe[0]);
+ return false;
+ }
+
+ char reply = '\0';
+ int startResult = qt_native_read(startedPipe[0], &reply, 1);
+ int result;
+ qt_native_close(startedPipe[0]);
+ while (::waitpid(childPid, &result, 0) == -1 && errno == EINTR)
+ { }
+ bool success = (startResult != -1 && reply == '\0');
+ if (success && pid) {
+ pid_t actualPid = 0;
+ if (qt_native_read(pidPipe[0], (char *)&actualPid, sizeof(pid_t)) == sizeof(pid_t)) {
+ *pid = actualPid;
+ } else {
+ *pid = 0;
+ }
+ }
+ qt_native_close(pidPipe[0]);
+ return success;
+}
+
+void QProcessPrivate::initializeProcessManager()
+{
+ (void) processManager();
+}
+
+QT_END_NAMESPACE
+
+#include "qprocess_unix.moc"
+
+#endif // QT_NO_PROCESS