update all uses of ABS_PATH
[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 (-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 (char *path, int mask)
127 {
128   int64_t r;
129   char *buf;
130
131   NEED_INOTIFY (-1);
132   ABS_PATH (path, return -1);
133
134   buf = sysroot_path (path);
135   if (!buf) {
136     reply_with_perror ("malloc");
137     return -1;
138   }
139
140   r = inotify_add_watch (inotify_fd, buf, mask);
141   free (buf);
142   if (r == -1) {
143     reply_with_perror ("inotify_add_watch: %s", path);
144     return -1;
145   }
146
147   return r;
148 }
149
150 int
151 do_inotify_rm_watch (int wd)
152 {
153   NEED_INOTIFY (-1);
154
155   if (inotify_rm_watch (inotify_fd, wd) == -1) {
156     reply_with_perror ("inotify_rm_watch: %d", wd);
157     return -1;
158   }
159
160   return 0;
161 }
162
163 guestfs_int_inotify_event_list *
164 do_inotify_read (void)
165 {
166   int space;
167   guestfs_int_inotify_event_list *ret;
168
169   NEED_INOTIFY (NULL);
170
171   ret = malloc (sizeof *ret);
172   if (ret == NULL) {
173     reply_with_perror ("malloc");
174     return NULL;
175   }
176   ret->guestfs_int_inotify_event_list_len = 0;
177   ret->guestfs_int_inotify_event_list_val = NULL;
178
179   /* Read events that are available, but make sure we won't exceed
180    * maximum message size.  In order to achieve this we have to
181    * guesstimate the remaining space available.
182    */
183   space = GUESTFS_MESSAGE_MAX / 2;
184
185   while (space > 0) {
186     struct inotify_event *event;
187     int r;
188     size_t n;
189
190     r = read (inotify_fd, inotify_buf + inotify_posn,
191               sizeof (inotify_buf) - inotify_posn);
192     if (r == -1) {
193       if (errno == EWOULDBLOCK || errno == EAGAIN) /* End of list. */
194         break;
195       reply_with_perror ("read");
196       goto error;
197     }
198     if (r == 0) {               /* End of file - we're not expecting it. */
199       reply_with_error ("inotify_read: unexpected end of file");
200       goto error;
201     }
202
203     inotify_posn += r;
204
205     /* Read complete events from the buffer and add them to the result. */
206     n = 0;
207     while (n < inotify_posn) {
208       guestfs_int_inotify_event *np;
209       guestfs_int_inotify_event *in;
210
211       event = (struct inotify_event *) &inotify_buf[n];
212
213       /* Have we got a complete event in the buffer? */
214 #ifdef __GNUC__
215       if (n + sizeof (struct inotify_event) > inotify_posn ||
216           n + sizeof (struct inotify_event) + event->len > inotify_posn)
217         break;
218 #else
219 #error "this code needs fixing so it works on non-GCC compilers"
220 #endif
221
222       np = realloc (ret->guestfs_int_inotify_event_list_val,
223                     (ret->guestfs_int_inotify_event_list_len + 1) *
224                     sizeof (guestfs_int_inotify_event));
225       if (np == NULL) {
226         reply_with_perror ("realloc");
227         goto error;
228       }
229       ret->guestfs_int_inotify_event_list_val = np;
230       in = &ret->guestfs_int_inotify_event_list_val[ret->guestfs_int_inotify_event_list_len];
231       ret->guestfs_int_inotify_event_list_len++;
232
233       in->in_wd = event->wd;
234       in->in_mask = event->mask;
235       in->in_cookie = event->cookie;
236
237       if (event->len > 0)
238         in->in_name = strdup (event->name);
239       else
240         in->in_name = strdup (""); /* Should have optional string fields XXX. */
241       if (in->in_name == NULL) {
242         reply_with_perror ("strdup");
243         goto error;
244       }
245
246       /* Estimate space used by this event in the message. */
247       space -= 16 + 4 + strlen (in->in_name) + 4;
248
249       /* Move pointer to next event. */
250 #ifdef __GNUC__
251       n += sizeof (struct inotify_event) + event->len;
252 #else
253 #error "this code needs fixing so it works on non-GCC compilers"
254 #endif
255     }
256
257     /* 'n' now points to the first unprocessed/incomplete
258      * message in the buffer. Copy that to offset 0 in the buffer.
259      */
260     memmove (inotify_buf, &inotify_buf[n], inotify_posn - n);
261     inotify_posn -= n;
262   }
263
264   /* Return the messages. */
265   return ret;
266
267  error:
268   xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) ret);
269   free (ret);
270   return NULL;
271 }
272
273 char **
274 do_inotify_files (void)
275 {
276   char **ret = NULL;
277   int size = 0, alloc = 0;
278   unsigned int i;
279   FILE *fp;
280   guestfs_int_inotify_event_list *events;
281   char buf[PATH_MAX];
282
283   NEED_INOTIFY (NULL);
284
285   fp = popen ("sort -u > /tmp/inotify", "w");
286   if (fp == NULL) {
287     reply_with_perror ("sort");
288     return NULL;
289   }
290
291   while (1) {
292     events = do_inotify_read ();
293     if (events == NULL)
294       goto error;
295
296     if (events->guestfs_int_inotify_event_list_len == 0) {
297       free (events);
298       break;                    /* End of list of events. */
299     }
300
301     for (i = 0; i < events->guestfs_int_inotify_event_list_len; ++i) {
302       const char *name = events->guestfs_int_inotify_event_list_val[i].in_name;
303
304       if (name[0] != '\0')
305         fprintf (fp, "%s\n", name);
306     }
307
308     xdr_free ((xdrproc_t) xdr_guestfs_int_inotify_event_list, (char *) events);
309     free (events);
310   }
311
312   pclose (fp);
313
314   fp = fopen ("/tmp/inotify", "r");
315   if (fp == NULL) {
316     reply_with_perror ("/tmp/inotify");
317     return NULL;
318   }
319
320   while (fgets (buf, sizeof buf, fp) != NULL) {
321     int len = strlen (buf);
322
323     if (len > 0 && buf[len-1] == '\n')
324       buf[len-1] = '\0';
325
326     if (add_string (&ret, &size, &alloc, buf) == -1) {
327       fclose (fp);
328       goto error;
329     }
330   }
331
332   fclose (fp);
333
334   if (add_string (&ret, &size, &alloc, NULL) == -1)
335     goto error;
336
337   unlink ("/tmp/inotify");
338   return ret;
339
340  error:
341   unlink ("/tmp/inotify");
342   return NULL;
343 }