QT

리눅스에서 USB 정보 가져오기

크게웃기 2019. 6. 16. 16:19
반응형

 

리눅스에서 USB 정보 가져오기

QT Application 진행 중 USB 정보를 읽어 오거나,

add, remove 에 대해 화면 출력 및 data 처리가 필요한 부분이 생겨 조사 하던 중 정리하여 블로그에 남겨 놓습니다.

 

참고로 QT version 5 부터는 QStorageInfo class를 이용하면 간단히 구현 가능 할 듯합니다.

기회가 된다면 QStorageInfo Class를 이용하여 리눅스에 usb port add/remove 기능을 블로그 해보도록 하겠습니다.

 

기능을 구현하기전에 아래와 같이 3가지 방법을 생각해보았습니다.

1. 임베디드 리눅스의 경우 USB 포트가 1,2개일 뿐이니 특정 패스를 지정해 놓고 USB 정보가 필요한 Event 발생시 해당 path 존재 여부를 확인하는 방법. (가장 간단할 듯합니다.)

2. /proc/scsi/usb-storeage path를 모니터링 하는 방법. 위의 방법과 비슷할 듯ㅍ합니다. 

3. socket api를 이용하여 kernel log를 모니터링 하여 add/remove event 발생시 application 단에서 필요한 기능을 적용하는 방법.

 

저는 일단 3번을 이용하여 프로그램을 구현해 보았습니다.

 

필요 코드 및 설명

1. socket 생성

{
	struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;
    int retval;

    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_groups = 1;

    fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    //netlink_socket = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); //SOCK_CLOEXEC may be not available
    if (fd == -1) {
        qWarning("error getting socket: %s", strerror(errno));
        return ;
    }

    /* set receive buffersize */
    setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
    retval = bind(fd, (struct sockaddr*) &snl, sizeof(struct sockaddr_nl));
    if (retval < 0) {
        qWarning("bind failed: %s", strerror(errno));
        close(fd);
        fd = -1;
        return;
    } else if (retval == 0) {
        //from libudev-monitor.c
        struct sockaddr_nl _snl;
        socklen_t _addrlen;

        /*
         * get the address the kernel has assigned us
         * it is usually, but not necessarily the pid
         */
        _addrlen = sizeof(struct sockaddr_nl);
        retval = getsockname(fd, (struct sockaddr *)&_snl, &_addrlen);
        if (retval == 0)
            snl.nl_pid = _snl.nl_pid;
    }
}

socket api를 이용하여 fd를 생성합니다.

socket 생성시 parameter에 "NETLINK_KOBJECT_UEVENT" 옵션을 지정하여 사용자 공간 커널 메모리를 수신 받습니다.(Kernel messages to user space.)

event 참고 : https://linux.die.net/man/7/netlink

 

2. socket event 발생시 수신 받을 notifier 지정.

{
    socket_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
    connect(socket_notifier, SIGNAL(activated(int)), SLOT(recvLogData(int)));
}

fd가 메시지 수신하였을 경우 slot으로 처리하기 위하여 notifier를 지정해 줍니다. ( QT 에서만 가능)
일반 프로그램일 경우 read , notify api를 이용하여 처리 하면 될 듯합니다.

 

socket_notifer 변수는 헤더파일에 private QSocketNotifier포인터 변수로 선언 하였습니다.

kernel에 USB 관련 이벤트 발생시 activated에 fd값을 담은 signal 이 발생하며 slot(recvLogData)가 호출 됩니다.

 

 

3. 애드블럭 해제시 코드가 나타납니다.

3. recvLogData(int) - Event 발생부 처리

{
    QByteArray data;
    data.resize(2048*2);
    data.fill(0);
    size_t len = read(socket_notifier->socket(), data.data(), 2048*2);
    data.resize(len);

    data = data.replace(0, '\n').trimmed();
    if (buffer.isOpen())
        buffer.close();
    buffer.setBuffer(&data);
    buffer.open(QIODevice::ReadOnly);
    while(!buffer.atEnd()) {
        parseLine(buffer.readLine().trimmed());
    }
    buffer.close();
}

log 수신 후 trimmed 처리하고 해당 log 전문을 가지고 parseLine api에서 처리 하도록 진행한다.

 

실제 Log는 아래와 같이 발생됩니다.

log 전문. (굳이 다 올릴 필요는 없지만, 올려 둡니다.)

