Ubuntu 1804 Desktop U盘/光驱插入监测及挂载监测

Ubuntu 1804 Desktop U盘/光驱插入监测及挂载监测

最近做的项目要求在不使用第三方库、不更改系统设置及普通用户权限情况下,实时监控U盘及CD-ROM的插入及挂载情况,现在把内容整理一下。

一. 主要数据结构介绍

本程序共有2个主要的数据结构:

  • Netlink操作队列
  • 设备队列:新插入设备类型、设备名、挂载目录队列

1. Netlink操作队列

本队列主要用于存储NetLink传来的数据,便于处理进程进行处理,数据结构及相关函数如下:

typedef struct _OperationQueue
{
    char operation[20];  //哪种操作
    char DeviceName[NAME_MAX];  //设备名
    struct _OperationQueue *pNext;  //下一节点
} OperationQueue;

//判断操作队列是否为空
bool IsEmpty_OperationQueue(OperationQueue *pOperationQueue)
{
    return ((pOperationQueue->pNext == NULL) ? true : false);
}


//向队列中存入数据
int Push_OperationQueue(OperationQueue *pOperationQueue, char *operation, char *DeviceName)
{
    pthread_mutex_lock(&OperationQueueLock);
    OperationQueue *LastNode = pOperationQueue;

    int QueueLen = 0;
    while (LastNode->pNext != NULL)
    {
        LastNode = LastNode->pNext; //找到尾节点
        QueueLen++;
    }
    if (QueueLen >= MAX_QUEUE_SIZE)
    {
        printf("Queue is full!\n");
        pthread_mutex_unlock(&OperationQueueLock);
        return -1;
    }

    LastNode->pNext = (OperationQueue *)malloc(sizeof(OperationQueue));
    memset(LastNode->pNext, 0, sizeof(OperationQueue));
    LastNode->pNext->pNext = NULL;

    strcpy(LastNode->pNext->operation, operation);
    strcpy(LastNode->pNext->DeviceName, DeviceName);

    pthread_mutex_unlock(&OperationQueueLock);
    return 0;
}


//从队列中取出数据
bool Pop_OperationQueue(OperationQueue *pOperationQueue, char *operation, char *DeviceName)
{
    pthread_mutex_lock(&OperationQueueLock);

    if (pOperationQueue->pNext == NULL)
    {
        printf("Queue is empty!\n");
        pthread_mutex_unlock(&OperationQueueLock);
        return false;
    }

    strcpy(operation, pOperationQueue->pNext->operation);
    strcpy(DeviceName, pOperationQueue->pNext->DeviceName);
    pOperationQueue->pNext = pOperationQueue->pNext->pNext;

    // printf("%s ------- %s\n", operation, DeviceName);
    // fflush(stdout);

    pthread_mutex_unlock(&OperationQueueLock);
    return true;
}

2. 设备队列:新插入设备类型、设备名、挂载目录队列

本队列主要用于存储处理后最终信息,数据结构及相关函数如下:

typedef struct _DeviceLink
{
    int type; //设备类型 0: udisk;  1: cdrom
    char DeviceName[PATH_MAX + NAME_MAX];  //设备名
    char MountPath[PATH_MAX + NAME_MAX];  //挂载路径
    struct _DeviceLink *pNext;  //下一节点
} DeviceLink;


//输出设备信息
void OutputDeviceLink(DeviceLink *pDeviceLink)
{
    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    while (CurNode != NULL)
    {
        printf("--------------------------\n");
        printf("type: %s\n", ((0 == CurNode->type) ? "udisk" : "cdrom"));
        printf("DeviceName: %s\n", CurNode->DeviceName);
        printf("MountPath: %s\n", CurNode->MountPath);
        fflush(stdout);
        CurNode = CurNode->pNext;
    }
    printf("--------------------------\n\n\n\n");
    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}


