Hi,

On 8/7/19 9:14 AM, Balamuruhan S wrote:
> Adds scripting interface with python library to call functions in
> python modules from Qemu that can be used to feed input externally
> and without recompiling Qemu that can be used for early development,
> testing and can be extended to abstract some of Qemu code out to a
> python script to ease maintenance.
> 
> Signed-off-by: Balamuruhan S <bal...@linux.ibm.com>
> ---
>  configure                   |  10 +++++
>  include/sysemu/python_api.h |  30 +++++++++++++
>  util/Makefile.objs          |   1 +
>  util/python_api.c           | 100 
> ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 141 insertions(+)
>  create mode 100644 include/sysemu/python_api.h
>  create mode 100644 util/python_api.c
> 
> diff --git a/configure b/configure
> index 714e7fb6a1..fddddcc879 100755
> --- a/configure
> +++ b/configure
> @@ -1866,6 +1866,11 @@ fi
>  # Preserve python version since some functionality is dependent on it
>  python_version=$($python -V 2>&1 | sed -e 's/Python\ //')
>  
> +# Python config to be used for CFLAGS and LDFLAGS
> +if ! [ -z "$python" ]; then
> +    python_config="$python-config"
> +fi
> +
>  # Suppress writing compiled files
>  python="$python -B"
>  
> @@ -6304,6 +6309,11 @@ echo_version() {
>      fi
>  }
>  
> +if ! [ -z "$python_config" ]; then
> +    QEMU_CFLAGS="$QEMU_CFLAGS $($python_config --includes)"
> +    QEMU_LDFLAGS="$QEMU_LDFLAGS $($python_config --ldflags)"
> +fi
> +
>  # prepend pixman and ftd flags after all config tests are done
>  QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS"
>  QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS"
> diff --git a/include/sysemu/python_api.h b/include/sysemu/python_api.h
> new file mode 100644
> index 0000000000..ff02d58377
> --- /dev/null
> +++ b/include/sysemu/python_api.h
> @@ -0,0 +1,30 @@
> +#ifndef _PPC_PNV_PYTHON_H
> +#define _PPC_PNV_PYTHON_H
> +
> +#include <stdbool.h>
> +#include <Python.h>
> +
> +extern PyObject *python_callback(const char *abs_module_path, const char 
> *mod,
> +                                 const char *func, char *args[],
> +                                 const int nargs);
> +
> +extern uint64_t python_callback_int(const char *abs_module_path,
> +                                    const char *mod,
> +                                    const char *func, char *args[],
> +                                    const int nargs);
> +
> +extern char *python_callback_str(const char *abs_module_path, const char 
> *mod,
> +                                 const char *func, char *args[],
> +                                 const int nargs);
> +
> +extern bool python_callback_bool(const char *abs_module_path, const char 
> *mod,
> +                                 const char *func, char *args[],
> +                                 const int nargs);
> +
> +extern void python_args_init_cast_int(char *args[], int arg, int pos);
> +
> +extern void python_args_init_cast_long(char *args[], uint64_t arg, int pos);
> +
> +extern void python_args_clean(char *args[], int nargs);
> +
> +#endif
> diff --git a/util/Makefile.objs b/util/Makefile.objs
> index 41bf59d127..05851c94a7 100644
> --- a/util/Makefile.objs
> +++ b/util/Makefile.objs
> @@ -50,6 +50,7 @@ util-obj-y += range.o
>  util-obj-y += stats64.o
>  util-obj-y += systemd.o
>  util-obj-y += iova-tree.o
> +util-obj-y += python_api.o

This is probably conditional to having python-dev (or whatever distribs
call the package) installed.

