/************************
酷安：风也吹不动
写完后只检查过一遍运行好像没什么问题(自己也懒得看。。。)，
如果有bug会修的修一下，不会修的留个言，非必要功能性的程序，没大碍就不修了。
中文注释我会尽量表达我的意图
************************/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <android/log.h>

#if 0
#include <signal.h>
#include <assert.h>
#endif

#define TEST 0
#define OLD_V 0
#define TOOL_NAME "inotify"
#define TOOL_VERSION "version: 23.09.25"
#define PRINT_OPT (printf("This '%c'\n", opt))
#define EVENT_NUM 15
#define BUF_LEN 1024
#define MAX_BUF_LEN 4 * BUF_LEN
#define RM_WATCH (inotify_rm_watch(inotifyFd, inotifyWd))
#define CLOSE_FD (close(inotifyFd))
#define LOG_TAG TOOL_NAME
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

#if TEST
#define DEBUG_ENABLE 1
pthread_spinlock_t spinlock; //自旋锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#else
pthread_rwlock_t rwlock; //读写锁
#endif


/* static global var */
static int usrstrlen;
static int logcat_i = 0;
static int bufmode = 0;
static int matchtype = 0;
static char *log_file = NULL;
static char *status_file = NULL;
static char global_tmp_buf[BUF_LEN] = {'\0'};
// 事件类型 随着内核不断更新，事件数量不固定，但这些够用了
static char *events[EVENT_NUM] = {
	"OPEN", "CLOSE_WRITE", "CLOSE_NOWRITE",
	"CLOSE", "CREATE", "MODIFY",
	"ATTRIB", "MOVE_SELF", "MOVED_FROM",
	"MOVED_TO", "MOVE", "DELETE_SELF",
	"DELETE", "ACCESS", "PROG_DEBUG"
};
struct timeval tv;
struct timezone tz;
struct tm *time_p;
struct stat st;



#if TEST
extern void (*signal(int sig, void(*handler)(int)))(int);
void signal_handler(int signal_num)
{
	printf("捕捉信号：%d\n", signal_num);
	sleep(1);
	exit(1);
}
#endif
static int quick_search_ch(char *, char *, char * const, const char);
static void *th_status_file (void *args);
static int read_only(const char *, const char *);
static inline void get_usr_time(char *);
static inline void mexit(int, const char *);
static int output_files(int, ...);
static bool parse_args(
				int *argc,
				char ***argv,
				char **str,
				char **file,
				char **event,
				char **log,
				char **status,
				int *bufmode,
				int *matchtype,
				int *run_init,
				int *event_index,
				int *logcat_i);
void print_help(void);





/*
写个简单的小算法用来查找最后一个匹配字符位置并分割，
失败返回-1，找不到返回-2，找到返回索引点位置
*/
int quick_search_ch(char *buf1, char *buf2, char *const str, const char c)
{
	if ((str == NULL) || (buf1 == NULL) || (buf2 == NULL))
		return -1;

	int index = -2;  //用来定位
	int len = strlen(str);
	if (!len)
		return index;

#if 1
	char *swap_ptr = str;
	swap_ptr += len;  //地址偏移至字符串的'\0'位置

	if (*swap_ptr != '\0')
		return -1;

	for (int i = len; i > 0; i--)
	{
		if (*swap_ptr == c)
		{
			index = i;  //定位到匹配的字符位置
			break;
		}
		swap_ptr--;  //地址每次偏移一个字符
	}
	
	if (index == -2)
		return index; //找不到匹配的字符

	// 格式化指向堆区
	swap_ptr = (char *)calloc(len + 2, sizeof(char));
	if (swap_ptr == NULL)
		return -1;
	// 将index位置之前的字符内容拷贝到堆区
	memmove(swap_ptr, str, index);
	sprintf(buf1, "%s", swap_ptr);  //index之前的内容存到buf1
	sprintf(buf2, "%s", str + (index + 1));  //index之后的内容存到buf2
	free(swap_ptr);
	swap_ptr = NULL;
#else
/* 上面算法是从字符串末尾开始往前找 找到就跳出。
   下面算法是从字符串首开始找，找到就标记，直至\0再跳出 */
	void *swap_ptr = str;
	for (int i=0; i<len; i++) {
		if (*str == c)
			index = i;
		str++;
	}
	if (index == -2) return index;
	str = (char *)calloc(len + 2, sizeof(char));
	if (str == NULL) return -1;
	memmove(str, swap_ptr, index);
	sprintf(buf1, "%s", str);
	sprintf(buf2, "%s", swap_ptr + (index + 1));
	free(str);
	str = NULL;
	swap_ptr = NULL;
#endif

	// 返回最后一个字符的索引位置
	return index;
}