//新增设备
void AddDeviceLink(int type, char *DeviceName, char *MountPath, DeviceLink *pDeviceLink)
{
    // printf("-+-+-+-+-+-+-AddDeviceLink-+-+-+-+-+-+-+-\n");
    // fflush(stdout);

    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    DeviceLink *LastNode = pDeviceLink;

    // 先遍历一圈,看看有没有
    while (CurNode != NULL)
    {
        if (strcmp(DeviceName, CurNode->DeviceName) == 0)
        {
            // CurNode->type = type;
            // // 判断一下挂载目录有没有变化,有变化就更新一下
            // if ((MountPath[0] != '\0') && (strcmp(MountPath, CurNode->MountPath) != 0))
            // {
            //     strcpy(MountPath, CurNode->MountPath);
            // }

            pthread_mutex_unlock(&DeviceLinkLock);
            return;
        }
        CurNode = CurNode->pNext;
    }

    // 没有就新建一个节点
    while (LastNode->pNext != NULL)
    {
        LastNode = LastNode->pNext; //找到尾节点
    }
    LastNode->pNext = (DeviceLink *)malloc(sizeof(DeviceLink));
    memset(LastNode->pNext, 0, sizeof(DeviceLink));
    LastNode->pNext->pNext = NULL;

    LastNode->pNext->type = type;
    // char temp[PATH_MAX + NAME_MAX] = "/dev/";
    // strcat(temp, DeviceName);
    // strcpy(LastNode->pNext->DeviceName, temp);
    strcpy(LastNode->pNext->DeviceName, DeviceName);
    strcpy(LastNode->pNext->MountPath, MountPath);

    pthread_mutex_unlock(&DeviceLinkLock);
    // OutputDeviceLink(pDeviceLink);
    return;
}


//更新设备信息
void UpdateDeviceLink(DeviceLink *pDeviceLink, MountMap *pMountMap, int mapnum)
{
    pthread_mutex_lock(&DeviceLinkLock);

    DeviceLink *CurpDeviceLinkNode = pDeviceLink->pNext;

    // 先遍历一圈pDeviceLink
    while (CurpDeviceLinkNode != NULL)
    {
        bool found = false;
        for(int i=0; i<mapnum; i++)
        {
            if (0 == strcmp(CurpDeviceLinkNode->DeviceName, pMountMap[i].DeviceName))  //判断当前设备是否在挂载列表中
            {
                found = true;
                strcpy(CurpDeviceLinkNode->MountPath, pMountMap[i].MountPath);
                break;
            }
        }
        if (false == found)
        {
            strcpy(CurpDeviceLinkNode->MountPath, "");
        }

        CurpDeviceLinkNode = CurpDeviceLinkNode->pNext;
    }

    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}


//删除设备信息
void DeleteDeviceLink(char *DeviceName, DeviceLink *pDeviceLink)
{
    // printf("-+-+-+-+-+-+-DeleteDeviceLink-+-+-+-+-+-+-+-\n");
    // printf("Delete %s\n", DeviceName);
    // fflush(stdout);

    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    DeviceLink *PreNode = pDeviceLink;

    while (CurNode != NULL)
    {
        if (strcmp(DeviceName, CurNode->DeviceName) == 0)
        {
            PreNode->pNext = CurNode->pNext;
            free(CurNode);
            CurNode = NULL;

            pthread_mutex_unlock(&DeviceLinkLock);
            return;
        }
        else
        {
            PreNode = CurNode;
            CurNode = CurNode->pNext;
        }
    }
    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}

二. 进程介绍

本程序共4个进程:

  • USB插入监测进程:从NetLink得到操作及设备名,存入Netlink操作队列中
  • 解析操作数据进程:从Netlink操作队列中取出数据,处理后存入设备队列中
  • 获取挂载信息进程:通过循环遍历系统挂载文件判断设备队列中设备挂载情况
  • 主进程:休眠并定时打印出新插入的设备是U盘或光驱、打印出U盘/光驱名及挂载情况

1. USB插入监测进程

