1 /* libguestfs - the guestfsd daemon
2 * Copyright (C) 2009-2011 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.
27 #ifdef HAVE_SYS_INOTIFY_H
28 #include <sys/inotify.h>
31 #include "guestfs_protocol.h"
34 #include "optgroups.h"
36 #ifdef HAVE_SYS_INOTIFY_H
37 /* Currently open inotify handle, or -1 if not opened. */
38 static int inotify_fd = -1;
40 static char inotify_buf[64*1024*1024]; /* Event buffer, [0..posn-1] is valid */
41 static size_t inotify_posn = 0;
44 optgroup_inotify_available (void)
48 #else /* !HAVE_SYS_INOTIFY_H */
50 optgroup_inotify_available (void)
56 /* Because inotify_init does NEED_ROOT, NEED_INOTIFY implies NEED_ROOT. */
57 #define NEED_INOTIFY(errcode) \
59 if (inotify_fd == -1) { \
60 reply_with_error ("%s: you must call 'inotify_init' first to initialize inotify", __func__); \
65 #define MQE_PATH "/proc/sys/fs/inotify/max_queued_events"
68 do_inotify_init (int max_events)
70 #ifdef HAVE_SYS_INOTIFY_H
73 NEED_ROOT (, return -1);
76 reply_with_error ("max_events < 0");
81 fp = fopen (MQE_PATH, "w");
83 reply_with_perror (MQE_PATH);
86 fprintf (fp, "%d\n", max_events);
91 if (do_inotify_close () == -1)
94 #ifdef HAVE_INOTIFY_INIT1
95 inotify_fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
96 if (inotify_fd == -1) {
97 reply_with_perror ("inotify_init1");
101 inotify_fd = inotify_init ();
102 if (inotify_fd == -1) {
103 reply_with_perror ("inotify_init");
106 if (fcntl (inotify_fd, F_SETFL, O_NONBLOCK) == -1) {
107 reply_with_perror ("fcntl: O_NONBLOCK");
112 if (fcntl (inotify_fd, F_SETFD, FD_CLOEXEC) == -1) {
113 reply_with_perror ("fcntl: FD_CLOEXEC");
127 do_inotify_close (void)
129 #ifdef HAVE_SYS_INOTIFY_H
132 if (inotify_fd == -1) {
133 reply_with_error ("handle is not open");
137 if (close (inotify_fd) == -1) {
138 reply_with_perror ("close");
152 do_inotify_add_watch (const char *path, int mask)
154 #ifdef HAVE_SYS_INOTIFY_H
160 buf = sysroot_path (path);
162 reply_with_perror ("malloc");
166 r = inotify_add_watch (inotify_fd, buf, mask);
169 reply_with_perror ("%s", path);
180 do_inotify_rm_watch (int wd)
182 #ifdef HAVE_SYS_INOTIFY_H
185 if (inotify_rm_watch (inotify_fd, wd) == -1) {
186 reply_with_perror ("%d", wd);
196 guestfs_int_inotify_event_list *
197 do_inotify_read (void)
199 #ifdef HAVE_SYS_INOTIFY_H
201 guestfs_int_inotify_event_list *ret;
205 ret = malloc (sizeof *ret);
207 reply_with_perror ("malloc");
210 ret->guestfs_int_inotify_event_list_len = 0;
211 ret->guestfs_int_inotify_event_list_val = NULL;
213 /* Read events that are available, but make sure we won't exceed
214 * maximum message size. In order to achieve this we have to
215 * guesstimate the remaining space available.
217 space = GUESTFS_MESSAGE_MAX / 2;
220 struct inotify_event *event;
224 r = read (inotify_fd, inotify_buf + inotify_posn,
225 sizeof (inotify_buf) - inotify_posn);
227 if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
229 reply_with_perror ("read");
232 if (r == 0) { /* End of file - we're not expecting it. */
233 reply_with_error ("unexpected end of file");
239 /* Read complete events from the buffer and add them to the result. */
241 while (n < inotify_posn) {
242 guestfs_int_inotify_event *np;
243 guestfs_int_inotify_event *in;
245 event = (struct inotify_event *) &inotify_buf[n];
247 /* Have we got a complete event in the buffer? */
249 if (n + sizeof (struct inotify_event) > inotify_posn ||
250 n + sizeof (struct inotify_event) + event->len > inotify_posn)
253 #error "this code needs fixing so it works on non-GCC compilers"
256 np = realloc (ret->guestfs_int_inotify_event_list_val,
257 (ret->guestfs_int_inotify_event_list_len + 1) *
258 sizeof (guestfs_int_inotify_event));
260 reply_with_perror ("realloc");
263 ret->guestfs_int_inotify_event_list_val = np;
264 in = &ret->guestfs_int_inotify_event_list_val[ret->guestfs_int_inotify_event_list_len];
265 ret->guestfs_int_inotify_event_list_len++;
267 in->in_wd = event->wd;
268 in->in_mask = event->mask;
269 in->in_cookie = event->cookie;
272 in->in_name = strdup (event->name);
274 in->in_name = strdup (""); /* Should have optional string fields XXX. */
275 if (in->in_name == NULL) {
276 reply_with_perror ("strdup");
280 /* Estimate space used by this event in the message. */
281 space -= 16 + 4 + strlen (in->in_name) + 4;
283 /* Move pointer to next event. */
285 n += sizeof (struct inotify_event) + event->len;
287 #error "this code needs fixing so it works on non-GCC compilers"
291 /* 'n' now points to the first unprocessed/incomplete
292 * message in the buffer. Copy that to offset 0 in the buffer.
294 memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
298 /* Return the messages. */
302 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
306 NOT_AVAILABLE (NULL);
311 do_inotify_files (void)
313 #ifdef HAVE_SYS_INOTIFY_H
315 int size = 0, alloc = 0;
318 guestfs_int_inotify_event_list *events;
320 char tempfile[] = "/tmp/inotifyXXXXXX";
326 fd = mkstemp (tempfile);
328 reply_with_perror ("mkstemp");
332 snprintf (cmd, sizeof cmd, "sort -u > %s", tempfile);
334 fp = popen (cmd, "w");
336 reply_with_perror ("sort");
341 events = do_inotify_read ();
345 if (events->guestfs_int_inotify_event_list_len == 0) {
347 break; /* End of list of events. */
350 for (i = 0; i < events->guestfs_int_inotify_event_list_len; ++i) {
351 const char *name = events->guestfs_int_inotify_event_list_val[i].in_name;
354 fprintf (fp, "%s\n", name);
357 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
363 fp = fdopen (fd, "r");
365 reply_with_perror ("%s", tempfile);
371 while (fgets (buf, sizeof buf, fp) != NULL) {
372 int len = strlen (buf);
374 if (len > 0 && buf[len-1] == '\n')
377 if (add_string (&ret, &size, &alloc, buf) == -1)
381 fclose (fp); /* implicitly closes fd */
384 if (add_string (&ret, &size, &alloc, NULL) == -1)
397 NOT_AVAILABLE (NULL);