1 #include "common.h"
2 #include "../config.h"
3 
4 #include <sys/stat.h>
5 #include <sys/types.h>
6 
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 
14 #include <inotifytools/inotifytools.h>
15 
16 #define MAXLEN 4096
17 #define LIST_CHUNK 1024
18 
resize_if_necessary(const int count,int * len,const char *** ptr)19 static void resize_if_necessary(const int count, int* len, const char*** ptr) {
20 	if (count >= *len - 1) {
21 		*len += LIST_CHUNK;
22 		*ptr = (const char**)realloc(*ptr, sizeof(char*) * *len);
23 	}
24 }
25 
print_event_descriptions()26 void print_event_descriptions() {
27     printf("\taccess\t\tfile or directory contents were read\n");
28     printf("\tmodify\t\tfile or directory contents were written\n");
29     printf("\tattrib\t\tfile or directory attributes changed\n");
30     printf("\tclose_write\tfile or directory closed, after being opened in\n"
31            "\t           \twritable mode\n");
32     printf("\tclose_nowrite\tfile or directory closed, after being opened in\n"
33            "\t           \tread-only mode\n");
34     printf("\tclose\t\tfile or directory closed, regardless of read/write "
35            "mode\n");
36     printf("\topen\t\tfile or directory opened\n");
37     printf("\tmoved_to\tfile or directory moved to watched directory\n");
38     printf("\tmoved_from\tfile or directory moved from watched directory\n");
39     printf("\tmove\t\tfile or directory moved to or from watched directory\n");
40     printf("\tmove_self\t\tA watched file or directory was moved.\n");
41     printf("\tcreate\t\tfile or directory created within watched directory\n");
42     printf("\tdelete\t\tfile or directory deleted within watched directory\n");
43     printf("\tdelete_self\tfile or directory was deleted\n");
44     printf("\tunmount\t\tfile system containing file or directory unmounted\n");
45 }
46 
isdir(char const * path)47 int isdir(char const *path) {
48     static struct stat my_stat;
49 
50     if (-1 == lstat(path, &my_stat)) {
51         if (errno == ENOENT)
52             return 0;
53         fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
54         return 0;
55     }
56 
57     return S_ISDIR(my_stat.st_mode) && !S_ISLNK(my_stat.st_mode);
58 }
59 
free_list(int argc,char ** argv,FileList * list)60 void free_list(int argc, char** argv, FileList* list) {
61 	char* start_of_stack = argv[0];
62 	char* end_of_stack = argv[argc - 1];
63 	for (int i = 0; argv[i]; ++i) {
64 		if (argv[i] < start_of_stack) {
65 			start_of_stack = argv[i];
66 		} else if (argv[i] > end_of_stack) {
67 			end_of_stack = argv[i];
68 		}
69 	}
70 
71 	while (*end_of_stack) {
72 		++end_of_stack;
73 	}
74 
75 	for (int i = 0; list->watch_files[i]; ++i) {
76 		if (list->watch_files[i] < start_of_stack ||
77 		    list->watch_files[i] > end_of_stack) {
78 			free((void*)list->watch_files[i]);
79 		}
80 	}
81 
82 	free(list->watch_files);
83 
84 	for (int i = 0; list->exclude_files[i]; ++i) {
85 		if (list->exclude_files[i] < start_of_stack ||
86 		    list->exclude_files[i] > end_of_stack) {
87 			free((void*)list->exclude_files[i]);
88 		}
89 	}
90 
91 	free(list->exclude_files);
92 }
93 
construct_path_list(int argc,char ** argv,char const * filename,FileList * list)94 void construct_path_list(int argc,
95 			 char** argv,
96 			 char const* filename,
97 			 FileList* list) {
98 	list->watch_files = 0;
99 	list->exclude_files = 0;
100 	FILE* file = 0;
101 
102 	if (filename) {
103 		if (!strcmp(filename, "-")) {
104 			file = stdin;
105 		} else {
106 			file = fopen(filename, "r");
107 		}
108 	}
109 
110 	int watch_len = LIST_CHUNK;
111 	int exclude_len = LIST_CHUNK;
112 	int watch_count = 0;
113 	int exclude_count = 0;
114 	list->watch_files = (char const**)malloc(sizeof(char*) * LIST_CHUNK);
115 	list->exclude_files = (char const**)malloc(sizeof(char*) * LIST_CHUNK);
116 
117 	char name[MAXLEN];
118 	while (file && fgets(name, MAXLEN, file)) {
119 		if (name[strlen(name) - 1] == '\n')
120 			name[strlen(name) - 1] = 0;
121 		if (strlen(name) == 0)
122 			continue;
123 		if ('@' == name[0] && strlen(name) == 1)
124 			continue;
125 		if ('@' == name[0]) {
126 			resize_if_necessary(exclude_count, &exclude_len,
127 					    &list->exclude_files);
128 			list->exclude_files[exclude_count++] = strdup(&name[1]);
129 		} else {
130 			resize_if_necessary(watch_count, &watch_len,
131 					    &list->watch_files);
132 			list->watch_files[watch_count++] = strdup(name);
133 		}
134 	}
135 
136 	if (file && file != stdin)
137 		fclose(file);
138 
139 	for (int i = 0; i < argc; ++i) {
140 		if (strlen(argv[i]) == 0)
141 			continue;
142 		if ('@' == argv[i][0] && strlen(argv[i]) == 1)
143 			continue;
144 		if ('@' == argv[i][0]) {
145 			resize_if_necessary(exclude_count, &exclude_len,
146 					    &list->exclude_files);
147 			list->exclude_files[exclude_count++] = &argv[i][1];
148 		} else {
149 			resize_if_necessary(watch_count, &watch_len,
150 					    &list->watch_files);
151 			list->watch_files[watch_count++] = argv[i];
152 		}
153 	}
154 
155 	list->exclude_files[exclude_count] = 0;
156 	list->watch_files[watch_count] = 0;
157 }
158 
warn_inotify_init_error(int fanotify)159 void warn_inotify_init_error(int fanotify) {
160 	const char* backend = fanotify ? "fanotify" : "inotify";
161 	const char* resource = fanotify ? "groups" : "instances";
162 	int error = inotifytools_error();
163 
164 	fprintf(stderr, "Couldn't initialize %s: %s\n", backend,
165 		strerror(error));
166 	if (error == EMFILE) {
167 		fprintf(stderr,
168 			"Try increasing the value of "
169 			"/proc/sys/fs/%s/max_user_%s\n",
170 			backend, resource);
171 	}
172 	if (fanotify && error == EINVAL) {
173 		fprintf(stderr,
174 			"fanotify support for reporting the events with "
175 			"file names was added in kernel v5.9.\n");
176 	}
177 	if (fanotify && error == EPERM) {
178 		fprintf(stderr, "fanotify watch requires admin privileges\n");
179 	}
180 }
181 
is_timeout_option_valid(unsigned int * timeout,char * o)182 bool is_timeout_option_valid(unsigned int* timeout, char* o) {
183 	if ((o == NULL) || (*o == '\0')) {
184 		fprintf(stderr,
185 			"The provided value is not a valid timeout value.\n"
186 			"Please specify a long int value.\n");
187 		return false;
188 	}
189 
190 	char* timeout_end = NULL;
191 	errno = 0;
192 	*timeout = strtol(o, &timeout_end, 10);
193 
194 	const int err = errno;
195 	if (err != 0) {
196 		fprintf(stderr,
197 			"Something went wrong with the timeout "
198 			"value you provided.\n");
199 		fprintf(stderr, "%s\n", strerror(err));
200 		return false;
201 	}
202 
203 	if (*timeout_end != '\0') {
204 		fprintf(stderr,
205 			"'%s' is not a valid timeout value.\n"
206 			"Please specify a long int value.\n",
207 			o);
208 		return false;
209 	}
210 
211 	return true;
212 }
213