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

@@ -94,6 +94,7 @@ public:
QProcess::ProcessError m_error = QProcess::UnknownError; QProcess::ProcessError m_error = QProcess::UnknownError;
QString m_errorString; QString m_errorString;
bool m_abortOnMetaChars = true; bool m_abortOnMetaChars = true;
bool m_runAsRoot = false;
QSettings *m_settings = nullptr; QSettings *m_settings = nullptr;
// Used on Unix only // Used on Unix only
@@ -545,6 +546,7 @@ bool ConsoleProcess::start()
} }
d->m_environment.unset(QLatin1String("TERM")); d->m_environment.unset(QLatin1String("TERM"));
const QStringList env = d->m_environment.toStringList(); const QStringList env = d->m_environment.toStringList();
if (!env.isEmpty()) { if (!env.isEmpty()) {
d->m_tempFile = new QTemporaryFile(); d->m_tempFile = new QTemporaryFile();
@@ -571,20 +573,25 @@ bool ConsoleProcess::start()
const QString stubPath = QCoreApplication::applicationDirPath() const QString stubPath = QCoreApplication::applicationDirPath()
+ QLatin1String("/" QTC_REL_TOOLS_PATH "/qtcreator_process_stub"); + QLatin1String("/" QTC_REL_TOOLS_PATH "/qtcreator_process_stub");
QStringList allArgs = terminalArgs.toUnixArgs()
<< stubPath QStringList allArgs = terminalArgs.toUnixArgs();
<< modeOption(d->m_mode) if (d->m_runAsRoot)
<< d->m_stubServer.fullServerName() allArgs << "sudo" << "-A";
<< msgPromptToClose()
<< workingDirectory() allArgs << stubPath
<< (d->m_tempFile ? d->m_tempFile->fileName() : QString()) << modeOption(d->m_mode)
<< QString::number(getpid()) << d->m_stubServer.fullServerName()
<< pcmd << msgPromptToClose()
<< pargs.toUnixArgs(); << workingDirectory()
<< (d->m_tempFile ? d->m_tempFile->fileName() : QString())
<< QString::number(getpid())
<< pcmd
<< pargs.toUnixArgs();
if (terminal.needsQuotes) if (terminal.needsQuotes)
allArgs = QStringList { QtcProcess::joinArgs(allArgs) }; allArgs = QStringList { QtcProcess::joinArgs(allArgs) };
d->m_process.setEnvironment(env);
d->m_process.start(terminal.command, allArgs); d->m_process.start(terminal.command, allArgs);
if (!d->m_process.waitForStarted()) { if (!d->m_process.waitForStarted()) {
stubServerShutdown(); stubServerShutdown();
@@ -958,6 +965,11 @@ Environment ConsoleProcess::environment() const
return d->m_environment; return d->m_environment;
} }
void Utils::ConsoleProcess::setRunAsRoot(bool on)
{
d->m_runAsRoot = on;
}
QProcess::ProcessError ConsoleProcess::error() const QProcess::ProcessError ConsoleProcess::error() const
{ {
return d->m_error; return d->m_error;

View File

@@ -66,6 +66,7 @@ public:
void setCommand(const Utils::CommandLine &command); void setCommand(const Utils::CommandLine &command);
Utils::CommandLine command() const; Utils::CommandLine command() const;
void setAbortOnMetaChars(bool abort); void setAbortOnMetaChars(bool abort);
void setWorkingDirectory(const QString &dir); void setWorkingDirectory(const QString &dir);
@@ -74,6 +75,8 @@ public:
void setEnvironment(const Environment &env); void setEnvironment(const Environment &env);
Environment environment() const; Environment environment() const;
void setRunAsRoot(bool on);
QProcess::ProcessError error() const; QProcess::ProcessError error() const;
QString errorString() const; QString errorString() const;

View File

@@ -169,6 +169,7 @@ public:
bool breakOnMain = false; bool breakOnMain = false;
bool multiProcess = false; // Whether to set detach-on-fork off. bool multiProcess = false; // Whether to set detach-on-fork off.
bool useTerminal = false; bool useTerminal = false;
bool runAsRoot = false;
ProjectExplorer::Runnable debugger; ProjectExplorer::Runnable debugger;
QString overrideStartScript; // Used in attach to core and remote debugging 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) { if (on && !d->terminalRunner && !useCdbConsole) {
d->terminalRunner = new TerminalRunner(runControl(), m_runParameters.inferior); d->terminalRunner = new TerminalRunner(runControl(), m_runParameters.inferior);
d->terminalRunner->setRunAsRoot(m_runParameters.runAsRoot);
addStartDependency(d->terminalRunner); addStartDependency(d->terminalRunner);
} }
if (!on && 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) void DebuggerRunTool::setCommandsAfterConnect(const QString &commands)
{ {
m_runParameters.commandsAfterConnect = commands; m_runParameters.commandsAfterConnect = commands;
@@ -948,6 +956,8 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
m_runParameters.symbolFile = symbolsAspect->filePath(); m_runParameters.symbolFile = symbolsAspect->filePath();
if (auto terminalAspect = runControl->aspect<TerminalAspect>()) if (auto terminalAspect = runControl->aspect<TerminalAspect>())
m_runParameters.useTerminal = terminalAspect->useTerminal(); m_runParameters.useTerminal = terminalAspect->useTerminal();
if (auto runAsRootAspect = runControl->aspect<RunAsRootAspect>())
m_runParameters.runAsRoot = runAsRootAspect->value();
Kit *kit = runControl->kit(); Kit *kit = runControl->kit();
QTC_ASSERT(kit, return); QTC_ASSERT(kit, return);

View File

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

View File

@@ -3853,11 +3853,20 @@ void GdbEngine::setupEngine()
if (!boolSetting(LoadGdbInit)) if (!boolSetting(LoadGdbInit))
gdbCommand.addArg("-n"); 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()); showMessage("STARTING " + gdbCommand.toUserOutput());
m_gdbProc.setCommand(gdbCommand); m_gdbProc.setCommand(gdbCommand);
if (QFileInfo(rp.debugger.workingDirectory).isDir()) if (QFileInfo(rp.debugger.workingDirectory).isDir())
m_gdbProc.setWorkingDirectory(rp.debugger.workingDirectory); m_gdbProc.setWorkingDirectory(rp.debugger.workingDirectory);
m_gdbProc.setEnvironment(rp.debugger.environment); m_gdbProc.setEnvironment(gdbEnv);
m_gdbProc.start(); m_gdbProc.start();
if (!m_gdbProc.waitForStarted()) { if (!m_gdbProc.waitForStarted()) {
@@ -4256,7 +4265,15 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
return; return;
} }
QString errorMessage; 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)); showMessage("Interrupted " + QString::number(pid));
} else { } else {
showMessage(errorMessage, LogError); showMessage(errorMessage, LogError);
@@ -4622,10 +4639,13 @@ void GdbEngine::interruptInferior2()
} }
} }
} else if (isTermEngine() || isPlainEngine()) { } else if (isPlainEngine()) {
interruptLocalInferior(inferiorPid()); interruptLocalInferior(inferiorPid());
} else if (isTermEngine()) {
terminal()->interruptProcess();
} }
} }

View File

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

View File

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

View File

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

View File

@@ -54,6 +54,7 @@ public:
void setProcessChannelMode(QProcess::ProcessChannelMode mode); void setProcessChannelMode(QProcess::ProcessChannelMode mode);
void setUseTerminal(bool on); void setUseTerminal(bool on);
void setRunAsRoot(bool on);
void start(const Runnable &runnable); void start(const Runnable &runnable);
void start(const Runnable &runnable, const IDevice::ConstPtr &device); void start(const Runnable &runnable, const IDevice::ConstPtr &device);
void stop(); 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) { envAspect->addModifier([this, libAspect](Environment &env) {
BuildTargetInfo bti = buildTargetInfo(); BuildTargetInfo bti = buildTargetInfo();
if (bti.runEnvModifier) if (bti.runEnvModifier)

View File

@@ -736,4 +736,19 @@ UseDyldSuffixAspect::UseDyldSuffixAspect()
LabelPlacement::AtCheckBox); 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 } // namespace ProjectExplorer

View File

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

View File

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

View File

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