/* Chrysalide - Outil d'analyse de fichiers binaires
* pychrysa.c - plugin permettant des extensions en Python
*
* Copyright (C) 2009-2014 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "pychrysa.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "plugin.h"
#include "quirks.h"
#include "analysis/module.h"
#include "arch/module.h"
#include "common/module.h"
#include "core/module.h"
#include "format/module.h"
#include "glibext/module.h"
#include "gtkext/module.h"
#include "gui/module.h"
DEFINE_CHRYSALIDE_ACTIVE_PLUGIN("PyChrysalide", "Provides bindings to Python", "0.1.0", PGA_PLUGIN_INIT);
/* Fournit la révision du programme global. */
static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
/* Fournit la version du programme global. */
static PyObject *py_chrysalide_version(PyObject *, PyObject *);
/* Fournit la version du greffon pour Python. */
static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
/* Recherche et fournit si elle existe une valeur globale. */
static PyObject *py_chrysalide_get_global_gobject(PyObject *, PyObject *);
/* Détermine si l'interpréteur lancé est celui pris en compte. */
static bool is_current_abi_suitable(void);
/* Charge autant de greffons composés en Python que possible. */
static bool load_python_plugins(GPluginModule *plugin, GObject *);
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
* args = non utilisé ici. *
* *
* Description : Fournit la révision du programme global. *
* *
* Retour : Numéro de révision. *
* *
* Remarques : - *
* *
******************************************************************************/
static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
{
return PyUnicode_FromString("r" XSTR(REVISION));
}
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
* args = non utilisé ici. *
* *
* Description : Fournit la version du programme global. *
* *
* Retour : Numéro de version. *
* *
* Remarques : - *
* *
******************************************************************************/
static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
{
char version[16];
int major;
int minor;
int revision;
major = REVISION / 1000;
minor = (REVISION - (major * 1000)) / 100;
revision = REVISION % 100;
snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
return PyUnicode_FromString(version);
}
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
* args = non utilisé ici. *
* *
* Description : Fournit la version du greffon pour Python. *
* *
* Retour : Numéro de version. *
* *
* Remarques : - *
* *
******************************************************************************/
static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
{
char version[16];
snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version);
return PyUnicode_FromString(version);
}
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
* args = contient la clef d'accès à un champ particulier. *
* *
* Description : Recherche et fournit si elle existe une valeur globale. *
* *
* Retour : Object attaché à l'espace de référencement global ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
static PyObject *py_chrysalide_get_global_gobject(PyObject *self, PyObject *args)
{
PyObject *result; /* Instance à retourner */
const char *key; /* Désignation du champ visé */
int ret; /* Bilan de lecture des args. */
void *data; /* Donnée quelconque */
ret = PyArg_ParseTuple(args, "s", &key);
if (!ret) Py_RETURN_NONE;
data = g_object_get_data(get_internal_ref(), key);
if (data == NULL) Py_RETURN_NONE;
if (!G_IS_OBJECT(data)) Py_RETURN_NONE;
result = pygobject_new(G_OBJECT(data));
return result;
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
* *
* Retour : true si l'exécution peut se poursuivre, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool is_current_abi_suitable(void)
{
bool result;
int fds[2];
int ret;
char cmds[128];
char content[64];
ssize_t got;
#define GRAB_ABI_FLAGS_IN_PYTHON \
"import sys" "\n" \
"import os" "\n" \
"os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\n"
result = false;
ret = pipe(fds);
if (ret == -1)
{
perror("pipe()");
return false;
}
snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
ret = PyRun_SimpleString(cmds);
if (ret != 0) goto icas_exit;
got = read(fds[0], content, sizeof(content));
if (got < 0)
{
perror("read()");
goto icas_exit;
}
content[got] = '\0';
result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
icas_exit:
if (!result)
PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
"the ones of the Python library used during the module compilation.");
return result;
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Point d'entrée pour l'initialisation de Python. *
* *
* Retour : ? *
* *
* Remarques : - *
* *
******************************************************************************/
#include // REMME
#include
#define PYCHRYSALIDE_DOC \
"PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \
"\n" \
"The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \
"* either from the Chrysalide's GUI, by registering hooks or GLib signals.\n" \
"* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
"\n" \
"In both cases, it is a good start point to have a look at already existing plugins to quickly learn " \
"how the API works.\n" \
"\n" \
"These plugins are located in the 'plugins/python' directory."
PyMODINIT_FUNC PyInit_pychrysalide(void)
{
PyObject *result; /* Module Python à retourner */
PyObject *pygobj_mod; /* Module Python-GObject */
bool status; /* Bilan des inclusions */
static PyMethodDef py_chrysalide_methods[] = {
{ "revision", py_chrysalide_revision,
METH_NOARGS,
"revision(/)\n--\n\nProvide the revision number of Chrysalide."
},
{ "version", py_chrysalide_version,
METH_NOARGS,
"version(/)\n--\n\nProvide the version number of Chrysalide."
},
{ "mod_version", py_chrysalide_mod_version,
METH_NOARGS,
"mod_version(/)\n--\n\nProvide the version number of Chrysalide module for Python."
},
{ "get_global_gobject", py_chrysalide_get_global_gobject,
METH_VARARGS,
"get_global_gobject(key, /)\n--\n\nRetrieve if it exists a global GObject registered with the given key."
},
{ NULL }
};
static PyModuleDef py_chrysalide_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "pychrysalide",
.m_doc = PYCHRYSALIDE_DOC,
.m_size = -1,
.m_methods = py_chrysalide_methods
};
#if 0
do
{
int argc = 0;
//char *argv[] = { NULL };
/* Initialisation de GTK */
g_set_prgname("Chrysalide");
setlocale (LC_ALL, "");
gtk_init(&argc, NULL/*&argv-*/);
} while (0);
add_pixmap_directory(PACKAGE_DATA_DIR);
add_pixmap_directory(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "pixmaps");
/**
* On évite d'initialiser deux fois...
*/
if (get_work_queue() != NULL)
init_work_queue(NULL/* !! */);
#endif
/**
* On évite d'initialiser deux fois...
*/
if (get_work_queue() == NULL)
init_work_queue(NULL/* !! */);
////////////////////////
if (!is_current_abi_suitable())
return NULL;
if (pygobject_init(-1, -1, -1) == NULL)
{
PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
return NULL;
}
if (!load_all_basic_components())
{
PyErr_SetString(PyExc_SystemError, "unable to load all basic components.");
return NULL;
}
/* Préparatifs préalables aux chargements */
/**
* Pour une raison non identifiée, si le module n'est pas préchargé,
* le flot d'exécution plante dans la fonction insertdict() de Objects/dictobject.c:818.
*/
pygobj_mod = PyImport_ImportModule("gi.repository.GObject");
if (pygobj_mod == NULL)
{
PyErr_SetString(PyExc_ImportError, "could not import gi.gobject");
return NULL;
}
/* Mise en place des fonctionnalités offertes */
result = PyModule_Create(&py_chrysalide_module);
status = register_python_plugin_module(result);
status &= add_analysis_module_to_python_module(result);
status &= add_arch_module_to_python_module(result);
status &= add_common_module_to_python_module(result);
status &= add_core_module_to_python_module(result);
status &= add_format_module_to_python_module(result);
status &= add_glibext_module_to_python_module(result);
status &= add_gtkext_module_to_python_module(result);
status &= add_gui_module_to_python_module(result);
if (!status)
{
PyErr_SetString(PyExc_SystemError, "fail to load all PyChrysalide components.");
return NULL;
}
return result;
}
/******************************************************************************
* *
* Paramètres : plugin = instance représentant le greffon Python d'origine. *
* ref = espace de référencement global. *
* *
* Description : Charge autant de greffons composés en Python que possible. *
* *
* Retour : true. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool load_python_plugins(GPluginModule *plugin, GObject *ref)
{
char *paths; /* Emplacements de greffons */
char *save; /* Sauvegarde pour ré-entrance */
char *path; /* Chemin à fouiller */
DIR *dir; /* Répertoire à parcourir */
struct dirent entry; /* Elément trouvé */
struct dirent *next; /* Prochain élément fourni */
int ret; /* Bilan d'un appel système */
char *modname; /* Nom du module pour Python */
char *filename; /* Chemin d'accès reconstruit */
GPluginModule *pyplugin; /* Lien vers un grffon Python */
paths = get_env_var("PYTHONPATH");
save = NULL; /* gcc... */
for (path = strtok_r(paths, ";", &save);
path != NULL;
path = strtok_r(NULL, ";", &save))
{
dir = opendir(path);
if (dir == NULL)
{
perror("opendir");
continue;
}
g_plugin_module_log_variadic_message(plugin, LMT_INFO,
_("Looking for Python plugins in '%s'..."),
path);
for (ret = readdir_r(dir, &entry, &next);
ret == 0 && next != NULL;
ret = readdir_r(dir, &entry, &next))
{
if (entry.d_type != DT_DIR) continue;
if (entry.d_name[0] == '.') continue;
modname = strdup(entry.d_name);
modname = stradd(modname, ".");
modname = stradd(modname, "__init__");
filename = strdup(path);
filename = stradd(filename, G_DIR_SEPARATOR_S);
filename = stradd(filename, entry.d_name);
pyplugin = g_python_plugin_new(modname, filename, ref);
if (pyplugin == NULL)
g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
_("No suitable Python plugin found in '%s'"),
filename);
else
{
g_plugin_module_log_variadic_message(plugin, LMT_PROCESS,
_("Loaded the Python plugin found in the '%s' directory"),
filename);
add_plugin_to_main_list(pyplugin);
}
free(filename);
free(modname);
}
closedir(dir);
}
return true;
}
/******************************************************************************
* *
* Paramètres : plugin = greffon à manipuler. *
* ref = espace de référencement global. *
* *
* Description : Prend acte du chargement du greffon. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin, GObject *ref)
{
bool result; /* Bilan à retourner */
DIR *dir; /* Répertoire à parcourir */
define_internal_ref(ref);
/* Définition des zones d'influence */
dir = opendir(PLUGINS_DIR G_DIR_SEPARATOR_S "python");
if (dir != NULL)
{
closedir(dir);
add_to_env_var("PYTHONPATH", PLUGINS_DIR G_DIR_SEPARATOR_S "python", ";");
}
else
add_to_env_var("PYTHONPATH", PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" \
G_DIR_SEPARATOR_S "python", ";");
/* Chargement du module pour Python */
PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
Py_Initialize();
PySys_SetArgv(0, (wchar_t *[]) { NULL });
result = load_python_plugins(plugin, ref);
return result;
}