-
Notifications
You must be signed in to change notification settings - Fork 0
/
dirchangelog.c
173 lines (163 loc) · 5.16 KB
/
dirchangelog.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#define MAX_WATCH 65536
#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX +1))
extern char *optarg;
/* THE inotify nstance */
int inotify;
/* store watch descriptor to pathname mapping */
struct watch {
int wd;
char *pathname;
};
struct watch watches[MAX_WATCH];
int nextWatch = 0;
void buildWatch(DIR *directory, char *pathPrefix)
{
errno = 0;
struct dirent *dir;
int wd = inotify_add_watch(inotify, pathPrefix, IN_ALL_EVENTS);
if (wd == -1) {
printf("failed to add watch for %s: %s\n", pathPrefix, strerror(errno));
exit(EXIT_FAILURE);
}
watches[nextWatch].wd = wd;
watches[nextWatch].pathname = pathPrefix;
nextWatch++;
while ((dir = readdir(directory)) != NULL) {
if (errno != 0) {
perror("directory read error");
exit(EXIT_FAILURE);
}
/* ignore . and .. */
if (!strcmp(dir->d_name, "..") || !strcmp(dir->d_name, "."))
continue;
/* create watch and enumerate for subdirectories if it is a directory */
if (dir->d_type & DT_DIR) {
char *pathname = malloc(PATH_MAX);
strcpy(pathname, pathPrefix);
strcat(pathname, "/");
strcat(pathname, dir->d_name);
DIR *childDir = opendir(pathname);
buildWatch(childDir, pathname);
}
}
}
void addToWatch(char *path)
{
char *pathname = malloc(PATH_MAX);
strcpy(pathname, path);
int wd = inotify_add_watch(inotify, pathname, IN_ALL_EVENTS);
if (wd == -1) {
printf("failed to add watch for %s: %s\n", pathname, strerror(errno));
exit(EXIT_FAILURE);
}
watches[nextWatch].wd = wd;
watches[nextWatch].pathname = pathname;
nextWatch++;
}
int findWatch(int wd)
{
int index = -1;
for (int i=0; i<nextWatch; i++) {
if (watches[i].wd == wd)
index = i;
}
return index;
}
void keepWatch(int log)
{
char buf[BUF_LEN];
while(1) {
int numRead = read(inotify, buf, BUF_LEN);
if (numRead == 0) {
puts("fatal: read returned 0\n");
exit(EXIT_FAILURE);
}
if (numRead == -1) {
perror("read error");
exit(EXIT_FAILURE);
}
/* log events in buffer */
for (char *p = buf; p < buf + numRead;) {
struct inotify_event *event = (struct inotify_event *) buf;
/* find where the event occurred */
int index = findWatch(event->wd);
char pathname[PATH_MAX];
if (index == -1)
strcpy(pathname, "(unknown)");
else
strcpy(pathname, watches[index].pathname);
if (strlen(event->name) > 1) {
strcat(pathname, "/");
strcat(pathname, event->name);
}
if (event->mask & IN_ACCESS) dprintf(log, "%s: file was accessed\n", pathname);
if (event->mask & IN_ATTRIB) dprintf(log, "%s: file metadata changed\n", pathname);
if (event->mask & IN_CLOSE_WRITE) dprintf(log, "%s: file opened for writing was closed\n", pathname);
if (event->mask & IN_CLOSE_NOWRITE) dprintf(log, "%s: file opened read-only was closed\n", pathname);
if (event->mask & IN_CREATE) {
struct stat statbuf;
if (stat(pathname, &statbuf) == -1) {
printf("failed to stat %s: %s\n", pathname, strerror(errno));
goto end;
}
if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
dprintf(log, "%s: directory created inside\n", pathname);
addToWatch(pathname);
dprintf(log, "%s: added to watch\n", pathname);
} else {
dprintf(log, "%s: file/directory created inside\n", pathname);
}
}
if (event->mask & IN_DELETE) dprintf(log, "%s: file/directory deleted inside\n", pathname);
if (event->mask & IN_DELETE_SELF) dprintf(log, "%s: file/directory was deleted\n", pathname);
if (event->mask & IN_MODIFY) dprintf(log, "%s: file was modified\n", pathname);
if (event->mask & IN_MOVE_SELF) dprintf(log, "%s: file/directory was moved\n", pathname);
if (event->mask & IN_MOVED_FROM) dprintf(log, "%s: file moved out of directory\n", pathname);
if (event->mask & IN_MOVED_TO) dprintf(log, "%s: file was moved into of directory\n", pathname);
if (event->mask & IN_OPEN) dprintf(log, "%s: file was opened\n", pathname);
if (event->cookie > 0) dprintf(log, "cookie: %d\n", event->cookie);
end:
p += sizeof(struct inotify_event) + event->len;
}
}
}
int main(int argc, char *argv[])
{
if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
printf("Usage: %s [-f <output file>] <directory>\n", argv[0]);
printf("\nWatch for changes in a direcotry and recursive subdirectories and log to a file or stdout. If a subdirectory is added after watch has started, it is watched too.\n");
exit(EXIT_SUCCESS);
}
inotify = inotify_init();
int log = STDOUT_FILENO;
DIR *directory = NULL;
char opt;
char *pathPrefix;
while ((opt = getopt(argc, argv, "f:")) != -1) {
switch (opt) {
case 'f':
directory = opendir(argv[3]);
log = open(optarg, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
pathPrefix = argv[3];
}
}
if (directory == NULL) {
directory = opendir(argv[1]);
pathPrefix = argv[1];
}
/* remove traliging slash */
if (!strcmp(&pathPrefix[strlen(pathPrefix)-1], "/"))
pathPrefix[strlen(pathPrefix)-1] = '\0';
buildWatch(directory, pathPrefix);
dprintf(log, "watching %d directories\n", nextWatch);
keepWatch(log);
}