static inline void mexit(int v, const char *s)
{
	if (v) {
		LOGE("%s (END BAD).", s);
		exit(EXIT_FAILURE);
	}
	LOGD("%s (END SUCCESS).", s);
	exit(EXIT_SUCCESS);
}



/* 命令行参数处理 */
static bool parse_args(
				int *argc,
				char ***argv,
				char **str,
				char **file,
				char **event,
				char **log,
				char **status,
				int *bufmode,
				int *matchtype,
				int *run_init,
				int *event_index,
				int *logcat_i)
{
	int none = 0;
	int error_flag = 0;
	int FULL_BUFF = 0;
	int LINE_BUFF = 1;
	int IO_READ = 2;
	int FuzzyMatch = 0;
	int AccurateMatch = 1;
	*bufmode = FULL_BUFF;
	*matchtype = FuzzyMatch;
	char opt;
#if TEST
	char *tool_name = **argv;
#endif

	// optstring 字符后含冒号的选项必须带参数
	const char short_opts[] = "VFLIhe:s:f:";
	struct option long_opts[] = {
		{"help", no_argument, NULL, 'h'},
		{"version", no_argument, NULL, 'V'},
		{"start", no_argument, NULL, 'S'},
		{"AM", no_argument, NULL, 'M'},
		{"event", required_argument, NULL, 'e'},
		{"str", optional_argument, NULL, 's'},
		{"file", optional_argument, NULL, 'f'},
		{"log_file", optional_argument, NULL, 'E'},
		{"status_file", optional_argument, NULL, 'l'},
		{"NONE", required_argument, &none, 1},
		{"INIT", no_argument, NULL, 'i'},
		{"LOG", no_argument, NULL, 'A'},
		{0, 0, 0, 0},
	};


	/* opterr optarg optind optopt 四个为头文件别处定义的全局变量 */
	opterr = 1;
	opt = getopt_long(*argc, *argv, short_opts, long_opts, NULL);
	while ((opt != '?') && (opt != (char)-1)) {
		switch (opt) {
			// --help 获取帮助信息
			case 'h':
#if TEST
				PRINT_OPT;
#endif
				(*argc) = 0;
				print_help();
				return true;
			// --version 获取版本信息
			case 'V':
#if TEST
				PRINT_OPT;
#endif
				(*argc) = 0;
				fprintf(stdout, "%s %s\n", TOOL_NAME, TOOL_VERSION);
				return true;
			// --start 查看启动是否正常
			case 'S':
#if TEST
				PRINT_OPT;
#endif
				(*argc) = 0;
				fprintf(stdout, "Start success!\n");
				return true;
			// -F 调用fread(标准IO)
			case 'F':
#if TEST
				PRINT_OPT;
#endif
				(*bufmode) = FULL_BUFF;
				break;
			// -L 调用fgets(标准IO)
			case 'L':
#if TEST
				PRINT_OPT;
#endif
				(*bufmode) = LINE_BUFF;
				break;
			// -I 掉用read(文件IO)
			case 'I':
#if TEST
				PRINT_OPT;
#endif
				(*bufmode) = IO_READ;
				break;
			// --AM 精准完全匹配
			case 'M':
#if TEST
				PRINT_OPT;
#endif
				(*matchtype) = AccurateMatch;
				break;
			// -e or --event 事件类型
			case 'e':
#if TEST
				PRINT_OPT;
#endif
				for (int i = 0; i < EVENT_NUM; i++) {
					if ( !strcmp(optarg, events[i]) ) {
						*event = optarg;
						*event_index = i;
						break;
					} else (*event) = NULL;
				}
				break;
			// -s or --str 输入被监听文件的预期内容
			case 's':
#if TEST
				PRINT_OPT;
#endif
				(*str) = optarg;
				break;
			// -f or --file 输入被监听文件的绝对路径
			case 'f':
				(*file) = optarg;
				break;
			// --log_file 日志输出到该文件。 统计文件(主要记录异常)
			case 'E':
#if TEST
				PRINT_OPT;
#endif
				stat(optarg, &st);
				if (!S_ISDIR(st.st_mode)) {
					(*log) = optarg;
				} else {
					error_flag++;
					fprintf(stderr, "<log_file> %s: Is a directory\n", optarg);
				}
				break;
			// --status_file 运行状态文件
			case 'l':
#if TEST
				PRINT_OPT;
#endif
				stat(optarg, &st);
				if (!S_ISDIR(st.st_mode)) {
					(*status) = optarg;
				} else {
					error_flag++;
					fprintf(stderr, "<status_file> %s: Is a directory\n", optarg);
				}
				break;
			case 'i':
#if TEST
				PRINT_OPT;
#endif
				(*run_init) = 1;
				break;
			case 'A':
#if TEST
				PRINT_OPT;
#endif
				(*logcat_i) = 1;
				break;
			case 0:
				if (none == 1) {
					(*file) = optarg;
					(*event) = events[EVENT_NUM - 1];
					(*event_index) = -1;
				}
				break;
			default:
#if TEST
				PRINT_OPT;
#endif
				return false;
		} //switch
#if TEST
		printf("optind=%d\n", optind);
#endif
		opt = getopt_long(*argc, *argv, short_opts, long_opts, NULL);
	} //while
	
	
	// 无效的选项或某必带参数的选项却未带
	if (opt == '?') return false;
	
	// str有问题不让过
	if (*str == NULL) {
		if (!none)
		{
			fprintf(stderr,
			"<STR> not found or bad argument\n");
			error_flag++;
		}
	} else {
		usrstrlen = strlen((*str));
		if (!usrstrlen)
		{
			fprintf(stderr, "<STRING> argument empty!\n");
			error_flag++;
		}
	}

	// event有问题不让过
	if (!*event) {
		fprintf(stderr,
		"<EVENT> not found or bad argument\n");
		error_flag++;
	}

	// file有问题不让过
	if (*file && strlen(*file))
	{
		stat((*file), &st);
		if (S_ISDIR(st.st_mode)) {
			fprintf(stderr, "%s: Is a directory\n", *file);
			exit(EXIT_FAILURE);
		}
		if (access((*file), F_OK)) {
			perror((*file));  //No such file or directory
			exit(EXIT_FAILURE);
		}
		if (access((*file), R_OK)) {
			perror((*file));  //Permission denied
			exit(EXIT_FAILURE);
		}
	}
	else {
		fprintf(stderr, "<FILE> not found or bad argument\n");
		return false;
	}


	if (error_flag) return false;
	return true;
}