"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/bsg/33:0:0:0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/bsg/33:0:0:0" 
"SUBSYSTEM=bsg" 
"MAJOR=246" 
"MINOR=2" 
"DEVNAME=bsg/33:0:0:0" 
"SEQNUM=5736" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_generic/sg2" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_generic/sg2" 
"SUBSYSTEM=scsi_generic" 
"MAJOR=21" 
"MINOR=2" 
"DEVNAME=sg2" 
"SEQNUM=5737" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_device/33:0:0:0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_device/33:0:0:0" 
"SUBSYSTEM=scsi_device" 
"SEQNUM=5738" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_disk/33:0:0:0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_disk/33:0:0:0" 
"SUBSYSTEM=scsi_disk" 
"SEQNUM=5739" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb/sdb1" 
"remove"  :  "/dev/sdb1" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb/sdb1" 
"SUBSYSTEM=block" 
"MAJOR=8" 
"MINOR=17" 
"DEVNAME=sdb1" 
"DEVTYPE=partition" 
"PARTN=1" 
"SEQNUM=5740" 
"remove@/devices/virtual/bdi/8:16" 
"ACTION=remove" 
"DEVPATH=/devices/virtual/bdi/8:16" 
"SUBSYSTEM=bdi" 
"SEQNUM=5741" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb" 
"remove"  :  "/dev/sdb" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb" 
oooo "DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb" 
"SUBSYSTEM=block" 
"MAJOR=8" 
"MINOR=16" 
"DEVNAME=sdb" 
"DEVTYPE=disk" 
"SEQNUM=5742" 
"unbind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"ACTION=unbind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_device" 
"SEQNUM=5743" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_device" 
"MODALIAS=scsi:t-0x00" 
"SEQNUM=5744" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_target" 
"SEQNUM=5745" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/scsi_host/host33" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/scsi_host/host33" 
"SUBSYSTEM=scsi_host" 
"SEQNUM=5746" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_host" 
"SEQNUM=5747" 
"unbind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"ACTION=unbind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"SUBSYSTEM=usb" 
"DEVTYPE=usb_interface" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"INTERFACE=8/6/80" 
"SEQNUM=5748" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"SUBSYSTEM=usb" 
"DEVTYPE=usb_interface" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"INTERFACE=8/6/80" 
"MODALIAS=usb:v0781p5567d0100dc00dsc00dp00ic08isc06ip50in00" 
"SEQNUM=5749" 
"unbind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"ACTION=unbind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"SUBSYSTEM=usb" 
"MAJOR=189" 
"MINOR=18" 
"DEVNAME=bus/usb/001/019" 
"DEVTYPE=usb_device" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"BUSNUM=001" 
"DEVNUM=019" 
"SEQNUM=5750" 
"remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"ACTION=remove" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"SUBSYSTEM=usb" 
"MAJOR=189" 
"MINOR=18" 
"DEVNAME=bus/usb/001/019" 
"DEVTYPE=usb_device" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"BUSNUM=001" 
"DEVNUM=019" 
"SEQNUM=5751" 
"remove@/devices/virtual/bdi/8:17-fuseblk" 
"ACTION=remove" 
"DEVPATH=/devices/virtual/bdi/8:17-fuseblk" 
"SUBSYSTEM=bdi" 
"SEQNUM=5752" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"SUBSYSTEM=usb" 
"MAJOR=189" 
"MINOR=19" 
"DEVNAME=bus/usb/001/020" 
"DEVTYPE=usb_device" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"BUSNUM=001" 
"DEVNUM=020" 
"SEQNUM=5753" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"SUBSYSTEM=usb" 
"DEVTYPE=usb_interface" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"INTERFACE=8/6/80" 
"MODALIAS=usb:v0781p5567d0100dc00dsc00dp00ic08isc06ip50in00" 
"SEQNUM=5754" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_host" 
"SEQNUM=5755" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/scsi_host/host33" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/scsi_host/host33" 
"SUBSYSTEM=scsi_host" 
"SEQNUM=5756" 
"bind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"ACTION=bind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0" 
"SUBSYSTEM=usb" 
"DEVTYPE=usb_interface" 
"DRIVER=usb-storage" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"INTERFACE=8/6/80" 
"MODALIAS=usb:v0781p5567d0100dc00dsc00dp00ic08isc06ip50in00" 
"SEQNUM=5757" 
"bind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"ACTION=bind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2" 
"SUBSYSTEM=usb" 
"MAJOR=189" 
"MINOR=19" 
"DEVNAME=bus/usb/001/020" 
"DEVTYPE=usb_device" 
"DRIVER=usb" 
"PRODUCT=781/5567/100" 
"TYPE=0/0/0" 
"BUSNUM=001" 
"DEVNUM=020" 
"SEQNUM=5758" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_target" 
"SEQNUM=5759" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_device" 
"MODALIAS=scsi:t-0x00" 
"SEQNUM=5760" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_disk/33:0:0:0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_disk/33:0:0:0" 
"SUBSYSTEM=scsi_disk" 
"SEQNUM=5761" 
"bind@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"ACTION=bind" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0" 
"SUBSYSTEM=scsi" 
"DEVTYPE=scsi_device" 
"DRIVER=sd" 
"MODALIAS=scsi:t-0x00" 
"SEQNUM=5762" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_device/33:0:0:0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_device/33:0:0:0" 
"SUBSYSTEM=scsi_device" 
"SEQNUM=5763" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_generic/sg2" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/scsi_generic/sg2" 
"SUBSYSTEM=scsi_generic" 
"MAJOR=21" 
"MINOR=2" 
"DEVNAME=sg2" 
"SEQNUM=5764" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/bsg/33:0:0:0" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/bsg/33:0:0:0" 
"SUBSYSTEM=bsg" 
"MAJOR=246" 
"MINOR=2" 
"DEVNAME=bsg/33:0:0:0" 
"SEQNUM=5765" 
"add@/devices/virtual/bdi/8:16" 
"ACTION=add" 
"DEVPATH=/devices/virtual/bdi/8:16" 
"SUBSYSTEM=bdi" 
"SEQNUM=5766" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb" 
"add"  :  "/dev/sdb" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb" 
"SUBSYSTEM=block" 
"MAJOR=8" 
"MINOR=16" 
"DEVNAME=sdb" 
"DEVTYPE=disk" 
"SEQNUM=5767" 
"add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb/sdb1" 
"add"  :  "/dev/sdb1" 
"ACTION=add" 
"DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-2/1-2:1.0/host33/target33:0:0/33:0:0:0/block/sdb/sdb1" 
"SUBSYSTEM=block" 
"MAJOR=8" 
"MINOR=17" 
"DEVNAME=sdb1" 
"DEVTYPE=partition" 
"PARTN=1" 
"SEQNUM=5768" 
"add@/devices/virtual/bdi/8:17-fuseblk" 
"ACTION=add" 
"DEVPATH=/devices/virtual/bdi/8:17-fuseblk" 
"SUBSYSTEM=bdi" 
"SEQNUM=5769" 

