当前位置: 首页 > news >正文

SElinux 导致 Keepalived 检测脚本无法执行

哈喽大家好,我是咸鱼

今天我们来看一个关于 Keepalived 检测脚本无法执行的问题

一位粉丝后台私信我,说他部署的 keepalived 集群 vrrp_script 模块中的脚本执行失败了,但是手动执行这个脚本却没有任何问题

在这里插入图片描述
这个问题也是咸鱼第一次遇到,为了能让更多的小伙伴以后不会踩这个坑,便有了今天这篇文章

前言

在正式开始之前,我们先来简单复习一下 Keepalived 中的资源检测功能

vrrp_script 模块

Keepalived 中,vrrp_script 模块是用于定义和配置虚拟路由冗余协议(VRRP)的自定义脚本检查,这个模块专门用于对集群中的服务资源进行监控

vrrp_script 模块搭配使用的是 track_script 模块,这个模块中可以引入监控脚本、命令组合、Shell 语句来实现对服务资源的监控

track_script 通过调用 vrrp_script,可以灵活地定义需要监测的服务或资源,例如网络连接、服务状态、系统资源等

当监测到故障时,Keepalived 可以触发状态转移,将主节点切换到备用节点,以确保服务的高可用性

  • 通过 killall -l 命令监测

killall 命令会发送一个信号给进程,以信号 0 为例,如果发现进程关闭或者异常,将返回状态码 1,反之进程运行正常,状态码返回0

vrrp_script nginx_check {script "killall -0 nginx"interval 2
}track_script {nginx_check
}
  • 通过端口监测

检测端口的运行状态也是较常见的监控方式

vrrp_script nginx_check {script "</dev/tcp/127.0.0.1/80"interval 2fall 2rise 1
}track_script {nginx_check
}

其中 fall表示检测到失败的最大次数(如果请求失败两次,就认为该节点发生了故障)

rise 表示如果请求一次成功,就认为该节点恢复正常

  • 通过 shell 语句监测
vrrp_script nginx_check {script "if [ $(pidof nginx | wc -l) -eq 0 ]; then exit 1; else exit 0; fi"interval 2fall 2rise 1
}track_script {nginx_check
}
  • 通过脚本监测
vrrp_script nginx_check {script "/etc/keepalived/nginx_check.sh"interval 2fall 2rise 1
}track_script {nginx_check
}

问题

在介绍完了 keepalived 的监测功能之后,我们来看下这个问题

我根据他的描述复现了这个场景:keepalived 通过监测上游网络来判断该节点是否正常运行,如果到上游的网络不通,则认为该节点发生了故障

检测脚本如下:

[root@localhost ~]# cat /etc/keepalived/check.sh 
#!/bin/bash
# 检查上行链路
check_route="192.168.149.135"
ping -c 3 $check_route >/dev/null 2>&1
result=$?
echo "${date}----checking..... result:${result}" >> /tools/log.log
if [ $? -eq 0 ]; thenexit 0          # 正常
elseexit 1          # 链路异常
fi

一切准备就绪之后启动 keepalived 服务发现报 Keepalived_vrrp[2653]: /etc/keepalived/check.sh exited with status 1

[root@localhost ~]# systemctl status keepalived.service 
● keepalived.service - LVS and VRRP High Availability MonitorLoaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; vendor preset: disabled)Active: active (running) since 三 2023-08-14 23:53:49 CST; 4min 56s ago...
814 23:53:49 localhost.localdomain Keepalived_vrrp[2653]: /etc/keepalived/check.sh exited with status 1
...

说明脚本没有执行成功,返回状态码 1 了,我尝试着手动执行,发现脚本没有任何问题

[root@localhost ~]# sh /etc/keepalived/check.sh 
[root@localhost ~]# echo $?
0

排查

首先看一下 /var/log/messages(如果 keepalived 没有专门指定日志文件路径,这个便是默认的日志文件路径)

...
Aug 14 23:53:49 localhost Keepalived_vrrp[17889]: SECURITY VIOLATION - scripts are being executed but script_security not enabled
...
Aug 14 23:53:49 localhost Keepalived_vrrp[17889]: /etc/keepalived/check.sh exited with status 1
...

SECURITY VIOLATION - scripts are being executed but script_security not enabled 这条信息引起了我的注意

”安全违规-脚本正在执行,但 script_security 未启用“,看输出应该是 keepalived 进程想要执行该脚本,但是受到了安全限制

