diff options
Diffstat (limited to 'src/corelib/io/qprocess_symbian.cpp')
-rw-r--r-- | src/corelib/io/qprocess_symbian.cpp | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/src/corelib/io/qprocess_symbian.cpp b/src/corelib/io/qprocess_symbian.cpp new file mode 100644 index 0000000000..8a74c7b4a2 --- /dev/null +++ b/src/corelib/io/qprocess_symbian.cpp @@ -0,0 +1,1067 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "private/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, const QString &nativeArguments, 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 + QLatin1Char(' '); + } else { + commandLine += tmp + QLatin1Char(' '); + } + } + + if (!nativeArguments.isEmpty()) + commandLine += nativeArguments; + else if (!commandLine.isEmpty()) // Chop the extra trailing space if any arguments were appended + commandLine.chop(1); +} + +static TInt qt_create_symbian_process(RProcess **proc, + const QString &programName, const QStringList &arguments, const QString &nativeArguments) +{ + RProcess *newProc = NULL; + newProc = new RProcess(); + + if (!newProc) + return KErrNoMemory; + + QString commandLine; + qt_create_symbian_commandline(arguments, nativeArguments, 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 main thread +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 main thread +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()"); + + // Check if manager thread is still alive. If this destructor is ran as part of global + // static cleanup, manager thread will most likely be terminated by kernel at this point, + // so trying to delete QProcessActives and QProcessMediators will panic as they + // will still be active. They can also no longer be canceled as the thread is already gone. + // In case manager thread has already died, we simply do nothing and let the deletion of + // the main heap at process exit take care of stray objects. + + if (managerThread.Handle() && managerThread.ExitType() == EExitPending) { + // 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, nativeArguments); + + 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 = KErrNone; + TRequestStatus logonStatus = KErrNone; + bool timeoutOccurred = false; + + // Logon to process to observe its death + if (qt_rprocess_running(symbianProcess)) { + symbianProcess->Logon(logonStatus); + + if (msecs < 0) { + // If timeout is negative, there is no timeout + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Waiting (just logon)..."); + User::WaitForRequest(logonStatus); + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Wait completed"); + } else { + // Create timer + RTimer timer; + timer.CreateLocal(); + TTimeIntervalMicroSeconds32 interval(msecs*1000); + timer.After(timerStatus, interval); + + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Waiting (logon + timer)..."); + User::WaitForRequest(logonStatus, timerStatus); + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Wait completed"); + + if (logonStatus != KRequestPending) { + timer.Cancel(); + User::WaitForRequest(timerStatus); + } else { + timeoutOccurred = true; + symbianProcess->LogonCancel(logonStatus); + User::WaitForRequest(logonStatus); + } + timer.Close(); + } + } 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((const QChar *)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, QString()); + + if (err == KErrNone) { + if (pid) + *pid = newProc->Id().Id(); + + newProc->Resume(); + newProc->Close(); + delete newProc; + return true; + } + + return false; +} + + +void QProcessPrivate::initializeProcessManager() +{ + (void) processManager(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_PROCESS |