void print_help(void)
{
	setvbuf(stdout, NULL, _IONBF, 0);
	printf("%s %s\n", TOOL_NAME, TOOL_VERSION);
	printf("Usage: %s [options] <string|event|file> ...\n",
	       TOOL_NAME);
	printf("\n");
	printf("Options:\n");
	printf("\t-h | --help\tShow this help text.\n");
	printf("\t-V | --version\tShow version.\n");
	printf("\t-s | --str\tString, search for string in FILE.\n");
	printf("\t-f | --file\tTarget FILE.\n");
	printf("\t-e | --event\tListen for specific event. See [Events]\n");
	printf("\t\t\n");
	printf("\t-F\tBuffer mode(Full Buff)\n");
	printf("\t-L\tBuffer mode(Line Buff)\n");
	printf("\t-I\tBuffer mode(Linux File IO)\n");
	printf("\t-AM\tMatch pattern(Accurate Match)\n");
	printf("\t\t\n");
	printf("\t--log_file=<FILE>\tLog File\n");
	printf("\t--status_file=<FILE>\tStatus File\n");
	printf("\t--start\t\t\tStart this program, if success show 'Start success'.\n");
	printf("\t--NONE <FILE>\t\tDebug option: Listen for all events(Keep listening for events forever).\n");
	printf("\n");
	printf("Events:\n");
	printf("  [ATTRIB]\t[CLOSE_WRITE]\t    [OPEN]\n");
	printf("  [CREATE]\t[CLOSE_NOWRITE]\t    [CLOSE]\n");
	printf("  [MODIFY]\t[DELETE_SELF]\t    [MOVE]\n");
	printf("  [ACCESS]\t[MOVE_SELF]\t    [MOVED_TO]\n");
	printf("  [DELETE]\t[MOVED_FROM]\n");
	printf("\n");
}