위에서 실제 필요한 log에는 "/block/" 이라는 문구가 꼭 들어 있고 가장 맨 앞에 

Event@ 가 포함되어 있습니다.

참고로 devtype은 disk와 partition 으로 나뉘며 partition 부분을 확인하면 됩니다.

 

4. parserLine 구현.

>{
    if (!line.contains("/block/"))
        return;

    QString action_str = line.left(line.indexOf('@')).toLower();
    QString tmp_dev = line.right(line.length() - line.lastIndexOf('/') - 1);
    if(tmp_dev.length() <= 3)
    {
        return;
    }

    QString dev = "/dev/" + tmp_dev;

    if (action_str==QLatin1String("add")) {
        qDebug() << action_str << " : " << dev;
    } else if (action_str==QLatin1String("remove")) {
        qDebug() << action_str << " : " << dev;
    } else if (action_str==QLatin1String("change")) {
        qDebug() << action_str << " : " << dev;
    }
}

위와 같이 로그 내에 "/block"이 포함되어져 있지 않으며 return 하고,

@ 로 부터 왼쪽 글짜만 가져오며, 

"/" 글자 우측으로만 가져와서 "/dev/"를 append 하도록 되어져 있습니다.

 

필요한 동작에 맞게 수정 또는 추가 하면 될 듯합니다.

 

블로그 광고로 수익을 내보고 싶어 애드블록 사용시 코드가 나오지 않도록 해놓았습니다.

양해 부탁드립니다.

 

참고 link :

https://github.com/wang-bin/qdevicewatcher

 

 

 

반응형