由于考虑到权限及不更改系统设置情况,udev就无法使用,经过网上检索,发现了NetLink监测USB插入。
Netlink是linux提供的用于内核和用户态进程之间的通信方式,这里就不展开介绍,通过socket与内核进行通信,得到USB设备插入、拔出等信息。
在网上找了一些NetLink的代码demo,进行了简单修改,直接把应用层接收到的NetLink信息打印出来,代码如下:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main(int argc, char *argv[])
{
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    int sock_fd;
    struct msghdr msg;

    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    memset(&msg, 0, sizeof(msg));
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); /* self pid */
                                //    src_addr.nl_groups = 0; /* not in mcast groups */
    src_addr.nl_groups = 1;     /* receive broadcast message*/
    bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;    /* For Linux Kernel */
    dest_addr.nl_groups = 0; /* unicast */

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    /* Fill the netlink message header */
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid(); /* self pid */
    nlh->nlmsg_flags = 0;
    /* Fill in the netlink message payload */
    //    strcpy(NLMSG_DATA(nlh), "Hello you!");

    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    while (1)
    {
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
        // printf(" Waiting message. ...\n");
        recvmsg(sock_fd, &msg, 0); //接收内核的消息

        printf("%s\n", nlh);
        fflush(stdout);
        
    }
    close(sock_fd);
}

使用gcc编译,后运行,结果如下:
NetLink插拔U盘原始数据
NetLink插拔光驱原始数据
发现NetLink发送的事件共有以下5种:add、bind、change、remove、unbind,而结合我们的设备名(U盘:sdb,sdb1,光驱:sr0),我们发现只有以下3种可以用到:

  • add:插入
  • change:更改
  • remove:移除

确定操作之后,要截取对应的设备名,将操作和设备名成对存入Netlink操作队列,截取部分代码如下所示:

char operation[20] = {0};
strncpy(operation, (char *)nlh, 19);
*(strchr(operation, '@')) = '\0';

char DeviceName[NAME_MAX] = {0};
strcpy(DeviceName, strrchr((char *)nlh, '/') + 1);

// printf("%s ------- %s\n", operation, DeviceName);
// fflush(stdout);

Push_OperationQueue(pOperationQueue, operation, DeviceName);

2. 解析操作数据进程

本进程负责当Netlink操作队列不为空时,处理Netlink操作队列中的数据,存入设备队列中,代码如下:

void ResolveOperationProcess(void *arg)
{
    OperationQueue *pOperationQueue = ((ResolveOperationProcessParams *)arg)->pOperationQueue;
    DeviceLink *pDeviceLink = ((ResolveOperationProcessParams *)arg)->pDeviceLink;

    // printf("-------------ResolveOperationProcess-----------------\n");
    // printf("pDeviceLink: %x\n", pDeviceLink);
    // printf("((ResolveOperationProcessParams *)arg)->pDeviceLink: %x\n", ((ResolveOperationProcessParams *)arg)->pDeviceLink);
    // printf("pOperationQueue: %x\n", pOperationQueue);
    // printf("((ResolveOperationProcessParams *)arg)->pOperationQueue: %x\n", ((ResolveOperationProcessParams *)arg)->pOperationQueue);
    // fflush(stdout);

    printf("ResolveOperationProcess ...\n");

    char operation[20] = {0};
    char DeviceName[NAME_MAX] = {0};

    while (1)
    {
        if (IsEmpty_OperationQueue(pOperationQueue))
        {
            // printf("-+-+-+-+-+-+-+-+-+-+-+-+-+-\n");
            // fflush(stdout);
            sleep(2);
            continue;
        }

        // if (!Pop_OperationQueue(pOperationQueue, operation, DeviceName));
        // {
        //     continue;
        // }

        // printf("-+-+-+-+-+-+-+-+-+-+-+-+-+-\n");
        // fflush(stdout);

        Pop_OperationQueue(pOperationQueue, operation, DeviceName);

        // printf("%s ------- %s\n", operation, DeviceName);
        // fflush(stdout);

        if (strlen(DeviceName) < 3)
            continue;

        if ('s' == DeviceName[0] && 'd' == DeviceName[1] && (DeviceName[2] >= 'a' || DeviceName[2] <= 'z')) //sda~sdz, udisk
        {
            if (0 == strcmp(operation, "add"))
            {
                AddDeviceLink(0, DeviceName, "", pDeviceLink);
            }
            else if (0 == strcmp(operation, "remove"))
            {
                DeleteDeviceLink(DeviceName, pDeviceLink);
            }
            else if (0 == strcmp(operation, "change"))
            {
                AddDeviceLink(0, DeviceName, "", pDeviceLink);
            }
            else
                continue;
        }
        else if ('s' == DeviceName[0] && 'r' == DeviceName[1] && (DeviceName[2] >= '0' || DeviceName[2] <= '9')) //sr0~sr9, cdrom
        {
            if (0 == strcmp(operation, "add"))
            {
                AddDeviceLink(1, DeviceName, "", pDeviceLink);
            }
            else if (0 == strcmp(operation, "remove"))
            {
                DeleteDeviceLink(DeviceName, pDeviceLink);
            }
            else if (0 == strcmp(operation, "change"))
            {
                AddDeviceLink(1, DeviceName, "", pDeviceLink);
            }
            else
                continue;
        }
        else
            continue;
    }
    return;
}

