build: avoid some autoconf warnings
[libguestfs.git] / daemon / inotify.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/inotify.h>
27
28 #include "../src/guestfs_protocol.h"
29 #include "daemon.h"
30 #include "actions.h"
31
32 /* Currently open inotify handle, or -1 if not opened. */
33 static int inotify_fd = -1;
34
35 static char inotify_buf[64*1024*1024];  /* Event buffer, [0..posn-1] is valid */
36 static size_t inotify_posn = 0;
37
38 /* Because inotify_init does NEED_ROOT, NEED_INOTIFY implies NEED_ROOT. */
39 #define NEED_INOTIFY(errcode)                                           \
40   do {                                                                  \
41     if (inotify_fd == -1) {                                             \
42       reply_with_error ("%s: you must call 'inotify_init' first to initialize inotify", __func__); \
43       return (errcode);                                                 \
44     }                                                                   \
45   } while (0)
46
47 #define MQE_PATH "/proc/sys/fs/inotify/max_queued_events"
48
49 int
50 do_inotify_init (int max_events)
51 {
52   FILE *fp;
53
54   NEED_ROOT (return -1);
55
56   if (max_events < 0) {
57     reply_with_error ("inotify_init: max_events < 0");
58     return -1;
59   }
60
61   if (max_events > 0) {
62     fp = fopen (MQE_PATH, "w");
63     if (fp == NULL) {
64       reply_with_perror (MQE_PATH);
65       return -1;
66     }
67     fprintf (fp, "%d\n", max_events);
68     fclose (fp);
69   }
70
71   if (inotify_fd >= 0)
72     if (do_inotify_close () == -1)
73       return -1;
74
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");
79     return -1;
80   }
81 #else
82   inotify_fd = inotify_init ();
83   if (inotify_fd == -1) {
84     reply_with_perror ("inotify_init");
85     return -1;
86   }
87   if (fcntl (inotify_fd, F_SETFL, O_NONBLOCK) == -1) {
88     reply_with_perror ("fcntl: O_NONBLOCK");
89     close (inotify_fd);
90     inotify_fd = -1;
91     return -1;
92   }
93   if (fcntl (inotify_fd, F_SETFD, FD_CLOEXEC) == -1) {
94     reply_with_perror ("fcntl: FD_CLOEXEC");
95     close (inotify_fd);
96     inotify_fd = -1;
97     return -1;
98   }
99 #endif
100
101   return 0;
102 }
103
104 int
105 do_inotify_close (void)
106 {
107   NEED_INOTIFY (-1);
108
109   if (inotify_fd == -1) {
110     reply_with_error ("inotify_close: handle is not open");
111     return -1;
112   }
113
114   if (close (inotify_fd) == -1) {
115     reply_with_perror ("close");
116     return -1;
117   }
118
119   inotify_fd = -1;
120   inotify_posn = 0;
121
122   return 0;
123 }
124
125 int64_t
126 do_inotify_add_watch (const char *path, int mask)
127 {
128   int64_t r;
129   char *buf;
130
131   NEED_INOTIFY (-1);
132
133   buf = sysroot_path (path);
134   if (!buf) {
135     reply_with_perror ("malloc");
136     return -1;
137   }
138
139   r = inotify_add_watch (inotify_fd, buf, mask);
140   free (buf);
141   if (r == -1) {
142     reply_with_perror ("inotify_add_watch: %s", path);
143     return -1;
144   }
145
146   return r;
147 }
148
149 int
150 do_inotify_rm_watch (int wd)
151 {
152   NEED_INOTIFY (-1);
153
154   if (inotify_rm_watch (inotify_fd, wd) == -1) {
155     reply_with_perror ("inotify_rm_watch: %d", wd);
156     return -1;
157   }
158
159   return 0;
160 }
161
162 guestfs_int_inotify_event_list *
163 do_inotify_read (void)
164 {
165   int space;
166   guestfs_int_inotify_event_list *ret;
167
168   NEED_INOTIFY (NULL);
169
170   ret = malloc (sizeof *ret);
171   if (ret == NULL) {
172     reply_with_perror ("malloc");
173     return NULL;
174   }
175   ret->guestfs_int_inotify_event_list_len = 0;
176   ret->guestfs_int_inotify_event_list_val = NULL;
177
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.
181    */
182   space = GUESTFS_MESSAGE_MAX / 2;
183
184   while (space > 0) {
185     struct inotify_event *event;
186     int r;
187     size_t n;
188
189     r = read (inotify_fd, inotify_buf + inotify_posn,
190               sizeof (inotify_buf) - inotify_posn);
191     if (r == -1) {
192       if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
193         break;
194       reply_with_perror ("read");
195       goto error;
196     }
197     if (r == 0) {               /* End of file - we're not expecting it. */
198       reply_with_error ("inotify_read: unexpected end of file");
199       goto error;
200     }
201
202     inotify_posn += r;
203
204     /* Read complete events from the buffer and add them to the result. */
205     n = 0;
206     while (n < inotify_posn) {
207       guestfs_int_inotify_event *np;
208       guestfs_int_inotify_event *in;
209
210       event = (struct inotify_event *) &inotify_buf[n];
211
212       /* Have we got a complete event in the buffer? */
213 #ifdef __GNUC__
214       if (n + sizeof (struct inotify_event) > inotify_posn ||
215           n + sizeof (struct inotify_event) + event->len > inotify_posn)
216         break;
217 #else
218 #error "this code needs fixing so it works on non-GCC compilers"
219 #endif
220
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));
224       if (np == NULL) {
225         reply_with_perror ("realloc");
226         goto error;
227       }
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++;
231
232       in->in_wd = event->wd;
233       in->in_mask = event->mask;
234       in->in_cookie = event->cookie;
235
236       if (event->len > 0)
237         in->in_name = strdup (event->name);
238       else
239         in->in_name = strdup (""); /* Should have optional string fields XXX. */
240       if (in->in_name == NULL) {
241         reply_with_perror ("strdup");
242         goto error;
243       }
244
245       /* Estimate space used by this event in the message. */
246       space -= 16 + 4 + strlen (in->in_name) + 4;
247
248       /* Move pointer to next event. */
249 #ifdef __GNUC__
250       n += sizeof (struct inotify_event) + event->len;
251 #else
252 #error "this code needs fixing so it works on non-GCC compilers"
253 #endif
254     }
255
256     /* 'n' now points to the first unprocessed/incomplete
257      * message in the buffer. Copy that to offset 0 in the buffer.
258      */
259     memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
260     inotify_posn -= n;
261   }
262
263   /* Return the messages. */
264   return ret;
265
266  error:
267   xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
268   free (ret);
269   return NULL;
270 }
271
272 char **
273 do_inotify_files (void)
274 {
275   char **ret = NULL;
276   int size = 0, alloc = 0;
277   unsigned int i;
278   FILE *fp;
279   guestfs_int_inotify_event_list *events;
280   char buf[PATH_MAX];
281
282   NEED_INOTIFY (NULL);
283
284   fp = popen ("sort -u > /tmp/inotify", "w");
285   if (fp == NULL) {
286     reply_with_perror ("sort");
287     return NULL;
288   }
289
290   while (1) {
291     events = do_inotify_read ();
292     if (events == NULL)
293       goto error;
294
295     if (events->guestfs_int_inotify_event_list_len == 0) {
296       free (events);
297       break;                    /* End of list of events. */
298     }
299
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;
302
303       if (name[0] != '\0')
304         fprintf (fp, "%s\n", name);
305     }
306
307     xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
308     free (events);
309   }
310
311   pclose (fp);
312
313   fp = fopen ("/tmp/inotify", "r");
314   if (fp == NULL) {
315     reply_with_perror ("/tmp/inotify");
316     return NULL;
317   }
318
319   while (fgets (buf, sizeof buf, fp) != NULL) {
320     int len = strlen (buf);
321
322     if (len > 0 && buf[len-1] == '\n')
323       buf[len-1] = '\0';
324
325     if (add_string (&ret, &size, &alloc, buf) == -1) {
326       fclose (fp);
327       goto error;
328     }
329   }
330
331   fclose (fp);
332
333   if (add_string (&ret, &size, &alloc, NULL) == -1)
334     goto error;
335
336   unlink ("/tmp/inotify");
337   return ret;
338
339  error:
340   unlink ("/tmp/inotify");
341   return NULL;
342 }