ProjectExplorer/Debugger: Add a 'Run as root' option for Unix hosts

For local run and GDB debug, with or without terminal.

Task-number: QTCREATORBUG-2831
Task-number: QTCREATORBUG-25330
Change-Id: I9b5d2156bcffea4f358474ecdbcad580a4419917
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
hjk
2021-02-15 16:55:28 +01:00
parent 9bc9a4d376
commit c4b7da9ab2
15 changed files with 128 additions and 20 deletions

View File

@@ -169,6 +169,7 @@ public:
bool breakOnMain = false;
bool multiProcess = false; // Whether to set detach-on-fork off.
bool useTerminal = false;
bool runAsRoot = false;
ProjectExplorer::Runnable debugger;
QString overrideStartScript; // Used in attach to core and remote debugging

View File

@@ -395,6 +395,7 @@ void DebuggerRunTool::setUseTerminal(bool on)
if (on && !d->terminalRunner && !useCdbConsole) {
d->terminalRunner = new TerminalRunner(runControl(), m_runParameters.inferior);
d->terminalRunner->setRunAsRoot(m_runParameters.runAsRoot);
addStartDependency(d->terminalRunner);
}
if (!on && d->terminalRunner) {
@@ -402,6 +403,13 @@ void DebuggerRunTool::setUseTerminal(bool on)
}
}
void DebuggerRunTool::setRunAsRoot(bool on)
{
m_runParameters.runAsRoot = on;
if (d->terminalRunner)
d->terminalRunner->setRunAsRoot(on);
}
void DebuggerRunTool::setCommandsAfterConnect(const QString &commands)
{
m_runParameters.commandsAfterConnect = commands;
@@ -948,6 +956,8 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
m_runParameters.symbolFile = symbolsAspect->filePath();
if (auto terminalAspect = runControl->aspect<TerminalAspect>())
m_runParameters.useTerminal = terminalAspect->useTerminal();
if (auto runAsRootAspect = runControl->aspect<RunAsRootAspect>())
m_runParameters.runAsRoot = runAsRootAspect->value();
Kit *kit = runControl->kit();
QTC_ASSERT(kit, return);

View File

@@ -108,6 +108,7 @@ public:
void setUseCtrlCStub(bool on);
void setBreakOnMain(bool on);
void setUseTerminal(bool on);
void setRunAsRoot(bool on);
void setCommandsAfterConnect(const QString &commands);
void setCommandsForReset(const QString &commands);

View File

@@ -3853,11 +3853,20 @@ void GdbEngine::setupEngine()
if (!boolSetting(LoadGdbInit))
gdbCommand.addArg("-n");
Environment gdbEnv = rp.debugger.environment;
if (rp.runAsRoot) {
CommandLine wrapped("sudo", {"-A"});
wrapped.addArgs(gdbCommand);
gdbCommand = wrapped;
RunControl::provideAskPassEntry(gdbEnv);
}
showMessage("STARTING " + gdbCommand.toUserOutput());
m_gdbProc.setCommand(gdbCommand);
if (QFileInfo(rp.debugger.workingDirectory).isDir())
m_gdbProc.setWorkingDirectory(rp.debugger.workingDirectory);
m_gdbProc.setEnvironment(rp.debugger.environment);
m_gdbProc.setEnvironment(gdbEnv);
m_gdbProc.start();
if (!m_gdbProc.waitForStarted()) {
@@ -4256,7 +4265,15 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
return;
}
QString errorMessage;
if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
if (runParameters().runAsRoot) {
Environment env = Environment::systemEnvironment();
RunControl::provideAskPassEntry(env);
QtcProcess proc;
proc.setCommand(CommandLine{"sudo", {"-A", "kill", "-s", "SIGINT", QString::number(pid)}});
proc.setEnvironment(env);
proc.start();
proc.waitForFinished();
} else if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
showMessage("Interrupted " + QString::number(pid));
} else {
showMessage(errorMessage, LogError);
@@ -4622,10 +4639,13 @@ void GdbEngine::interruptInferior2()
}
}
} else if (isTermEngine() || isPlainEngine()) {
} else if (isPlainEngine()) {
interruptLocalInferior(inferiorPid());
} else if (isTermEngine()) {
terminal()->interruptProcess();
}
}

View File

@@ -188,8 +188,18 @@ void TerminalRunner::interruptProcess()
m_stubProc.interruptProcess();
}
void TerminalRunner::setRunAsRoot(bool on)
{
m_runAsRoot = on;
}
void TerminalRunner::start()
{
if (m_runAsRoot) {
m_stubProc.setRunAsRoot(true);
RunControl::provideAskPassEntry(m_stubRunnable.environment);
}
m_stubProc.setEnvironment(m_stubRunnable.environment);
m_stubProc.setWorkingDirectory(m_stubRunnable.workingDirectory);

View File

@@ -78,6 +78,7 @@ public:
qint64 applicationMainThreadId() const { return m_applicationMainThreadId; }
void interruptProcess();
void setRunAsRoot(bool on);
private:
void start() final;
@@ -90,6 +91,7 @@ private:
ProjectExplorer::Runnable m_stubRunnable;
qint64 m_applicationPid = 0;
qint64 m_applicationMainThreadId = 0;
bool m_runAsRoot = false;
};
} // namespace Internal

