1 /* libguestfs - the guestfsd daemon
2 * Copyright (C) 2009 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <sys/inotify.h>
28 #include "../src/guestfs_protocol.h"
32 /* Currently open inotify handle, or -1 if not opened. */
33 static int inotify_fd = -1;
35 static char inotify_buf[64*1024*1024]; /* Event buffer, [0..posn-1] is valid */
36 static size_t inotify_posn = 0;
38 /* Because inotify_init does NEED_ROOT, NEED_INOTIFY implies NEED_ROOT. */
39 #define NEED_INOTIFY(errcode) \
41 if (inotify_fd == -1) { \
42 reply_with_error ("%s: you must call 'inotify_init' first to initialize inotify", __func__); \
47 #define MQE_PATH "/proc/sys/fs/inotify/max_queued_events"
50 do_inotify_init (int max_events)
54 NEED_ROOT (return -1);
57 reply_with_error ("inotify_init: max_events < 0");
62 fp = fopen (MQE_PATH, "w");
64 reply_with_perror (MQE_PATH);
67 fprintf (fp, "%d\n", max_events);
72 if (do_inotify_close () == -1)
75 #ifdef HAVE_INOTIFY_INIT1
76 inotify_fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
77 if (inotify_fd == -1) {
78 reply_with_perror ("inotify_init");
82 inotify_fd = inotify_init ();
83 if (inotify_fd == -1) {
84 reply_with_perror ("inotify_init");
87 if (fcntl (inotify_fd, F_SETFL, O_NONBLOCK) == -1) {
88 reply_with_perror ("fcntl: O_NONBLOCK");
93 if (fcntl (inotify_fd, F_SETFD, FD_CLOEXEC) == -1) {
94 reply_with_perror ("fcntl: FD_CLOEXEC");
105 do_inotify_close (void)
109 if (inotify_fd == -1) {
110 reply_with_error ("inotify_close: handle is not open");
114 if (close (inotify_fd) == -1) {
115 reply_with_perror ("close");
126 do_inotify_add_watch (const char *path, int mask)
132 ABS_PATH (path, return -1);
134 buf = sysroot_path (path);
136 reply_with_perror ("malloc");
140 r = inotify_add_watch (inotify_fd, buf, mask);
143 reply_with_perror ("inotify_add_watch: %s", path);
151 do_inotify_rm_watch (int wd)
155 if (inotify_rm_watch (inotify_fd, wd) == -1) {
156 reply_with_perror ("inotify_rm_watch: %d", wd);
163 guestfs_int_inotify_event_list *
164 do_inotify_read (void)
167 guestfs_int_inotify_event_list *ret;
171 ret = malloc (sizeof *ret);
173 reply_with_perror ("malloc");
176 ret->guestfs_int_inotify_event_list_len = 0;
177 ret->guestfs_int_inotify_event_list_val = NULL;
179 /* Read events that are available, but make sure we won't exceed
180 * maximum message size. In order to achieve this we have to
181 * guesstimate the remaining space available.
183 space = GUESTFS_MESSAGE_MAX / 2;
186 struct inotify_event *event;
190 r = read (inotify_fd, inotify_buf + inotify_posn,
191 sizeof (inotify_buf) - inotify_posn);
193 if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
195 reply_with_perror ("read");
198 if (r == 0) { /* End of file - we're not expecting it. */
199 reply_with_error ("inotify_read: unexpected end of file");
205 /* Read complete events from the buffer and add them to the result. */
207 while (n < inotify_posn) {
208 guestfs_int_inotify_event *np;
209 guestfs_int_inotify_event *in;
211 event = (struct inotify_event *) &inotify_buf[n];
213 /* Have we got a complete event in the buffer? */
215 if (n + sizeof (struct inotify_event) > inotify_posn ||
216 n + sizeof (struct inotify_event) + event->len > inotify_posn)
219 #error "this code needs fixing so it works on non-GCC compilers"
222 np = realloc (ret->guestfs_int_inotify_event_list_val,
223 (ret->guestfs_int_inotify_event_list_len + 1) *
224 sizeof (guestfs_int_inotify_event));
226 reply_with_perror ("realloc");
229 ret->guestfs_int_inotify_event_list_val = np;
230 in = &ret->guestfs_int_inotify_event_list_val[ret->guestfs_int_inotify_event_list_len];
231 ret->guestfs_int_inotify_event_list_len++;
233 in->in_wd = event->wd;
234 in->in_mask = event->mask;
235 in->in_cookie = event->cookie;
238 in->in_name = strdup (event->name);
240 in->in_name = strdup (""); /* Should have optional string fields XXX. */
241 if (in->in_name == NULL) {
242 reply_with_perror ("strdup");
246 /* Estimate space used by this event in the message. */
247 space -= 16 + 4 + strlen (in->in_name) + 4;
249 /* Move pointer to next event. */
251 n += sizeof (struct inotify_event) + event->len;
253 #error "this code needs fixing so it works on non-GCC compilers"
257 /* 'n' now points to the first unprocessed/incomplete
258 * message in the buffer. Copy that to offset 0 in the buffer.
260 memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
264 /* Return the messages. */
268 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
274 do_inotify_files (void)
277 int size = 0, alloc = 0;
280 guestfs_int_inotify_event_list *events;
285 fp = popen ("sort -u > /tmp/inotify", "w");
287 reply_with_perror ("sort");
292 events = do_inotify_read ();
296 if (events->guestfs_int_inotify_event_list_len == 0) {
298 break; /* End of list of events. */
301 for (i = 0; i < events->guestfs_int_inotify_event_list_len; ++i) {
302 const char *name = events->guestfs_int_inotify_event_list_val[i].in_name;
305 fprintf (fp, "%s\n", name);
308 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
314 fp = fopen ("/tmp/inotify", "r");
316 reply_with_perror ("/tmp/inotify");
320 while (fgets (buf, sizeof buf, fp) != NULL) {
321 int len = strlen (buf);
323 if (len > 0 && buf[len-1] == '\n')
326 if (add_string (&ret, &size, &alloc, buf) == -1) {
334 if (add_string (&ret, &size, &alloc, NULL) == -1)
337 unlink ("/tmp/inotify");
341 unlink ("/tmp/inotify");