Commit a06af356 authored by David Schulz's avatar David Schulz

ProjectExplorer: Introduce DeviceProcessSignalOperation.

Every device can now return a DeviceProcessSignalOperation,
which allows to kill or interrupt processes running on the
device.

Change-Id: Idaa04ebc767e09ca167fa033ed93860b9b81479e
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@digia.com>
Reviewed-by: default avatarDavid Kaspar <dkaspar@blackberry.com>
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent ca15d0aa
......@@ -89,6 +89,11 @@ bool AndroidDevice::canAutoDetectPorts() const
return true;
}
DeviceProcessSignalOperation::Ptr AndroidDevice::signalOperation() const
{
return DeviceProcessSignalOperation::Ptr();
}
IDevice::Ptr AndroidDevice::clone() const
{
return IDevice::Ptr(new AndroidDevice(*this));
......
......@@ -47,6 +47,7 @@ public:
QString displayNameForActionId(Core::Id actionId) const;
void executeAction(Core::Id actionId, QWidget *parent = 0);
bool canAutoDetectPorts() const;
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const;
ProjectExplorer::IDevice::Ptr clone() const;
......
......@@ -32,6 +32,7 @@
#include "deviceprocesslist.h"
#include "localprocesslist.h"
#include "desktopdeviceconfigurationwidget.h"
#include "desktopprocesssignaloperation.h"
#include <projectexplorer/projectexplorerconstants.h>
#include <ssh/sshconnection.h>
......@@ -114,6 +115,11 @@ DeviceProcess *DesktopDevice::createProcess(QObject *parent) const
return new Internal::DesktopDeviceProcess(sharedFromThis(), parent);
}
DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
{
return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation());
}
IDevice::Ptr DesktopDevice::clone() const
{
return Ptr(new DesktopDevice(*this));
......
......@@ -55,6 +55,7 @@ public:
DeviceProcessList *createProcessListModel(QObject *parent) const;
bool canCreateProcess() const { return true; }
DeviceProcess *createProcess(QObject *parent) const;
DeviceProcessSignalOperation::Ptr signalOperation() const;
IDevice::Ptr clone() const;
......
......@@ -28,13 +28,11 @@
****************************************************************************/
#include "desktopdeviceprocess.h"
#include "idevice.h"
#include <utils/environment.h>
#include <utils/qtcassert.h>
#ifdef Q_OS_UNIX
#include <signal.h>
#endif
#include <utils/qtcprocess.h>
namespace ProjectExplorer {
namespace Internal {
......@@ -59,11 +57,7 @@ void DesktopDeviceProcess::start(const QString &executable, const QStringList &a
void DesktopDeviceProcess::interrupt()
{
#ifdef Q_OS_UNIX
::kill(m_process->pid(), SIGINT);
#elif defined(Q_OS_WIN)
// tbd
#endif
device()->signalOperation()->interruptProcess(Utils::qPidToPid(m_process->pid()));
}
void DesktopDeviceProcess::terminate()
......
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "desktopprocesssignaloperation.h"
#include "localprocesslist.h"
#include <utils/winutils.h>
#include <QDir>
#ifdef Q_OS_WIN
#define _WIN32_WINNT 0x0502
#include <windows.h>
#ifndef PROCESS_SUSPEND_RESUME
#define PROCESS_SUSPEND_RESUME 0x0800
#endif // PROCESS_SUSPEND_RESUME
#else // Q_OS_WIN
#include <errno.h>
#include <signal.h>
#endif // else Q_OS_WIN
namespace ProjectExplorer {
void DesktopProcessSignalOperation::killProcess(int pid)
{
killProcessSilently(pid);
emit finished(m_errorMessage);
}
void DesktopProcessSignalOperation::killProcess(const QString &filePath)
{
m_errorMessage.clear();
foreach (const DeviceProcessItem &process, Internal::LocalProcessList::getLocalProcesses()) {
if (process.cmdLine == filePath)
killProcessSilently(process.pid);
}
emit finished(m_errorMessage);
}
void DesktopProcessSignalOperation::interruptProcess(int pid)
{
m_errorMessage.clear();
interruptProcessSilently(pid);
emit finished(m_errorMessage);
}
void DesktopProcessSignalOperation::interruptProcess(const QString &filePath)
{
interruptProcess(filePath, NoSpecialInterrupt);
}
void DesktopProcessSignalOperation::interruptProcess(int pid, SpecialInterrupt specialInterrupt)
{
m_errorMessage.clear();
interruptProcessSilently(pid, specialInterrupt);
}
void DesktopProcessSignalOperation::interruptProcess(const QString &filePath,
SpecialInterrupt specialInterrupt)
{
m_errorMessage.clear();
foreach (const DeviceProcessItem &process, Internal::LocalProcessList::getLocalProcesses()) {
if (process.cmdLine == filePath)
interruptProcessSilently(process.pid, specialInterrupt);
}
emit finished(m_errorMessage);
}
void DesktopProcessSignalOperation::appendMsgCannotKill(int pid, const QString &why)
{
if (!m_errorMessage.isEmpty())
m_errorMessage += QChar::fromLatin1('\n');
m_errorMessage += tr("Cannot kill process with pid %1: %3 ").arg(pid).arg(why);
}
void DesktopProcessSignalOperation::appendMsgCannotInterrupt(int pid, const QString &why)
{
if (!m_errorMessage.isEmpty())
m_errorMessage += QChar::fromLatin1('\n');
m_errorMessage += tr("Cannot interrupt process with pid %1: %3 ").arg(pid).arg(why);
}
void DesktopProcessSignalOperation::killProcessSilently(int pid)
{
#ifdef Q_OS_WIN
const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION
|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
|PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME;
if (const HANDLE handle = OpenProcess(rights, FALSE, pid)) {
if (!TerminateProcess(handle, UINT(-1)))
appendMsgCannotKill(pid, Utils::winErrorMessage(GetLastError()));
CloseHandle(handle);
} else {
appendMsgCannotKill(pid, tr("Cannot open process."));
}
#else
if (pid <= 0)
appendMsgCannotKill(pid, tr("Invalid process id."));
else if (kill(pid, SIGKILL))
appendMsgCannotKill(pid, QString::fromLocal8Bit(strerror(errno)));
#endif // Q_OS_WIN
}
void DesktopProcessSignalOperation::interruptProcessSilently(
int pid, SpecialInterrupt specialInterrupt)
{
#ifdef Q_OS_WIN
/*
Windows 64 bit has a 32 bit subsystem (WOW64) which makes it possible to run a
32 bit application inside a 64 bit environment.
When GDB is used DebugBreakProcess must be called from the same system (32/64 bit) running
the inferior. If CDB is used we could in theory break wow64 processes,
but the break is actually a wow64 breakpoint. CDB is configured to ignore these
breakpoints, because they also appear on module loading.
Therefore we need helper executables (win(32/64)interrupt.exe) on Windows 64 bit calling
DebugBreakProcess from the correct system.
DebugBreak matrix for windows
Api = UseDebugBreakApi
Win64 = UseWin64InterruptHelper
Win32 = UseWin32InterruptHelper
N/A = This configuration is not possible
| Windows 32bit | Windows 64bit
| QtCreator 32bit | QtCreator 32bit | QtCreator 64bit
| Inferior 32bit | Inferior 32bit | Inferior 64bit | Inferior 32bit | Inferior 64bit
----------|-----------------|-----------------|-----------------|-----------------|----------------
CDB 32bit | Api | Api | N/A | Win32 | N/A
64bit | N/A | Win64 | Win64 | Api | Api
----------|-----------------|-----------------|-----------------|-----------------|----------------
GDB 32bit | Api | Api | N/A | Win32 | N/A
64bit | N/A | N/A | Win64 | N/A | Api
----------|-----------------|-----------------|-----------------|-----------------|----------------
*/
HANDLE inferior = NULL;
do {
const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION
|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
|PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME;
inferior = OpenProcess(rights, FALSE, pid);
if (inferior == NULL) {
appendMsgCannotInterrupt(pid, tr("Cannot open process: %1")
+ Utils::winErrorMessage(GetLastError()));
break;
}
bool creatorIs64Bit = Utils::winIs64BitBinary(qApp->applicationFilePath());
if (!Utils::winIs64BitSystem() ||
specialInterrupt == NoSpecialInterrupt ||
specialInterrupt == Win64Interrupt && creatorIs64Bit ||
specialInterrupt == Win32Interrupt && !creatorIs64Bit) {
if (!DebugBreakProcess(inferior)) {
appendMsgCannotInterrupt(pid, tr("DebugBreakProcess failed: ")
+ Utils::winErrorMessage(GetLastError()));
}
} else if (specialInterrupt == Win32Interrupt
|| specialInterrupt == Win64Interrupt) {
QString executable = QCoreApplication::applicationDirPath();
executable += specialInterrupt == Win32Interrupt
? QLatin1String("/win32interrupt.exe")
: QLatin1String("/win64interrupt.exe");
if (!QFile::exists(executable)) {
appendMsgCannotInterrupt(pid, tr( "%1 does not exist. If you have built QtCreator "
"on your own ,checkout http://qt.gitorious.org/"
"qt-creator/binary-artifacts.").
arg(QDir::toNativeSeparators(executable)));
}
switch (QProcess::execute(executable, QStringList(QString::number(pid)))) {
case -2:
appendMsgCannotInterrupt(pid, tr(
"Cannot start %1. Check src\\tools\\win64interrupt\\win64interrupt.c "
"for more information.").arg(QDir::toNativeSeparators(executable)));
break;
case 0:
break;
default:
appendMsgCannotInterrupt(pid, QDir::toNativeSeparators(executable)
+ tr(" could not break the process."));
break;
}
}
} while (false);
if (inferior != NULL)
CloseHandle(inferior);
#else
Q_UNUSED(specialInterrupt)
if (pid <= 0)
appendMsgCannotInterrupt(pid, tr("Invalid process id."));
else if (kill(pid, SIGINT))
appendMsgCannotInterrupt(pid, QString::fromLocal8Bit(strerror(errno)));
#endif // Q_OS_WIN
}
} // namespace ProjectExplorer
/**************************************************************************
/****************************************************************************
**
** Copyright (C) 2011 - 2013 Research In Motion
**
** Contact: Research In Motion (blackberry-qt@qnx.com)
** Contact: KDAB (info@kdab.com)
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
......@@ -28,29 +26,41 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef WINDOWSPROCESSSIGNALOPERATION_H
#define WINDOWSPROCESSSIGNALOPERATION_H
#include "idevice.h"
#include "blackberrydeviceprocesssupport.h"
#include <projectexplorer/projectexplorer_export.h>
using namespace Qnx;
using namespace Qnx::Internal;
namespace ProjectExplorer {
static QString signalProcessByNameCommandLine(const QString &filePath, int signal)
class PROJECTEXPLORER_EXPORT DesktopProcessSignalOperation : public DeviceProcessSignalOperation
{
QString executable = filePath;
Q_OBJECT
public:
enum SpecialInterrupt { NoSpecialInterrupt, Win32Interrupt, Win64Interrupt };
return QString::fromLatin1("for PID in $(pidin -F \"%a %A\" | grep \"%1\" | awk '/%1/ {print $1}'); "
"do "
"kill -%2 $PID; "
"done").arg(executable.replace(QLatin1String("/"), QLatin1String("\\/"))).arg(signal);
}
~DesktopProcessSignalOperation() {}
void killProcess(int pid);
void killProcess(const QString &filePath);
void interruptProcess(int pid);
void interruptProcess(const QString &filePath);
void interruptProcess(int pid, SpecialInterrupt specialInterrupt);
void interruptProcess(const QString &filePath, SpecialInterrupt specialInterrupt);
QString BlackBerryDeviceProcessSupport::killProcessByNameCommandLine(const QString &filePath) const
{
return QString::fromLatin1("%1; %2").arg(signalProcessByNameCommandLine(filePath, 15),
signalProcessByNameCommandLine(filePath, 9));
}
private:
void killProcessSilently(int pid);
void interruptProcessSilently(int pid, SpecialInterrupt = NoSpecialInterrupt);
QString BlackBerryDeviceProcessSupport::interruptProcessByNameCommandLine(const QString &filePath) const
{
return signalProcessByNameCommandLine(filePath, 2);
}
void appendMsgCannotKill(int pid, const QString &why);
void appendMsgCannotInterrupt(int pid, const QString &why);
protected:
explicit DesktopProcessSignalOperation() {}
friend class DesktopDevice;
};
} // namespace ProjectExplorer
#endif // WINDOWSPROCESSSIGNALOPERATION_H
......@@ -435,6 +435,10 @@ private:
QString displayNameForActionId(Core::Id) const { return QString(); }
void executeAction(Core::Id, QWidget *) { }
Ptr clone() const { return Ptr(new TestDevice(*this)); }
DeviceProcessSignalOperation::Ptr signalOperation() const
{
return DeviceProcessSignalOperation::Ptr();
}
};
void ProjectExplorerPlugin::testDeviceManager()
......
......@@ -196,7 +196,6 @@ public:
} // namespace Internal
PortsGatheringMethod::~PortsGatheringMethod() { }
DeviceProcessSupport::~DeviceProcessSupport() { }
DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { }
IDevice::IDevice() : d(new Internal::IDevicePrivate)
......@@ -255,11 +254,6 @@ Core::Id IDevice::id() const
return d->id;
}
DeviceProcessSupport::Ptr IDevice::processSupport() const
{
return DeviceProcessSupport::Ptr();
}
PortsGatheringMethod::Ptr IDevice::portsGatheringMethod() const
{
return PortsGatheringMethod::Ptr();
......
......@@ -55,15 +55,25 @@ namespace Internal { class IDevicePrivate; }
class IDeviceWidget;
class DeviceTester;
class PROJECTEXPLORER_EXPORT DeviceProcessSupport
class PROJECTEXPLORER_EXPORT DeviceProcessSignalOperation : public QObject
{
Q_OBJECT
public:
typedef QSharedPointer<const DeviceProcessSupport> Ptr;
~DeviceProcessSignalOperation() {}
typedef QSharedPointer<DeviceProcessSignalOperation> Ptr;
virtual void killProcess(int pid) = 0;
virtual void killProcess(const QString &filePath) = 0;
virtual void interruptProcess(int pid) = 0;
virtual void interruptProcess(const QString &filePath) = 0;
virtual ~DeviceProcessSupport();
virtual QString killProcessByPidCommandLine(int pid) const = 0;
virtual QString killProcessByNameCommandLine(const QString &filePath) const = 0;
virtual QString interruptProcessByNameCommandLine(const QString &filePath) const = 0;
signals:
// If the error message is empty the operation was successful
void finished(const QString &errorMessage);
protected:
explicit DeviceProcessSignalOperation() {}
QString m_errorMessage;
};
class PROJECTEXPLORER_EXPORT PortsGatheringMethod
......@@ -113,7 +123,6 @@ public:
virtual QString displayNameForActionId(Core::Id actionId) const = 0;
virtual void executeAction(Core::Id actionId, QWidget *parent = 0) = 0;
virtual DeviceProcessSupport::Ptr processSupport() const;
// Devices that can auto detect ports need not return a ports gathering method. Such devices can
// obtain a free port on demand. eg: Desktop device.
virtual bool canAutoDetectPorts() const { return false; }
......@@ -125,6 +134,7 @@ public:
virtual bool canCreateProcess() const { return false; }
virtual DeviceProcess *createProcess(QObject *parent) const;
virtual DeviceProcessSignalOperation::Ptr signalOperation() const = 0;
enum DeviceState { DeviceReadyToUse, DeviceConnected, DeviceDisconnected, DeviceStateUnknown };
DeviceState deviceState() const;
......
......@@ -51,9 +51,6 @@
#include <utils/winutils.h>
#include <tlhelp32.h>
#include <psapi.h>
#ifndef PROCESS_SUSPEND_RESUME
#define PROCESS_SUSPEND_RESUME 0x0800
#endif
#endif
namespace ProjectExplorer {
......@@ -125,28 +122,8 @@ QList<DeviceProcessItem> LocalProcessList::getLocalProcesses()
return processes;
}
void LocalProcessList::doKillProcess(const DeviceProcessItem &process)
{
const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION
|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
|PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME;
m_error.clear();
if (const HANDLE handle = OpenProcess(rights, FALSE, process.pid)) {
if (!TerminateProcess(handle, UINT(-1))) {
m_error = tr("Cannot terminate process %1: %2").
arg(process.pid).arg(Utils::winErrorMessage(GetLastError()));
}
CloseHandle(handle);
} else {
m_error = tr("Cannot open process %1: %2").
arg(process.pid).arg(Utils::winErrorMessage(GetLastError()));
}
QTimer::singleShot(0, this, SLOT(reportDelayedKillStatus()));
}
#endif //Q_OS_WIN
#ifdef Q_OS_UNIX
LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent)
: DeviceProcessList(device, parent)
......@@ -258,17 +235,16 @@ QList<DeviceProcessItem> LocalProcessList::getLocalProcesses()
return procDir.exists() ? getLocalProcessesUsingProc(procDir) : getLocalProcessesUsingPs();
}
#endif // QT_OS_UNIX
void LocalProcessList::doKillProcess(const DeviceProcessItem &process)
{
if (kill(process.pid, SIGKILL) == -1)
m_error = QString::fromLocal8Bit(strerror(errno));
else
m_error.clear();
QTimer::singleShot(0, this, SLOT(reportDelayedKillStatus()));
DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation();
connect(signalOperation.data(), SIGNAL(finished(QString)),
SLOT(reportDelayedKillStatus(QString)));
signalOperation->killProcess(process.pid);
}
#endif // QT_OS_UNIX
Qt::ItemFlags LocalProcessList::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = DeviceProcessList::flags(index);
......@@ -287,12 +263,12 @@ void LocalProcessList::doUpdate()
QTimer::singleShot(0, this, SLOT(handleUpdate()));
}
void LocalProcessList::reportDelayedKillStatus()
void LocalProcessList::reportDelayedKillStatus(const QString &errorMessage)
{
if (m_error.isEmpty())
if (errorMessage.isEmpty())
reportProcessKilled();
else
reportError(m_error);
reportError(errorMessage);
}
} // namespace Internal
......
......@@ -57,11 +57,11 @@ private:
private slots:
void handleUpdate();
void reportDelayedKillStatus();
void reportDelayedKillStatus(const QString &errorMessage);
private:
const qint64 m_myPid;
QString m_error;
};
} // namespace Internal
......
......@@ -295,13 +295,11 @@ void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(QSsh::SshRemoteProcess:
if (serverSupportsSignals) {
process->sendSignal(signal);
} else {
const DeviceProcessSupport::Ptr processSupport = q->device()->processSupport();
QString signalCommandLine = signal == QSsh::SshRemoteProcess::IntSignal
? processSupport->interruptProcessByNameCommandLine(executable)
: processSupport->killProcessByNameCommandLine(executable);
const QSsh::SshRemoteProcess::Ptr signalProcess
= connection->createRemoteProcess(signalCommandLine.toUtf8());
signalProcess->start();
DeviceProcessSignalOperation::Ptr signalOperation = q->device()->signalOperation();
if (signal == QSsh::SshRemoteProcess::IntSignal)
signalOperation->interruptProcess(executable);
else
signalOperation->killProcess(executable);
}
break;
}
......
......@@ -28,6 +28,8 @@
****************************************************************************/
#include "sshdeviceprocesslist.h"
#include "idevice.h"
#include <ssh/sshremoteprocessrunner.h>
#include <utils/qtcassert.h>
......@@ -39,6 +41,7 @@ class SshDeviceProcessList::SshDeviceProcessListPrivate
{
public:
SshRemoteProcessRunner process;
DeviceProcessSignalOperation::Ptr signalOperation;
};
SshDeviceProcessList::SshDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) :
......@@ -53,7 +56,6 @@ SshDeviceProcessList::~SshDeviceProcessList()
void SshDeviceProcessList::doUpdate()
{
QTC_ASSERT(device()->processSupport(), return);
connect(&d->process, SIGNAL(connectionError()), SLOT(handleConnectionError()));
connect(&d->process, SIGNAL(processClosed(int)), SLOT(handleListProcessFinished(int)));
d->process.run(listProcessesCommandLine().toUtf8(), device()->sshParameters());
......@@ -61,11 +63,11 @@ void SshDeviceProcessList::doUpdate()
void SshDeviceProcessList::doKillProcess(const DeviceProcessItem &process)
{
QTC_ASSERT(device()->processSupport(), return);
connect(&d->process, SIGNAL(connectionError()), SLOT(handleConnectionError()));
connect(&d->process, SIGNAL(processClosed(int)), SLOT(handleKillProcessFinished(int)));
d->process.run(device()->processSupport()->killProcessByPidCommandLine(process.pid).toUtf8(),
device()->sshParameters());
d->signalOperation = device()->signalOperation();
QTC_ASSERT(d->signalOperation, return);
connect(d->signalOperation.data(), SIGNAL(finished(QString)),
SLOT(handleKillProcessFinished(QString)));
d->signalOperation->killProcess(process.pid);
}
void SshDeviceProcessList::handleConnectionError()
......@@ -102,29 +104,13 @@ void SshDeviceProcessList::handleListProcessFinished(int exitStatus)
}
}
void SshDeviceProcessList::handleKillProcessFinished(int exitStatus)
void SshDeviceProcessList::handleKillProcessFinished(const QString &errorString)
{
if (errorString.isEmpty())
reportProcessKilled();
else
reportError(tr("Error: Kill process failed: %1").arg(errorString));
setFinished();
switch (exitStatus) {
case SshRemoteProcess::FailedToStart:
handleProcessError(tr("Error: Kill process failed to start: %1")
.arg(d->process.processErrorString()));
break;
case SshRemoteProcess::CrashExit:
handleProcessError(tr("Error: Kill process crashed: %1")
.arg(d->process.processErrorString()));
break;
case SshRemoteProcess::NormalExit: {
const int exitCode = d->process.processExitCode();
if (exitCode == 0)
reportProcessKilled();
else
handleProcessError(tr("Kill process failed with exit code %1.").arg(exitCode));
break;
}
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid exit status");
}
}
void SshDeviceProcessList::handleProcessError(const QString &errorMessage)
......@@ -139,6 +125,10 @@ void SshDeviceProcessList::handleProcessError(const QString &errorMessage)
void SshDeviceProcessList::setFinished()
{
d->process.disconnect(this);
if (d->signalOperation) {
d->signalOperation->disconnect(this);
d->signalOperation.clear();
}
}
} // namespace ProjectExplorer
......@@ -43,7 +43,7 @@ public:
private slots:
void handleConnectionError();
void handleListProcessFinished(int exitStatus);
void handleKillProcessFinished(int exitStatus);
void handleKillProcessFinished(const QString &errorString);
private:
virtual QString listProcessesCommandLine() const = 0;
......
......@@ -131,6 +131,7 @@ HEADERS += projectexplorer.h \