Re: [Qemu-devel] [PATCH 08/10] qapi: add strict mode to input visitor

2012-04-02 Thread Paolo Bonzini
Il 02/04/2012 12:34, Laurent Desnogues ha scritto:
>> >  static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
>> >  {
>> > +GHashTableIter iter;
> GHashTableIter is alas not available in the glib (2.12) that
> the distros we use at work run.  Is there a workaround for
> this issue?

Yeah, since the hash table is going to be destroyed anyway, and will
always be empty in the common case, you can call
g_hash_table_foreach_remove, and raise an error if it returns > 0

Something like

gboolean hash_table_empty_check(gpointer key, gpointer value,
gpointer user_data)
{
gpointer *p_key = user_data;
if (!*p_key) {
*p_key = key;
}
return true;
}



key = NULL;
if (g_hash_table_foreach_remove(qiv->stack[qiv->nb_stack - 1].h,
hash_table_empty_check, &key) {
error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
}
g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);

Can you make a patch?

Paolo



Re: [Qemu-devel] [PATCH 08/10] qapi: add strict mode to input visitor

2012-04-02 Thread Laurent Desnogues
Hello,

On Thu, Mar 22, 2012 at 12:51 PM, Paolo Bonzini  wrote:
> While QMP in general is designed so that it is possible to ignore
> unknown arguments, in the case of the QMP server it is better to
> reject them to detect bad clients.  In fact, we're already doing
> this at the top level in the argument checker.  To extend this to
> complex structures, add a mode to the input visitor where it checks
> for unvisited keys and raises an error if it finds one.
>
> Signed-off-by: Paolo Bonzini 
> ---
>  qapi/qmp-input-visitor.c |   48 +-
>  qapi/qmp-input-visitor.h |    2 +
>  test-qmp-input-strict.c  |  234 
> ++
>  tests/Makefile           |    5 +-
>  4 files changed, 285 insertions(+), 4 deletions(-)
>  create mode 100644 test-qmp-input-strict.c
>
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 97a0186..eb09874 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -24,6 +24,7 @@ typedef struct StackObject
>  {
>     QObject *obj;
>     const QListEntry *entry;
> +    GHashTable *h;
>  } StackObject;
>
>  struct QmpInputVisitor
> @@ -31,6 +32,7 @@ struct QmpInputVisitor
>     Visitor visitor;
>     StackObject stack[QIV_STACK_SIZE];
>     int nb_stack;
> +    bool strict;
>  };
>
>  static QmpInputVisitor *to_qiv(Visitor *v)
> @@ -45,6 +47,9 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
>
>     if (qobj) {
>         if (name && qobject_type(qobj) == QTYPE_QDICT) {
> +            if (qiv->stack[qiv->nb_stack - 1].h) {
> +                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
> +            }
>             return qdict_get(qobject_to_qdict(qobj), name);
>         } else if (qiv->stack[qiv->nb_stack - 1].entry) {
>             return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
> @@ -54,20 +59,47 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
>     return qobj;
>  }
>
> +static void qdict_add_key(const char *key, QObject *obj, void *opaque)
> +{
> +    GHashTable *h = opaque;
> +    g_hash_table_insert(h, (gpointer) key, NULL);
> +}
> +
>  static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
>  {
> -    qiv->stack[qiv->nb_stack].obj = obj;
> -    qiv->stack[qiv->nb_stack].entry = NULL;
> -    qiv->nb_stack++;
> +    GHashTable *h;
>
>     if (qiv->nb_stack >= QIV_STACK_SIZE) {
>         error_set(errp, QERR_BUFFER_OVERRUN);
>         return;
>     }
> +
> +    qiv->stack[qiv->nb_stack].obj = obj;
> +    qiv->stack[qiv->nb_stack].entry = NULL;
> +    qiv->stack[qiv->nb_stack].h = NULL;
> +
> +    if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
> +        h = g_hash_table_new(g_str_hash, g_str_equal);
> +        qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
> +        qiv->stack[qiv->nb_stack].h = h;
> +    }
> +
> +    qiv->nb_stack++;
>  }
>
>  static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
>  {
> +    GHashTableIter iter;

GHashTableIter is alas not available in the glib (2.12) that
the distros we use at work run.  Is there a workaround for
this issue?

Thanks,

Laurent

> +    gpointer key;
> +
> +    if (qiv->strict && qiv->stack[qiv->nb_stack - 1].h) {
> +        g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h);
> +        if (g_hash_table_iter_next(&iter, &key, NULL)) {
> +            error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
> +        }
> +        g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);
> +    }
> +
>     assert(qiv->nb_stack > 0);
>     qiv->nb_stack--;
>  }
> @@ -257,3 +289,13 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>
>     return v;
>  }
> +
> +QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
> +{
> +    QmpInputVisitor *v;
> +
> +    v = qmp_input_visitor_new(obj);
> +    v->strict = true;
> +
> +    return v;
> +}
> diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
> index 3f798f0..e0a48a5 100644
> --- a/qapi/qmp-input-visitor.h
> +++ b/qapi/qmp-input-visitor.h
> @@ -20,6 +20,8 @@
>  typedef struct QmpInputVisitor QmpInputVisitor;
>
>  QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
> +QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj);
> +
>  void qmp_input_visitor_cleanup(QmpInputVisitor *v);
>
>  Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
> diff --git a/test-qmp-input-strict.c b/test-qmp-input-strict.c
> new file mode 100644
> index 000..f6df8cb
> --- /dev/null
> +++ b/test-qmp-input-strict.c
> @@ -0,0 +1,234 @@
> +/*
> + * QMP Input Visitor unit-tests (strict mode).
> + *
> + * Copyright (C) 2011-2012 Red Hat Inc.
> + *
> + * Authors:
> + *  Luiz Capitulino 
> + *  Paolo Bonzini 
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include 
> +#include 
> +
> +#include "qapi/qmp-input-visitor.h"
> +#include "test-qapi-types.h"
> +#include "test-qapi-visit.h"
> +#include "qe

Re: [Qemu-devel] [PATCH 08/10] qapi: add strict mode to input visitor

2012-03-22 Thread Anthony Liguori

On 03/22/2012 06:51 AM, Paolo Bonzini wrote:

While QMP in general is designed so that it is possible to ignore
unknown arguments, in the case of the QMP server it is better to
reject them to detect bad clients.  In fact, we're already doing
this at the top level in the argument checker.  To extend this to
complex structures, add a mode to the input visitor where it checks
for unvisited keys and raises an error if it finds one.

Signed-off-by: Paolo Bonzini


We need to document this behavior in QMP/qmp-spec.txt.

Regards,

Anthony Liguori


---
  qapi/qmp-input-visitor.c |   48 +-
  qapi/qmp-input-visitor.h |2 +
  test-qmp-input-strict.c  |  234 ++
  tests/Makefile   |5 +-
  4 files changed, 285 insertions(+), 4 deletions(-)
  create mode 100644 test-qmp-input-strict.c

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 97a0186..eb09874 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -24,6 +24,7 @@ typedef struct StackObject
  {
  QObject *obj;
  const QListEntry *entry;
+GHashTable *h;
  } StackObject;

  struct QmpInputVisitor
@@ -31,6 +32,7 @@ struct QmpInputVisitor
  Visitor visitor;
  StackObject stack[QIV_STACK_SIZE];
  int nb_stack;
+bool strict;
  };

  static QmpInputVisitor *to_qiv(Visitor *v)
@@ -45,6 +47,9 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,

  if (qobj) {
  if (name&&  qobject_type(qobj) == QTYPE_QDICT) {
+if (qiv->stack[qiv->nb_stack - 1].h) {
+g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+}
  return qdict_get(qobject_to_qdict(qobj), name);
  } else if (qiv->stack[qiv->nb_stack - 1].entry) {
  return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
@@ -54,20 +59,47 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
  return qobj;
  }

+static void qdict_add_key(const char *key, QObject *obj, void *opaque)
+{
+GHashTable *h = opaque;
+g_hash_table_insert(h, (gpointer) key, NULL);
+}
+
  static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
  {
-qiv->stack[qiv->nb_stack].obj = obj;
-qiv->stack[qiv->nb_stack].entry = NULL;
-qiv->nb_stack++;
+GHashTable *h;

  if (qiv->nb_stack>= QIV_STACK_SIZE) {
  error_set(errp, QERR_BUFFER_OVERRUN);
  return;
  }
+
+qiv->stack[qiv->nb_stack].obj = obj;
+qiv->stack[qiv->nb_stack].entry = NULL;
+qiv->stack[qiv->nb_stack].h = NULL;
+
+if (qiv->strict&&  qobject_type(obj) == QTYPE_QDICT) {
+h = g_hash_table_new(g_str_hash, g_str_equal);
+qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
+qiv->stack[qiv->nb_stack].h = h;
+}
+
+qiv->nb_stack++;
  }

  static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
  {
+GHashTableIter iter;
+gpointer key;
+
+if (qiv->strict&&  qiv->stack[qiv->nb_stack - 1].h) {
+g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h);
+if (g_hash_table_iter_next(&iter,&key, NULL)) {
+error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
+}
+g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);
+}
+
  assert(qiv->nb_stack>  0);
  qiv->nb_stack--;
  }
@@ -257,3 +289,13 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)

  return v;
  }
+
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
+{
+QmpInputVisitor *v;
+
+v = qmp_input_visitor_new(obj);
+v->strict = true;
+
+return v;
+}
diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
index 3f798f0..e0a48a5 100644
--- a/qapi/qmp-input-visitor.h
+++ b/qapi/qmp-input-visitor.h
@@ -20,6 +20,8 @@
  typedef struct QmpInputVisitor QmpInputVisitor;

  QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj);
+
  void qmp_input_visitor_cleanup(QmpInputVisitor *v);

  Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
diff --git a/test-qmp-input-strict.c b/test-qmp-input-strict.c
new file mode 100644
index 000..f6df8cb
--- /dev/null
+++ b/test-qmp-input-strict.c
@@ -0,0 +1,234 @@
+/*
+ * QMP Input Visitor unit-tests (strict mode).
+ *
+ * Copyright (C) 2011-2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino
+ *  Paolo Bonzini
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include
+#include
+
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+QObject *obj;
+QmpInputVisitor *qiv;
+} TestInputVisitorData;
+
+static void validate_teardown(TestInputVisitorData *data,
+   const void *unused)
+{
+qobject_decref(data->obj);
+data->obj = NULL;
+
+if (data->qiv) {
+ 

[Qemu-devel] [PATCH 08/10] qapi: add strict mode to input visitor

2012-03-22 Thread Paolo Bonzini
While QMP in general is designed so that it is possible to ignore
unknown arguments, in the case of the QMP server it is better to
reject them to detect bad clients.  In fact, we're already doing
this at the top level in the argument checker.  To extend this to
complex structures, add a mode to the input visitor where it checks
for unvisited keys and raises an error if it finds one.

Signed-off-by: Paolo Bonzini 
---
 qapi/qmp-input-visitor.c |   48 +-
 qapi/qmp-input-visitor.h |2 +
 test-qmp-input-strict.c  |  234 ++
 tests/Makefile   |5 +-
 4 files changed, 285 insertions(+), 4 deletions(-)
 create mode 100644 test-qmp-input-strict.c

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 97a0186..eb09874 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -24,6 +24,7 @@ typedef struct StackObject
 {
 QObject *obj;
 const QListEntry *entry;
+GHashTable *h;
 } StackObject;
 
 struct QmpInputVisitor
@@ -31,6 +32,7 @@ struct QmpInputVisitor
 Visitor visitor;
 StackObject stack[QIV_STACK_SIZE];
 int nb_stack;
+bool strict;
 };
 
 static QmpInputVisitor *to_qiv(Visitor *v)
@@ -45,6 +47,9 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
 
 if (qobj) {
 if (name && qobject_type(qobj) == QTYPE_QDICT) {
+if (qiv->stack[qiv->nb_stack - 1].h) {
+g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+}
 return qdict_get(qobject_to_qdict(qobj), name);
 } else if (qiv->stack[qiv->nb_stack - 1].entry) {
 return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
@@ -54,20 +59,47 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
 return qobj;
 }
 
+static void qdict_add_key(const char *key, QObject *obj, void *opaque)
+{
+GHashTable *h = opaque;
+g_hash_table_insert(h, (gpointer) key, NULL);
+}
+
 static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
 {
-qiv->stack[qiv->nb_stack].obj = obj;
-qiv->stack[qiv->nb_stack].entry = NULL;
-qiv->nb_stack++;
+GHashTable *h;
 
 if (qiv->nb_stack >= QIV_STACK_SIZE) {
 error_set(errp, QERR_BUFFER_OVERRUN);
 return;
 }
+
+qiv->stack[qiv->nb_stack].obj = obj;
+qiv->stack[qiv->nb_stack].entry = NULL;
+qiv->stack[qiv->nb_stack].h = NULL;
+
+if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
+h = g_hash_table_new(g_str_hash, g_str_equal);
+qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
+qiv->stack[qiv->nb_stack].h = h;
+}
+
+qiv->nb_stack++;
 }
 
 static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
 {
+GHashTableIter iter;
+gpointer key;
+
+if (qiv->strict && qiv->stack[qiv->nb_stack - 1].h) {
+g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h);
+if (g_hash_table_iter_next(&iter, &key, NULL)) {
+error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
+}
+g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);
+}
+
 assert(qiv->nb_stack > 0);
 qiv->nb_stack--;
 }
@@ -257,3 +289,13 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
 
 return v;
 }
+
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
+{
+QmpInputVisitor *v;
+
+v = qmp_input_visitor_new(obj);
+v->strict = true;
+
+return v;
+}
diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
index 3f798f0..e0a48a5 100644
--- a/qapi/qmp-input-visitor.h
+++ b/qapi/qmp-input-visitor.h
@@ -20,6 +20,8 @@
 typedef struct QmpInputVisitor QmpInputVisitor;
 
 QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj);
+
 void qmp_input_visitor_cleanup(QmpInputVisitor *v);
 
 Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
diff --git a/test-qmp-input-strict.c b/test-qmp-input-strict.c
new file mode 100644
index 000..f6df8cb
--- /dev/null
+++ b/test-qmp-input-strict.c
@@ -0,0 +1,234 @@
+/*
+ * QMP Input Visitor unit-tests (strict mode).
+ *
+ * Copyright (C) 2011-2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino 
+ *  Paolo Bonzini 
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include 
+#include 
+
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+QObject *obj;
+QmpInputVisitor *qiv;
+} TestInputVisitorData;
+
+static void validate_teardown(TestInputVisitorData *data,
+   const void *unused)
+{
+qobject_decref(data->obj);
+data->obj = NULL;
+
+if (data->qiv) {
+qmp_input_visitor_cleanup(data->qiv);
+data->qiv = NULL;
+}
+}
+
+/* This is provided instead of a test setup function so that the JSON
+