static inline void get_usr_time(char *time_str)
{
	gettimeofday(&tv, &tz);
	time_p = localtime(&tv.tv_sec);
	sprintf(time_str, "%02d/%02d %02d:%02d:%02d.%03ld",
		(1 + time_p->tm_mon), time_p->tm_mday,
		(time_p->tm_hour), time_p->tm_min, time_p->tm_sec, (long)(tv.tv_usec / 1000));
}


#if OLD_V
/* 参数很少，懒得用getopt了 */
void printf_help(void)
{
	int i;
	char *p[11] = {
		"USE: COMMAND STRING FILE [EVENT] [MATCHTYPE] [BUFFERMODE]",
		"......",
		"[MATCHTYPE]: 1(AccurateMatch), default(FuzzyMatch)",
		"[BUFFERMODE]: 1(LineBuff), 2(IO Read), default(FullBuff) ",
		"[EVENT]:",
		"使用方法：命令 预期内容 文件位置 [事件] [匹配类型] [缓冲类型]",
		"[匹配类型]: 预期内容与文件内容匹配，参数1(精准匹配包括换行符)，默认：模糊匹配忽略大小写与长度，无需输入换行符",
		"[缓冲类型]: 参数1(行缓冲)，参数2(Linux的IO读)，默认全缓存读",
		"[事件类型]: ......",
		"例如：inotify 'abc' /sdcard/a.txt OPEN",
		"例如：inotify 'abc' /sdcard/a.txt OPEN 1 2"
	};
	for (i = 0; i < 4; i++)
		printf("%s\n", p[i]);

	printf("%s", p[i++]);
	for (int i = 0; i < EVENT_NUM; i++)
		fprintf(stdout, " | %s", event_arg[i]);
	
	putchar('\n');
	putchar('\n');
	for ( ; i < 11; i++)
		printf("%s\n", p[i]);
	
	mexit(0);
}
#endif