3. 获取挂载信息进程

Ubuntu 18.04 Desktop中设备挂载情况存储再/proc/mounts文件中,通过遍历该文件,得到所有挂载对应关系,然后对比设备队列,更新设备队列中的挂载路径。代码如下:

void CheckMountProcess(DeviceLink *pDeviceLink)
{
    printf("CheckMountProcess ...\n");

    while (1)
    {
        // 获取行数,用于确定创建对照表的大小
        int row = 0;
        char cmd[MAX_BUFF_LEN] = {0};
        char buffer[MAX_BUFF_LEN] = {0};
        FILE *pf;
        sprintf(cmd, "cat %s | grep -n \" \" | awk -F \":\" '{print $1}' | tail -n1", FILE_MOUNT_CHECK);
        // system(cmd);
        pf = popen(cmd, "r");
        fread(buffer, sizeof(buffer), 1, pf);
        row = atoi(buffer);
        pclose(pf);

        // printf("ROW: %d\n", row);

        char RawData[1024] = {0};

        FILE *fp = fopen(FILE_MOUNT_CHECK, "r");
        if (NULL == fp)
        {
            printf("failed to open dos.txt\n");
            continue;
        }

        MountMap *pMountMap = (MountMap *)malloc(sizeof(MountMap) * row);
        if (NULL == pMountMap)
        {
            printf("Malloc failed!\n");
            continue;
        }

        memset(pMountMap, 0, sizeof(MountMap) * row);
        int mapnum = 0;  // 对照表实际大小

        while (!feof(fp))
        {
            memset(RawData, 0, sizeof(RawData));
            fgets(RawData, sizeof(RawData) - 1, fp); // 包含了换行符
            // printf("%s", RawData);

            if (strlen(RawData) <= 5)
                continue;

            if ((RawData[0] != '/') || (RawData[1] != 'd') || (RawData[2] != 'e') || (RawData[3] != 'v') || (RawData[4] != '/')) // 不是/dev/开头,直接pass
                continue;

            char DeviceName[PATH_MAX + NAME_MAX] = {0};
            char MountPath[PATH_MAX + NAME_MAX] = {0};

            strcpy(DeviceName, RawData + 5); // +5是为了去掉/dev/
            *strchr(DeviceName, ' ') = '\0';

            strcpy(MountPath, strchr(RawData, ' ') + 1);
            *strchr(MountPath, ' ') = '\0';

            // printf("DeviceName: %s \t MountPath: %s\n", DeviceName, MountPath);

            strcpy(pMountMap[mapnum].DeviceName, DeviceName);
            strcpy(pMountMap[mapnum].MountPath, MountPath);

            mapnum ++;
        }
        fclose(fp);

        UpdateDeviceLink(pDeviceLink, pMountMap, mapnum);

        free(pMountMap);
        pMountMap = NULL;

        sleep(2); //2秒轮询一次
    }
    return;
}

4. 主进程

每隔5秒钟,输出一下设备队列。代码如下:

while (1)
{
    sleep(5);
    OutputDeviceLink(pDeviceLink);
    // printf("Sleep .........\n");
    fflush(stdout);
}

三. 现象展示

执行gcc UsbMonitor.c -o UsbMonitor -lpthread编译后运行。结果如下:
现象-U盘1
现象-U盘2
光驱现象类似,就不具体展示了。

四. 完整代码

完整代码如下:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <linux/limits.h>

#define UEVENT_BUFFER_SIZE 2048 /* maximum payload size*/
#define MAX_QUEUE_SIZE 1024     //队列大小
#define MAX_PAYLOAD 1024        /* maximum payload size*/
#define MAX_BUFF_LEN 128  //命令最大字符数

#define FILE_MOUNT_CHECK        "/proc/mounts"                                          //用来检测设备是否被mount

