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