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.
24 #include <sys/inotify.h>
26 #include "../src/guestfs_protocol.h"
30 /* Currently open inotify handle, or -1 if not opened. */
31 static int inotify_fd = -1;
33 static char inotify_buf[64*1024*1024]; /* Event buffer, [0..posn-1] is valid */
34 static int inotify_posn = 0;
36 /* Because inotify_init does NEED_ROOT, NEED_INOTIFY implies NEED_ROOT. */
37 #define NEED_INOTIFY(errcode) \
39 if (inotify_fd == -1) { \
40 reply_with_error ("%s: you must call 'inotify_init' first to initialize inotify", __func__); \
45 #define MQE_PATH "/proc/sys/fs/inotify/max_queued_events"
48 do_inotify_init (int max_events)
55 reply_with_error ("inotify_init: max_events < 0");
60 fp = fopen (MQE_PATH, "w");
62 reply_with_perror (MQE_PATH);
65 fprintf (fp, "%d\n", max_events);
70 if (do_inotify_close () == -1)
73 inotify_fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
74 if (inotify_fd == -1) {
75 reply_with_perror ("inotify_init");
83 do_inotify_close (void)
87 if (inotify_fd == -1) {
88 reply_with_error ("inotify_close: handle is not open");
92 if (close (inotify_fd) == -1) {
93 reply_with_perror ("close");
104 do_inotify_add_watch (char *path, int mask)
112 buf = sysroot_path (path);
114 reply_with_perror ("malloc");
118 r = inotify_add_watch (inotify_fd, buf, mask);
121 reply_with_perror ("inotify_add_watch: %s", path);
129 do_inotify_rm_watch (int wd)
133 if (inotify_rm_watch (inotify_fd, wd) == -1) {
134 reply_with_perror ("inotify_rm_watch: %d", wd);
141 guestfs_int_inotify_event_list *
142 do_inotify_read (void)
145 guestfs_int_inotify_event_list *ret;
149 ret = malloc (sizeof *ret);
151 reply_with_perror ("malloc");
154 ret->guestfs_int_inotify_event_list_len = 0;
155 ret->guestfs_int_inotify_event_list_val = NULL;
157 /* Read events that are available, but make sure we won't exceed
158 * maximum message size. In order to achieve this we have to
159 * guesstimate the remaining space available.
161 space = GUESTFS_MESSAGE_MAX / 2;
164 struct inotify_event *event;
167 r = read (inotify_fd, inotify_buf + inotify_posn,
168 sizeof (inotify_buf) - inotify_posn);
170 if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
172 reply_with_perror ("read");
175 if (r == 0) { /* End of file - we're not expecting it. */
176 reply_with_error ("inotify_read: unexpected end of file");
182 /* Read complete events from the buffer and add them to the result. */
184 while (n < inotify_posn) {
185 guestfs_int_inotify_event *np;
186 guestfs_int_inotify_event *in;
188 event = (struct inotify_event *) &inotify_buf[n];
190 /* Have we got a complete event in the buffer? */
192 if (n + sizeof (struct inotify_event) > inotify_posn ||
193 n + sizeof (struct inotify_event) + event->len > inotify_posn)
196 #error "this code needs fixing so it works on non-GCC compilers"
199 np = realloc (ret->guestfs_int_inotify_event_list_val,
200 (ret->guestfs_int_inotify_event_list_len + 1) *
201 sizeof (guestfs_int_inotify_event));
203 reply_with_perror ("realloc");
206 ret->guestfs_int_inotify_event_list_val = np;
207 in = &ret->guestfs_int_inotify_event_list_val[ret->guestfs_int_inotify_event_list_len];
208 ret->guestfs_int_inotify_event_list_len++;
210 in->in_wd = event->wd;
211 in->in_mask = event->mask;
212 in->in_cookie = event->cookie;
215 in->in_name = strdup (event->name);
217 in->in_name = strdup (""); /* Should have optional string fields XXX. */
218 if (in->in_name == NULL) {
219 reply_with_perror ("strdup");
223 /* Estimate space used by this event in the message. */
224 space -= 16 + 4 + strlen (in->in_name) + 4;
226 /* Move pointer to next event. */
228 n += sizeof (struct inotify_event) + event->len;
230 #error "this code needs fixing so it works on non-GCC compilers"
234 /* 'n' now points to the first unprocessed/incomplete
235 * message in the buffer. Copy that to offset 0 in the buffer.
237 memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
241 /* Return the messages. */
245 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
251 do_inotify_files (void)
254 int size = 0, alloc = 0;
257 guestfs_int_inotify_event_list *events;
262 fp = popen ("sort -u > /tmp/inotify", "w");
264 reply_with_perror ("sort");
269 events = do_inotify_read ();
273 if (events->guestfs_int_inotify_event_list_len == 0) {
275 break; /* End of list of events. */
278 for (i = 0; i < events->guestfs_int_inotify_event_list_len; ++i) {
279 const char *name = events->guestfs_int_inotify_event_list_val[i].in_name;
282 fprintf (fp, "%s\n", name);
285 xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
291 fp = fopen ("/tmp/inotify", "r");
293 reply_with_perror ("/tmp/inotify");
297 while (fgets (buf, sizeof buf, fp) != NULL) {
298 int len = strlen (buf);
300 if (len > 0 && buf[len-1] == '\n')
303 if (add_string (&ret, &size, &alloc, buf) == -1) {
311 if (add_string (&ret, &size, &alloc, NULL) == -1)
314 unlink ("/tmp/inotify");
318 unlink ("/tmp/inotify");