/* 线程(分离态) 一旦线程内inotify初始化成功，不改状态文件以及没有报错内容，该线程相当于长眠... */
static void *th_status_file (void *args)
{
	int fd, ret, inotifyFd, inotifyWd;
	int pth_err = 0;
	char *p = NULL;
	char path[512] = {'\0'};
	char file_name[256] = {'\0'};
	char buf[BUF_LEN] = {0};
	char buffer[BUF_LEN] = {'\0'};
	struct inotify_event *event = NULL;
	// 判断文件是否存在
	if (access(status_file, F_OK)) {
		// 不存在则创建一个
		int fd = open(status_file, O_RDWR|O_CREAT, 0644);
		if (fd == -1) {
			sprintf(buffer, "create status_file failed: %s (pthread exit).",
											strerror(errno));
			LOGE("%s", buffer);
			pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
			output_files(1, log_file, buffer, "a");
			pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
			pthread_exit(NULL);
		}
		sprintf(buffer, "%s\n", "status=inotify");
		write(fd, buffer, strlen(buffer));
		close(fd);
	}
	
	// 0644 rw-r-r
	chmod(status_file, S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR);
	if (access(status_file, R_OK)) {
		sprintf(buffer, "read status_file error: %s (pthread exit).",
										strerror(errno));
		LOGE("%s", buffer);
		pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
		output_files(1, log_file, buffer, "a");
		pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
		pthread_exit(NULL);
	}
	
	
	
	pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
	output_files(1, status_file, "status=inotify", "w");
	pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
	
	
	if (-1 != quick_search_ch(path, file_name, status_file, '/')) {
		stat(path, &st);
		// 判断是否为目录
		if (!S_ISDIR(st.st_mode)) {
			LOGE("unknown path: %s (pthread exit).",
						path);
			pthread_exit(NULL);
		}
	} else {
		LOGE("unknown (pthread exit).");
		pthread_exit(NULL);
	}
	
	;
INOTIFY:
	;
	inotifyFd = inotify_init();
	if (inotifyFd == -1) {
		sprintf(buffer, "inotify init %s (pthread exit).", strerror(errno));
		LOGE("%s", buffer);
		pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
		output_files(1, log_file, buffer, "a");
		pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
		pthread_exit(NULL);
	}
	inotifyWd = inotify_add_watch(inotifyFd, path, IN_MOVE_SELF |
			IN_DELETE | IN_DELETE_SELF | IN_MOVE |
			IN_ATTRIB | IN_MODIFY | IN_CREATE);
	if (inotifyWd == -1) {
		CLOSE_FD;
		sprintf(buffer, "inotify watch: %s %s (pthread exit).",
								path, strerror(errno));
		LOGE("%s", buffer);
		pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
		output_files(1, log_file, buffer, "a");
		pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
		pthread_exit(NULL);
	}
	
	for ( ; ; )
	{
		ret = read(inotifyFd, buf, BUF_LEN);
		if (ret < 0) {
			usleep(500000); //0.5sec
			pth_err++;
			if (pth_err > 20)
			{
				sprintf(buffer, "inotify watch error max:%d, %s (pthread exit).",
													pth_err, strerror(errno));
				LOGE("%s", buffer);
				pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
				output_files(1, log_file, buffer, "a");
				pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
				pthread_exit(NULL);
			}
			continue;
		}
		pth_err = 0;
		p = buf;
		event = (struct inotify_event *)p;
		
		if (!strcmp((event->name), file_name))
		{
			for (int i = 0; i < 48; i++)
			{
				fd = open(status_file, O_RDONLY);
				if (fd == -1)
				{
					pth_err++;
					usleep(20000); //20ms
					continue;
				}
				pth_err = 0;
				break;
			}
			if ((pth_err >= 48) && (fd == -1))
			{
				sprintf(buffer, "/system/bin/echo 'status=inotify' > '%s'", status_file);
				/* 懒得写了，直接调用命令吧 */
				system(buffer);
				LOGD("status_file not found. create file: %s", status_file);
				pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
				output_files(1, log_file, "status_file not found, create.", "a");
				pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
				continue;
			}
			
			
			pthread_rwlock_rdlock(&rwlock); //读文件，不允许其他线程写(读写锁)
			if (read(fd, buffer, BUF_LEN) == -1)
			{
				LOGE("status_file read error: %s",
									strerror(errno));
				pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
				close(fd);
				continue;
			}
			pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
			close(fd);
			
			
			// 内容为status=debug就进
			if (!strncmp(buffer, "status=debug", 12))
			{
				pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
				output_files(1, log_file, "changed state: 'status=debug'", "a");
				fprintf(stdout, "status=debug\n");
				mexit(0, "changed state: 'status=debug'");
			}
			// 内容不是status=inotify也不是debug就进
			if (strncmp(buffer, "status=inotify", 14))
			{
				pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
				// 失败就调用命令(不过...结果估计都一样)，写入成功就没事了
				if (0 < output_files(1, status_file, "status=inotify", "w"))
				{
					memset(buffer, '\0', BUF_LEN);
					LOGD("writing 'status=inotify' to status_file.");
					sprintf(buffer, "/system/bin/echo 'status=inotify' > '%s'", status_file);
					system(buffer);
				}
				pthread_rwlock_unlock(&rwlock);  //释放锁(读写锁)
				RM_WATCH;
				CLOSE_FD;
				goto INOTIFY;
			}
		}
		memset(buffer, '\0', BUF_LEN);
	}
	
	return NULL;
}








