From c4b7da9ab286c9c18e474b6c63938f9ac0045c37 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 15 Feb 2021 16:55:28 +0100 Subject: [PATCH] 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 Reviewed-by: Christian Kandeler --- src/libs/utils/consoleprocess.cpp | 32 +++++++++++++------ src/libs/utils/consoleprocess.h | 3 ++ src/plugins/debugger/debuggerengine.h | 1 + src/plugins/debugger/debuggerruncontrol.cpp | 10 ++++++ src/plugins/debugger/debuggerruncontrol.h | 1 + src/plugins/debugger/gdb/gdbengine.cpp | 26 +++++++++++++-- src/plugins/debugger/terminal.cpp | 10 ++++++ src/plugins/debugger/terminal.h | 2 ++ .../projectexplorer/applicationlauncher.cpp | 32 +++++++++++++++---- .../projectexplorer/applicationlauncher.h | 1 + .../desktoprunconfiguration.cpp | 3 ++ .../runconfigurationaspects.cpp | 15 +++++++++ .../projectexplorer/runconfigurationaspects.h | 8 +++++ src/plugins/projectexplorer/runcontrol.cpp | 3 ++ src/plugins/projectexplorer/runcontrol.h | 1 + 15 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/libs/utils/consoleprocess.cpp b/src/libs/utils/consoleprocess.cpp index af3b352905f..bd6f6231b8c 100644 --- a/src/libs/utils/consoleprocess.cpp +++ b/src/libs/utils/consoleprocess.cpp @@ -94,6 +94,7 @@ public: QProcess::ProcessError m_error = QProcess::UnknownError; QString m_errorString; bool m_abortOnMetaChars = true; + bool m_runAsRoot = false; QSettings *m_settings = nullptr; // Used on Unix only @@ -545,6 +546,7 @@ bool ConsoleProcess::start() } d->m_environment.unset(QLatin1String("TERM")); + const QStringList env = d->m_environment.toStringList(); if (!env.isEmpty()) { d->m_tempFile = new QTemporaryFile(); @@ -571,20 +573,25 @@ bool ConsoleProcess::start() const QString stubPath = QCoreApplication::applicationDirPath() + QLatin1String("/" QTC_REL_TOOLS_PATH "/qtcreator_process_stub"); - QStringList allArgs = terminalArgs.toUnixArgs() - << stubPath - << modeOption(d->m_mode) - << d->m_stubServer.fullServerName() - << msgPromptToClose() - << workingDirectory() - << (d->m_tempFile ? d->m_tempFile->fileName() : QString()) - << QString::number(getpid()) - << pcmd - << pargs.toUnixArgs(); + + QStringList allArgs = terminalArgs.toUnixArgs(); + if (d->m_runAsRoot) + allArgs << "sudo" << "-A"; + + allArgs << stubPath + << modeOption(d->m_mode) + << d->m_stubServer.fullServerName() + << msgPromptToClose() + << workingDirectory() + << (d->m_tempFile ? d->m_tempFile->fileName() : QString()) + << QString::number(getpid()) + << pcmd + << pargs.toUnixArgs(); if (terminal.needsQuotes) allArgs = QStringList { QtcProcess::joinArgs(allArgs) }; + d->m_process.setEnvironment(env); d->m_process.start(terminal.command, allArgs); if (!d->m_process.waitForStarted()) { stubServerShutdown(); @@ -958,6 +965,11 @@ Environment ConsoleProcess::environment() const return d->m_environment; } +void Utils::ConsoleProcess::setRunAsRoot(bool on) +{ + d->m_runAsRoot = on; +} + QProcess::ProcessError ConsoleProcess::error() const { return d->m_error; diff --git a/src/libs/utils/consoleprocess.h b/src/libs/utils/consoleprocess.h index e436e8c9485..ce53e0d87c7 100644 --- a/src/libs/utils/consoleprocess.h +++ b/src/libs/utils/consoleprocess.h @@ -66,6 +66,7 @@ public: void setCommand(const Utils::CommandLine &command); Utils::CommandLine command() const; + void setAbortOnMetaChars(bool abort); void setWorkingDirectory(const QString &dir); @@ -74,6 +75,8 @@ public: void setEnvironment(const Environment &env); Environment environment() const; + void setRunAsRoot(bool on); + QProcess::ProcessError error() const; QString errorString() const; diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 1502fe8d569..50162002fde 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -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 diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 49f8ed79eb2..d10e13938a6 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -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()) m_runParameters.useTerminal = terminalAspect->useTerminal(); + if (auto runAsRootAspect = runControl->aspect()) + m_runParameters.runAsRoot = runAsRootAspect->value(); Kit *kit = runControl->kit(); QTC_ASSERT(kit, return); diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index a971c25807e..eb04d9a7b07 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -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); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 93dafe23632..42ee110f7e8 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -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(); } } diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp index 5cc062b7723..c54336c0456 100644 --- a/src/plugins/debugger/terminal.cpp +++ b/src/plugins/debugger/terminal.cpp @@ -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); diff --git a/src/plugins/debugger/terminal.h b/src/plugins/debugger/terminal.h index 205e47bba9b..410d0fb7d91 100644 --- a/src/plugins/debugger/terminal.h +++ b/src/plugins/debugger/terminal.h @@ -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 diff --git a/src/plugins/projectexplorer/applicationlauncher.cpp b/src/plugins/projectexplorer/applicationlauncher.cpp index 060b42d23c4..37a707968a8 100644 --- a/src/plugins/projectexplorer/applicationlauncher.cpp +++ b/src/plugins/projectexplorer/applicationlauncher.cpp @@ -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); diff --git a/src/plugins/projectexplorer/applicationlauncher.h b/src/plugins/projectexplorer/applicationlauncher.h index 68376fece0b..ca93c69abd8 100644 --- a/src/plugins/projectexplorer/applicationlauncher.h +++ b/src/plugins/projectexplorer/applicationlauncher.h @@ -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(); diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp index a37817e108f..dcf100ac9de 100644 --- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp +++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp @@ -88,6 +88,9 @@ DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Utils::Id id, K }); } + if (HostOsInfo::isAnyUnixHost()) + addAspect(); + envAspect->addModifier([this, libAspect](Environment &env) { BuildTargetInfo bti = buildTargetInfo(); if (bti.runEnvModifier) diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index b0ca3792874..6aca37d98c1 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -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 diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 55984905c74..508d308ef95 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -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 diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 0ba5ca28e06..7f35f525aba 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1154,6 +1154,8 @@ SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) setId("SimpleTargetRunner"); if (auto terminalAspect = runControl->aspect()) m_useTerminal = terminalAspect->useTerminal(); + if (auto runAsRootAspect = runControl->aspect()) + 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 QString rawDisplayName = runnable.displayName(); diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 03b8041246b..b900ee9b8df 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -302,6 +302,7 @@ private: bool m_stopReported = false; bool m_useTerminal = false; + bool m_runAsRoot = false; }; class PROJECTEXPLORER_EXPORT OutputFormatterFactory