既然是跟系统安全相关的,我们就先来看看这个脚本的权限吧

# 查看脚本权限
[root@localhost ~]# ll /etc/keepalived/check.sh 
-rwxr-xr-x. 1 root root 281 89 15:52 /etc/keepalived/check.sh# 查看是 keepalived 进程的属主
[root@localhost ~]# ps -ef | grep keep
root      19163      1  0 01:00 ?        00:00:00 /usr/sbin/keepalived -D
...

由上面的输出我们可以得知 keepalived 进程的属主是 root ,而 root 用户是可以去执行这个脚本的(有 x 权限)

权限没问题,我们再来查看下 /var/log/audit/audit.log

/var/log/audit/audit.log 是一个存储系统审计日志的文件

这个文件记录了系统中发生的各种安全事件、用户操作和系统行为,以及与安全相关的信息

系统审计日志是用来监控系统活动、检测潜在的安全问题和追踪系统事件的重要工具之一

...
type=SYSCALL msg=audit(1692033018.624:12555): arch=c000003e syscall=4 success=no exit=-13 a0=190abc0 a1=7ffd028eafc0 a2=7ffd028eafc0 a3=7ffd028eaae0 items=0 ppid=19472 pid=19473 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="check.sh" exe="/usr/bin/bash" subj=system_u:system_r:keepalived_t:s0 key=(null)type=AVC msg=audit(1692033018.624:12555): avc:  denied  { getattr } for  pid=19473 comm="check.sh" path="/usr/bin/ping" dev="dm-0" ino=50555278 scontext=system_u:system_r:keepalived_t:s0 tcontext=system_u:object_r:ping_exec_t:s0 tclass=file permissive=0
...

我们现在来看一下上面日志输出表示什么

先看第一段:

type=SYSCALL: 这个部分是系统调用事件类型。arch=c000003e syscall=4 success=no exit=-13: 这里描述了系统调用的详细信息,包括系统调用号、是否成功等

  • subj=system_u:system_r:keepalived_t:s0:描述了进程的安全上下文信息
  • 后面内容则是一些更多与系统调用相关的信息,如参数、进程信息、用户信息等

再看第二段:

type=AVC: 这个部分指示了事件类型为 AVC(Access Vector Cache),是 SELinux 的访问控制事件,

  • avc: denied { getattr }: 表示操作是一个 getattr(获取属性)操作,但是被拒绝。
  • pid=19473 comm="check.sh": 进程 ID 是 19473,进程名称是 check.sh
  • path="/usr/bin/ping": 文件路径是 /usr/bin/ping,表示被操作的文件。
  • scontext=system_u:system_r:keepalived_t:s0: 发起操作的进程的安全上下文。
  • tcontext=system_u:object_r:ping_exec_t:s0: 被操作文件的安全上下文。
  • tclass=file: 被操作对象的类型是文件。
  • permissive=0: 表示 SELinux 不是处于宽松模式

总结一下:

这个是 SELinux 的审计日志,这两条日志记录了一个操作,即 keepalived 进程试图通过执行 check.sh" 脚本访问 /usr/bin/ping 文件,但被 SELinux 拒绝了

解决

排查到这思路就逐渐清晰起来了,这是因为 SELinux 权限导致的,在解决这个问题之前,我们先来简单复习一下 SELinux (后面我会专门写一篇文章来介绍 SELinux)

SELinux(Security Enhanced Linux,安全强化的 Linux) ,由美国国家安全局(NSA)开发的

为什么要开发 SELinux

我们知道系统的账户主要分为系统管理员(root)和普通用户,这两种身份能否使用系统上面的文件资源则与 rwx 权限设置有关

所以当某个进程想要对文件进行读写操作时,系统就会根据该进程的属主和属组去比对文件权限,只有通过权限检查,才能够进一步操作文件

这种方式被称为自主访问控制(Discretionary Access Control, DAC),DAC 是 Linux 操作系统中的一种基本权限控制机制,用于限制用户对系统资源的访问权限

但是 DAC 的一大问题就是当用户获取到进程之后,他可以通过这个进程与自己所获得的权限去处理对应的文件资源

万一用户对系统不熟悉,就很容易导致资源误用的问题出现

举个例子,你们公司的新人为了自身方便,将网页所在目录 /var/www/html 目录的权限设置成了 777,则代表所有进程都可以对该目录进行读写

