简述
Qt程序在Linux下以root权限启动且询问密码授权,此功能有多种方法能实现且各有各的问题以下简单说明他人实现和个人实现的方式。
1.通过pkexec弹出授权框来启动程序.
2.通过sudo -S 来启动程序,下面详细讲解如何启动和传入密码.
3.查看QtIFW源码,IFW安装包在linux下安装过程中安装到usr目录会弹出授权.
方法一:
这里采用的是pkexec方法,主要适用于非双击启动,且需要root权限下写一次配置文件,参考博客点击跳转.
方法二:
以下代码方式主要思路就是在usr下创建隐藏文件夹。
如果创建失败就是非root权限循环输入密码传给bash来执行。
此处创建文件夹的目的只是为了让上一个非root权限启动的程序知道已经启动了一个root权限的进程然后退出。
暂时未考虑到更好的方式有思路还请大佬评论区展现风采。
#if defined (Q_OS_LINUX)
__uid_t id = getuid();
int uid = id;
qint64 pid = QCoreApplication::applicationPid();
QString newExeName = QCoreApplication::applicationName();
//此功能主要是限制一下防止多次启动,
QProcess* exeLsit = new QProcess;
exeLsit->start("ps -ef");
exeLsit->waitForFinished();
QString psData = QString::fromLocal8Bit(exeLsit->readAll());
int count = psData.count(newExeName);
//这里要注意的是如果在qt编译会生成编译目录路径带exeName导致会+1所以需要--,如果打包出来的就注释掉,
count--;
if (count > 1 && uid != 0) {
exit(0);
}
//此判断用于再次启动后为了下面 创建文件夹保证和上一个程序的判断文件夹名相等!
if (argc >= 2) {
QString temp = argv[1];
pid = temp.toLongLong();
}
qDebug() << "uid=" << uid;
qDebug() << "newExeName=" << newExeName;
qDebug() << "count=" << count;
qDebug() << "pid=" << pid;
//每次根据随机的pid创建
QString mkdirPath = ".QWERZDFASHGFXZASRwqeasd" + QString::number(pid);
QDir d("/usr");
d.rmdir(mkdirPath);
//普通用户运行删除和创建都失败那么就进入else,热如果是root直接打开窗口
if (d.mkdir(mkdirPath)) {
QThread::msleep(1000);
//此处root权限启动后可以在给他删掉上面加延时的原因一方面为了效果,另一方面是为了下面的while未执行完导致创建出来的删掉了,所以还会弹出一次密码框,此处需要优化。依靠延时若果卡了一下就凉了
//d.rmdir("/usr/" + mkdirPath);
language* w = new language;
w->show();
}
else {
//从linux下proc获取进程绝对路径
QString path = "/proc/" + QString::number(QCoreApplication::applicationPid()) + "/cmdline";
QFile f(path);
QString appPath;
//此处QFile如果不是appimagetool制作的程序可以省略直接获取运行的绝对路径即可
if (f.open(QIODevice::ReadOnly)) {
//appPath = f.readAll();//如果是appimagetool制作的程序那么使用下面的获取路径会不准
appPath = QCoreApplication::applicationFilePath();//自测,用于非appimagetool制作程序
}
else {
//防止特殊异常情况判断
}
QThread::msleep(500);
QString passwordError;
int num = 0;
while (!QDir("/usr/" + mkdirPath).exists()) {
QStringList args;
QProcess* ps = new QProcess;
if (num >= 3) {
QMessageBox::warning(0,"密码错误次数过多", "请使用root权限启动");
exit(0);
}
QString text = "";
if (passwordError.isEmpty()) {
QInputDialog* input = new QInputDialog;
input->resize(300, 300);
input->setWindowTitle("在安装前请先进行程序授权!");
input->setLabelText("输入密码");
input->setTextEchoMode(QLineEdit::Password);
input->exec();
text = input->textValue();
}
else {
QInputDialog* input = new QInputDialog;
input->resize(410, 300);
input->setWindowTitle(passwordError);
input->setLabelText("输入密码");
input->setTextEchoMode(QLineEdit::Password);
input->exec();
text = input->textValue();
}
if (!text.isEmpty()) {
args.clear();
args << "-c";
args << "echo '" + text + "'" + " | sudo -S " + appPath + QString(" %1").arg(pid);
qDebug() << args;
ps->start("/bin/bash", args); //此处也可运行sh进程
ps->waitForFinished(1000);
}
else {
exit(0);
}
num++;
passwordError = "密码错误" + QString::number(num) + "次,请重新输入密码,或者以root权限启动程序";
}
exit(0);
}
#else
#endif
return a.exec();
非root权限启动后弹出授权框,密码正确,则以root权限启动程序。
方法三:
参考一下QT的IFW源码,IFW制作出的安装包在安装过程中,如果安装到非root权限目录会弹出授权框,密码正确后会进行安装。
鄙人看过他的这个授权类,学识短浅,里面的大部分函数都没用过,但是大体知道是制作了一个伪终端,也是去利用sudo -S去启动一个进程,但他不像方法二,一样需要重新进入,源码函数调用传入的也是applicationFilePath自身程序,不清楚是做到了临时提权还是,又启动了另一个进程,有懂的大佬还请评论区讲解。