static pthread_mutex_t DeviceLinkLock;
static pthread_mutex_t OperationQueueLock;

typedef struct _DeviceLink
{
    int type; //设备类型 0: udisk;  1: cdrom
    char DeviceName[PATH_MAX + NAME_MAX];  //设备名
    char MountPath[PATH_MAX + NAME_MAX];  //挂载路径
    struct _DeviceLink *pNext;  //下一节点
} DeviceLink;


typedef struct _OperationQueue
{
    char operation[20];  //哪种操作
    char DeviceName[NAME_MAX];  //设备名
    struct _OperationQueue *pNext;  //下一节点
} OperationQueue;


typedef struct _ResolveOperationProcessParams
{
    DeviceLink *pDeviceLink;
    OperationQueue *pOperationQueue;
} ResolveOperationProcessParams;


typedef struct _MountMap
{
    char DeviceName[PATH_MAX + NAME_MAX];
    char MountPath[PATH_MAX + NAME_MAX];
} MountMap;


//判断操作队列是否为空
bool IsEmpty_OperationQueue(OperationQueue *pOperationQueue)
{
    return ((pOperationQueue->pNext == NULL) ? true : false);
}


//向队列中存入数据
int Push_OperationQueue(OperationQueue *pOperationQueue, char *operation, char *DeviceName)
{
    pthread_mutex_lock(&OperationQueueLock);
    OperationQueue *LastNode = pOperationQueue;

    int QueueLen = 0;
    while (LastNode->pNext != NULL)
    {
        LastNode = LastNode->pNext; //找到尾节点
        QueueLen++;
    }
    if (QueueLen >= MAX_QUEUE_SIZE)
    {
        printf("Queue is full!\n");
        pthread_mutex_unlock(&OperationQueueLock);
        return -1;
    }

    LastNode->pNext = (OperationQueue *)malloc(sizeof(OperationQueue));
    memset(LastNode->pNext, 0, sizeof(OperationQueue));
    LastNode->pNext->pNext = NULL;

    strcpy(LastNode->pNext->operation, operation);
    strcpy(LastNode->pNext->DeviceName, DeviceName);

    pthread_mutex_unlock(&OperationQueueLock);
    return 0;
}


//从队列中取出数据
bool Pop_OperationQueue(OperationQueue *pOperationQueue, char *operation, char *DeviceName)
{
    pthread_mutex_lock(&OperationQueueLock);

    if (pOperationQueue->pNext == NULL)
    {
        printf("Queue is empty!\n");
        pthread_mutex_unlock(&OperationQueueLock);
        return false;
    }

    strcpy(operation, pOperationQueue->pNext->operation);
    strcpy(DeviceName, pOperationQueue->pNext->DeviceName);
    pOperationQueue->pNext = pOperationQueue->pNext->pNext;

    // printf("%s ------- %s\n", operation, DeviceName);
    // fflush(stdout);

    pthread_mutex_unlock(&OperationQueueLock);
    return true;
}


//输出设备信息
void OutputDeviceLink(DeviceLink *pDeviceLink)
{
    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    while (CurNode != NULL)
    {
        printf("--------------------------\n");
        printf("type: %s\n", ((0 == CurNode->type) ? "udisk" : "cdrom"));
        printf("DeviceName: %s\n", CurNode->DeviceName);
        printf("MountPath: %s\n", CurNode->MountPath);
        fflush(stdout);
        CurNode = CurNode->pNext;
    }
    printf("--------------------------\n\n\n\n");
    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}


