ewmscp  ..
fanotify_watch.cpp
Go to the documentation of this file.
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  const struct fanotify_event_metadata *metadata;
16  struct fanotify_event_metadata buf[200];
17  ssize_t len;
18  char path[PATH_MAX];
19  ssize_t path_len;
20  char procfd_path[PATH_MAX];
21  struct fanotify_response response;
22 
23  /* Loop while events can be read from fanotify file descriptor */
24 
25  for (;;) {
26 
27  /* Read some events */
28 
29  len = read(fd, (void *) &buf, sizeof(buf));
30  if (len == -1 && errno != EAGAIN) {
31  perror("read");
32  exit(EXIT_FAILURE);
33  }
34 
35  /* Check if end of available data reached */
36 
37  if (len <= 0) {
38  break;
39  }
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 
64  /* Handle open permission event */
65 
66  if (metadata->mask & FAN_OPEN_PERM) {
67  printf("FAN_OPEN_PERM: ");
68 
69  /* Allow file to be opened */
70 
71  response.fd = metadata->fd;
72  response.response = FAN_ALLOW;
73  write(fd, &response,
74  sizeof(struct fanotify_response));
75  }
76 
77  /* Handle closing of writable file event */
78 
79  if (metadata->mask & FAN_CLOSE_WRITE) {
80  printf("FAN_CLOSE_WRITE: ");
81  }
82 
83  /* Retrieve and print pathname of the accessed file */
84 
85  snprintf(procfd_path, sizeof(procfd_path),
86  "/proc/self/fd/%d", metadata->fd);
87  path_len = readlink(procfd_path, path,
88  sizeof(path) - 1);
89  if (path_len == -1) {
90  perror("readlink");
91  exit(EXIT_FAILURE);
92  }
93 
94  path[path_len] = '\0';
95  printf("File %s\n", path);
96 
97  /* Close the file descriptor of the event */
98 
99  close(metadata->fd);
100  }
101 
102  /* Advance to next event */
103 
104  metadata = FAN_EVENT_NEXT(metadata, len);
105  }
106  }
107 }
108 
109 int
110 main(int argc, char *argv[]) {
111  char buf;
112  int fd, poll_num;
113  nfds_t nfds;
114  struct pollfd fds[2];
115 
116  /* Check mount point is supplied */
117 
118  if (argc != 2) {
119  fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
120  exit(EXIT_FAILURE);
121  }
122 
123  printf("Press enter key to terminate.\n");
124 
125  /* Create the file descriptor for accessing the fanotify API */
126 
127  fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
128  O_RDONLY | O_LARGEFILE);
129  if (fd == -1) {
130  perror("fanotify_init");
131  exit(EXIT_FAILURE);
132  }
133 
134  /* Mark the mount for:
135  - permission events before opening files
136  - notification events after closing a write-enabled
137  file descriptor */
138 
139  if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
140  FAN_CLOSE_WRITE, AT_FDCWD,
141  argv[1]) == -1) {
142  perror("fanotify_mark");
143  exit(EXIT_FAILURE);
144  }
145 
146  /* Prepare for polling */
147 
148  nfds = 2;
149 
150  /* Console input */
151 
152  fds[0].fd = STDIN_FILENO;
153  fds[0].events = POLLIN;
154 
155  /* Fanotify input */
156 
157  fds[1].fd = fd;
158  fds[1].events = POLLIN;
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 
170  perror("poll"); /* Unexpected error */
171  exit(EXIT_FAILURE);
172  }
173 
174  if (poll_num > 0) {
175  if (fds[0].revents & POLLIN) {
176 
177  /* Console input is available: empty stdin and quit */
178 
179  while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n') {
180  continue;
181  }
182  break;
183  }
184 
185  if (fds[1].revents & POLLIN) {
186 
187  /* Fanotify events are available */
188 
189  handle_events(fd);
190  }
191  if (poll_num > 0) {
192  if (fds[0].revents & POLLIN) {
193 
194  /* Console input is available: empty stdin and quit */
195 
196  while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n') {
197  continue;
198  }
199  break;
200  }
201 
202  if (fds[1].revents & POLLIN) {
203 
204  /* Fanotify events are available */
205 
206  handle_events(fd);
207  }
208  }
209  }
210 
211  printf("Listening for events stopped.\n");
212  exit(EXIT_SUCCESS);
213  }
214 
215 }
handle_events
static void handle_events(int fd)
Definition: fanotify_watch.cpp:14
main
int main(int argc, char *argv[])
Definition: fanotify_watch.cpp:110