· 6 years ago · Feb 13, 2020, 09:34 AM
1#define _GNU_SOURCE /* Needed to get O_LARGEFILE definition */
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <limits.h>
5 #include <poll.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/fanotify.h>
9 #include <unistd.h>
10
11 /* Read all available fanotify events from the file descriptor 'fd' */
12
13 static void
14 handle_events(int fd)
15 {
16 const struct fanotify_event_metadata *metadata;
17 struct fanotify_event_metadata buf[200];
18 ssize_t len;
19 char path[PATH_MAX];
20 ssize_t path_len;
21 char procfd_path[PATH_MAX];
22 struct fanotify_response response;
23
24 /* Loop while events can be read from fanotify file descriptor */
25
26 for(;;) {
27
28 /* Read some events */
29
30 len = read(fd, (void *) &buf, sizeof(buf));
31 if (len == -1 && errno != EAGAIN) {
32 perror("read");
33 exit(EXIT_FAILURE);
34 }
35
36 /* Check if end of available data reached */
37
38 if (len <= 0)
39 break;
40
41 /* Point to the first event in the buffer */
42
43 metadata = buf;
44
45 /* Loop over all events in the buffer */
46
47 while (FAN_EVENT_OK(metadata, len)) {
48
49 /* Check that run-time and compile-time structures match */
50
51 if (metadata->vers != FANOTIFY_METADATA_VERSION) {
52 fprintf(stderr,
53 "Mismatch of fanotify metadata version.\n");
54 exit(EXIT_FAILURE);
55 }
56
57 /* metadata->fd contains either FAN_NOFD, indicating a
58 queue overflow, or a file descriptor (a nonnegative
59 integer). Here, we simply ignore queue overflow. */
60
61 if (metadata->fd >= 0) {
62
63 /* Handle open permission event */
64
65 if (metadata->mask & FAN_OPEN_PERM) {
66 printf("FAN_OPEN_PERM: ");
67
68 /* Allow file to be opened */
69
70 response.fd = metadata->fd;
71 response.response = FAN_ALLOW;
72 write(fd, &response,
73 sizeof(struct fanotify_response));
74 }
75
76 /* Handle closing of writable file event */
77
78 if (metadata->mask & FAN_CLOSE_WRITE)
79 printf("FAN_CLOSE_WRITE: ");
80
81 /* Retrieve and print pathname of the accessed file */
82
83 snprintf(procfd_path, sizeof(procfd_path),
84 "/proc/self/fd/%d", metadata->fd);
85 path_len = readlink(procfd_path, path,
86 sizeof(path) - 1);
87 if (path_len == -1) {
88 perror("readlink");
89 exit(EXIT_FAILURE);
90 }
91
92 path[path_len] = '\0';
93 printf("File %s\n", path);
94
95 /* Close the file descriptor of the event */
96
97 close(metadata->fd);
98 }
99
100 /* Advance to next event */
101
102 metadata = FAN_EVENT_NEXT(metadata, len);
103 }
104 }
105 }
106
107 int
108 main(int argc, char *argv[])
109 {
110 char buf;
111 int fd, poll_num;
112 nfds_t nfds;
113 struct pollfd fds[2];
114
115 /* Check mount point is supplied */
116
117 if (argc != 2) {
118 fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
119 exit(EXIT_FAILURE);
120 }
121
122 printf("Press enter key to terminate.\n");
123
124 /* Create the file descriptor for accessing the fanotify API */
125
126 fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
127 O_RDONLY | O_LARGEFILE);
128 if (fd == -1) {
129 perror("fanotify_init");
130 exit(EXIT_FAILURE);
131 }
132
133 /* Mark the mount for:
134 - permission events before opening files
135 - notification events after closing a write-enabled
136 file descriptor */
137
138 if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
139 FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD,
140 argv[1]) == -1) {
141 perror("fanotify_mark");
142 exit(EXIT_FAILURE);
143 }
144
145 /* Prepare for polling */
146
147 nfds = 2;
148
149 /* Console input */
150
151 fds[0].fd = STDIN_FILENO;
152 fds[0].events = POLLIN;
153
154 /* Fanotify input */
155
156 fds[1].fd = fd;
157 fds[1].events = POLLIN;
158
159 /* This is the loop to wait for incoming events */
160
161 printf("Listening for events.\n");
162
163 while (1) {
164 poll_num = poll(fds, nfds, -1);
165 if (poll_num == -1) {
166 if (errno == EINTR) /* Interrupted by a signal */
167 continue; /* Restart poll() */
168
169 perror("poll"); /* Unexpected error */
170 exit(EXIT_FAILURE);
171 }
172
173 if (poll_num > 0) {
174 if (fds[0].revents & POLLIN) {
175
176 /* Console input is available: empty stdin and quit */
177
178 while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
179 continue;
180 break;
181 }
182
183 if (fds[1].revents & POLLIN) {
184
185 /* Fanotify events are available */
186
187 handle_events(fd);
188 }
189 }
190 }
191
192 printf("Listening for events stopped.\n");
193 exit(EXIT_SUCCESS);
194 }