//新增设备
void AddDeviceLink(int type, char *DeviceName, char *MountPath, DeviceLink *pDeviceLink)
{
    // printf("-+-+-+-+-+-+-AddDeviceLink-+-+-+-+-+-+-+-\n");
    // fflush(stdout);

    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    DeviceLink *LastNode = pDeviceLink;

    // 先遍历一圈,看看有没有
    while (CurNode != NULL)
    {
        if (strcmp(DeviceName, CurNode->DeviceName) == 0)
        {
            // CurNode->type = type;
            // // 判断一下挂载目录有没有变化,有变化就更新一下
            // if ((MountPath[0] != '\0') && (strcmp(MountPath, CurNode->MountPath) != 0))
            // {
            //     strcpy(MountPath, CurNode->MountPath);
            // }

            pthread_mutex_unlock(&DeviceLinkLock);
            return;
        }
        CurNode = CurNode->pNext;
    }

    // 没有就新建一个节点
    while (LastNode->pNext != NULL)
    {
        LastNode = LastNode->pNext; //找到尾节点
    }
    LastNode->pNext = (DeviceLink *)malloc(sizeof(DeviceLink));
    memset(LastNode->pNext, 0, sizeof(DeviceLink));
    LastNode->pNext->pNext = NULL;

    LastNode->pNext->type = type;
    // char temp[PATH_MAX + NAME_MAX] = "/dev/";
    // strcat(temp, DeviceName);
    // strcpy(LastNode->pNext->DeviceName, temp);
    strcpy(LastNode->pNext->DeviceName, DeviceName);
    strcpy(LastNode->pNext->MountPath, MountPath);

    pthread_mutex_unlock(&DeviceLinkLock);
    // OutputDeviceLink(pDeviceLink);
    return;
}


//更新设备信息
void UpdateDeviceLink(DeviceLink *pDeviceLink, MountMap *pMountMap, int mapnum)
{
    pthread_mutex_lock(&DeviceLinkLock);

    DeviceLink *CurpDeviceLinkNode = pDeviceLink->pNext;

    // 先遍历一圈pDeviceLink
    while (CurpDeviceLinkNode != NULL)
    {
        bool found = false;
        for(int i=0; i<mapnum; i++)
        {
            if (0 == strcmp(CurpDeviceLinkNode->DeviceName, pMountMap[i].DeviceName))  //判断当前设备是否在挂载列表中
            {
                found = true;
                strcpy(CurpDeviceLinkNode->MountPath, pMountMap[i].MountPath);
                break;
            }
        }
        if (false == found)
        {
            strcpy(CurpDeviceLinkNode->MountPath, "");
        }

        CurpDeviceLinkNode = CurpDeviceLinkNode->pNext;
    }

    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}


//删除设备信息
void DeleteDeviceLink(char *DeviceName, DeviceLink *pDeviceLink)
{
    // printf("-+-+-+-+-+-+-DeleteDeviceLink-+-+-+-+-+-+-+-\n");
    // printf("Delete %s\n", DeviceName);
    // fflush(stdout);

    pthread_mutex_lock(&DeviceLinkLock);
    DeviceLink *CurNode = pDeviceLink->pNext;
    DeviceLink *PreNode = pDeviceLink;

    while (CurNode != NULL)
    {
        if (strcmp(DeviceName, CurNode->DeviceName) == 0)
        {
            PreNode->pNext = CurNode->pNext;
            free(CurNode);
            CurNode = NULL;

            pthread_mutex_unlock(&DeviceLinkLock);
            return;
        }
        else
        {
            PreNode = CurNode;
            CurNode = CurNode->pNext;
        }
    }
    pthread_mutex_unlock(&DeviceLinkLock);
    return;
}

void NetLinkProcess(OperationQueue *pOperationQueue)
{
    printf("NetLinkProcess ...\n");

    // printf("-------------NetLinkProcess-----------------\n");
    // printf("pOperationQueue: %x\n", pOperationQueue);
    // fflush(stdout);

    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    int sock_fd;
    struct msghdr msg;

    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    memset(&msg, 0, sizeof(msg));
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); /* self pid */
                                //    src_addr.nl_groups = 0; /* not in mcast groups */
    src_addr.nl_groups = 1;     /* receive broadcast message*/
    bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;    /* For Linux Kernel */
    dest_addr.nl_groups = 0; /* unicast */

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    /* Fill the netlink message header */
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid(); /* self pid */
    nlh->nlmsg_flags = 0;
    /* Fill in the netlink message payload */
    //    strcpy(NLMSG_DATA(nlh), "Hello you!");

    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    while (1)
    {
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
        // printf(" Waiting message. ...\n");
        recvmsg(sock_fd, &msg, 0); //接收内核的消息

        char operation[20] = {0};
        strncpy(operation, (char *)nlh, 19);
        *(strchr(operation, '@')) = '\0';

        char DeviceName[NAME_MAX] = {0};
        strcpy(DeviceName, strrchr((char *)nlh, '/') + 1);

        // printf("%s ------- %s\n", operation, DeviceName);
        // fflush(stdout);

        Push_OperationQueue(pOperationQueue, operation, DeviceName);
    }
    close(sock_fd);
}

