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