Ubuntu: inotify递归监控文件系统状态

参考:

用 inotify 监控 Linux 文件系统事件


GITHUB


inih读取配置文件


inotify只能监控单层目录变化,不能监控子目录中的变化情况。所以只能遍历目录,将所有子目录添加入监控列表

当所监控目录超过8192时,导致too many open files, 两种解决方案:
1:
  需要更改下列文件数值大小:
      /proc/sys/fs/inotify/max_user_watches
                This specifies an upper limit on the number of watches that
              can be created per real user ID.
 但是这种方式在系统重启后会被重置为8192

2: [推荐]
     vim /etc/sysctl.conf

           添加 fs.inotify.max_user_watches=[max_watch_number]

开机自启:
    cp monitor_file_system.sh /etc/init.d
    cd /etc/init.d
    添加:
        update-rc.d monitor_file_system.sh defaults'
    删除: 
        update-rc.d -f monitor_file_system.sh remove
使用:
    service monitor_file_system { start | stop | restart | status }
    /etc/init.d/monitor_file_system { start | stop | restart | stauts }



#!/bin/bash
### BEGIN INIT INFO
#
# Provides:	 lz19960321lz@163.com
# Required-Start:	$local_fs  $remote_fs
# Required-Stop:	$local_fs  $remote_fs
# Default-Start: 	2 3 4 5
# Default-Stop: 	0 1 6
# Short-Description:	initscript
# Description: 	删除监控文件夹中文件所带\r
#
### END INIT INFO

#Author: lzgabel

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DAEMON=/usr/sbin/monitor_file_system/monitor_file_systemd
. /lib/lsb/init-functions

start() {
    status
        if [[ $? -ne 0 ]]; then
            return 1
        else 
            echo "Starting monitor_file_systemd..."
            $DAEMON
        fi
        return $?
}

stop() {
    status
        if [[ $? -eq 0 ]]; then
            return 1
        else 
            echo "Stopping monitor_file_systemd..."
            `ps -ef | grep "$DAEMON" | grep -v "grep" | awk '{print $2}' | xargs kill -9`
            if [[ $? -eq 0 ]]; then
                echo "Done."
                return 0
            else
                return 1
            fi
        fi
        
}

restart() {
    status
        if [[ $? -eq 0 ]]; then
            return 1
        else 
            echo "Stopping monitor_file_systemd..."
            stop 
            echo "Restart monitor_file_systemd..."
            start
            return 0
        fi
}

status() {
        ProCount=`ps -ef | grep "$DAEMON" | grep -v "grep" | awk '{print $2}' |wc -l`
        if [[ $ProCount -le 0 ]]; then
            echo "* : monitor_file_systemd is not running."
            return 0
        else 
            echo "* : monitor_file_systemd is running.";
            return 1
        fi 
}


case "$1" in 
    'start')
        start
        ;;
    'stop')
        stop
        ;;
    'restart')
        restart
        ;;
    'status')
        status
        ;;
    *)
        echo "Usage: "$DAEMON" {start|stop|restart|status}"
        exit 1
        ;;
esac
exit 0


/*************************************************************************
	> File Name: daemon.c
	> Author: lzgabel
	> Mail: lz19960321lz@163.com
	> Created Time: 2017年08月05日 星期六 20时19分55秒
    > 守护进程实时监听文件状态,删除文件中所有\r

 ************************************************************************/

#include <stdio.h>
#include <sys/resource.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/inotify.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include "ini.h"

#define EVENT_SIZE (sizeof(struct inotify_event))

static int WD_SIZE; //监听事件个数
static char watch_directory[BUFSIZ]; //目录

typedef struct wd_dir
{
    int     wd; //inotify_add_watch返回:Watch descriptor
    char    dirname[BUFSIZ]; //监控目录绝对路径
}*WD_DIR;

WD_DIR wd_array;
int wd_count = 0;  //事件发生个数

