nbd-trplay: Add simple CLI
authorManfred Spraul <manfred.spraul@de.bosch.com>
Fri, 21 Jan 2022 17:57:09 +0000 (18:57 +0100)
committerWouter Verhelst <w@uter.be>
Thu, 3 Mar 2022 09:53:01 +0000 (11:53 +0200)
Add an initial CLI.
Planned CLI extentions:
- define what TRIM should do: keep unchanged, set to 0, take the content
  from another block.
- do not apply all sectors, instead behave like a write-back cache
  and drop a few random sectors.
- Create a log file of what was skipped/applied.
- Replay a log file.

Signed-off-by: Manfred Spraul <manfred.spraul@de.bosch.com>
Signed-off-by: Wouter Verhelst <w@uter.be>
nbd-trplay.c

index 7af255d..19c7edf 100644 (file)
@@ -1,14 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * nbd-trplay.c
  *
  * Takes an nbd transaction log file and replays some/all of the write commands.
+ *
+ * Based on nbd-trdump
+ * (C) Robert Bosch GmbH, 2021
  */
 
 #include <stdlib.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <fcntl.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <unistd.h>
@@ -24,6 +30,16 @@ static char tmpbuf[BUFSIZE];
 
 static bool g_with_datalog = false;
 
+#define        VERBOSE_DEBUG   3
+#define        VERBOSE_DETAILS 2
+#define        VERBOSE_NORMAL  1
+#define        VERBOSE_OFF     0
+
+int g_verbose = 0;
+
+unsigned long g_blocksize = 512;
+unsigned long long g_max_blocks = ULLONG_MAX;
+
 static inline void doread(int f, void *buf, size_t len) {
         ssize_t res;
 
@@ -39,7 +55,7 @@ static inline void doread(int f, void *buf, size_t len) {
         }
 }
 
-int main(int argc, char**argv) {
+int main_loop(int logfd, int imagefd) {
        struct nbd_request req;
        struct nbd_reply rep;
        uint32_t magic;
@@ -49,26 +65,14 @@ int main(int argc, char**argv) {
        uint32_t len;
        uint64_t offset;
        const char * ctext;
-       int readfd = 0; /* stdin */
-
-       if(argc > 1) {
-               int retval=0;
-               if(strcmp(argv[1], "--help") && strcmp(argv[1], "-h")) {
-                       printf("E: unknown option %s.\n", argv[1]);
-                       retval=1;
-               }
-               printf("This is nbd-trplay, part of nbd %s.\n", PACKAGE_VERSION);
-               printf("Use: %s < transactionlog\n", argv[0]);
-               return retval;
-       }
 
        while (1) {
                /* Read a request or reply from the transaction file */
-               doread(readfd, &magic, sizeof(magic));
+               doread(logfd, &magic, sizeof(magic));
                magic = ntohl(magic);
                switch (magic) {
                case NBD_REQUEST_MAGIC:
-                       doread(readfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
+                       doread(logfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
                        handle = ntohll(*((long long int *)(req.handle)));
                        offset = ntohll(req.from);
                        len = ntohl(req.len);
@@ -90,14 +94,14 @@ int main(int argc, char**argv) {
 
                                        if (tmplen > BUFSIZE)
                                                tmplen = BUFSIZE;
-                                       doread(readfd, tmpbuf, tmplen);
+                                       doread(logfd, tmpbuf, tmplen);
                                        len -= tmplen;
                                }
                        }
 
                        break;
                case NBD_REPLY_MAGIC:
-                       doread(readfd, sizeof(magic)+(char *)(&rep), sizeof(struct nbd_reply)-sizeof(magic));
+                       doread(logfd, sizeof(magic)+(char *)(&rep), sizeof(struct nbd_reply)-sizeof(magic));
                        handle = ntohll(*((long long int *)(rep.handle)));
                        error = ntohl(rep.error);
 
@@ -107,7 +111,7 @@ int main(int argc, char**argv) {
                        break;
 
                case NBD_TRACELOG_MAGIC:
-                       doread(readfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
+                       doread(logfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
                        handle = ntohll(*((long long int *)(req.handle)));
                        offset = ntohll(req.from);
                        len = ntohl(req.len);
@@ -144,3 +148,88 @@ int main(int argc, char**argv) {
        /* never reached */
        return 0;
 }
+
+static void show_help(const char *progname) {
+       printf("\n");
+       printf("This is nbd-trplay, part of nbd %s.\n", PACKAGE_VERSION);
+       printf("Use: %s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", progname);
+       printf(" Applies up to <max blocks> elements from file <log> to disk image <image>.\n");
+       printf(" Command line parameters:\n");
+       printf(" <image>: name of the initial image file.\n");
+       printf(" <log>: nbd trace log. Must contain actual data (datalog=true).\n");
+       printf(" <block size>: device block size. Default 512.\n");
+       printf(" <max blocks>: where to stop the replay. Default all.\n");
+       printf("  -v: Increase verbose level. Specify multiple times to increase further.\n");
+
+}
+
+
+int main(int argc, char **argv) {
+       int opt;
+       int imagefd = -1;
+       int logfd = -1;
+
+       printf("%s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", argv[0]);
+
+       while ((opt = getopt(argc, argv, "i:l:m:b:hv")) != -1) {
+               switch(opt) {
+               case 'v':
+                       g_verbose++;
+                       break;
+               default:
+               case '?':
+               case 'h':
+                       show_help(argv[0]);
+                       return 0;
+               case 'm':
+                       g_max_blocks = strtoull(optarg, NULL, 0);
+                       if (g_max_blocks == 0) {
+                               printf("  Invalid block count.\n");
+                               return 1;
+                       }
+                       break;
+               case 'b':
+                       g_blocksize = strtoul(optarg, NULL, 0);
+                       if (g_blocksize == 0) {
+                               printf("  Invalid block size.\n");
+                               return 1;
+                       }
+                       if (g_blocksize > BUFSIZE) {
+                               printf(" block size is larger than %d, not supported.\n", (int)BUFSIZE);
+                               return 1;
+                       }
+                       break;
+               case 'i':
+                       imagefd = open(optarg, O_RDWR, 0);
+                       if (imagefd == -1) {
+                               printf("  Opening disk image failed, errno %d.", errno);
+                               return 1;
+                       }
+                       break;
+               case 'l':
+                       logfd = open(optarg, O_RDONLY, 0);
+                       if (logfd == -1) {
+                               printf("  Opening disk image failed, errno %d.", errno);
+                               return 1;
+                       }
+                       break;
+               }
+       }
+
+       if (logfd == -1) {
+               printf("  Log file not specified, this is mandatory.\n");
+               return 1;
+       }
+       if (imagefd == -1) {
+               printf("  Disk image not specified, this is mandatory.\n");
+               return 1;
+       }
+
+       if (g_verbose >= VERBOSE_NORMAL) {
+               printf(" block size: %ld bytes (0x%lx bytes).\n", g_blocksize, g_blocksize);
+               printf(" max blocks to apply: %llx.\n", g_max_blocks);
+       }
+       main_loop(logfd, imagefd);
+
+       return 0;
+}