View File

@@ -100,6 +100,7 @@ public:
ApplicationLauncher *q;
bool m_isLocal = true;
bool m_runAsRoot = false;
// Local
QtcProcess m_guiProcess;
@@ -179,6 +180,11 @@ void ApplicationLauncher::setUseTerminal(bool on)
d->m_useTerminal = on;
}
void ApplicationLauncher::setRunAsRoot(bool on)
{
d->m_runAsRoot = on;
}
void ApplicationLauncher::stop()
{
d->stop();
@@ -368,8 +374,13 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::
const QString fixedPath = FileUtils::normalizePathName(runnable.workingDirectory);
m_guiProcess.setWorkingDirectory(fixedPath);
m_consoleProcess.setWorkingDirectory(fixedPath);
m_guiProcess.setEnvironment(runnable.environment);
m_consoleProcess.setEnvironment(runnable.environment);
Environment env = runnable.environment;
if (m_runAsRoot)
RunControl::provideAskPassEntry(env);
m_guiProcess.setEnvironment(env);
m_consoleProcess.setEnvironment(env);
m_processRunning = true;
#ifdef Q_OS_WIN
@@ -377,13 +388,20 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::
WinDebugInterface::instance()->start(); // Try to start listener again...
#endif
if (!m_useTerminal) {
m_guiProcess.setCommand(runnable.commandLine());
CommandLine cmdLine = runnable.commandLine();
if (m_runAsRoot) {
CommandLine wrapped("sudo", {"-A"});
wrapped.addArgs(cmdLine);
cmdLine = wrapped;
}
if (m_useTerminal) {
m_consoleProcess.setCommand(cmdLine);
m_consoleProcess.start();
} else {
m_guiProcess.setCommand(cmdLine);
m_guiProcess.closeWriteChannel();
m_guiProcess.start();
} else {
m_consoleProcess.setCommand(runnable.commandLine());
m_consoleProcess.start();
}
} else {
QTC_ASSERT(m_state == Inactive, return);

View File

@@ -54,6 +54,7 @@ public:
void setProcessChannelMode(QProcess::ProcessChannelMode mode);
void setUseTerminal(bool on);
void setRunAsRoot(bool on);
void start(const Runnable &runnable);
void start(const Runnable &runnable, const IDevice::ConstPtr &device);
void stop();

View File

@@ -88,6 +88,9 @@ DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Utils::Id id, K
});
}
if (HostOsInfo::isAnyUnixHost())
addAspect<RunAsRootAspect>();
envAspect->addModifier([this, libAspect](Environment &env) {
BuildTargetInfo bti = buildTargetInfo();
if (bti.runEnvModifier)

View File

@@ -736,4 +736,19 @@ UseDyldSuffixAspect::UseDyldSuffixAspect()
LabelPlacement::AtCheckBox);
}
/*!
\class ProjectExplorer::RunAsRootAspect
\inmodule QtCreator
\brief The RunAsRootAspect class lets a user specify whether the
application should run with root permissions.
*/
RunAsRootAspect::RunAsRootAspect()
{
setId("RunAsRoot");
setSettingsKey("RunConfiguration.RunAsRoot");
setLabel(tr("Run as root user"), LabelPlacement::AtCheckBox);
}
} // namespace ProjectExplorer

View File

@@ -147,6 +147,14 @@ public:
UseDyldSuffixAspect();
};
class PROJECTEXPLORER_EXPORT RunAsRootAspect : public Utils::BoolAspect
{
Q_OBJECT
public:
RunAsRootAspect();
};
class PROJECTEXPLORER_EXPORT ExecutableAspect : public Utils::BaseAspect
{
Q_OBJECT

View File

@@ -1154,6 +1154,8 @@ SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl)
setId("SimpleTargetRunner");
if (auto terminalAspect = runControl->aspect<TerminalAspect>())
m_useTerminal = terminalAspect->useTerminal();
if (auto runAsRootAspect = runControl->aspect<RunAsRootAspect>())
m_runAsRoot = runAsRootAspect->value();
}
void SimpleTargetRunner::start()
@@ -1169,6 +1171,7 @@ void SimpleTargetRunner::doStart(const Runnable &runnable, const IDevice::ConstP
m_stopReported = false;
m_launcher.disconnect(this);
m_launcher.setUseTerminal(m_useTerminal);
m_launcher.setRunAsRoot(m_runAsRoot);
const bool isDesktop = device.isNull() || device.dynamicCast<const DesktopDevice>();
const QString rawDisplayName = runnable.displayName();

View File

@@ -302,6 +302,7 @@ private:
bool m_stopReported = false;
bool m_useTerminal = false;
bool m_runAsRoot = false;
};
class PROJECTEXPLORER_EXPORT OutputFormatterFactory