void 
init_daemon()
{
    pid_t               pid;
    struct rlimit       rl;
    struct sigaction    sa;
    unsigned int        i;
    umask(0);

    //创建子进程
    if((pid = fork()) < 0) {
         perror("fork error");
         exit(EXIT_FAILURE);
    } else if(pid > 0) {
        exit(EXIT_SUCCESS); //使父进程退出
    } 

    setsid();  //创建会话

    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGHUP|SIGCHLD,&sa,NULL);

    //创建子进程避免获取控制终端
    if((pid = fork()) < 0) {
         perror("fork error");
         exit(EXIT_FAILURE);
    } else if(pid > 0) {
        exit(EXIT_SUCCESS);
    }

    //修改目录
    if (chdir("/") == -1) {
        perror("Can't change directory");
        exit(EXIT_FAILURE);
    }
    //获取文件描述符最大值
    getrlimit(RLIMIT_NOFILE,&rl);
    //关闭不需要的文件描述符
    if(rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for(i = 0; i < rl.rlim_max; ++i)
        close(i);
}

/**
 *   将当前目录添加监听列表,遍历整个目录,将当前目录下子目录入监控列表 
 */

void
add_watch(int watch_fd, char *dir, int mask)
{
    DIR             *odir;
    int             wd;
    struct dirent   *dirent;
    if ( (odir = opendir(dir)) == NULL ) {
        perror("opendir error");
        exit(EXIT_FAILURE);
    }

    wd = wd_array[wd_count].wd =  inotify_add_watch(watch_fd, dir, mask);
    strcpy(wd_array[wd_count++].dirname, dir);

    if (wd == -1) {
        perror("Couldn't add watch list");
        return;
    }

    while ((dirent = readdir(odir)) != NULL) {
        if (strcmp(dirent->d_name, "..") == 0 
            || strcmp(dirent->d_name, ".") == 0) {
                continue;
            }
        if (dirent->d_type == DT_DIR) {
            char *subdir = (char *)malloc(BUFSIZ*sizeof(char));
            sprintf(subdir, "%s/%s", dir, dirent->d_name);
            add_watch(watch_fd, subdir, mask);
            free(subdir);
        } 
        /*处理已存在文件
       if (dirent->d_type == DT_REG )
        { 
            FILE *fp;
            char     *file_name = (char *)malloc(BUFSIZ*sizeof(char));
            char     *cmd_string = (char *)malloc(BUFSIZ*sizeof(char));
            sprintf(file_name, "%s/%s", dir, dirent->d_name);
            sprintf(cmd_string, "sed -i \"s/\\r//g\" %s", file_name);
	    fp = popen(cmd_string, "r");
            pclose(fp);
        }
        */
    }
    closedir(odir);
    return;
}

/**
 *  获取文件/目录的父目录绝对路径 
 */

char *
get_absolute_path(struct inotify_event *event)
{
    int     i;
    char    *dir = (char *)malloc(BUFSIZ*sizeof(char));

    for(i = 0; i < wd_count; i++) {
        if (wd_array[i].wd == event->wd) {
            strcpy(dir ,wd_array[i].dirname);
            break;
        }
    }
    return dir;
}

/**
 * 将子目录添加进入监听列表
 */

void 
append_subdir(int watch_fd, struct inotify_event *event, int mask)
{
    char    *dir = (char *)malloc(BUFSIZ*sizeof(char));
    char    *subdir = (char *)malloc(BUFSIZ*sizeof(char));

    dir = get_absolute_path(event);
    sprintf(subdir, "%s/%s", dir, event->name);
    add_watch(watch_fd, subdir, mask);
    free(dir);
    free(subdir);
    return;
}

/**
 *  将删除文件夹移除监听列表
 */

void 
remove_dir(int watch_fd, char *dir)
{
    int         i;
    for (i = 0; i < wd_count; i++) {
        if (strcmp(dir, wd_array[i].dirname) == 0) {
            inotify_rm_watch(watch_fd, wd_array[i].wd);
            return;
        }
            
    }
    return;
}

/**
 *  监听目录等待事件触发
 */