void ResolveOperationProcess(void *arg)
{
    OperationQueue *pOperationQueue = ((ResolveOperationProcessParams *)arg)->pOperationQueue;
    DeviceLink *pDeviceLink = ((ResolveOperationProcessParams *)arg)->pDeviceLink;

    // printf("-------------ResolveOperationProcess-----------------\n");
    // printf("pDeviceLink: %x\n", pDeviceLink);
    // printf("((ResolveOperationProcessParams *)arg)->pDeviceLink: %x\n", ((ResolveOperationProcessParams *)arg)->pDeviceLink);
    // printf("pOperationQueue: %x\n", pOperationQueue);
    // printf("((ResolveOperationProcessParams *)arg)->pOperationQueue: %x\n", ((ResolveOperationProcessParams *)arg)->pOperationQueue);
    // fflush(stdout);

    printf("ResolveOperationProcess ...\n");

    char operation[20] = {0};
    char DeviceName[NAME_MAX] = {0};

    while (1)
    {
        if (IsEmpty_OperationQueue(pOperationQueue))
        {
            // printf("-+-+-+-+-+-+-+-+-+-+-+-+-+-\n");
            // fflush(stdout);
            sleep(2);
            continue;
        }

        // if (!Pop_OperationQueue(pOperationQueue, operation, DeviceName));
        // {
        //     continue;
        // }

        // printf("-+-+-+-+-+-+-+-+-+-+-+-+-+-\n");
        // fflush(stdout);

        Pop_OperationQueue(pOperationQueue, operation, DeviceName);

        // printf("%s ------- %s\n", operation, DeviceName);
        // fflush(stdout);

        if (strlen(DeviceName) < 3)
            continue;

        if ('s' == DeviceName[0] && 'd' == DeviceName[1] && (DeviceName[2] >= 'a' || DeviceName[2] <= 'z')) //sda~sdz, udisk
        {
            if (0 == strcmp(operation, "add"))
            {
                AddDeviceLink(0, DeviceName, "", pDeviceLink);
            }
            else if (0 == strcmp(operation, "remove"))
            {
                DeleteDeviceLink(DeviceName, pDeviceLink);
            }
            else if (0 == strcmp(operation, "change"))
            {
                AddDeviceLink(0, DeviceName, "", pDeviceLink);
            }
            else
                continue;
        }
        else if ('s' == DeviceName[0] && 'r' == DeviceName[1] && (DeviceName[2] >= '0' || DeviceName[2] <= '9')) //sr0~sr9, cdrom
        {
            if (0 == strcmp(operation, "add"))
            {
                AddDeviceLink(1, DeviceName, "", pDeviceLink);
            }
            else if (0 == strcmp(operation, "remove"))
            {
                DeleteDeviceLink(DeviceName, pDeviceLink);
            }
            else if (0 == strcmp(operation, "change"))
            {
                AddDeviceLink(1, DeviceName, "", pDeviceLink);
            }
            else
                continue;
        }
        else
            continue;
    }
    return;
}

