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)
133 buf = sysroot_path (path);
135 reply_with_perror ("malloc");
139 r = inotify_add_watch (inotify_fd, buf, mask);
142 reply_with_perror ("inotify_add_watch: %s", path);
150 do_inotify_rm_watch (int wd)
154 if (inotify_rm_watch (inotify_fd, wd) == -1) {
155 reply_with_perror ("inotify_rm_watch: %d", wd);
162 guestfs_int_inotify_event_list *
163 do_inotify_read (void)
166 guestfs_int_inotify_event_list *ret;
170 ret = malloc (sizeof *ret);
172 reply_with_perror ("malloc");
175 ret->guestfs_int_inotify_event_list_len = 0;
176 ret->guestfs_int_inotify_event_list_val = NULL;
178 /* Read events that are available, but make sure we won't exceed
179 * maximum message size. In order to achieve this we have to
180 * guesstimate the remaining space available.
182 space = GUESTFS_MESSAGE_MAX / 2;
185 struct inotify_event *event;
189 r = read (inotify_fd, inotify_buf + inotify_posn,
190 sizeof (inotify_buf) - inotify_posn);
192 if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
194 reply_with_perror ("read");
197 if (r == 0) { /* End of file - we're not expecting it. */
198 reply_with_error ("inotify_read: unexpected end of file");
204 /* Read complete events from the buffer and add them to the result. */
206 while (n < inotify_posn) {
207 guestfs_int_inotify_event *np;
208 guestfs_int_inotify_event *in;
210 event = (struct inotify_event *) &inotify_buf[n];
212 /* Have we got a complete event in the buffer? */
214 if (n + sizeof (struct inotify_event) > inotify_posn ||
215 n + sizeof (struct inotify_event) + event->len > inotify_posn)
218 #error "this code needs fixing so it works on non-GCC compilers"
221 np = realloc (ret->guestfs_int_inotify_event_list_val,
222 (ret->guestfs_int_inotify_event_list_len + 1) *
223 sizeof (guestfs_int_inotify_event));
225 reply_with_perror ("realloc");
228 ret->guestfs_int_inotify_event_list_val = np;
229 in = &ret->guestfs_int_inotify_event_list_val[ret->guestfs_int_inotify_event_list_len];
230 ret->guestfs_int_inotify_event_list_len++;
232 in->in_wd = event->wd;
233 in->in_mask = event->mask;
234 in->in_cookie = event->cookie;
237 in->in_name = strdup (event->name);
239 in->in_name = strdup (""); /* Should have optional string fields XXX. */
240 if (in->in_name == NULL) {
241 reply_with_perror ("strdup");
245 /* Estimate space used by this event in the message. */
246 space -= 16 + 4 + strlen (in->in_name) + 4;
248 /* Move pointer to next event. */
250 n += sizeof (struct inotify_event) + event->len;
252 #error "this code needs fixing so it works on non-GCC compilers"
256 /* 'n' now points to the first unprocessed/incomplete
257 * message in the buffer. Copy that to offset 0 in the buffer.
259 memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
263 /* Return the messages. */
267 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
273 do_inotify_files (void)
276 int size = 0, alloc = 0;
279 guestfs_int_inotify_event_list *events;
284 fp = popen ("sort -u > /tmp/inotify", "w");
286 reply_with_perror ("sort");
291 events = do_inotify_read ();
295 if (events->guestfs_int_inotify_event_list_len == 0) {
297 break; /* End of list of events. */
300 for (i = 0; i < events->guestfs_int_inotify_event_list_len; ++i) {
301 const char *name = events->guestfs_int_inotify_event_list_val[i].in_name;
304 fprintf (fp, "%s\n", name);
307 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
313 fp = fopen ("/tmp/inotify", "r");
315 reply_with_perror ("/tmp/inotify");
319 while (fgets (buf, sizeof buf, fp) != NULL) {
320 int len = strlen (buf);
322 if (len > 0 && buf[len-1] == '\n')
325 if (add_string (&ret, &size, &alloc, buf) == -1) {
333 if (add_string (&ret, &size, &alloc, NULL) == -1)
336 unlink ("/tmp/inotify");
340 unlink ("/tmp/inotify");