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. */
172 /* The per-connection handle. */
176 /* See log_operation below. */
177 struct operation last;
178 struct operation current;
181 /* Create the per-connection handle. */
183 logger_open (int readonly)
188 h = malloc (sizeof *h);
190 nbdkit_error ("malloc: %m");
194 memset (&h->last, 0, sizeof h->last);
195 memset (&h->current, 0, sizeof h->current);
197 flags = O_CLOEXEC|O_NOCTTY;
203 h->fd = open (file, flags);
205 nbdkit_error ("open: %s: %m", file);
213 /* Free up the per-connection handle. */
215 logger_close (void *handle)
217 struct handle *h = handle;
223 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
225 /* Get the file size. */
227 logger_get_size (void *handle)
229 struct handle *h = handle;
232 if (fstat (h->fd, &statbuf) == -1) {
233 nbdkit_error ("stat: %m");
237 return statbuf.st_size;
241 priority_of_object (const char *object)
244 case 'v': return 1; /* whole device (least important) */
248 case 'f': return 5; /* file (most important) */
253 /* Callback from find_range. Save the highest priority object into
254 * the handle for later printing.
257 log_callback (uint64_t start, uint64_t end, const char *object, void *opaque)
259 struct handle *h = opaque;
260 int priority = priority_of_object (object);
262 if (priority > h->current.priority) {
263 h->current.priority = priority;
264 h->current.start = start;
265 h->current.end = end;
266 h->current.object = object;
271 log_operation (struct handle *h, uint64_t offset, uint32_t count, int is_read)
273 h->current.is_read = is_read;
274 h->current.priority = 0;
275 find_range (ranges, offset, offset+count, log_callback, h);
277 if (h->current.priority > 0) {
278 FILE *fp = logfp ? logfp : stdout;
280 if (h->current.priority != h->last.priority ||
281 h->current.is_read != h->last.is_read ||
282 h->last.object == NULL ||
283 strcmp (h->current.object, h->last.object) != 0) {
287 is_read ? "read" : "write", h->current.object);
289 h->last = h->current;
292 /* It would be nice to print an offset relative to the current
293 * object here, but that's not possible since we don't have the
294 * information about precisely what file offsets map to what
297 fprintf (fp, " %" PRIx64 "-%" PRIx64, offset, offset+count);
302 /* Read data from the file. */
304 logger_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
306 struct handle *h = handle;
308 log_operation (h, offset, count, 1);
311 ssize_t r = pread (h->fd, buf, count, offset);
313 nbdkit_error ("pread: %m");
317 nbdkit_error ("pread: unexpected end of file");
328 /* Write data to the file. */
330 logger_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
332 struct handle *h = handle;
334 log_operation (h, offset, count, 0);
337 ssize_t r = pwrite (h->fd, buf, count, offset);
339 nbdkit_error ("pwrite: %m");
350 /* Flush the file to disk. */
352 logger_flush (void *handle)
354 struct handle *h = handle;
356 if (fdatasync (h->fd) == -1) {
357 nbdkit_error ("fdatasync: %m");
364 /* Register the nbdkit plugin. */
366 static struct nbdkit_plugin plugin = {
367 .name = "bmaplogger",
368 .version = PACKAGE_VERSION,
369 .config = logger_config,
370 .config_help = logger_config_help,
371 .config_complete = logger_config_complete,
373 .close = logger_close,
374 .get_size = logger_get_size,
375 .pread = logger_pread,
376 .unload = logger_unload,
379 NBDKIT_REGISTER_PLUGIN(plugin)