如果黑客接触到了某个进程,就极有可能向你的系统里面写入某些东西

为了避免 DAC 的问题,便有了 SELinux

SELinux MAC 机制

SELinux 引入了强制访问控制(Mandatory Access Control, MAC)机制

强制访问控制(MAC,Mandatory Access Control)是 Linux 操作系统中一种更加严格和细粒度的访问控制机制,用于加强对系统资源的保护和控制

MAC 有趣的地方在于它可以针对特定的进程与特定的文件资源来管理权限,即使你是 root 用户,在使用不同的进程时你所能获取的权限也不一定是 root

在这里插入图片描述
安全上下文

前面我们知道,SELinux 是通过 MAC 的方式来管理进程的权限的

它控制的主体是【进程】,而【目标】则是该【进程】能否读取的文件资源

  • 主体

SELinux 主要管理的就是进程,进程和主体可以画上等号

  • 目标

主体能否读写的目标一般就是文件资源

除了策略指定之外,主体与目标的安全上下文必须一致才能够顺利读写

安全上下文有点类似于文件系统的 rwx ,如果设置错误,主体就无法读写目标资源了

在这里插入图片描述
安全上下文存放在文件的 inode 内,可以通过 ll -Z 命令去查看(前提是 SELinux 是开启着的)

回到我们的案例上来,日志输出说进程 check.sh 试图获取 /usr/bin/ping 的属性(getattr)操作,但被 SELinux 拒绝了

但是我们发现没有这个进程

[root@localhost ~]# ps -ef | grep check

也就是说,由于 SELinux 权限问题 ,keepalived 一开始想要调用 check.sh 脚本就失败了

我们分别来看一下 keepalived 进程和 check.sh 的安全上下文

[root@localhost ~]# ps -eZ | grep keepalived 
system_u:system_r:keepalived_t:s0 19609 ?       00:00:00 keepalived[root@localhost ~]# ll -Z /etc/keepalived/check.sh 
-rwxr-xr-x. root root system_u:object_r:etc_t:s0       /etc/keepalived/check.sh

可以看到 keepalived 进程的安全上下文类型为 keepalived_t;而 check.sh 的安全上下文是 etc_t

即——主体与目标的安全上下文并不一致,就算有 rwx 权限也无法执行

  • 解决方案一:关闭 SELinux

简单粗暴,直接关闭 SELinux ,就没有这么多乱七八糟的限制了

# 以 CentOS 7 为例
# 临时关闭
[root@localhost ~]# setenforce 0#永久关闭,将 SELINUX=enforcing 改为 SELINUX=disabled,然后保存退出
[root@localhost ~]# sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
  • 解决方案二(失败了):修改 check.sh 的安全上下文与 keepalived 一致

我想把 check.sh 的安全上下文改成与 keepalived 一致,可是失败了,有小伙伴知道原因吗

[root@localhost ~]# chcon -t keepalived_t  /etc/keepalived/check.sh
chcon: 改变"/etc/keepalived/check.sh" 的环境到"system_u:object_r:keepalived_t:s0" 失败: 权限不够
  • 解决方案三:修改 check.sh 的安全上下文

依旧是修改 check.sh 的安全上下文,不过这次参考了资料

[root@localhost ~]# chcon -t keepalived_unconfined_script_exec_t  /etc/keepalived/check.sh

keepalived_unconfined_script_exec_t 是一个在 SELinux 中用于标识 Keepalived 执行未限制脚本的上下文,这个上下文允许 Keepalived 进程在执行脚本时绕过一些 SELinux 限制,从而可以在需要的情况下执行脚本

  • 解决方案四:将脚本转移到 /usr/libexec/keepalived 目录中
# keepalived 配置
vrrp_script nginx_check {script "/usr/libexec/keepalived/check.sh"interval 2fall 2rise 1
}

这个目录的安全上下文是 keepalived_unconfined_script_exec_t,与解决方案三同理

[root@localhost ~]# ll -Z /usr/libexec/keepalived/ -d
drwxr-xr-x. root root system_u:object_r:keepalived_unconfined_script_exec_t:s0 /usr/libexec/keepalived/
  • 解决方案五: keepalived 全局配置添加 enable_script_security 字段

加了这个字段意味着如果脚本路径的任一部分对于非 root 用户来说,都具有可写权限,则不会以 root 身份运行脚本

以非 root 身份运行的脚本就能够通过 SELinux 的审查吗?这一块我不太懂,有懂的小伙伴可以告诉我

global_defs {...enable_script_security...
}

参考资料:

https://github.com/acassen/keepalived/issues/1322

https://serverfault.com/questions/709428/track-script-doesnt-work-after-keepalived-update

https://www.mankier.com/8/keepalived_selinux

https://linux.vbird.org/linux_basic/centos7/0440processcontrol.php#selinux

相关文章:

SElinux 导致 Keepalived 检测脚本无法执行

哈喽大家好&#xff0c;我是咸鱼 今天我们来看一个关于 Keepalived 检测脚本无法执行的问题 一位粉丝后台私信我&#xff0c;说他部署的 keepalived 集群 vrrp_script 模块中的脚本执行失败了&#xff0c;但是手动执行这个脚本却没有任何问题 这个问题也是咸鱼第一次遇到&…...

2022年电赛C题——小车跟随行驶系统——做题记录以及经验分享

前言 自己打算将做过的电赛真题&#xff0c;主要包含控制组的&#xff0c;近几年出现的小车控制题目&#xff0c;自己做过的真题以及在准备电赛期间刷真题出现的问题以及经验分享给大家 这次带来的是22年电赛C题——小车跟随行驶系统&#xff0c;这道题目指定使用的是TI的单片…...

vscode + python

序 参考链接&#xff1a; 【教程】VScode中配置Python运行环境_哔哩哔哩_bilibili Python部分 Python Releases for Windows | Python.org vscode部分 Visual Studio Code - Code Editing. Redefined 一路next&#xff0c;全部勾上&#xff1a; 就可以了&#xff1a; 安装插…...

badgerdb里面的事务

事务的ACID A 原子性&#xff08;Atomicity&#xff09; 多步骤操作&#xff0c;只能是两种状态&#xff0c;要么所有的步骤都成功执行&#xff0c;要么所有的步骤都不执行&#xff0c;举例说明就是小明向小红转账30元的场景&#xff0c;拆分成两个步骤&#xff0c;步骤1&#…...

C# this.Invoke(new Action(() => { /* some code */ }))用法说明

在 C# 中&#xff0c;this.Invoke(new Action(() > { /* some code */ })) 是一种用于在 UI 线程上执行代码的方法&#xff0c;通常用于在后台线程中更新 UI 控件的值或执行其他需要在 UI 线程上执行的操作。 在 Windows Forms 或 WPF 等图形界面应用程序中&#xff0c;UI …...

MongoDB:MySQL,Redis,ES,MongoDB的应用场景

简单明了说明MySQL,ES,MongoDB的各自特点,应用场景,以及MongoDB如何使用的第一章节. 一. SQL与NoSQL SQL被称为结构化查询语言.是传统意义上的数据库,数据之间存在很明确的关联关系,例如主外键关联,这种结构可以确保数据的完整性(数据没有缺失并且正确).但是正因为这种严密的结…...

leetcode每日一题_2682.找出转圈游戏输家

2682.找出转圈游戏输家 题目: n 个朋友在玩游戏。这些朋友坐成一个圈&#xff0c;按 顺时针方向 从 1 到 n 编号。从第 i 个朋友的位置开始顺时针移动 1 步会到达第 (i 1) 个朋友的位置&#xff08;1 < i < n&#xff09;&#xff0c;而从第 n 个朋友的位置开始顺时针移…...

OpenCV之薄板样条插值(ThinPlateSpline)

官方文档&#xff1a;OpenCV: cv::ThinPlateSplineShapeTransformer Class Reference 使用方法&#xff1a; 头文件&#xff1a;#include <opencv2/shape/shape_transformer.hpp> &#xff08;1&#xff09;点匹配 一般根据有多少个样本&#xff08;或者点&#xff09;…...

034_小驰私房菜_[问题复盘] Qcom平台,某些三方相机拍照旋转90度

全网最具价值的Android Camera开发学习系列资料~ 作者:8年Android Camera开发,从Camera app一直做到Hal和驱动~ 欢迎订阅,相信能扩展你的知识面,提升个人能力~ 【一、问题】 某些三方相机,预览正常,拍照旋转90度 【二、问题排查】 1 ) HAL这边Jpeg编码数据在哪个地方…...

【TI-CCS笔记】工程编译配置 bin文件的编译和生成 各种架构的Post-build配置汇总

【TI-CCS笔记】工程编译配置 bin文件的编译和生成 各种架构的Post-build配置汇总 TI编译器分类 在CCS按照目录下 有个名为${CG_TOOL_ROOT}的目录 其下就是当前工程的编译器 存放目录为&#xff1a; C:\ti\ccs1240\ccs\tools\compiler按类型分为五种&#xff1a; ti-cgt-arm…...

深入探索Java中的File类与IO操作:从路径到文件的一切

文章目录 1. File类的作用与构造方法2. File类常用方法&#xff1a;获取、判断和创建2.1 获取功能方法2.2 判断功能方法2.3 创建和删除功能方法2.4 目录的遍历方法 3. 递归&#xff1a;探索更深的层次代码示例&#xff1a;递归遍历文件夹 结论 &#x1f389;欢迎来到Java学习路…...

Python 处理 Excel 表格的 14 个常用操作

目录 1. 安装依赖库 2. 导入库 3. 读取Excel文件 4. 写入Excel文件 5. 创建工作表 6. 访问工作表 7. 读取单元格数据 8. 写入单元格数据 9. 获取行数和列数 10. 过滤数据 11. 排序数据 12. 添加新行 13. 删除行或列 14. 计算汇总统计 总结 无论是数据分析师、财…...

PyQt有哪些主要组件?

这是一个非常强大的跨平台GUI库&#xff0c;可以让你用Python语言创建美观且功能强大的桌面应用程序。让我们先来了解一下它的主要组件。 首先&#xff0c;我们要介绍的是窗口。窗口是PyQt应用程序的基本元素&#xff0c;所有的GUI元素都放置在窗口中。你可以创建主窗口、模态…...

力推C语言必会题目终章(完结篇)

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 今天是分享C语言必会题目最终章&#xff0c;全部都是硬货&#xff0c;大家都坐好准备开始喽&#xff01;&#xff01;&#xff01; 编写一个函数&#xff0c;计算字符串中含有的不同字符的个数。字符在 ASCII 码范围内…...

CS5263替代停产IT6561连接DP转HDMI音视频转换器ASL 集睿致远CS5263设计电路原理图

ASL集睿致远CS5263是一款DP1.4到HDMI2.0b转换器芯片&#xff0c;设计用于将DP1.4源连接到HDMI2.0b接收器。 CS5263功能特性&#xff1a; DP接口包括4条主通道、辅助通道和HPD信号。接收器支持每通道5.4Gbps&#xff08;HBR2&#xff09;数据速率。DP接收机结合了HDCP1.4和HDCP…...

数据分析 | 随机森林如何确定参数空间的搜索范围

1. 随机森林超参数 极其重要的三个超参数是必须要调整的&#xff0c;一般再加上两到三个其他超参数进行优化即可。 2. 学习曲线确定n_estimators搜索范围 首先导入必要的库&#xff0c;使用sklearn自带的房价预测数据集&#xff1a; import numpy as np import pandas as pd f…...

5G+AI数字化智能工厂建设解决方案PPT

导读&#xff1a;原文《5GAI数字化智能工厂建设解决方案》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。数字化智能工厂定义 智能基础架构协同框架 - 端、边、云、网…...

Windows配置编译ffmpeg +音视频地址

Windows配置MinGW及MinGW-make使用实例 https://blog.csdn.net/Henoiiy/article/details/122550618 ffmpeg安装遇错&#xff1a;nasm/yasm not found or too old. Use --disable-x86asm for a crippled build. https://blog.csdn.net/sayyy/article/details/124337834https://…...

C语言 常用工具型API --------system()

函数名&#xff1a; system&#xff08;&#xff09; 用 法&#xff1a; int system(char *command); 原理&#xff1a; 加载一个子进程去执行指定的程序&#xff0c;而想Linux命令基本都是一个单独的进程实现的&#xff0c;所以你所掌握的Linux命令越多&#xff0c;该函数功…...

车规级半导体分类(汽车芯片介绍)

车规级半导体&#xff0c;也被称为“汽车芯片”&#xff0c;主要应用于车辆控制装置、车载监控系统和车载电子控制装置等领域。这些半导体器件主要分布在车体控制模块上&#xff0c;以及车载信息娱乐系统方面&#xff0c;包括动力传动综合控制系统、主动安全系统和高级辅助驾驶…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...