-
Notifications
You must be signed in to change notification settings - Fork 248
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement new perf elevated privilege system
Instead of using a script that changes system configuration temporarily, just run perf as root. To properly synchronize with a launched app (that should not run as root), launch the app in a separate, initially stopped process, and use the control fifo feature of perf to properly synchronize with it. The control fifos are also needed to be able to stop sudo-perf, as one does not have permission to SIGINT it anymore.
- Loading branch information
1 parent
b2ee046
commit 171f568
Showing
10 changed files
with
335 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
#include "elevateprivilegeshelper.h" | ||
|
||
#include <QDebug> | ||
#include <QFile> | ||
#include <QSocketNotifier> | ||
#include <QStandardPaths> | ||
#include <QUuid> | ||
|
||
#include <fcntl.h> | ||
#include <signal.h> | ||
#include <sys/stat.h> | ||
#include <sys/types.h> | ||
#include <sys/wait.h> | ||
#include <vector> | ||
|
||
InitiallyStoppedProcess::~InitiallyStoppedProcess() | ||
{ | ||
kill(); | ||
} | ||
|
||
bool InitiallyStoppedProcess::reset(const QString &exePath, const QStringList &exeOptions, const QString &workingDirectory) | ||
{ | ||
kill(); | ||
|
||
// convert arguments and working dir into what the C API needs | ||
|
||
std::vector<QByteArray> args; | ||
args.reserve(exeOptions.size() + 1); | ||
args.emplace_back(exePath.toLocal8Bit()); | ||
for (const auto &opt : exeOptions) | ||
args.emplace_back(opt.toLocal8Bit()); | ||
const auto wd = workingDirectory.toLocal8Bit(); | ||
|
||
std::vector<char*> argsArray(args.size() + 1); | ||
for (size_t i = 0; i < args.size(); ++i) | ||
argsArray[i] = args[i].data(); | ||
argsArray.back() = nullptr; | ||
|
||
// fork | ||
m_pid = fork(); | ||
|
||
if (m_pid == 0) { // inside child process | ||
// change working dir | ||
if (!wd.isEmpty() && chdir(wd.data()) != 0) | ||
qFatal("Failed to change working directory to %s", wd.data()); | ||
|
||
// stop self | ||
raise(SIGSTOP); | ||
|
||
// exec | ||
execvp(argsArray[0], argsArray.data()); | ||
qFatal("Failed to exec %s", argsArray[0]); | ||
} else if (m_pid < 0) { | ||
qCritical("Failed to fork (?)"); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool InitiallyStoppedProcess::run() | ||
{ | ||
if (m_pid <= 0) | ||
return false; | ||
|
||
// wait for child to be stopped | ||
|
||
int wstatus; | ||
waitpid(m_pid, &wstatus, WUNTRACED); | ||
if (!WIFSTOPPED(wstatus)) { | ||
m_pid = -1; | ||
return false; | ||
} | ||
|
||
// continue | ||
|
||
::kill(m_pid, SIGCONT); | ||
return true; | ||
} | ||
|
||
void InitiallyStoppedProcess::terminate() | ||
{ | ||
if (m_pid > 0) | ||
::kill(m_pid, SIGTERM); | ||
} | ||
|
||
void InitiallyStoppedProcess::kill() | ||
{ | ||
if (m_pid > 0) { | ||
::kill(m_pid, SIGKILL); | ||
waitpid(m_pid, nullptr, 0); | ||
m_pid = -1; | ||
} | ||
} | ||
|
||
PerfControlFifoWrapper::~PerfControlFifoWrapper() | ||
{ | ||
close(); | ||
} | ||
|
||
bool PerfControlFifoWrapper::open() | ||
{ | ||
close(); | ||
|
||
QString fifoParentPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); | ||
if (fifoParentPath.isEmpty()) | ||
fifoParentPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation); | ||
|
||
const auto fifoBasePath = QStringLiteral("%1/hotspot-%2-%3-perf").arg(fifoParentPath, | ||
QString::number(getpid()), | ||
QUuid::createUuid().toString().mid(1, 6)); | ||
m_ctlFifoPath = fifoBasePath + QStringLiteral("-control.fifo"); | ||
m_ackFifoPath = fifoBasePath + QStringLiteral("-ack.fifo"); | ||
|
||
if (mkfifo(m_ctlFifoPath.toLocal8Bit().data(), 0600) != 0) { | ||
qCritical() << "Cannot create fifo" << m_ctlFifoPath; | ||
return false; | ||
} | ||
if (mkfifo(m_ackFifoPath.toLocal8Bit().data(), 0600) != 0) { | ||
qCritical() << "Cannot create fifo" << m_ackFifoPath; | ||
return false; | ||
} | ||
|
||
m_ctlFifoFd = ::open(m_ctlFifoPath.toLocal8Bit().data(), O_RDWR); | ||
if (m_ctlFifoFd < 0) { | ||
qCritical() << "Cannot open fifo" << m_ctlFifoPath; | ||
return false; | ||
} | ||
m_ackFifoFd = ::open(m_ackFifoPath.toLocal8Bit().data(), O_RDONLY | O_NONBLOCK); | ||
if (m_ackFifoFd < 0) { | ||
qCritical() << "Cannot open fifo" << m_ackFifoPath; | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void PerfControlFifoWrapper::start() | ||
{ | ||
if (m_ctlFifoFd < 0) | ||
return; | ||
|
||
m_ackReady = new QSocketNotifier(m_ackFifoFd, QSocketNotifier::Read); | ||
connect(m_ackReady, &QSocketNotifier::activated, this, [this] () { | ||
char buf[10]; | ||
read(m_ackFifoFd, buf, sizeof(buf)); | ||
emit started(); | ||
m_ackReady->disconnect(this); | ||
}); | ||
|
||
const char start_cmd[] = "enable\n"; | ||
write(m_ctlFifoFd, start_cmd, sizeof(start_cmd) - 1); | ||
} | ||
|
||
void PerfControlFifoWrapper::stop() | ||
{ | ||
if (m_ctlFifoFd < 0) | ||
return; | ||
const char stop_cmd[] = "stop\n"; | ||
write(m_ctlFifoFd, stop_cmd, sizeof(stop_cmd) - 1); | ||
} | ||
|
||
void PerfControlFifoWrapper::close() | ||
{ | ||
if (m_ackReady) { | ||
delete m_ackReady; | ||
m_ackReady = nullptr; | ||
} | ||
if (m_ctlFifoFd >= 0) { | ||
::close(m_ctlFifoFd); | ||
m_ctlFifoFd = -1; | ||
} | ||
if (m_ackFifoFd >= 0) { | ||
::close(m_ackFifoFd); | ||
m_ackFifoFd = -1; | ||
} | ||
if (!m_ctlFifoPath.isEmpty()) { | ||
QFile::remove(m_ctlFifoPath); | ||
m_ctlFifoPath.clear(); | ||
} | ||
if (!m_ackFifoPath.isEmpty()) { | ||
QFile::remove(m_ackFifoPath); | ||
m_ackFifoPath.clear(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
SPDX-FileCopyrightText: Zeno Endemann <[email protected]> | ||
SPDX-FileCopyrightText: 2016-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected] | ||
SPDX-License-Identifier: GPL-2.0-or-later | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <QObject> | ||
#include <QString> | ||
#include <QStringList> | ||
|
||
#include <unistd.h> | ||
|
||
class QSocketNotifier; | ||
|
||
class InitiallyStoppedProcess { | ||
public: | ||
InitiallyStoppedProcess() = default; | ||
~InitiallyStoppedProcess(); | ||
|
||
InitiallyStoppedProcess(const InitiallyStoppedProcess&) = delete; | ||
InitiallyStoppedProcess operator=(const InitiallyStoppedProcess&) = delete; | ||
|
||
pid_t processPID() const { return m_pid; } | ||
|
||
bool reset(const QString& exePath, const QStringList& exeOptions, const QString& workingDirectory); | ||
bool run(); | ||
void terminate(); | ||
void kill(); | ||
|
||
private: | ||
pid_t m_pid = -1; | ||
}; | ||
|
||
class PerfControlFifoWrapper : public QObject { | ||
Q_OBJECT | ||
|
||
public: | ||
using QObject::QObject; | ||
~PerfControlFifoWrapper(); | ||
|
||
bool isOpen() const { return m_ctlFifoFd >= 0; } | ||
QString controlFifoPath() const { return m_ctlFifoPath; } | ||
QString ackFifoPath() const { return m_ackFifoPath; } | ||
|
||
bool open(); | ||
void start(); | ||
void stop(); | ||
void close(); | ||
|
||
signals: | ||
void started(); | ||
|
||
private: | ||
QSocketNotifier *m_ackReady = nullptr; | ||
QString m_ctlFifoPath; | ||
QString m_ackFifoPath; | ||
int m_ctlFifoFd = -1; | ||
int m_ackFifoFd = -1; | ||
}; |
Oops, something went wrong.