/* 用法：(文件数量, 文件1,目标内容,访问模式,  文件2,目标内容,访问模式 ......) */
static int output_files(int n, ...)
{
	va_list parg;
	va_start(parg, n);
	int ef = 0;
	char time_str[32];
	char buffer[BUF_LEN];
	char *array1[n];
	char *array2[n];
	char *array3[n];
	char **file = array1;
	char **str = array2;
	char **mode = array3;


	FILE *fp = NULL;
	/* 文件 内容 打开模式 文件 内容 打开模式 ... */
	for (int i = 0; i < n; i++) {
		*(file + i) = va_arg(parg, char *);
		*(str + i) = va_arg(parg, char *);
		*(mode + i) = va_arg(parg, char *);
		if (file[i] == NULL)
			continue;

		fp = fopen(file[i], mode[i]);
		if (fp == NULL) {
			LOGE("output %s error: %s.",
				file[i], strerror(errno));
			ef++;
			continue;
		}
		
		// (rw-r-r，0644)
		chmod(file[i], S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR);
		
		// 若是追加模式写入，带上时间记录
		if (**(mode + i) == 'a') {
			memset(time_str, 0, sizeof(time_str));
			memset(buffer, 0, BUF_LEN);
			get_usr_time(time_str);
			sprintf(buffer, "%s: %s",
				time_str, str[i]);
			// 指向堆区再拷贝
			str[i] = malloc(sizeof(buffer) + 2);
			if (str[i] != NULL) {
				memmove(str[i], buffer, sizeof(buffer));
				// strlen把该下标的整条字符串算出来，如果是换行减一位替换成结束符，不然日志带两个换行
				if ((str[i][strlen(str[i]) - 1]) == '\n') {
					(str[i][strlen(str[i]) - 1]) = '\0';  //堆区替换，非栈区和常量区
				}
			} else LOGE("Out Of Memory (OOM).");
		}
		
		
		memset(buffer, 0, BUF_LEN);
		// 设置写缓冲区，避免意外读取时不完整
		setvbuf(fp, buffer, _IOLBF, BUF_LEN);
		if (str[i] != NULL) {
			if (fprintf(fp, "%s\n", str[i]) < 0) {
				LOGE("write '%s' error: %s: %s.",
					str[i], file[i], strerror(errno));
				ef++;
			}
		}

		fclose(fp);
		fp = NULL;
		// 避免释放只读区域的内存空间
		if ((**(mode + i) == 'a') && (str[i] != NULL))
			free(str[i]);
	}

	va_end(parg);
	return ef;
}


static int read_only(const char *str, const char *file)
{
	int fd, success = 0;
	long filesize = 0;
	static char *buf = NULL;
	
	FILE *fp = fopen(file, "r");
	if (fp == NULL) {
		perror("File read error ");
		memset(global_tmp_buf, 0, BUF_LEN);
		sprintf(global_tmp_buf, "%s (File_read_error:  %s)",
								file,       strerror(errno));
		pthread_rwlock_wrlock(&rwlock);  //上锁 写日志(读写锁)
		output_files(2,
		log_file,     global_tmp_buf,    "a",
		status_file,  "status=debug",    "w");
		mexit(1, global_tmp_buf);
	}


	/* 指向文件尾，仅算文件大小，后续不再操作随机读 */
	fseek(fp, 0, SEEK_END);
	filesize = ftell(fp);
	rewind(fp);
	
	
	if (filesize > 0) {
		buf = (char *)malloc(16 * sizeof(char) + (size_t)filesize);  //申请比文件大一点的内存，buf后续存放文件的内容，一次性存
		if (buf != NULL) {
			memset(buf, '\0', 16 * sizeof(char) + (size_t)filesize);
		} else {
			fprintf(stderr, "Size: %ld ", filesize + 16 * sizeof(char));
			perror("Failed, Cannot request memory: ");
			LOGE("Out Of Memory(OOM).");
			// mexit(1, "OOM");
			abort();
		}
	} else {
		fclose(fp); //File is empty
		return -1;
	}
	
	
	switch (bufmode) {
		case 1:
			/* 顺序缓存读(行缓) */
			memset(global_tmp_buf, '\0', BUF_LEN);
			while ( (fgets(global_tmp_buf, BUF_LEN, fp)) != NULL ) {
				strcat(buf, global_tmp_buf);
				memset(global_tmp_buf, '\0', BUF_LEN);
			}
			break;
		case 2:
			/* 顺序io读 */
			fd = open(file, O_RDONLY);
			if (fd == -1) {
				perror("open fd error ");
				free(buf);
				memset(global_tmp_buf, 0, BUF_LEN);
				sprintf(global_tmp_buf, "%s (open_fd_error: %s)",
										file,      strerror(errno));
				mexit(1, global_tmp_buf);
			}
			
			if ( read(fd, (void *)buf, filesize + sizeof(char)) < 0 ) {
				perror("io read error");
				close(fd);
				free(buf);
				memset(global_tmp_buf, 0, BUF_LEN);
				sprintf(global_tmp_buf, "%s (io_read_error: %s)",
										file,      strerror(errno));
				mexit(1, global_tmp_buf);
			}
			
			close(fd);
			break;
		default:
			/* 顺序缓存读(全缓) */
			fread( buf, (size_t)(filesize + 2 * sizeof(char)), sizeof(char), fp );
			break;
	} //switch
	
	
	if (matchtype == 1) {
		//精准匹配
		if (!strcmp(buf, str)) {
			printf("%s", buf);  /* 输出被监控文件的内容 */
			pthread_rwlock_wrlock(&rwlock);  //上锁 写日志，要结束了不放锁(读写锁)
			output_files(2,
				log_file, buf, "a",
				status_file, "status=inotify", "w");
			success = 1;
		}
		if (logcat_i) LOGD("%s: %s", file, buf);
	}
	else {
		if ( !strncasecmp(buf, str, (size_t)usrstrlen) ) {
			printf("%s", buf);  /* 输出被监控文件的内容 */
			pthread_rwlock_wrlock(&rwlock);  //上锁 写日志，要结束了不放锁(读写锁)
			output_files(2,
				log_file, buf, "a",
				status_file, "status=inotify", "w");
			success = 1;
		}
		if (logcat_i) LOGD("%s: %s", file, buf);
	}
	
	
	free(buf); //malloc//
	fclose(fp);
	buf = NULL;
	fp = NULL;
	if (success) return 0;
	
	return -1;
}




