[Libguestfs] [PATCH 07/10] inspect: Use command mini-library to parse the output of db_dump command.

Richard W.M. Jones rjones at redhat.com
Thu Oct 18 21:14:08 UTC 2012


From: "Richard W.M. Jones" <rjones at redhat.com>

---
 src/dbdump.c | 181 ++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 103 insertions(+), 78 deletions(-)

diff --git a/src/dbdump.c b/src/dbdump.c
index 201fa6e..6c9d7a7 100644
--- a/src/dbdump.c
+++ b/src/dbdump.c
@@ -39,111 +39,136 @@
 
 #if defined(DB_DUMP)
 
+static void read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len);
 static unsigned char *convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen, size_t *binlen_rtn);
 
+struct cb_data {
+  guestfs___db_dump_callback callback;
+  void *opaque;
+  enum { reading_header,
+         reading_key, reading_value,
+         reading_finished,
+         reading_failed } state;
+  unsigned char *key;
+  size_t keylen;
+};
+
 /* This helper function is specialized to just reading the hash-format
  * output from db_dump/db4_dump.  It's just enough to support the RPM
- * database format.  Note that the filename must not contain any shell
- * characters (this is guaranteed by the caller).
+ * database format.
  */
 int
 guestfs___read_db_dump (guestfs_h *g,
                         const char *dumpfile, void *opaque,
                         guestfs___db_dump_callback callback)
 {
-#define cmd_len (strlen (dumpfile) + 64)
-  char cmd[cmd_len];
-  FILE *pp = NULL;
-  char *line = NULL;
-  size_t len = 0;
-  ssize_t linelen;
-  unsigned char *key = NULL, *value = NULL;
-  size_t keylen, valuelen;
-  int ret = -1;
-
-  snprintf (cmd, cmd_len, DB_DUMP " -k '%s'", dumpfile);
-
-  debug (g, "read_db_dump command: %s", cmd);
-
-  pp = popen (cmd, "r");
-  if (pp == NULL) {
-    perrorf (g, "popen: %s", cmd);
-    goto out;
-  }
+  struct cb_data data;
+  struct command *cmd;
+  int r;
 
-  /* Ignore everything to end-of-header marker. */
-  while ((linelen = getline (&line, &len, pp)) != -1) {
-    if (STRPREFIX (line, "HEADER=END"))
-      break;
-  }
+  data.callback = callback;
+  data.opaque = opaque;
+  data.state = reading_header;
+  data.key = NULL;
+
+  cmd = guestfs___new_command (g);
+  guestfs___cmd_add_arg (cmd, DB_DUMP);
+  guestfs___cmd_add_arg (cmd, "-k");
+  guestfs___cmd_add_arg (cmd, dumpfile);
+  guestfs___cmd_set_stdout_callback (cmd, read_db_dump_line, &data, 0);
+
+  r = guestfs___cmd_run (cmd);
+  guestfs___cmd_close (cmd);
+  free (data.key);
 
-  if (linelen == -1) {
-    error (g, _("unexpected end of output from db_dump command before end of header"));
-    goto out;
+  if (r == -1)
+    return -1;
+  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
+    error (g, _("%s: command failed"), DB_DUMP);
+    return -1;
+  }
+  if (data.state != reading_finished) {
+    error (g, _("%s: unexpected error or end of output"), DB_DUMP);
+    return -1;
   }
 
-  /* Now read the key, value pairs.  They are prefixed with a space and
-   * printed as hex strings, so convert those strings to binary.  Pass
-   * the strings up to the callback function.
-   */
-  while ((linelen = getline (&line, &len, pp)) != -1) {
-    if (STRPREFIX (line, "DATA=END"))
-      break;
-
-    if (linelen < 1 || line[0] != ' ') {
-      error (g, _("unexpected line from db_dump command, no space prefix"));
-      goto out;
-    }
+  return 0;
+}
 
-    if ((key = convert_hex_to_binary (g, &line[1], linelen-1,
-                                      &keylen)) == NULL)
-      goto out;
+static void
+read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len)
+{
+  struct cb_data *data = datav;
+  unsigned char *value;
+  size_t valuelen;
 
-    if ((linelen = getline (&line, &len, pp)) == -1)
-      break;
+  switch (data->state) {
+  case reading_finished:
+  case reading_failed:
+    return;
 
-    if (linelen < 1 || line[0] != ' ') {
-      error (g, _("unexpected line from db_dump command, no space prefix"));
-      goto out;
+  case reading_header:
+    /* Ignore everything to end-of-header marker. */
+    if (STRPREFIX (line, "HEADER=END"))
+      data->state = reading_key;
+    return;
+
+    /* Read the key, value pairs using a state machine.  They are
+     * prefixed with a space and printed as hex strings, so convert
+     * those strings to binary.  Pass the strings up to the callback
+     * function.
+     */
+  case reading_key:
+    if (STRPREFIX (line, "DATA=END")) {
+      data->state = reading_finished;
+      return;
     }
 
-    if ((value = convert_hex_to_binary (g, &line[1], linelen-1,
-                                        &valuelen)) == NULL)
-      goto out;
+    if (len < 1 || line[0] != ' ') {
+      debug (g, _("unexpected line from db_dump command, no space prefix"));
+      data->state = reading_failed;
+      return;
+    }
 
-    if (callback (g, key, keylen, value, valuelen, opaque) == -1)
-      goto out;
+    data->key = convert_hex_to_binary (g, &line[1], len-1, &data->keylen);
+    if (data->key == NULL) {
+      data->state = reading_failed;
+      return;
+    }
 
-    free (key);
-    free (value);
-    key = value = NULL;
-  }
+    data->state = reading_value;
+    return;
 
-  if (linelen == -1) {
-    error (g, _("unexpected end of output from db_dump command before end of data"));
-    goto out;
-  }
+  case reading_value:
+    if (len < 1 || line[0] != ' ') {
+      debug (g, _("unexpected line from db_dump command, no space prefix"));
+      data->state = reading_failed;
+      return;
+    }
 
-  /* Catch errors from the db_dump command. */
-  if (pclose (pp) != 0) {
-    perrorf (g, "pclose: %s", cmd);
-    pp = NULL;
-    goto out;
-  }
-  pp = NULL;
+    value = convert_hex_to_binary (g, &line[1], len-1, &valuelen);
+    if (value == NULL) {
+      data->state = reading_failed;
+      return;
+    }
 
-  ret = 0;
+    if (data->callback (g, data->key, data->keylen,
+                        value, valuelen, data->opaque) == -1) {
+      data->state = reading_failed;
+      return;
+    }
 
- out:
-  if (pp)
-    pclose (pp);
+    free (data->key);
+    data->key = NULL;
+    free (value);
+    value = NULL;
 
-  free (line);
-  free (key);
-  free (value);
+    data->state = reading_key;
+    return;
 
-  return ret;
-#undef cmd_len
+  default:
+    abort ();
+  }
 }
 
 static int
-- 
1.7.11.4




More information about the Libguestfs mailing list