>  util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o
>  util-obj-$(CONFIG_LINUX) += vfio-helpers.o
>  util-obj-$(CONFIG_POSIX) += drm.o
> diff --git a/util/python_api.c b/util/python_api.c
> new file mode 100644
> index 0000000000..854187e00f
> --- /dev/null
> +++ b/util/python_api.c
> @@ -0,0 +1,100 @@
> +#include "sysemu/python_api.h"
> +#include "qemu/osdep.h"
> +
> +PyObject *python_callback(const char *abs_module_path, const char *mod,
> +                          const char *func, char *args[], const int nargs)
> +{
> +    PyObject *mod_name, *module, *mod_ref, *function, *arguments;
> +    PyObject *result = 0;
> +    PyObject *value = NULL;
> +
> +    /* Set PYTHONPATH to absolute module path directory */
> +    if (!abs_module_path)
> +        abs_module_path = ".";
> +    setenv("PYTHONPATH", abs_module_path, 1);
> +
> +    /* Initialize the Python Interpreter */
> +    Py_Initialize();
> +    mod_name = PyUnicode_FromString(mod);
> +    /* Import module object */
> +    module = PyImport_Import(mod_name);
> +    if (!module) {
> +        PyErr_Print();
> +        fprintf(stderr, "Failed to load \"%s\"\n", mod);
> +        exit(EXIT_FAILURE);
> +    }
> +    mod_ref = PyModule_GetDict(module);
> +    function = PyDict_GetItemString(mod_ref, func);
> +    if (function && PyCallable_Check(function)) {
> +        arguments = PyTuple_New(nargs);
> +        for (int i = 0; i < nargs; i++) {
> +            value = PyUnicode_FromString(args[i]);
> +            if (!value) {
> +                Py_DECREF(arguments);
> +                Py_DECREF(module);
> +                fprintf(stderr, "Cannot convert argument\n");
> +                exit(EXIT_FAILURE);
> +            }
> +            PyTuple_SetItem(arguments, i, value);
> +        }
> +        PyErr_Print();
> +        result = PyObject_CallObject(function, arguments);
> +        PyErr_Print();
> +    }
> +    else {
> +        if (PyErr_Occurred())
> +            PyErr_Print();
> +        fprintf(stderr, "Cannot find function \"%s\"\n", func);
> +        exit(EXIT_FAILURE);
> +    }
> +    /* Clean up */
> +    Py_DECREF(value);
> +    Py_DECREF(module);
> +    Py_DECREF(mod_name);
> +    /* Finish the Python Interpreter */
> +    Py_Finalize();
> +    return result;
> +}
> +
> +uint64_t python_callback_int(const char *abs_module_path, const char *mod,
> +                             const char *func, char *args[], const int nargs)
> +{
> +    PyObject *result;
> +    result = python_callback(abs_module_path, mod, func, args, nargs);
> +    return PyLong_AsLong(result);
> +}
> +
> +char *python_callback_str(const char *abs_module_path, const char *mod,
> +                          const char *func, char *args[], const int nargs)
> +{
> +    PyObject *result;
> +    result = python_callback(abs_module_path, mod, func, args, nargs);
> +    return PyUnicode_AsUTF8(result);
> +}
> +
> +bool python_callback_bool(const char *abs_module_path, const char *mod,
> +                          const char *func, char *args[], const int nargs)
> +{
> +    PyObject *result;
> +    result = python_callback(abs_module_path, mod, func, args, nargs);
> +    return (result == Py_True);
> +}
> +
> +void python_args_init_cast_int(char *args[], int arg, int pos)
> +{
> +    args[pos]= malloc(sizeof(int));
> +    sprintf(args[pos], "%d", arg);
> +}
> +
> +void python_args_init_cast_long(char *args[], uint64_t arg, int pos)
> +{
> +    args[pos]= g_malloc(sizeof(uint64_t) * 2);
> +    sprintf(args[pos], "%lx", arg);
> +}
> +
> +void python_args_clean(char *args[], int nargs)
> +{
> +    for (int i = 0; i < nargs; i++) {
> +        g_free(args[i]);
> +    }
> +}
> 

Wondering about security, is this feature safe to enable in production
environment? It seems to bypass all the hard effort to harden QEMU security.

Reply via email to