/* 入口 */
int main(int argc, char **argv)
{
#if TEST
	signal(SIGINT, signal_handler);
#endif
	register int ret;
	int run_init = 0;
	char *target_str = NULL;
	char *target_file = NULL;
	char *target_event = NULL;
	int event_index = -1;
	int inotifyFd, inotifyWd;
	char buf[BUF_LEN];
	char *p = NULL;
	char time_str[32];
	struct inotify_event *event = NULL;
	
	
		if (argc < 2) {
			print_help();
			return -1;
		}
	// 开始解析命令行参数
	if (!parse_args(&argc, &argv,
		&target_str, &target_file, &target_event,
		&log_file, &status_file, &bufmode,
		&matchtype, &run_init, &event_index,
						&logcat_i)) {
			fprintf(stderr,
				"Try '%s --help' for more information.\n",
				TOOL_NAME);
				return EXIT_FAILURE;
	} else if (!argc) return EXIT_SUCCESS;
	
	LOGD("---- %s start. ----", TOOL_NAME);


#if OLD_V
	setvbuf(stdout, NULL, _IONBF, 0);
	
	if (argc < 3) {
		if ( (argv[1] != NULL) && (!strcmp(argv[1], "--help")) )
			printf_help();
		
		fprintf(stderr, "USE: COMMAND STRING FILE EVENT\n");
		mexit(1);
	}
	if (!strlen(argv[1])) {
		fprintf(stderr, "str is empty\n");
		mexit(1);
	}
	if ( (argv[3] != NULL) && (strlen(argv[3]) > 0) ) {
		for (int i = 0; i < EVENT_NUM; i++) {
			if ( !strcmp(argv[3], event_arg[i]) )
			{
				arg_event = i;
				if (!strcmp(argv[3], *event_arg)) {
					//非线程版禁止open，理解一下
					fprintf(stderr, "event:[%s] not support\n", *event_arg);
					mexit(1);
				}
				break;
			}
			else arg_event = -1;
		}
	}
	else arg_event = -1;
	
	if ( (argv[4] != NULL) && (strlen(argv[4]) > 0) )
		matchtype = atoi(argv[4]);
	
	if ( (argv[5] != NULL) && (strlen(argv[5]) > 0) )
		bufmode = atoi(argv[5]);
	
	if ( (argv[6] != NULL) && (strlen(argv[6]) > 0) && (!strcmp(argv[6], "-INIT")) )
		run_init = 1;
	
	usrstrlen = strlen(argv[1]);

	stat(file, &st);
	if (S_ISDIR(st.st_mode)) {
		fprintf(stderr, "%s: Is a directory\n", file);
		mexit(1);
	}
	/*
	if (access(file, R_OK)) {
		fprintf(stderr, "%s: Read-only permission denied\n", file);
		mexit(1);
	}
	*/
#endif



	;
INIT:
	;
	inotifyFd = inotify_init();
	if (inotifyFd == -1) {
		perror("inotify init");
		mexit(1, "inotify_init failed!");
	}

	inotifyWd = inotify_add_watch(inotifyFd, target_file, IN_ALL_EVENTS);
	if (inotifyWd == -1) {
		CLOSE_FD;
		fprintf(stderr, "inotify watch, ");
		perror(target_file);
		sprintf(global_tmp_buf, "inotify watch, %s%s", target_file, strerror(errno));
		mexit(1, global_tmp_buf);
	}

	/* 初始化、创建线程 */
	if (status_file != NULL) {
#if TEST
		pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
#else
		pthread_rwlock_init(&rwlock, NULL);
#endif
		pthread_t th;
		if (!pthread_create(&th, NULL, th_status_file, NULL)) {
			// 分离 不考虑回收
			pthread_detach(th);
		} else {
			LOGE("pthread create error.");
			output_files(1, log_file, "pthread create error.", "a");
		}
	}


	if (event_index == -1) {
		setvbuf(stdout, NULL, _IONBF, 0);
		fprintf(stdout, "Debugging...\n");
	}

	for ( ; ; )
	{
		// memset(buf, 0, BUF_LEN);
		ret = read(inotifyFd, buf, BUF_LEN);  //阻塞等待，事件发生后继续往下
		if (ret < 0) {
			perror("inotify read:");
			sprintf(global_tmp_buf, "inotify read: %s", strerror(errno));
			mexit(1, global_tmp_buf);
		}
		p = buf;
		event = (struct inotify_event *)p;
		
		switch (event_index) {
		case 0:
				//文件被打开，如调用open
			if (event->mask & IN_OPEN) {
				ret = read_only(target_str, target_file);  //非阻塞唤醒线程
			}
			break;
		case 1:
				//文件被关闭(可写)
			if (event->mask & IN_CLOSE_WRITE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 2:
				//文件被关闭(不可写)
			if (event->mask & IN_CLOSE_NOWRITE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 3:
				//文件被关闭，该事件包含上面两个
			if (event->mask & IN_CLOSE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 4:
				//文件被创建，一般用于监控整个目录下的状态
			if (event->mask & IN_CREATE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 5:
				//文件被写入
			if (event->mask & IN_MODIFY) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 6:
				//文件属性发生更改 比如权限、时间被改
			if (event->mask & IN_ATTRIB) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 7:
				//文件移动
			if (event->mask & IN_MOVE_SELF) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 8:
				//文件移动，一般用于监控目录，文件被移走
			if (event->mask & IN_MOVED_FROM) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 9:
				//文件移动，一般用于监控目录，文件被移进
			if (event->mask & IN_MOVED_TO) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 10:
				//文件移动，该事件包含FROM、TO
			if (event->mask & IN_MOVE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 11:
				//文件被删除
			if (event->mask & IN_DELETE_SELF) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 12:
				//文件被删除
			if (event->mask & IN_DELETE) {
				ret = read_only(target_str, target_file);
			}
			break;
		case 13:
				//文件被读取
			if (event->mask & IN_ACCESS) {
				ret = read_only(target_str, target_file);
			}
			break;
		default:
			memset(time_str, 0, sizeof(time_str));
			get_usr_time(time_str);
			printf("%s ", time_str);
			if (event->mask & IN_OPEN) printf("OPEN\n");
			if (event->mask & IN_CLOSE_WRITE) printf("CLOSE_WRITE\n");
			if (event->mask & IN_CLOSE_NOWRITE) printf("CLOSE_NOWRITE\n");
			if (event->mask & IN_CLOSE) printf("CLOSE\n");
			if (event->mask & IN_CREATE) printf("CREATE\n");
			if (event->mask & IN_MODIFY) printf("MODIFY\n");
			if (event->mask & IN_ATTRIB) printf("ATTRIB\n");
			if (event->mask & IN_MOVE_SELF) printf("MOVE_SELF\n");
			if (event->mask & IN_MOVED_FROM) printf("MOVED_FROM\n");
			if (event->mask & IN_MOVED_TO) printf("MOVED_TO\n");
			if (event->mask & IN_MOVE) printf("MOVE\n");
			if (event->mask & IN_DELETE_SELF) printf("DELETE_SELF\n");
			if (event->mask & IN_DELETE) printf("DELETE\n");
			if (event->mask & IN_ACCESS) printf("ACCESS\n");
			ret = -1;
		} //switch
		if (!ret) goto end_success;
		
		if (run_init) {
			RM_WATCH;
			CLOSE_FD;
			goto INIT;
		}
		
#if TEST
		printf("[Main]Count=%d\n", ++count);
#endif
	} // for(;;)

end_success:
	RM_WATCH;
	CLOSE_FD;
	mexit(0, "completed");
} //main()
