forked from qt-creator/qt-creator
ProjectExplorer/QNX/RemoteLinux: Merge {RemoteLinux,Simple}RunControl
Currently still kind-of-two implementations due to the kind-of-two current ApplicationLauncher message handling, but getting one step closer to a unified implementation. This also de-emphasizes the "wrong" QNX-IS-A-RemoteLinux relation by removing the QnxRunControl inheritance of RemoteLinuxRunControl. Change-Id: Iec8abaa53223a5a47ec51817b3994d37d444557a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: James McDonnell <jmcdonnell@blackberry.com> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io> Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -802,6 +802,16 @@ public:
|
|||||||
|
|
||||||
} // Internal
|
} // Internal
|
||||||
|
|
||||||
|
// FIXME: Remove once ApplicationLauncher signalling does not depend on device.
|
||||||
|
static bool isSynchronousLauncher(RunControl *runControl)
|
||||||
|
{
|
||||||
|
RunConfiguration *runConfig = runControl->runConfiguration();
|
||||||
|
Target *target = runConfig ? runConfig->target() : nullptr;
|
||||||
|
Kit *kit = target ? target->kit() : nullptr;
|
||||||
|
Core::Id deviceId = DeviceTypeKitInformation::deviceTypeId(kit);
|
||||||
|
return !deviceId.isValid() || deviceId == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
SimpleRunControl::SimpleRunControl(RunConfiguration *runConfiguration, Core::Id mode)
|
SimpleRunControl::SimpleRunControl(RunConfiguration *runConfiguration, Core::Id mode)
|
||||||
: RunControl(runConfiguration, mode), d(new Internal::SimpleRunControlPrivate)
|
: RunControl(runConfiguration, mode), d(new Internal::SimpleRunControlPrivate)
|
||||||
{
|
{
|
||||||
@@ -824,35 +834,76 @@ void SimpleRunControl::start()
|
|||||||
reportApplicationStart();
|
reportApplicationStart();
|
||||||
d->m_launcher.disconnect(this);
|
d->m_launcher.disconnect(this);
|
||||||
|
|
||||||
connect(&d->m_launcher, &ApplicationLauncher::appendMessage,
|
Runnable r = runnable();
|
||||||
this, static_cast<void(RunControl::*)(const QString &, Utils::OutputFormat)>(&RunControl::appendMessage));
|
|
||||||
connect(&d->m_launcher, &ApplicationLauncher::processStarted,
|
if (isSynchronousLauncher(this)) {
|
||||||
this, &SimpleRunControl::onProcessStarted);
|
|
||||||
connect(&d->m_launcher, &ApplicationLauncher::processExited,
|
connect(&d->m_launcher, &ApplicationLauncher::appendMessage,
|
||||||
this, &SimpleRunControl::onProcessFinished);
|
this, static_cast<void(RunControl::*)(const QString &, OutputFormat)>(&RunControl::appendMessage));
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::processStarted,
|
||||||
|
this, &SimpleRunControl::onProcessStarted);
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::processExited,
|
||||||
|
this, &SimpleRunControl::onProcessFinished);
|
||||||
|
|
||||||
|
QTC_ASSERT(r.is<StandardRunnable>(), return);
|
||||||
|
const QString executable = r.as<StandardRunnable>().executable;
|
||||||
|
if (executable.isEmpty()) {
|
||||||
|
appendMessage(RunControl::tr("No executable specified.") + '\n',
|
||||||
|
Utils::ErrorMessageFormat);
|
||||||
|
reportApplicationStop();
|
||||||
|
} else if (!QFileInfo::exists(executable)) {
|
||||||
|
appendMessage(RunControl::tr("Executable %1 does not exist.")
|
||||||
|
.arg(QDir::toNativeSeparators(executable)) + '\n',
|
||||||
|
Utils::ErrorMessageFormat);
|
||||||
|
reportApplicationStop();
|
||||||
|
} else {
|
||||||
|
QString msg = RunControl::tr("Starting %1...").arg(QDir::toNativeSeparators(executable)) + '\n';
|
||||||
|
appendMessage(msg, Utils::NormalMessageFormat);
|
||||||
|
d->m_launcher.start(r);
|
||||||
|
setApplicationProcessHandle(d->m_launcher.applicationPID());
|
||||||
|
}
|
||||||
|
|
||||||
QTC_ASSERT(runnable().is<StandardRunnable>(), return);
|
|
||||||
auto r = runnable().as<StandardRunnable>();
|
|
||||||
if (r.executable.isEmpty()) {
|
|
||||||
appendMessage(RunControl::tr("No executable specified.") + QLatin1Char('\n'), Utils::ErrorMessageFormat);
|
|
||||||
reportApplicationStop();
|
|
||||||
} else if (!QFileInfo::exists(r.executable)) {
|
|
||||||
appendMessage(RunControl::tr("Executable %1 does not exist.")
|
|
||||||
.arg(QDir::toNativeSeparators(r.executable)) + QLatin1Char('\n'),
|
|
||||||
Utils::ErrorMessageFormat);
|
|
||||||
reportApplicationStop();
|
|
||||||
} else {
|
} else {
|
||||||
QString msg = RunControl::tr("Starting %1...").arg(QDir::toNativeSeparators(r.executable)) + QLatin1Char('\n');
|
|
||||||
appendMessage(msg, Utils::NormalMessageFormat);
|
connect(&d->m_launcher, &ApplicationLauncher::reportError,
|
||||||
d->m_launcher.start(r);
|
this, [this](const QString &error) {
|
||||||
setApplicationProcessHandle(d->m_launcher.applicationPID());
|
appendMessage(error, Utils::ErrorMessageFormat);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::remoteStderr,
|
||||||
|
this, [this](const QByteArray &output) {
|
||||||
|
appendMessage(QString::fromUtf8(output), Utils::StdErrFormatSameLine);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::remoteStdout,
|
||||||
|
this, [this](const QByteArray &output) {
|
||||||
|
appendMessage(QString::fromUtf8(output), Utils::StdOutFormatSameLine);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::finished,
|
||||||
|
this, [this] {
|
||||||
|
d->m_launcher.disconnect(this);
|
||||||
|
reportApplicationStop();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&d->m_launcher, &ApplicationLauncher::reportProgress,
|
||||||
|
this, [this](const QString &progressString) {
|
||||||
|
appendMessage(progressString + '\n', Utils::NormalMessageFormat);
|
||||||
|
});
|
||||||
|
|
||||||
|
d->m_launcher.start(r, device());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RunControl::StopResult SimpleRunControl::stop()
|
RunControl::StopResult SimpleRunControl::stop()
|
||||||
{
|
{
|
||||||
d->m_launcher.stop();
|
d->m_launcher.stop();
|
||||||
return StoppedSynchronously;
|
|
||||||
|
if (isSynchronousLauncher(this))
|
||||||
|
return StoppedSynchronously;
|
||||||
|
else
|
||||||
|
return AsynchronousStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleRunControl::onProcessStarted()
|
void SimpleRunControl::onProcessStarted()
|
||||||
|
@@ -431,7 +431,9 @@ public:
|
|||||||
virtual void onProcessFinished(int exitCode, QProcess::ExitStatus status);
|
virtual void onProcessFinished(int exitCode, QProcess::ExitStatus status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Internal::SimpleRunControlPrivate *d;
|
void setFinished();
|
||||||
|
|
||||||
|
Internal::SimpleRunControlPrivate * const d;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ProjectExplorer
|
} // namespace ProjectExplorer
|
||||||
|
@@ -33,14 +33,13 @@
|
|||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace RemoteLinux;
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Qnx {
|
namespace Qnx {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
QnxRunControl::QnxRunControl(RunConfiguration *runConfig)
|
QnxRunControl::QnxRunControl(RunConfiguration *runConfig)
|
||||||
: RemoteLinuxRunControl(runConfig)
|
: SimpleRunControl(runConfig, ProjectExplorer::Constants::NORMAL_RUN_MODE)
|
||||||
, m_slog2Info(0)
|
, m_slog2Info(0)
|
||||||
{
|
{
|
||||||
IDevice::ConstPtr dev = DeviceKitInformation::device(runConfig->target()->kit());
|
IDevice::ConstPtr dev = DeviceKitInformation::device(runConfig->target()->kit());
|
||||||
@@ -61,7 +60,7 @@ QnxRunControl::QnxRunControl(RunConfiguration *runConfig)
|
|||||||
RunControl::StopResult QnxRunControl::stop()
|
RunControl::StopResult QnxRunControl::stop()
|
||||||
{
|
{
|
||||||
m_slog2Info->stop();
|
m_slog2Info->stop();
|
||||||
return RemoteLinuxRunControl::stop();
|
return SimpleRunControl::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QnxRunControl::printMissingWarning()
|
void QnxRunControl::printMissingWarning()
|
||||||
|
@@ -25,25 +25,24 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <remotelinux/remotelinuxruncontrol.h>
|
#include <projectexplorer/runconfiguration.h>
|
||||||
|
|
||||||
namespace Qnx {
|
namespace Qnx {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class Slog2InfoRunner;
|
class Slog2InfoRunner;
|
||||||
|
|
||||||
class QnxRunControl : public RemoteLinux::RemoteLinuxRunControl
|
class QnxRunControl : public ProjectExplorer::SimpleRunControl
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit QnxRunControl(ProjectExplorer::RunConfiguration *runConfig);
|
explicit QnxRunControl(ProjectExplorer::RunConfiguration *runConfig);
|
||||||
|
|
||||||
RemoteLinux::RemoteLinuxRunControl::StopResult stop() override;
|
StopResult stop() override;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void printMissingWarning();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void printMissingWarning();
|
||||||
|
|
||||||
Slog2InfoRunner *m_slog2Info;
|
Slog2InfoRunner *m_slog2Info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -16,7 +16,6 @@ HEADERS += \
|
|||||||
genericlinuxdeviceconfigurationfactory.h \
|
genericlinuxdeviceconfigurationfactory.h \
|
||||||
remotelinuxrunconfigurationwidget.h \
|
remotelinuxrunconfigurationwidget.h \
|
||||||
remotelinuxrunconfigurationfactory.h \
|
remotelinuxrunconfigurationfactory.h \
|
||||||
remotelinuxruncontrol.h \
|
|
||||||
remotelinuxruncontrolfactory.h \
|
remotelinuxruncontrolfactory.h \
|
||||||
remotelinuxdebugsupport.h \
|
remotelinuxdebugsupport.h \
|
||||||
genericlinuxdeviceconfigurationwizardpages.h \
|
genericlinuxdeviceconfigurationwizardpages.h \
|
||||||
@@ -64,7 +63,6 @@ SOURCES += \
|
|||||||
genericlinuxdeviceconfigurationfactory.cpp \
|
genericlinuxdeviceconfigurationfactory.cpp \
|
||||||
remotelinuxrunconfigurationwidget.cpp \
|
remotelinuxrunconfigurationwidget.cpp \
|
||||||
remotelinuxrunconfigurationfactory.cpp \
|
remotelinuxrunconfigurationfactory.cpp \
|
||||||
remotelinuxruncontrol.cpp \
|
|
||||||
remotelinuxruncontrolfactory.cpp \
|
remotelinuxruncontrolfactory.cpp \
|
||||||
remotelinuxdebugsupport.cpp \
|
remotelinuxdebugsupport.cpp \
|
||||||
genericlinuxdeviceconfigurationwizardpages.cpp \
|
genericlinuxdeviceconfigurationwizardpages.cpp \
|
||||||
|
@@ -98,8 +98,6 @@ Project {
|
|||||||
"remotelinuxrunconfigurationfactory.h",
|
"remotelinuxrunconfigurationfactory.h",
|
||||||
"remotelinuxrunconfigurationwidget.cpp",
|
"remotelinuxrunconfigurationwidget.cpp",
|
||||||
"remotelinuxrunconfigurationwidget.h",
|
"remotelinuxrunconfigurationwidget.h",
|
||||||
"remotelinuxruncontrol.cpp",
|
|
||||||
"remotelinuxruncontrol.h",
|
|
||||||
"remotelinuxruncontrolfactory.cpp",
|
"remotelinuxruncontrolfactory.cpp",
|
||||||
"remotelinuxruncontrolfactory.h",
|
"remotelinuxruncontrolfactory.h",
|
||||||
"remotelinuxsignaloperation.cpp",
|
"remotelinuxsignaloperation.cpp",
|
||||||
|
@@ -1,108 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** 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 The Qt Company. For licensing terms
|
|
||||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
||||||
** information use the contact form at https://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU
|
|
||||||
** General Public License version 3 as published by the Free Software
|
|
||||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
||||||
** included in the packaging of this file. Please review the following
|
|
||||||
** information to ensure the GNU General Public License requirements will
|
|
||||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#include "remotelinuxruncontrol.h"
|
|
||||||
|
|
||||||
#include <projectexplorer/applicationlauncher.h>
|
|
||||||
|
|
||||||
#include <utils/utilsicons.h>
|
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
|
||||||
|
|
||||||
namespace RemoteLinux {
|
|
||||||
|
|
||||||
class RemoteLinuxRunControl::RemoteLinuxRunControlPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ApplicationLauncher launcher;
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoteLinuxRunControl::RemoteLinuxRunControl(RunConfiguration *rc)
|
|
||||||
: RunControl(rc, ProjectExplorer::Constants::NORMAL_RUN_MODE), d(new RemoteLinuxRunControlPrivate)
|
|
||||||
{
|
|
||||||
setIcon(Utils::Icons::RUN_SMALL_TOOLBAR);
|
|
||||||
setRunnable(rc->runnable());
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoteLinuxRunControl::~RemoteLinuxRunControl()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::start()
|
|
||||||
{
|
|
||||||
reportApplicationStart();
|
|
||||||
d->launcher.disconnect(this);
|
|
||||||
connect(&d->launcher, &ApplicationLauncher::reportError,
|
|
||||||
this, &RemoteLinuxRunControl::handleErrorMessage);
|
|
||||||
connect(&d->launcher, &ApplicationLauncher::remoteStderr,
|
|
||||||
this, &RemoteLinuxRunControl::handleRemoteErrorOutput);
|
|
||||||
connect(&d->launcher, &ApplicationLauncher::remoteStdout,
|
|
||||||
this, &RemoteLinuxRunControl::handleRemoteOutput);
|
|
||||||
connect(&d->launcher, &ApplicationLauncher::finished,
|
|
||||||
this, &RemoteLinuxRunControl::handleRunnerFinished);
|
|
||||||
connect(&d->launcher, &ApplicationLauncher::reportProgress,
|
|
||||||
this, &RemoteLinuxRunControl::handleProgressReport);
|
|
||||||
d->launcher.start(runnable(), device());
|
|
||||||
}
|
|
||||||
|
|
||||||
RunControl::StopResult RemoteLinuxRunControl::stop()
|
|
||||||
{
|
|
||||||
d->launcher.stop();
|
|
||||||
return AsynchronousStop;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::handleErrorMessage(const QString &error)
|
|
||||||
{
|
|
||||||
appendMessage(error, Utils::ErrorMessageFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::handleRunnerFinished()
|
|
||||||
{
|
|
||||||
setFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::handleRemoteOutput(const QByteArray &output)
|
|
||||||
{
|
|
||||||
appendMessage(QString::fromUtf8(output), Utils::StdOutFormatSameLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::handleRemoteErrorOutput(const QByteArray &output)
|
|
||||||
{
|
|
||||||
appendMessage(QString::fromUtf8(output), Utils::StdErrFormatSameLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::handleProgressReport(const QString &progressString)
|
|
||||||
{
|
|
||||||
appendMessage(progressString + QLatin1Char('\n'), Utils::NormalMessageFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteLinuxRunControl::setFinished()
|
|
||||||
{
|
|
||||||
d->launcher.disconnect(this);
|
|
||||||
reportApplicationStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace RemoteLinux
|
|
@@ -1,56 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** 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 The Qt Company. For licensing terms
|
|
||||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
||||||
** information use the contact form at https://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU
|
|
||||||
** General Public License version 3 as published by the Free Software
|
|
||||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
||||||
** included in the packaging of this file. Please review the following
|
|
||||||
** information to ensure the GNU General Public License requirements will
|
|
||||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "remotelinux_export.h"
|
|
||||||
|
|
||||||
#include <projectexplorer/runconfiguration.h>
|
|
||||||
|
|
||||||
namespace RemoteLinux {
|
|
||||||
|
|
||||||
class REMOTELINUX_EXPORT RemoteLinuxRunControl : public ProjectExplorer::RunControl
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit RemoteLinuxRunControl(ProjectExplorer::RunConfiguration *runConfig);
|
|
||||||
~RemoteLinuxRunControl() override;
|
|
||||||
|
|
||||||
virtual void start() override;
|
|
||||||
virtual StopResult stop() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void handleErrorMessage(const QString &error);
|
|
||||||
void handleRunnerFinished();
|
|
||||||
void handleRemoteOutput(const QByteArray &output);
|
|
||||||
void handleRemoteErrorOutput(const QByteArray &output);
|
|
||||||
void handleProgressReport(const QString &progressString);
|
|
||||||
void setFinished();
|
|
||||||
|
|
||||||
class RemoteLinuxRunControlPrivate;
|
|
||||||
RemoteLinuxRunControlPrivate * const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace RemoteLinux
|
|
@@ -29,7 +29,6 @@
|
|||||||
#include "remotelinuxdebugsupport.h"
|
#include "remotelinuxdebugsupport.h"
|
||||||
#include "remotelinuxcustomrunconfiguration.h"
|
#include "remotelinuxcustomrunconfiguration.h"
|
||||||
#include "remotelinuxrunconfiguration.h"
|
#include "remotelinuxrunconfiguration.h"
|
||||||
#include "remotelinuxruncontrol.h"
|
|
||||||
|
|
||||||
#include <debugger/analyzer/analyzermanager.h>
|
#include <debugger/analyzer/analyzermanager.h>
|
||||||
#include <debugger/analyzer/analyzerruncontrol.h>
|
#include <debugger/analyzer/analyzerruncontrol.h>
|
||||||
@@ -82,7 +81,7 @@ RunControl *RemoteLinuxRunControlFactory::create(RunConfiguration *runConfig, Co
|
|||||||
QTC_ASSERT(canRun(runConfig, mode), return 0);
|
QTC_ASSERT(canRun(runConfig, mode), return 0);
|
||||||
|
|
||||||
if (mode == ProjectExplorer::Constants::NORMAL_RUN_MODE)
|
if (mode == ProjectExplorer::Constants::NORMAL_RUN_MODE)
|
||||||
return new RemoteLinuxRunControl(runConfig);
|
return new SimpleRunControl(runConfig, mode);
|
||||||
|
|
||||||
const auto rcRunnable = runConfig->runnable();
|
const auto rcRunnable = runConfig->runnable();
|
||||||
QTC_ASSERT(rcRunnable.is<StandardRunnable>(), return 0);
|
QTC_ASSERT(rcRunnable.is<StandardRunnable>(), return 0);
|
||||||
|
Reference in New Issue
Block a user