1 /* virt-bmap logger plugin
2 * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 /* This is an nbdkit plugin which observes guest activity and displays
20 * what files are being accessed.
33 #include <sys/types.h>
37 #include <nbdkit-plugin.h>
42 static char *file = NULL;
43 static char *bmap = NULL;
44 static char *logfile = NULL;
45 static void *ranges = NULL;
46 static FILE *logfp = NULL;
49 logger_config (const char *key, const char *value)
51 if (strcmp (key, "file") == 0) {
53 file = nbdkit_absolute_path (value);
57 else if (strcmp (key, "bmap") == 0) {
59 bmap = nbdkit_absolute_path (value);
63 else if (strcmp (key, "logfile") == 0) {
65 logfile = nbdkit_absolute_path (value);
70 nbdkit_error ("unknown parameter '%s'", key);
78 logger_config_complete (void)
81 const char *bmap_file = bmap ? bmap : "bmap";
82 CLEANUP_FREE char *line = NULL;
88 nbdkit_error ("missing 'file=...' parameter, see virt-bmap(1)");
92 ranges = new_ranges ();
94 /* Load ranges from bmap file. */
95 fp = fopen (bmap_file, "r");
97 nbdkit_error ("open: %s: %m", bmap_file);
102 while (errno = 0, (len = getline (&line, &alloc, fp)) != -1) {
107 if (len > 0 && line[len-1] == '\n')
110 if (sscanf (line, "1 %" SCNx64 " %" SCNx64 " %n",
111 &start, &end, &object_offset) >= 2) {
113 object = line + object_offset;
114 insert_range (ranges, start, end, object);
119 nbdkit_error ("getline: %s: %m", bmap_file);
125 nbdkit_error ("no ranges were read from block map file: %s", bmap_file);
129 if (fclose (fp) == -1) {
130 nbdkit_error ("fclose: %s: %m", bmap_file);
134 /* Set up log file. */
136 logfp = fopen (logfile, "w");
138 nbdkit_error ("cannot open log file: %s: %m", logfile);
152 free_ranges (ranges);
158 #define logger_config_help \
159 "file=<DISK> Input disk filename\n" \
160 "logfile=<OUTPUT> Log file (default: stdout)\n" \
161 "bmap=<BMAP> Block map (default: \"bmap\")" \
163 /* See log_operation below. */
174 /* The per-connection handle. */
178 /* See log_operation below. */
179 struct operation last;
180 struct operation current;
183 /* Create the per-connection handle. */
185 logger_open (int readonly)
190 h = malloc (sizeof *h);
192 nbdkit_error ("malloc: %m");
196 memset (&h->last, 0, sizeof h->last);
197 memset (&h->current, 0, sizeof h->current);
199 flags = O_CLOEXEC|O_NOCTTY;
205 h->fd = open (file, flags);
207 nbdkit_error ("open: %s: %m", file);
215 /* Free up the per-connection handle. */
217 logger_close (void *handle)
219 struct handle *h = handle;
225 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
227 /* Get the file size. */
229 logger_get_size (void *handle)
231 struct handle *h = handle;
234 if (fstat (h->fd, &statbuf) == -1) {
235 nbdkit_error ("stat: %m");
239 return statbuf.st_size;
243 priority_of_object (const char *object)
246 case 'v': return 1; /* whole device (least important) */
250 case 'f': return 5; /* file (most important) */
255 /* Callback from find_range. Save the highest priority object into
256 * the handle for later printing.
259 log_callback (uint64_t start, uint64_t end, const char *object, void *opaque)
261 struct handle *h = opaque;
262 int priority = priority_of_object (object);
264 if (priority > h->current.priority) {
265 h->current.priority = priority;
266 h->current.start = start;
267 h->current.end = end;
268 h->current.object = object;
273 log_operation (struct handle *h, uint64_t offset, uint32_t count, int is_read)
275 /* Because Boost interval_map is really bloody slow, implement a
276 * shortcut here. We can remove this once Boost performance
277 * problems have been fixed.
279 if (h->current.is_read == is_read &&
280 h->current.count == count &&
281 h->current.offset == offset)
282 goto skip_find_range;
284 h->current.is_read = is_read;
285 h->current.count = count;
286 h->current.offset = offset;
287 h->current.priority = 0;
288 find_range (ranges, offset, offset+count, log_callback, h);
291 if (h->current.priority > 0) {
292 FILE *fp = logfp ? logfp : stdout;
294 if (h->current.priority != h->last.priority ||
295 h->current.is_read != h->last.is_read ||
296 h->last.object == NULL ||
297 strcmp (h->current.object, h->last.object) != 0) {
301 is_read ? "read" : "write", h->current.object);
303 h->last = h->current;
306 /* It would be nice to print an offset relative to the current
307 * object here, but that's not possible since we don't have the
308 * information about precisely what file offsets map to what
311 fprintf (fp, " %" PRIx64 "-%" PRIx64, offset, offset+count);
316 /* Read data from the file. */
318 logger_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
320 struct handle *h = handle;
322 log_operation (h, offset, count, 1);
325 ssize_t r = pread (h->fd, buf, count, offset);
327 nbdkit_error ("pread: %m");
331 nbdkit_error ("pread: unexpected end of file");
342 /* Write data to the file. */
344 logger_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
346 struct handle *h = handle;
348 log_operation (h, offset, count, 0);
351 ssize_t r = pwrite (h->fd, buf, count, offset);
353 nbdkit_error ("pwrite: %m");
364 /* Flush the file to disk. */
366 logger_flush (void *handle)
368 struct handle *h = handle;
370 if (fdatasync (h->fd) == -1) {
371 nbdkit_error ("fdatasync: %m");
378 /* Register the nbdkit plugin. */
380 static struct nbdkit_plugin plugin = {
381 .name = "bmaplogger",
382 .version = PACKAGE_VERSION,
383 .config = logger_config,
384 .config_help = logger_config_help,
385 .config_complete = logger_config_complete,
387 .close = logger_close,
388 .get_size = logger_get_size,
389 .pread = logger_pread,
390 .unload = logger_unload,
393 NBDKIT_REGISTER_PLUGIN(plugin)