src/corelib/io/qprocess_symbian.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/io/qprocess_symbian.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1042 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QPROCESS_DEBUG
+
+#ifdef QPROCESS_DEBUG
+#include "qdebug.h"
+#define QPROCESS_DEBUG_PRINT(args...) qDebug(args);
+#else
+#define QPROCESS_DEBUG_PRINT(args...)
+#endif
+
+#ifndef QT_NO_PROCESS
+
+#define QPROCESS_ASSERT(check, panicReason, args...) \
+    if (!(check)) { \
+        qWarning(args); \
+        User::Panic(KQProcessPanic, panicReason); \
+    }
+
+#include <exception>
+#include <e32base.h>
+#include <e32std.h>
+#include <stdio.h>
+#include "qplatformdefs.h"
+
+#include "qdir.h"
+#include "qstring.h"
+#include "qprocess.h"
+#include "qprocess_p.h"
+#include "qeventdispatcher_symbian_p.h"
+
+#include <private/qthread_p.h>
+#include <qmutex.h>
+#include <qmap.h>
+#include <qsocketnotifier.h>
+
+#include <errno.h>
+
+
+QT_BEGIN_NAMESPACE
+
+_LIT(KQProcessManagerThreadName, "QProcManThread");
+_LIT(KQProcessPanic, "QPROCESS");
+enum TQProcessPanic {
+    EProcessManagerMediatorRunError         = 1,
+    EProcessManagerMediatorInactive         = 2,
+    EProcessManagerMediatorNotPending       = 3,
+    EProcessManagerMediatorInvalidCmd       = 4,
+    EProcessManagerMediatorCreationFailed   = 5,
+    EProcessManagerMediatorThreadOpenFailed = 6,
+    EProcessManagerMediatorNullObserver     = 7,
+    EProcessActiveRunError                  = 10,
+    EProcessActiveNullParameter             = 11,
+    EProcessManagerMutexCreationFail        = 20,
+    EProcessManagerThreadCreationFail       = 21,
+    EProcessManagerSchedulerCreationFail    = 22,
+    EProcessManagerNullParam                = 23
+};
+
+// Forward declarations
+class QProcessManager;
+
+
+// Active object to listen for child process death
+class QProcessActive : public CActive
+{
+public:
+    static QProcessActive *construct(QProcess *process,
+                                     RProcess **proc,
+                                     int serial,
+                                     int deathPipe);
+
+    virtual ~QProcessActive();
+
+    void start();
+    void stop();
+
+    bool error();
+
+protected:
+
+    // Inherited from CActive
+    void RunL();
+    TInt RunError(TInt aError);
+    void DoCancel();
+
+    QProcessActive();
+
+private:
+
+    QProcess *process;
+    RProcess **pproc;
+    int serial;
+    int deathPipe;
+    bool errorValue;
+};
+
+// Active object to communicate synchronously with process manager thread
+class QProcessManagerMediator : public CActive
+{
+public:
+    static QProcessManagerMediator *construct();
+
+    virtual ~QProcessManagerMediator();
+
+    bool add(QProcessActive *processObserver);
+    void remove(QProcessActive *processObserver);
+    void terminate();
+
+protected:
+
+    enum Commands {
+        ENoCommand,
+        EAdd,
+        ERemove,
+        ETerminate
+    };
+
+    // Inherited from CActive
+    void RunL();
+    TInt RunError(TInt aError);
+    void DoCancel();
+
+    QProcessManagerMediator();
+
+    bool notify(QProcessActive *processObserver, Commands command);
+
+private:
+    QProcessActive *currentObserver;
+    Commands currentCommand;
+
+    RThread processManagerThread;
+};
+
+// Process manager manages child process death listeners.
+//
+// Note: Because QProcess can be used outside event loop, we cannot be guaranteed
+// an active scheduler exists for us to add our process death listener objects.
+// We can't just install active scheduler on the calling thread, as that would block it
+// if we want to actually use it, so a separate manager thread is required.
+class QProcessManager
+{
+public:
+    QProcessManager();
+    ~QProcessManager();
+
+    void startThread();
+
+    TInt run(void *param);
+    bool add(QProcess *process);
+    void remove(QProcess *process);
+
+    inline void setMediator(QProcessManagerMediator *newMediator) {
+        mediator = newMediator;
+    };
+
+private:
+    inline void lock() {
+        managerMutex.Wait();
+    };
+    inline void unlock() {
+        managerMutex.Signal();
+    };
+
+    QMap<int, QProcessActive *> children;
+    QProcessManagerMediator *mediator;
+    RMutex managerMutex;
+    bool threadStarted;
+    RThread managerThread;
+};
+
+static bool qt_rprocess_running(RProcess *proc)
+{
+    if (proc && proc->Handle()) {
+        TExitType et = proc->ExitType();
+        if (et == EExitPending)
+            return true;
+    }
+
+    return false;
+}
+
+static void qt_create_symbian_commandline(const QStringList &arguments, QString &commandLine)
+{
+    for (int i = 0; i < arguments.size(); ++i) {
+        QString tmp = arguments.at(i);
+        // in the case of \" already being in the string the \ must also be escaped
+        tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\""));
+        // escape a single " because the arguments will be parsed
+        tmp.replace(QLatin1String("\""), QLatin1String("\\\""));
+        if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
+            // The argument must not end with a \ since this would be interpreted
+            // as escaping the quote -- rather put the \ behind the quote: e.g.
+            // rather use "foo"\ than "foo\"
+            QString endQuote(QLatin1String("\""));
+            int i = tmp.length();
+            while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) {
+                --i;
+                endQuote += QLatin1String("\\");
+            }
+            commandLine += QLatin1String(" \"") + tmp.left(i) + endQuote;
+        } else {
+            commandLine += QLatin1Char(' ') + tmp;
+        }
+    }
+}
+
+static TInt qt_create_symbian_process(RProcess **proc, const QString &programName, const QStringList &arguments)
+{
+    RProcess *newProc = NULL;
+    newProc = new RProcess();
+
+    if (!newProc)
+        return KErrNoMemory;
+
+    QString commandLine;
+    qt_create_symbian_commandline(arguments, commandLine);
+
+    TPtrC program_ptr(reinterpret_cast<const TText*>(programName.constData()));
+    TPtrC cmdline_ptr(reinterpret_cast<const TText*>(commandLine.constData()));
+
+    TInt err = newProc->Create(program_ptr, cmdline_ptr);
+
+    if (err == KErrNotFound) {
+        // Strip path from program name and try again (i.e. try from default location "\sys\bin")
+        int index = programName.lastIndexOf(QDir::separator());
+        int index2 = programName.lastIndexOf(QChar(QLatin1Char('/')));
+        index = qMax(index, index2);
+
+        if (index != -1 && programName.length() >= index) {
+            QString strippedName;
+            strippedName = programName.mid(index + 1);
+            QPROCESS_DEBUG_PRINT("qt_create_symbian_process() Executable '%s' not found, trying stripped version '%s'",
+                                 qPrintable(programName), qPrintable(strippedName));
+
+            TPtrC stripped_ptr(reinterpret_cast<const TText*>(strippedName.constData()));
+            err = newProc->Create(stripped_ptr, cmdline_ptr);
+
+            if (err != KErrNone) {
+                QPROCESS_DEBUG_PRINT("qt_create_symbian_process() Unable to create process '%s': %d",
+                                     qPrintable(strippedName), err);
+            }
+        }
+    }
+
+    if (err == KErrNone)
+        *proc = newProc;
+    else
+        delete newProc;
+
+    return err;
+}
+
+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);
+
+    QPROCESS_DEBUG_PRINT("qt_native_read(): fd: %d, result: %d, errno = %d", fd, (int)ret, errno);
+
+    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);
+
+    QPROCESS_DEBUG_PRINT("qt_native_write(): fd: %d, result: %d, errno = %d", fd, (int)ret, errno);
+
+    return ret;
+}
+
+static void qt_native_close(int fd)
+{
+    int ret;
+    do {
+        ret = ::close(fd);
+    } while (ret == -1 && errno == EINTR);
+}
+
+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]);
+    if (::pipe(pipe) != 0) {
+        qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s",
+                 pipe, qPrintable(qt_error_string(errno)));
+    } else {
+        QPROCESS_DEBUG_PRINT("qt_create_pipe(): Created pipe %d - %d", pipe[0], pipe[1]);
+    }
+}
+
+// Called from ProcessManagerThread
+QProcessActive *QProcessActive::construct(QProcess *process,
+                                          RProcess **proc,
+                                          int serial,
+                                          int deathPipe)
+{
+    QPROCESS_ASSERT((process || proc || *proc),
+                    EProcessActiveNullParameter,
+                    "QProcessActive::construct(): process (0x%x), proc (0x%x) or *proc == NULL, not creating an instance", process, proc)
+
+    QProcessActive *newInstance = new QProcessActive();
+
+    if (!newInstance) {
+        QPROCESS_DEBUG_PRINT("QProcessActive::construct(): Failed to create new instance");
+    } else {
+        newInstance->process = process;
+        newInstance->pproc = proc;
+        newInstance->serial = serial;
+        newInstance->deathPipe = deathPipe;
+        newInstance->errorValue = false;
+    }
+
+    return newInstance;
+}
+
+// Called from ProcessManagerThread
+QProcessActive::QProcessActive()
+        : CActive(CActive::EPriorityStandard)
+{
+    // Nothing to do
+}
+
+// Called from ProcessManagerThread
+QProcessActive::~QProcessActive()
+{
+    process = NULL;
+    pproc = NULL;
+}
+
+// Called from ProcessManagerThread
+void QProcessActive::start()
+{
+    if (qt_rprocess_running(*pproc)) {
+        CActiveScheduler::Add(this);
+        (*pproc)->Logon(iStatus);
+        SetActive();
+        QPROCESS_DEBUG_PRINT("QProcessActive::start(): Started monitoring for process exit.");
+    } else {
+        QPROCESS_DEBUG_PRINT("QProcessActive::start(): Process doesn't exist or is already dead");
+        // Assume process has already died
+        qt_native_write(deathPipe, "", 1);
+        errorValue = true;
+    }
+}
+
+// Called from ProcessManagerThread
+void QProcessActive::stop()
+{
+    QPROCESS_DEBUG_PRINT("QProcessActive::stop()");
+
+    // Remove this from scheduler (also cancels the request)
+    Deque();
+}
+
+bool QProcessActive::error()
+{
+    return errorValue;
+}
+
+// Called from ProcessManagerThread
+void QProcessActive::RunL()
+{
+    // If this method gets executed, the monitored process has died
+
+    // Notify main thread
+    qt_native_write(deathPipe, "", 1);
+    QPROCESS_DEBUG_PRINT("QProcessActive::RunL() sending death notice to %d", deathPipe);
+}
+
+// Called from ProcessManagerThread
+TInt QProcessActive::RunError(TInt aError)
+{
+    Q_UNUSED(aError);
+    // Handle RunL leave (should never happen)
+    QPROCESS_ASSERT(0, EProcessActiveRunError, "QProcessActive::RunError(): Should never get here!")
+    return 0;
+}
+
+// Called from ProcessManagerThread
+void QProcessActive::DoCancel()
+{
+    QPROCESS_DEBUG_PRINT("QProcessActive::DoCancel()");
+
+    if (qt_rprocess_running(*pproc)) {
+        (*pproc)->LogonCancel(iStatus);
+        QPROCESS_DEBUG_PRINT("QProcessActive::DoCancel(): Stopped monitoring for process exit.");
+    } else {
+        QPROCESS_DEBUG_PRINT("QProcessActive::DoCancel(): Process doesn't exist");
+    }
+}
+
+
+// Called from ProcessManagerThread
+QProcessManagerMediator *QProcessManagerMediator::construct()
+{
+    QProcessManagerMediator *newInstance = new QProcessManagerMediator;
+    TInt err(KErrNone);
+
+    newInstance->currentCommand = ENoCommand;
+    newInstance->currentObserver = NULL;
+
+    if (newInstance) {
+        err = newInstance->processManagerThread.Open(newInstance->processManagerThread.Id());
+        QPROCESS_ASSERT((err == KErrNone),
+                        EProcessManagerMediatorThreadOpenFailed,
+                        "QProcessManagerMediator::construct(): Failed to open processManagerThread (err:%d)", err)
+    } else {
+        QPROCESS_ASSERT(0,
+                        EProcessManagerMediatorCreationFailed,
+                        "QProcessManagerMediator::construct(): Failed to open construct mediator")
+    }
+
+    // Activate mediator
+    CActiveScheduler::Add(newInstance);
+    newInstance->iStatus = KRequestPending;
+    newInstance->SetActive();
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::construct(): new instance successfully created and activated");
+
+    return newInstance;
+}
+
+// Called from ProcessManagerThread
+QProcessManagerMediator::QProcessManagerMediator()
+        : CActive(CActive::EPriorityStandard)
+{
+    // Nothing to do
+}
+
+// Called from ProcessManagerThread
+QProcessManagerMediator::~QProcessManagerMediator()
+{
+    processManagerThread.Close();
+    currentCommand = ENoCommand;
+    currentObserver = NULL;
+}
+
+// Called from main thread
+bool QProcessManagerMediator::add(QProcessActive *processObserver)
+{
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::add()");
+    return notify(processObserver, EAdd);
+}
+
+// Called from main thread
+void QProcessManagerMediator::remove(QProcessActive *processObserver)
+{
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::remove()");
+    notify(processObserver, ERemove);
+}
+
+// Called from main thread
+void QProcessManagerMediator::terminate()
+{
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::terminate()");
+    notify(NULL, ETerminate);
+}
+
+// Called from main thread
+bool QProcessManagerMediator::notify(QProcessActive *processObserver, Commands command)
+{
+    bool success(true);
+
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::Notify(): Command: %d, processObserver: 0x%x", command, processObserver);
+
+    QPROCESS_ASSERT((command == ETerminate || processObserver),
+                    EProcessManagerMediatorNullObserver,
+                    "QProcessManagerMediator::Notify(): NULL processObserver not allowed for command: %d", command)
+
+    QPROCESS_ASSERT(IsActive(),
+                    EProcessManagerMediatorInactive,
+                    "QProcessManagerMediator::Notify(): Mediator is not active!")
+
+    QPROCESS_ASSERT(iStatus == KRequestPending,
+                    EProcessManagerMediatorNotPending,
+                    "QProcessManagerMediator::Notify(): Mediator request not pending!")
+
+    currentObserver = processObserver;
+    currentCommand = command;
+
+    // Sync with process manager thread
+    TRequestStatus pmStatus;
+    processManagerThread.Rendezvous(pmStatus);
+
+    // Complete request -> RunL will run in the process manager thread
+    TRequestStatus *status = &iStatus;
+    processManagerThread.RequestComplete(status, command);
+
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::Notify(): Waiting process manager to complete...");
+    User::WaitForRequest(pmStatus);
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::Notify(): Wait over");
+
+    if (currentObserver) {
+        success = !(currentObserver->error());
+        QPROCESS_DEBUG_PRINT("QProcessManagerMediator::Notify(): success = %d", success);
+    }
+
+    currentObserver = NULL;
+    currentCommand = ENoCommand;
+
+    return success;
+}
+
+// Called from ProcessManagerThread
+void QProcessManagerMediator::RunL()
+{
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::RunL(): currentCommand: %d, iStatus: %d", currentCommand, iStatus.Int());
+    switch (currentCommand) {
+    case EAdd:
+        currentObserver->start();
+        break;
+    case ERemove:
+        currentObserver->stop();
+        break;
+    case ETerminate:
+        Deque();
+        CActiveScheduler::Stop();
+        return;
+    default:
+        QPROCESS_ASSERT(0,
+                        EProcessManagerMediatorInvalidCmd,
+                        "QProcessManagerMediator::RunL(): Invalid command!")
+        break;
+    }
+
+    iStatus = KRequestPending;
+    SetActive();
+
+    // Notify main thread that we are done
+    RThread::Rendezvous(KErrNone);
+}
+
+// Called from ProcessManagerThread
+TInt QProcessManagerMediator::RunError(TInt aError)
+{
+    Q_UNUSED(aError);
+    // Handle RunL leave (should never happen)
+    QPROCESS_ASSERT(0,
+                    EProcessManagerMediatorRunError,
+                    "QProcessManagerMediator::RunError(): Should never get here!")
+    return 0;
+}
+
+// Called from ProcessManagerThread
+void QProcessManagerMediator::DoCancel()
+{
+    QPROCESS_DEBUG_PRINT("QProcessManagerMediator::DoCancel()");
+    TRequestStatus *status = &iStatus;
+    processManagerThread.RequestComplete(status, KErrCancel);
+}
+
+Q_GLOBAL_STATIC(QProcessManager, processManager)
+
+TInt processManagerThreadFunction(TAny *param)
+{
+    QPROCESS_ASSERT(param,
+                    EProcessManagerNullParam,
+                    "processManagerThreadFunction(): NULL param")
+
+    QProcessManager *manager = reinterpret_cast<QProcessManager*>(param);
+
+    CActiveScheduler *scheduler = new CQtActiveScheduler();
+
+    QPROCESS_ASSERT(scheduler,
+                    EProcessManagerSchedulerCreationFail,
+                    "processManagerThreadFunction(): Scheduler creation failed")
+
+    CActiveScheduler::Install(scheduler);
+
+    //Creating mediator also adds it to scheduler and activates it. Failure will panic.
+    manager->setMediator(QProcessManagerMediator::construct());
+    RThread::Rendezvous(KErrNone);
+
+    CActiveScheduler::Start();
+
+    CActiveScheduler::Install(NULL);
+    delete scheduler;
+
+    return KErrNone;
+}
+
+QProcessManager::QProcessManager()
+        : mediator(NULL), threadStarted(false)
+{
+    TInt err = managerMutex.CreateLocal();
+
+    QPROCESS_ASSERT(err == KErrNone,
+                    EProcessManagerMutexCreationFail,
+                    "QProcessManager::QProcessManager(): Failed to create new managerMutex (err: %d)", err)
+}
+
+QProcessManager::~QProcessManager()
+{
+    QPROCESS_DEBUG_PRINT("QProcessManager::~QProcessManager()");
+    // Cancel death listening for all child processes
+    if (mediator) {
+        QMap<int, QProcessActive *>::Iterator it = children.begin();
+        while (it != children.end()) {
+            // Remove all monitors
+            QProcessActive *active = it.value();
+            mediator->remove(active);
+
+            QPROCESS_DEBUG_PRINT("QProcessManager::~QProcessManager() removed listening for a process");
+            ++it;
+        }
+
+        // Terminate process manager thread.
+        mediator->terminate();
+        delete mediator;
+    }
+
+    qDeleteAll(children.values());
+    children.clear();
+    managerThread.Close();
+    managerMutex.Close();
+}
+
+void QProcessManager::startThread()
+{
+    lock();
+
+    if (!threadStarted) {
+        TInt err = managerThread.Create(KQProcessManagerThreadName,
+                                        processManagerThreadFunction,
+                                        0x5000,
+                                        (RAllocator*)NULL,
+                                        (TAny*)this,
+                                        EOwnerProcess);
+
+        QPROCESS_ASSERT(err == KErrNone,
+                        EProcessManagerThreadCreationFail,
+                        "QProcessManager::startThread(): Failed to create new managerThread (err:%d)", err)
+
+        threadStarted = true;
+
+        // Manager thread must start running before we continue, so sync with rendezvous
+        TRequestStatus status;
+        managerThread.Rendezvous(status);
+        managerThread.Resume();
+        User::WaitForRequest(status);
+    }
+
+    unlock();
+}
+
+static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+bool QProcessManager::add(QProcess *process)
+{
+    QPROCESS_ASSERT(process,
+                    EProcessManagerNullParam,
+                    "QProcessManager::add(): Failed to add QProcessActive to ProcessManager - NULL process")
+
+    lock();
+
+    int serial = idCounter.fetchAndAddRelaxed(1);
+    process->d_func()->serial = serial;
+
+    QPROCESS_DEBUG_PRINT("QProcessManager::add(): serial: %d, deathPipe: %d - %d, symbianProcess: 0x%x", serial, process->d_func()->deathPipe[0], process->d_func()->deathPipe[1], process->d_func()->symbianProcess);
+
+    QProcessActive *newActive =
+        QProcessActive::construct(process,
+                                  &(process->d_func()->symbianProcess),
+                                  serial,
+                                  process->d_func()->deathPipe[1]);
+
+    if (newActive) {
+        if (mediator->add(newActive)) {
+            children.insert(serial, newActive);
+            unlock();
+            return true;
+        } else {
+            QPROCESS_DEBUG_PRINT("QProcessManager::add(): Failed to add QProcessActive to ProcessManager");
+            delete newActive;
+        }
+    }
+
+    unlock();
+
+    return false;
+}
+
+void QProcessManager::remove(QProcess *process)
+{
+    QPROCESS_ASSERT(process,
+                    EProcessManagerNullParam,
+                    "QProcessManager::remove(): Failed to remove QProcessActive from ProcessManager - NULL process")
+
+    lock();
+
+    int serial = process->d_func()->serial;
+    QProcessActive *active = children.value(serial);
+    if (!active) {
+        unlock();
+        return;
+    }
+
+    mediator->remove(active);
+
+    children.remove(serial);
+    delete active;
+
+    unlock();
+}
+
+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;
+    }
+}
+
+bool QProcessPrivate::createChannel(Channel &channel)
+{
+    Q_UNUSED(channel);
+    // No channels used
+    return false;
+}
+
+void QProcessPrivate::startProcess()
+{
+    Q_Q(QProcess);
+
+    QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess()");
+
+    // Start the process (platform dependent)
+    q->setProcessState(QProcess::Starting);
+
+    processManager()->startThread();
+
+    qt_create_pipe(deathPipe);
+    if (threadData->eventDispatcher) {
+        deathNotifier = new QSocketNotifier(deathPipe[0],
+                                            QSocketNotifier::Read, q);
+        QObject::connect(deathNotifier, SIGNAL(activated(int)),
+                         q, SLOT(_q_processDied()));
+    }
+
+    TInt err = qt_create_symbian_process(&symbianProcess, program, arguments);
+
+    if (err == KErrNone) {
+        pid = symbianProcess->Id().Id();
+
+        ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK);
+
+        if (!processManager()->add(q)) {
+            qWarning("QProcessPrivate::startProcess(): Failed to start monitoring for process death.");
+            err = KErrNoMemory;
+        }
+    }
+
+    if (err != KErrNone) {
+        // Cleanup, report error and return
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess() Process open failed, err: %d, '%s'", err, qPrintable(program));
+        q->setProcessState(QProcess::NotRunning);
+        processError = QProcess::FailedToStart;
+        q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Resource error (qt_create_symbian_process failure)")));
+        emit q->error(processError);
+        cleanup();
+        return;
+    }
+
+    processLaunched = true;
+
+    symbianProcess->Resume();
+
+    QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess(): this: 0x%x, pid: %ld", this, pid);
+
+    // Notify child start
+    _q_startupNotification();
+
+}
+
+bool QProcessPrivate::processStarted()
+{
+    QPROCESS_DEBUG_PRINT("QProcessPrivate::processStarted() == %s", processLaunched ? "true" : "false");
+
+    // Since we cannot get information whether process has actually been launched
+    // or not in Symbian, we need to fake it. Assume process is started if launch was
+    // successful.
+
+    return processLaunched;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStdout() const
+{
+    // In Symbian, stdout is not supported
+    return 0;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStderr() const
+{
+    // In Symbian, stderr is not supported
+    return 0;
+}
+
+qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(maxlen);
+    // In Symbian, stdout is not supported
+    return 0;
+}
+
+qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(maxlen);
+    // In Symbian, stderr is not supported
+    return 0;
+}
+
+qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(maxlen);
+    // In Symbian, stdin is not supported
+    return 0;
+}
+
+void QProcessPrivate::terminateProcess()
+{
+    // Needs PowerMgmt capability if process has been started; will panic kern-exec 46 otherwise.
+    // Always works if process is not yet started.
+    if (qt_rprocess_running(symbianProcess)) {
+        symbianProcess->Terminate(0);
+    } else {
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::terminateProcess(), Process not running");
+    }
+}
+
+void QProcessPrivate::killProcess()
+{
+    // Needs PowerMgmt capability if process has been started; will panic kern-exec 46 otherwise.
+    // Always works if process is not yet started.
+    if (qt_rprocess_running(symbianProcess)) {
+        symbianProcess->Kill(0);
+    } else {
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::killProcess(), Process not running");
+    }
+}
+
+bool QProcessPrivate::waitForStarted(int msecs)
+{
+    Q_UNUSED(msecs);
+    // Since we can get no actual feedback from process beyond its death,
+    // assume that started has already been emitted if process has been launched
+    return processLaunched;
+}
+
+bool QProcessPrivate::waitForReadyRead(int msecs)
+{
+    // Functionality not supported in Symbian
+    Q_UNUSED(msecs);
+    return false;
+}
+
+bool QProcessPrivate::waitForBytesWritten(int msecs)
+{
+    // Functionality not supported in Symbian
+    Q_UNUSED(msecs);
+    return false;
+}
+
+bool QProcessPrivate::waitForFinished(int msecs)
+{
+    Q_Q(QProcess);
+    QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished(%d)", msecs);
+
+    TRequestStatus timerStatus = 0;
+    TRequestStatus logonStatus = 0;
+    bool timeoutOccurred = false;
+
+    // Logon to process to observe its death
+    if (qt_rprocess_running(symbianProcess)) {
+        symbianProcess->Logon(logonStatus);
+
+        // Create timer
+        RTimer timer;
+        timer.CreateLocal();
+        TTimeIntervalMicroSeconds32 interval(msecs*1000);
+        timer.After(timerStatus, interval);
+
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Waiting...");
+        User::WaitForRequest(logonStatus, timerStatus);
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Wait completed");
+
+        if (timerStatus == KErrNone)
+            timeoutOccurred = true;
+
+        timer.Cancel();
+        timer.Close();
+
+        symbianProcess->LogonCancel(logonStatus);
+
+        // Eat cancel request completion so that it won't mess up main thread scheduling later
+        User::WaitForRequest(logonStatus, timerStatus);
+    } else {
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished(), qt_rprocess_running returned false");
+    }
+
+    if (timeoutOccurred) {
+        processError = QProcess::Timedout;
+        q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Process operation timed out")));
+        return false;
+    }
+
+    _q_processDied();
+
+    return true;
+}
+
+bool QProcessPrivate::waitForWrite(int msecs)
+{
+    // Functionality not supported in Symbian
+    Q_UNUSED(msecs);
+    return false;
+}
+
+// Deceptively named function. Exit code is actually got in waitForDeadChild().
+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);
+
+    if (symbianProcess && symbianProcess->Handle()) {
+        TExitType et = symbianProcess->ExitType();
+        QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForDeadChild() symbianProcess->ExitType: %d", et);
+        if (et != EExitPending) {
+            processManager()->remove(q);
+            exitCode = symbianProcess->ExitReason();
+            crashed = (et == EExitPanic);
+#if defined QPROCESS_DEBUG
+            TExitCategoryName catName = symbianProcess->ExitCategory();
+            qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode"
+                     << exitCode << ", crashed:" << crashed
+                     << ", category:" << QString::fromUtf16(catName.Ptr());
+#endif
+        } else {
+            QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForDeadChild() not dead!");
+        }
+    }
+
+    return true;
+}
+
+void QProcessPrivate::_q_notified()
+{
+    // Nothing to do in Symbian
+}
+
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+    QPROCESS_DEBUG_PRINT("QProcessPrivate::startDetached()");
+    Q_UNUSED(workingDirectory);
+
+    RProcess *newProc = NULL;
+
+    TInt err = qt_create_symbian_process(&newProc, program, arguments);
+
+    if (err == KErrNone) {
+        if (pid)
+            *pid = newProc->Id().Id();
+
+        newProc->Resume();
+        newProc->Close();
+        return true;
+    }
+
+    return false;
+}
+
+
+void QProcessPrivate::initializeProcessManager()
+{
+    (void) processManager();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS