背景
在项目中使用到了一个windows的钩子函数,主要是键盘钩子,感觉很有意思,于是想看看linux下的钩子函数,也实现一个监测键盘输入的功能,这样就可以屏蔽一些按键的输入。
过程
手上只有一个ubuntu20.04系统的电脑,所以就用这个来研究一下,刚开始就是去找linux的钩子函数,但是在网上查找了一下没有找到,到目前为止我也不确定linux到底有没有类型windows这样的键盘钩子函数,如果有哪位大神知道请指点我一下。
没有找到linux下的钩子,但是我看到有的文章说可以使用设备文件,参考了这篇文章,感谢感谢。
(1) /dev/input/by-id 你可以看到该路径下有一些设备文件,比如event0,但是现在这样无法知道设备文件对应的设备。
所以你可以在进入到by-id 目录下再看看,你会发现里面都是软连接,这样你可以清楚的了解设备对应的设备文件。
还有一个by-path目录和这个作用一样。
(2) /proc/bus/input ---> 有一个devices的文件,里面是设备对应的设备文件,比如你要获取别的设备时可以看看这个文件。比如说摄像头什么的。
最后,我就是读取设备文件,因此程序也主要是读取设备文件。
代码
#include <stdio.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <errno.h>
#include <fstream>
using namespace std;
/*
*作用: 找到键盘设备对应的事件文件 例如 event7
* linux 插入键盘设备,每一个设备会有一个事件编号,但是目前这个ls -l排序是按照设备名称排,所以
* 最上面的键盘设备并不会是最新的设备,因此 此函数在键盘设备多的时候 会获取一个不确定的键盘事件文件
usb-026d_0002-event-if01 -> ../event12
usb-026d_0002-event-kbd -> ../event6
usb-413c_Dell_KB216_Wired_Keyboard-event-if01 -> ../event14
usb-413c_Dell_KB216_Wired_Keyboard-event-kbd -> ../event13 这是最新的键盘设备,但是没有排到最上面
usb-Logitech_USB_Receiver-if01-event-mouse -> ../event2
usb-Logitech_USB_Receiver-if01-mouse -> ../mouse0
usb-SONiX_USB_Keyboard-event-if01 -> ../event4
usb-SONiX_USB_Keyboard-event-kbd -> ../event3
*/
string FindKdbEvet()
{
FILE *stream;
FILE *wstream;
char buf[20];
memset( buf, '\0', sizeof(buf) );//初始化buf,以免后面写如乱码到文件中
//将命令的输出 通过管道读取(“r”参数)到FILE* stream
stream = popen("cd /dev/input/by-id ; ls -l | grep \"kbd\" | head -1 | cut -d \"/\" -f 2", "r");
fread( buf, sizeof(char), sizeof(buf), stream); //将刚刚FILE* stream的数据流读取到buf中
pclose(stream);
string str = buf;
return str;
}
/*
*作用: 打开事件文件
*输入: _infile 对应的文件流变量
*返回值: int 返回0 为打开成功 返回-1 为打开失败
*/
int OpenEventFile(ifstream & _infile)
{
string kdbevet = FindKdbEvet(); //获取键盘事件例如 event7
int isize = kdbevet.size();
if(isize > 0 && kdbevet[isize-1]) //这里需要去除结尾的换行符号 ascii码是10
{
kdbevet = kdbevet.substr(0,kdbevet.size()-1);
}
string shCommand = "/dev/input/" + kdbevet;
_infile.open(shCommand.c_str(),ios_base::in);
if(!_infile.is_open())
{
cout <<"open Keyboard device error, error code = <<" << errno << "!" <<endl;
return -1;
}
return 0;
}
int main ()
{
struct input_event t;
ifstream infile;
if(OpenEventFile(infile) == -1)
{
return 0;
}
bool blPressIsCtrl = false;
bool blPressIsC = false;
while (1)
{
if(infile.read((char *)&t,sizeof(t)))
{//读取文件成功
if(t.type == EV_KEY)
{
if((t.code == KEY_LEFTCTRL || t.code == KEY_RIGHTCTRL) && t.value == 1)
{
blPressIsCtrl = true;
}
if ((t.code == KEY_LEFTCTRL || t.code == KEY_RIGHTCTRL) && t.value == 0)
{
blPressIsCtrl = false;
}
if(t.code == KEY_C && t.value == 1 && blPressIsCtrl == true)
{
cout<<"press ctrl + c"<<endl;
}
cout << "you use key value is "<<t.code<<endl;
}
}
else
{//读取文件失败 需要再次进行一次读取
cout <<"read file error"<<endl;
infile.close();
if(OpenEventFile(infile) == -1)
{
return 0;
}
}
}
infile.close();
return 0;
}
后记
目前这个小代码,可以实现监测linux系统下键盘设备(USB接口)的按键输入,监测到输入Ctrl+C 就会输出日志。同一个键盘设备插拔程序依旧可以监测。
不足的地方是不能同时监测多个键盘设备的输入,因为现在就是读取一个设备文件(windows钩子函数就可以)。而且结束程序,插入多个键盘设备,再次执行程序,不一定会继续监测上次监测的键盘设备,这是因为by-id 目录中软链接的排序不是稳定的,shell语句是查询是第一个对应的设备文件。这点还需要完善。