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