void
watch_dir(int watch_fd, int mask)
{
    int     length, i;
    char    buffer[BUFSIZ];
   /*
    * read从inotify的文件描述符中读取事件,从而判定发生了那些事件。若读取之时还没有发生任何事件,则read会阻塞指导事件发生
    */
    length = read(watch_fd, buffer, BUFSIZ);
    if (length == -1) {
        perror("read error !");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < length; ){
        struct inotify_event *event = (struct inotify_event *) &buffer[i];  //event->name是引发事件的文件名, 并不是完整路径
        if (event->len) {
            if (event->mask & IN_CREATE) {
                FILE * FP = fopen("/tmp/file.log" , "a+");
                if (event->mask & IN_ISDIR) {
                    char     *dir = (char *)malloc(BUFSIZ*sizeof(char));
                    dir = get_absolute_path(event);
                    fprintf(FP, "Directory [%s] was created in directory [%s]\n", event->name, dir);
                    append_subdir(watch_fd, event, mask);
                } else {
                    pid_t    cpid;
                    if ((cpid = fork()) < 0) {
                        perror("fork error");
                        exit(EXIT_FAILURE);
                    } else if (cpid == 0) {
                        FILE     *fp;
                        char     *file_name = (char *)malloc(BUFSIZ*sizeof(char));
                        char     *cmd_string = (char *)malloc(BUFSIZ*sizeof(char));
                        char     *dir = (char *)malloc(BUFSIZ*sizeof(char));
                        dir = get_absolute_path(event);
                        if (strncmp(event->name, "sed", 3) == 0)
                            exit(EXIT_SUCCESS);
                        sprintf(file_name, "%s/%s", dir, event->name);
                        sprintf(cmd_string, "sed -i \"s/\\r//g\" %s", file_name);
                        fp = popen(cmd_string, "r");
                        fprintf(FP, "File [%s] was created in directory [%s]\n", event->name, dir);
                        pclose(fp);
                        free(dir);
                        free(file_name);
                        free(cmd_string);
                        exit(EXIT_SUCCESS);
                    }

                }
                fclose(FP);
            } else if (event->mask & IN_DELETE) {
                FILE * FP = fopen("/tmp/file.log" , "a+");
                if (event->mask & IN_ISDIR) {
                    char    *dir = (char *)malloc(BUFSIZ*sizeof(char));
                    char    *subdir = (char *)malloc(BUFSIZ*sizeof(char));
                    dir = get_absolute_path(event);
                    sprintf(subdir, "%s/%s", dir, event->name);
                    remove_dir(watch_fd, subdir);
                    fprintf(FP, "\033[41;37mDirectory [ %s ] was deleted from directory [ %s ] \033[0m\n", event->name, dir);
                    free(dir);
                } else {
                    char    *dir = (char *)malloc(BUFSIZ*sizeof(char));
                    dir = get_absolute_path(event);
                    fprintf(FP, "\033[41;37mFile [ %s ] was deleted from directory [ %s ] \033[0m\n", event->name, dir);
                    free(dir);
                }
                fclose(FP);
            }
        }
        i += EVENT_SIZE + event->len;
    }
    return;
}

/**
 *   监听初始化
 */

int 
watch_init(char *root, int mask) 
{
    int     fd;
    if ((fd = inotify_init()) < 0) {
        perror("inotify_init error");
        exit(EXIT_FAILURE);
    } 
    add_watch(fd, root, mask);
    return fd;
}

static int handler(void* user, const char* section, const char* name,
                  const char* value)
{
    if(strncmp(name, "watch_directory", 15) == 0)
        strcpy(watch_directory, value);
    if(strncmp(name, "WD_SIZE", 7) == 0) {
        WD_SIZE = atoi(value);
        wd_array = (WD_DIR)malloc(sizeof(WD_DIR)*WD_SIZE);
    }
    return 1;
}

int
main(int argc,char *argv[])
{
    int                watch_fd;
    int                error;
    int                mask=IN_CREATE|IN_DELETE;
    init_daemon();
    error = ini_parse("/usr/sbin/monitor_file_system/monitor_file_systemd.conf", handler, NULL);
    if (error < 0) {
        perror("Can't read config file");
        exit(EXIT_FAILURE);
    }
    else if (error) {
        perror("Bad config file !");
        exit(EXIT_FAILURE);
    }
    // daemone(0,0); 系统调用
    watch_fd = watch_init(watch_directory, mask);
    for (;;) {
            watch_dir(watch_fd, mask);
    }
    exit(EXIT_SUCCESS);
}


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值