void CheckMountProcess(DeviceLink *pDeviceLink)
{
    printf("CheckMountProcess ...\n");

    while (1)
    {
        // 获取行数,用于确定创建对照表的大小
        int row = 0;
        char cmd[MAX_BUFF_LEN] = {0};
        char buffer[MAX_BUFF_LEN] = {0};
        FILE *pf;
        sprintf(cmd, "cat %s | grep -n \" \" | awk -F \":\" '{print $1}' | tail -n1", FILE_MOUNT_CHECK);
        // system(cmd);
        pf = popen(cmd, "r");
        fread(buffer, sizeof(buffer), 1, pf);
        row = atoi(buffer);
        pclose(pf);

        // printf("ROW: %d\n", row);

        char RawData[1024] = {0};

        FILE *fp = fopen(FILE_MOUNT_CHECK, "r");
        if (NULL == fp)
        {
            printf("failed to open dos.txt\n");
            continue;
        }

        MountMap *pMountMap = (MountMap *)malloc(sizeof(MountMap) * row);
        if (NULL == pMountMap)
        {
            printf("Malloc failed!\n");
            continue;
        }

        memset(pMountMap, 0, sizeof(MountMap) * row);
        int mapnum = 0;  // 对照表实际大小

        while (!feof(fp))
        {
            memset(RawData, 0, sizeof(RawData));
            fgets(RawData, sizeof(RawData) - 1, fp); // 包含了换行符
            // printf("%s", RawData);

            if (strlen(RawData) <= 5)
                continue;

            if ((RawData[0] != '/') || (RawData[1] != 'd') || (RawData[2] != 'e') || (RawData[3] != 'v') || (RawData[4] != '/')) // 不是/dev/开头,直接pass
                continue;

            char DeviceName[PATH_MAX + NAME_MAX] = {0};
            char MountPath[PATH_MAX + NAME_MAX] = {0};

            strcpy(DeviceName, RawData + 5); // +5是为了去掉/dev/
            *strchr(DeviceName, ' ') = '\0';

            strcpy(MountPath, strchr(RawData, ' ') + 1);
            *strchr(MountPath, ' ') = '\0';

            // printf("DeviceName: %s \t MountPath: %s\n", DeviceName, MountPath);

            strcpy(pMountMap[mapnum].DeviceName, DeviceName);
            strcpy(pMountMap[mapnum].MountPath, MountPath);

            mapnum ++;
        }
        fclose(fp);

        UpdateDeviceLink(pDeviceLink, pMountMap, mapnum);

        free(pMountMap);
        pMountMap = NULL;

        sleep(2); //2秒轮询一次
    }
    return;
}

int main(int argc, char *argv[])
{
    pthread_mutex_init(&DeviceLinkLock, NULL);
    pthread_mutex_init(&OperationQueueLock, NULL);

    DeviceLink *pDeviceLink = (DeviceLink *)malloc(sizeof(DeviceLink)); // 设备详情链表
    if (NULL == pDeviceLink)
    {
        printf("Malloc failed\n");
        exit(-3);
    }
    pDeviceLink->pNext = NULL;

    OperationQueue *pOperationQueue = (OperationQueue *)malloc(sizeof(OperationQueue)); //Netlink操作队列
    if (NULL == pOperationQueue)
    {
        printf("Malloc failed\n");
        exit(-3);
    }
    pOperationQueue->pNext = NULL;

    int res;
    pthread_t tid_NetLink;
    res = pthread_create(&tid_NetLink, NULL, (void *)NetLinkProcess, (void *)pOperationQueue);
    if (0 != res)
    {
        printf("pthread_create error.\n");
        fflush(stdout);
        exit(-1);
    }

    ResolveOperationProcessParams ropargs = {pDeviceLink, pOperationQueue};

    // printf("-------------main-----------------\n");
    // printf("pDeviceLink: %x\n", pDeviceLink);
    // printf("ropargs.pDeviceLink: %x\n", ropargs.pDeviceLink);
    // printf("pOperationQueue: %x\n", pOperationQueue);
    // printf("ropargs.pOperationQueue: %x\n", ropargs.pOperationQueue);
    // fflush(stdout);

    pthread_t tid_OperationQueue;
    res = pthread_create(&tid_OperationQueue, NULL, (void *)ResolveOperationProcess, &ropargs);
    if (0 != res)
    {
        printf("pthread_create error.\n");
        fflush(stdout);
        exit(-1);
    }

    pthread_t tid_CheckMount;
    res = pthread_create(&tid_CheckMount, NULL, (void *)CheckMountProcess, (void *)pDeviceLink);
    if (0 != res)
    {
        printf("pthread_create error.\n");
        fflush(stdout);
        exit(-1);
    }

    // res = pthread_join(tid_NetLink, NULL);
    // if (0 != res)
    // {
    //     printf("Thread joined error.\n");
    //     fflush(stdout);
    //     exit(-2);
    // }

    // printf("wait .........\n");
    while (1)
    {
        sleep(5);
        OutputDeviceLink(pDeviceLink);
        // printf("Sleep .........\n");
        fflush(stdout);
    }

    free(pDeviceLink);
    pDeviceLink = NULL;
    free(pOperationQueue);
    pOperationQueue = NULL;
    return 0;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值