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

Richard W.M. Jones
2014-09-05T12:08:59Z

On Thu, Sep 04, 2014 at 05:18:28PM +0200, Pino Toscano
wrote:
> 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;
> +  }

Wow, that is tedious.  Be nice if Augeas had an API call to get the
version fields directly ...

I would be tempted to use sscanf to parse up the version number here.
The code would be a lot shorter and simpler.

But yes, ACK, although sscanf better.

Rich.

> +  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
>
> _______________________________________________
> Libguestfs mailing list
> Libguestfs@redhat.com
> https://www.redhat.com/mailman/listinfo/libguestfs

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
libguestfs lets you edit virtual machines.  Supports shell scripting,
bindings from many languages.  http://libguestfs.org