如何使用C++/Qt与PolKit-Qt-1创建具有root权限的文件
如何通过PolKit-Qt-1实现以root权限创建/写入文件?
我是首次在StackOverflow提问,若有不当之处请见谅。日常在Linux环境下用C++/Qt Widget或C++/QML开发,现在需要实现点击按钮时以root权限创建或写入文件,所以基于PolKit-Qt-1开发测试C++/Qt Widget程序。用polkit-qt-gui-1实现相关功能,但目前文件是以执行用户权限创建/写入,出现权限错误,怀疑是配置文件或代码有问题。希望实现点击按钮时以root权限创建或写入文件,求解决方法。
源代码与配置文件
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); PolkitQt1::Gui::ActionButton *bt = new PolkitQt1::Gui::ActionButton(ui->pushButton, "org.qt.policykit.examples.write", this); bt->setText("Run with administrative privileges"); bt->setVisible(true, PolkitQt1::Gui::Action::No | PolkitQt1::Gui::Action::Auth | PolkitQt1::Gui::Action::Yes); bt->setEnabled(true, PolkitQt1::Gui::Action::No | PolkitQt1::Gui::Action::Auth | PolkitQt1::Gui::Action::Yes); connect(bt, SIGNAL(triggered(bool)), this, SLOT(activateAction())); connect(bt, SIGNAL(clicked(QAbstractButton*,bool)), bt, SLOT(activate())); connect(bt, SIGNAL(authorized()), this, SLOT(onBtnClicked())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::activateAction() { PolkitQt1::Gui::Action *action = qobject_cast<PolkitQt1::Gui::Action *>(sender()); action->activate(); } void MainWindow::onBtnClicked() { PolkitQt1::Gui::Action *action = qobject_cast<PolkitQt1::Gui::Action *>(sender()); qDebug() << "pretending to be the mechanism for action:" << action->actionId(); PolkitQt1::UnixProcessSubject subject(static_cast<uint>(QCoreApplication::applicationPid())); PolkitQt1::Authority::Result result = PolkitQt1::Authority::instance()->checkAuthorizationSync(action->actionId(), subject, PolkitQt1::Authority::AllowUserInteraction); if (result == PolkitQt1::Authority::Yes) { // Write /opt/sample.txt file with root privilege. writeTextFile("/opt/sample.txt", "foo bar"); } else { return; } return; } void MainWindow::writeTextFile(QString FileName, QString strOutputData) { QFileInfo FileInfo(FileName); QFile File(FileName); if(!File.open(QIODevice::WriteOnly)) { QString strErrMsg = "File(" + FileInfo.fileName() + ") Open Error: " + File.errorString(); qDebug() << strErrMsg; return; } QTextStream OutStream(&File); OutStream << strOutputData; File.close(); return; }
mainwindow.h
#define MAINWINDOW_H #include <QMainWindow> #include <QtWidgets> #include <QDBusContext> #include <QDBusMessage> #include <QDBusConnection> #include <polkit-qt5-1/polkitqt1-gui-action.h> #include <polkit-qt5-1/polkitqt1-gui-actionbutton.h> #include <polkit-qt5-1/polkitqt1-gui-actionbuttons.h> #include <polkit-qt5-1/polkitqt1-authority.h> #include <dbus/dbus.h> #include <QDebug> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow, protected QDBusContext { Q_OBJECT private: void writeTextFile(QString FileName, QString strOutputData); public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; private Q_SLOTS: void activateAction(); void onBtnClicked(); }; #endif // MAINWINDOW_H
/usr/share/polkit-1/actions/org.qt.policykit.examples.policy
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE policyconfig PUBLIC '-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN' 'http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd'> <policyconfig> <vendor>KDE</vendor> <vendor_url>http://www.kde.org</vendor_url> <action id="org.qt.policykit.examples.write"> <description>Write</description> <message>Prevents PolKit-Qt-1 example from writing</message> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> </action> </policyconfig>
问题分析与解决方法
核心问题:PolKit只是权限授权机制,不是直接的权限提升工具。你当前的代码仅完成了权限校验,但写入文件的操作仍在当前用户进程中执行,并未切换到root权限。要实现root权限的文件操作,有两种可行方案:
方案一:用pkexec启动独立辅助进程执行写入
PolKit配合pkexec可以让你以root身份启动专门的工具程序,由它完成文件写入,步骤如下:
- 编写独立的文件写入工具
filewriter:
// filewriter.cpp #include <QCoreApplication> #include <QFile> #include <QTextStream> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); if (argc != 3) { qDebug() << "Usage:" << argv[0] << "<file_path>" << "<content>"; return 1; } QString filePath = argv[1]; QString content = argv[2]; QFile file(filePath); if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open file:" << file.errorString(); return 1; } QTextStream out(&file); out << content; file.close(); return 0; }
编译并安装:
g++ -o filewriter filewriter.cpp -lQt5Core sudo install -m 755 filewriter /usr/local/bin/
- 修改主程序
onBtnClicked函数,调用pkexec执行工具:
void MainWindow::onBtnClicked() { PolkitQt1::Gui::Action *action = qobject_cast<PolkitQt1::Gui::Action *>(sender()); PolkitQt1::UnixProcessSubject subject(static_cast<uint>(QCoreApplication::applicationPid())); PolkitQt1::Authority::Result result = PolkitQt1::Authority::instance()->checkAuthorizationSync(action->actionId(), subject, PolkitQt1::Authority::AllowUserInteraction); if (result == PolkitQt1::Authority::Yes) { QProcess process; process.start("pkexec", {"/usr/local/bin/filewriter", "/opt/sample.txt", "foo bar"}); process.waitForFinished(); if (process.exitCode() != 0) { qDebug() << "File write failed:" << process.readAllStandardError(); } } }
- 更新PolKit配置文件,允许
pkexec调用该工具:
修改/usr/share/polkit-1/actions/org.qt.policykit.examples.policy的<action>节点:
<action id="org.qt.policykit.examples.write"> <description>Write file as root</description> <message>需要权限以root身份写入系统文件</message> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> <annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/filewriter</annotate> <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate> </action>
方案二:通过root权限的DBus服务执行写入
适合复杂操作场景,需要编写一个运行在root权限下的DBus服务,主程序授权后调用服务完成写入:
- 编写DBus接口定义
org.example.FileWriter.xml:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.example.FileWriter"> <method name="WriteFile"> <arg type="s" name="filePath" direction="in"/> <arg type="s" name="content" direction="in"/> <arg type="b" name="success" direction="out"/> </method> </interface> </node>
- 生成DBus适配代码并编写服务程序:
qdbusxml2cpp -c FileWriterAdaptor -a org.example.FileWriter.h:org.example.FileWriter.cpp org.example.FileWriter.xml
// filewriter_service.cpp #include <QCoreApplication> #include <QDBusConnection> #include <QFile> #include <QTextStream> #include "org.example.FileWriter.h" class FileWriterAdaptor : public org::example::FileWriterAdaptor, public QObject { Q_OBJECT public: FileWriterAdaptor(QObject *parent) : org::example::FileWriterAdaptor(parent) {} public slots: bool WriteFile(const QString &filePath, const QString &content) { QFile file(filePath); if (!file.open(QIODevice::WriteOnly)) { return false; } QTextStream out(&file); out << content; file.close(); return true; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QDBusConnection bus = QDBusConnection::systemBus(); if (!bus.registerService("org.example.FileWriter")) { qDebug() << "Failed to register service:" << bus.lastError().message(); return 1; } QObject obj; new FileWriterAdaptor(&obj); if (!bus.registerObject("/org/example/FileWriter", &obj)) { qDebug() << "Failed to register object:" << bus.lastError().message(); return 1; } return a.exec(); }
编译安装:
g++ -o filewriter_service filewriter_service.cpp org.example.FileWriter.cpp -lQt5Core -lQt5DBus sudo install -m 755 filewriter_service /usr/local/bin/
- 创建systemd服务文件
/etc/systemd/system/filewriter.service:
[Unit] Description=File Writer DBus Service [Service] ExecStart=/usr/local/bin/filewriter_service User=root [Install] WantedBy=multi-user.target
启动并启用服务:
sudo systemctl daemon-reload sudo systemctl start filewriter.service sudo systemctl enable filewriter.service
- 修改主程序
onBtnClicked函数调用DBus服务:
void MainWindow::onBtnClicked() { PolkitQt1::Gui::Action *action = qobject_cast<PolkitQt1::Gui::Action *>(sender()); PolkitQt1::UnixProcessSubject subject(static_cast<uint>(QCoreApplication::applicationPid())); PolkitQt1::Authority::Result result = PolkitQt1::Authority::instance()->checkAuthorizationSync(action->actionId(), subject, PolkitQt1::Authority::AllowUserInteraction); if (result == PolkitQt1::Authority::Yes) { QDBusInterface interface("org.example.FileWriter", "/org/example/FileWriter", "org.example.FileWriter", QDBusConnection::systemBus()); QDBusReply<bool> reply = interface.call("WriteFile", "/opt/sample.txt", "foo bar"); if (!reply.isValid()) { qDebug() << "DBus call failed:" << reply.error().message(); } else { qDebug() << "File write success:" << reply.value(); } } }
- 更新PolKit配置文件,允许调用该DBus服务:
在org.qt.policykit.examples.policy的<action>节点中添加:
<annotate key="org.freedesktop.policykit.dbus.name">org.example.FileWriter</annotate> <annotate key="org.freedesktop.policykit.dbus.path">/org/example/FileWriter</annotate> <annotate key="org.freedesktop.policykit.dbus.interface">org.example.FileWriter</annotate> <annotate key="org.freedesktop.policykit.dbus.method">WriteFile</annotate>
额外注意事项
- 确保PolKit配置文件权限正确:
sudo chmod 644 /usr/share/polkit-1/actions/org.qt.policykit.examples.policy - 测试前重启PolKit服务:
sudo systemctl restart polkit.service - 不要尝试在主进程中直接提升权限,不符合PolKit设计理念且存在安全风险
内容的提问来源于stack exchange,提问作者presire
相关产品推荐
相关产品推荐

