[Libguestfs] [PATCH 2/5] daemon: add a way to check for the version of augeas

Pino Toscano
2014-09-04T15:18:28Z

Query augeas for its version when required, i.e. only once when using
the new augeas_is_version function.
---
 daemon/augeas.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 daemon/daemon.h |  16 +++++++-
 2 files changed, 131 insertions(+), 1 deletion(-)

diff --git a/daemon/augeas.c b/daemon/augeas.c
index 74f3ba7..8dc6a7c 100644
--- a/daemon/augeas.c
+++ b/daemon/augeas.c
@@ -24,10 +24,46 @@
 #include <unistd.h>

 #include <augeas.h>
+#include <pcre.h>

 #include "daemon.h"
 #include "actions.h"
 #include "optgroups.h"
+#include "xstrtol.h"
+
+#ifdef HAVE_ATTRIBUTE_CLEANUP
+#define CLEANUP_PCRE_FREE __attribute__((cleanup(cleanup_pcre_free)))
+
+static void
+cleanup_pcre_free (void *ptr)
+{
+  pcre *re = * (pcre **) ptr;
+
+  if (re != NULL)
+    pcre_free (re);
+}
+
+#else
+#define CLEANUP_PCRE_FREE
+#endif
+
+#define FPRINTF_AUGEAS_ERROR(aug,fs,...)                                \
+  do {                                                                  \
+    int code = aug_error (aug);                                         \
+    if (code == AUG_ENOMEM)                                             \
+      reply_with_error (fs ": augeas out of memory", ##__VA_ARGS__);
\
+    else {                                                              \
+      const char *message = aug_error_message (aug);                    \
+      const char *minor = aug_error_minor_message (aug);                \
+      const char *details = aug_error_details (aug);                    \
+      fprintf (stderr, fs ": %s%s%s%s%s", ##__VA_ARGS__,
\
+                          message,                                      \
+                          minor ? ": " : "", minor ? minor
: "",        \
+                          details ? ": " : "", details ?
details : ""); \
+    }                                                                   \
+  } while (0)
+
+int augeas_version;

 /* The Augeas handle.  We maintain a single handle per daemon, which
  * is all that is necessary and reduces the complexity of the API
@@ -35,6 +71,86 @@
  */
 static augeas *aug = NULL;

+void
+aug_read_version (void)
+{
+  CLEANUP_AUG_CLOSE augeas *ah = NULL;
+  int r;
+  const char *str;
+  CLEANUP_PCRE_FREE pcre *re = NULL;
+  const char *errptr;
+  int erroffset;
+  size_t len;
+#define N_MATCHES 4
+  int vec[N_MATCHES * 4];
+  unsigned long int major = 0, minor = 0, patch = 0;
+
+  if (augeas_version != 0)
+    return;
+
+  /* Optimization: do not load the files nor the lenses, since we are
+   * only interested in the version.
+   */
+  ah = aug_init ("/", NULL, AUG_NO_ERR_CLOSE | AUG_NO_LOAD |
AUG_NO_STDINC);
+  if (!ah) {
+    FPRINTF_AUGEAS_ERROR (ah, "augeas initialization failed");
+    return;
+  }
+
+  if (aug_error (ah) != AUG_NOERROR) {
+    FPRINTF_AUGEAS_ERROR (ah, "aug_init");
+    return;
+  }
+
+  r = aug_get (ah, "/augeas/version", &str);
+  if (r != 1) {
+    FPRINTF_AUGEAS_ERROR (ah, "aug_get");
+    return;
+  }
+
+  re = pcre_compile ("(\\d+)\\.(\\d+)(\\.(\\d+))?",
+                     0, &errptr, &erroffset, NULL);
+  if (re == NULL) {
+    fprintf (stderr, "cannot compile the augeas version regexp\n");
+    return;
+  }
+
+  len = strlen (str);
+  r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof (vec) / sizeof
(vec[0]));
+  if (r == PCRE_ERROR_NOMATCH) {
+    fprintf (stderr, "cannot match the version string in
'%s'\n", str);
+    return;
+  }
+
+  if (r > 1) {
+    if (xstrtoul (&str[vec[2]], NULL, 10, &major, NULL) != LONGINT_OK)
{
+      fprintf (stderr, "could not parse '%*s' as integer\n",
+               vec[3]-vec[2], &str[vec[2]]);
+      return;
+    }
+  }
+  if (r > 2) {
+    if (xstrtoul (&str[vec[4]], NULL, 10, &minor, NULL) != LONGINT_OK)
{
+      fprintf (stderr, "could not parse '%*s' as integer\n",
+               vec[5]-vec[4], &str[vec[4]]);
+      return;
+    }
+  }
+  if (r > 4) {
+    if (xstrtoul (&str[vec[8]], NULL, 10, &patch, NULL) != LONGINT_OK)
{
+      fprintf (stderr, "could not parse '%*s' as integer\n",
+               vec[9]-vec[8], &str[vec[8]]);
+      return;
+    }
+  }
+
+  if (verbose)
+    fprintf (stderr, "augeas version: %ld.%ld.%ld\n", major, minor,
patch);
+
+  augeas_version = (int) ((major << 16) | (minor << 8) | patch);
+#undef N_MATCHES
+}
+
 /* Clean up the augeas handle on daemon exit. */
 void aug_finalize (void) __attribute__((destructor));
 void
diff --git a/daemon/daemon.h b/daemon/daemon.h
index b9e7402..0ccbc9e 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -228,8 +228,22 @@ extern void copy_lvm (void);
 /*-- in zero.c --*/
 extern void wipe_device_before_mkfs (const char *device);

-/*-- in augeas.c, hivex.c, journal.c --*/
+/*-- in augeas.c --*/
+extern void aug_read_version (void);
 extern void aug_finalize (void);
+
+/* The version of augeas, saved as:
+ * (MAJOR << 16) | (MINOR << 8) | PATCH
+ */
+extern int augeas_version;
+static inline int
+augeas_is_version (int major, int minor, int patch)
+{
+  aug_read_version (); /* Lazy version reading. */
+  return augeas_version >= ((major << 16) | (minor << 8) |
patch);
+}
+
+/*-- hivex.c, journal.c --*/
 extern void hivex_finalize (void);
 extern void journal_finalize (void);

--
1.9.3