Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : // Enable this if you use s# variants with PyArg_ParseTuple(), see
16 : : // http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
17 : : //#define PY_SSIZE_T_CLEAN
18 : :
19 : : // Python.h needs to be placed at the head of the program file, see:
20 : : // http://docs.python.org/py3k/extending/extending.html#a-simple-example
21 : : #include <Python.h>
22 : :
23 : : #include <string>
24 : : #include <stdexcept>
25 : :
26 : : #include <boost/shared_ptr.hpp>
27 : :
28 : : #include <util/python/pycppwrapper_util.h>
29 : :
30 : : #include <cc/data.h>
31 : :
32 : : #include <acl/dns.h>
33 : :
34 : : #include "dns.h"
35 : : #include "dns_requestacl_python.h"
36 : : #include "dns_requestloader_python.h"
37 : :
38 : : using namespace std;
39 : : using namespace isc::util::python;
40 : : using namespace isc::data;
41 : : using namespace isc::acl::dns;
42 : : using namespace isc::acl::dns::python;
43 : :
44 : : //
45 : : // Definition of the classes
46 : : //
47 : :
48 : : // For each class, we need a struct, a helper functions (init, destroy,
49 : : // and static wrappers around the methods we export), a list of methods,
50 : : // and a type description
51 : :
52 : : //
53 : : // RequestLoader
54 : : //
55 : :
56 : : // Trivial constructor.
57 : 0 : s_RequestLoader::s_RequestLoader() : cppobj(NULL) {
58 : 0 : }
59 : :
60 : : // Import pydoc text
61 : : #include "dns_requestloader_inc.cc"
62 : :
63 : : namespace {
64 : : //
65 : : // We declare the functions here, the definitions are below
66 : : // the type definition of the object, since both can use the other
67 : : //
68 : :
69 : : int
70 : 1 : RequestLoader_init(PyObject*, PyObject*, PyObject*) {
71 : : PyErr_SetString(getACLException("Error"),
72 : 1 : "RequestLoader cannot be directly constructed");
73 : 1 : return (-1);
74 : : }
75 : :
76 : : void
77 : 1 : RequestLoader_destroy(PyObject* po_self) {
78 : 1 : s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
79 [ - + ]: 1 : delete self->cppobj;
80 : 1 : self->cppobj = NULL;
81 : 1 : Py_TYPE(self)->tp_free(self);
82 : 1 : }
83 : :
84 : : // This C structure corresponds to a Python callable object for json.dumps().
85 : : // This is initialized at the class initialization time (in
86 : : // initModulePart_RequestLoader() below) and it's ensured to be non NULL and
87 : : // valid in the rest of the class implementation.
88 : : // Getting access to the json module this way and call one of its functions
89 : : // via PyObject_CallObject() may exceed the reasonably acceptable level for
90 : : // straightforward bindings. But the alternative would be to write a Python
91 : : // frontend for the entire module only for this conversion, which would also
92 : : // be too much. So, right now, we implement everything within the binding
93 : : // implementation. If future extensions require more such non trivial
94 : : // wrappers, we should consider the frontend approach more seriously.
95 : : PyObject* json_dumps_obj = NULL;
96 : :
97 : : PyObject*
98 : 178 : RequestLoader_load(PyObject* po_self, PyObject* args) {
99 : 178 : s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
100 : :
101 : : try {
102 [ + - ][ + - ]: 178 : PyObjectContainer c1, c2; // placeholder for temporary py objects
[ + - ][ + - ]
103 : : const char* acl_config;
104 : :
105 : : // First, try string
106 [ + - ]: 178 : int py_result = PyArg_ParseTuple(args, "s", &acl_config);
107 [ + + ]: 178 : if (!py_result) {
108 [ + - ]: 131 : PyErr_Clear(); // need to clear the error from ParseTuple
109 : :
110 : : // If that fails, confirm the argument is a single Python object,
111 : : // and pass the argument to json.dumps() without conversion.
112 : : // Note that we should pass 'args', not 'json_obj' to
113 : : // PyObject_CallObject(), since this function expects a form of
114 : : // tuple as its argument parameter, just like ParseTuple.
115 : : PyObject* json_obj;
116 [ + - ][ + + ]: 131 : if (PyArg_ParseTuple(args, "O", &json_obj)) {
117 [ + - ][ + + ]: 130 : c1.reset(PyObject_CallObject(json_dumps_obj, args));
118 [ + - ][ + - ]: 129 : c2.reset(Py_BuildValue("(O)", c1.get()));
119 [ + - ]: 129 : py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config);
120 : : }
121 : : }
122 [ + + ]: 177 : if (py_result) {
123 : : boost::shared_ptr<RequestACL> acl(
124 [ + - ][ + + ]: 526 : self->cppobj->load(Element::fromJSON(acl_config)));
[ + + ]
125 : : s_RequestACL* py_acl = static_cast<s_RequestACL*>(
126 [ + - ]: 116 : requestacl_type.tp_alloc(&requestacl_type, 0));
127 [ + - ]: 116 : if (py_acl != NULL) {
128 : 116 : py_acl->cppobj = acl;
129 : : }
130 : 116 : return (py_acl);
131 : : }
132 : 1 : } catch (const PyCPPWrapperException&) {
133 : : // If the wrapper utility throws, it's most likely because an invalid
134 : : // type of argument is passed (and the call to json.dumps() failed
135 : : // above), rather than a rare case of system errors such as memory
136 : : // allocation failure. So we fall through to the end of this function
137 : : // and raise a TypeError.
138 : : ;
139 : 120 : } catch (const exception& ex) {
140 [ - + ][ - + ]: 60 : PyErr_SetString(getACLException("LoaderError"), ex.what());
141 : : return (NULL);
142 [ + + - ]: 61 : } catch (...) {
143 [ # # ]: 0 : PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
144 : : return (NULL);
145 : : }
146 : :
147 : : PyErr_SetString(PyExc_TypeError, "RequestLoader.load() "
148 : 2 : "expects str or python representation of JSON");
149 : 178 : return (NULL);
150 : : }
151 : :
152 : : // This list contains the actual set of functions we have in
153 : : // python. Each entry has
154 : : // 1. Python method name
155 : : // 2. Our static function here
156 : : // 3. Argument type
157 : : // 4. Documentation
158 : : PyMethodDef RequestLoader_methods[] = {
159 : : { "load", RequestLoader_load, METH_VARARGS, RequestLoader_load_doc },
160 : : { NULL, NULL, 0, NULL }
161 : : };
162 : : } // end of unnamed namespace
163 : :
164 : : namespace isc {
165 : : namespace acl {
166 : : namespace dns {
167 : : namespace python {
168 : : // This defines the complete type for reflection in python and
169 : : // parsing of PyObject* to s_RequestLoader
170 : : // Most of the functions are not actually implemented and NULL here.
171 : : PyTypeObject requestloader_type = {
172 : : PyVarObject_HEAD_INIT(NULL, 0)
173 : : "isc.acl._dns.RequestLoader",
174 : : sizeof(s_RequestLoader), // tp_basicsize
175 : : 0, // tp_itemsize
176 : : RequestLoader_destroy, // tp_dealloc
177 : : NULL, // tp_print
178 : : NULL, // tp_getattr
179 : : NULL, // tp_setattr
180 : : NULL, // tp_reserved
181 : : NULL, // tp_repr
182 : : NULL, // tp_as_number
183 : : NULL, // tp_as_sequence
184 : : NULL, // tp_as_mapping
185 : : NULL, // tp_hash
186 : : NULL, // tp_call
187 : : NULL, // tp_str
188 : : NULL, // tp_getattro
189 : : NULL, // tp_setattro
190 : : NULL, // tp_as_buffer
191 : : Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags
192 : : RequestLoader_doc,
193 : : NULL, // tp_traverse
194 : : NULL, // tp_clear
195 : : NULL, // tp_richcompare
196 : : 0, // tp_weaklistoffset
197 : : NULL, // tp_iter
198 : : NULL, // tp_iternext
199 : : RequestLoader_methods, // tp_methods
200 : : NULL, // tp_members
201 : : NULL, // tp_getset
202 : : NULL, // tp_base
203 : : NULL, // tp_dict
204 : : NULL, // tp_descr_get
205 : : NULL, // tp_descr_set
206 : : 0, // tp_dictoffset
207 : : RequestLoader_init, // tp_init
208 : : NULL, // tp_alloc
209 : : PyType_GenericNew, // tp_new
210 : : NULL, // tp_free
211 : : NULL, // tp_is_gc
212 : : NULL, // tp_bases
213 : : NULL, // tp_mro
214 : : NULL, // tp_cache
215 : : NULL, // tp_subclasses
216 : : NULL, // tp_weaklist
217 : : NULL, // tp_del
218 : : 0 // tp_version_tag
219 : : };
220 : :
221 : : bool
222 : 2 : initModulePart_RequestLoader(PyObject* mod) {
223 : : // We initialize the static description object with PyType_Ready(),
224 : : // then add it to the module. This is not just a check! (leaving
225 : : // this out results in segmentation faults)
226 [ + - ]: 2 : if (PyType_Ready(&requestloader_type) < 0) {
227 : : return (false);
228 : : }
229 : 2 : void* p = &requestloader_type;
230 [ + - ]: 2 : if (PyModule_AddObject(mod, "RequestLoader",
231 : 2 : static_cast<PyObject*>(p)) < 0) {
232 : : return (false);
233 : : }
234 : :
235 : : // Get and hold our own reference to json.dumps() for later use.
236 : : // Normally it should succeed as __init__.py of the isc.acl package
237 : : // explicitly imports the json module, and the code below should be
238 : : // error free (e.g. they don't require memory allocation) under this
239 : : // condition.
240 : : // This could still fail with deviant or evil Python code such as those
241 : : // that first import json and then delete the reference to it from
242 : : // sys.modules before it imports the acl.dns module. The RequestLoader
243 : : // class could still work as long as it doesn't use the JSON decoder,
244 : : // but we'd rather refuse to import the module than allowing the partially
245 : : // workable class to keep running.
246 : 2 : PyObject* json_module = PyImport_AddModule("json");
247 [ + - ]: 2 : if (json_module != NULL) {
248 : 2 : PyObject* json_dict = PyModule_GetDict(json_module);
249 [ + - ]: 2 : if (json_dict != NULL) {
250 : 2 : json_dumps_obj = PyDict_GetItemString(json_dict, "dumps");
251 : : }
252 : : }
253 [ + - ]: 2 : if (json_dumps_obj != NULL) {
254 : 2 : Py_INCREF(json_dumps_obj);
255 : : } else {
256 : : PyErr_SetString(PyExc_RuntimeError,
257 : : "isc.acl.dns.RequestLoader needs the json module, but "
258 : 0 : "it's missing");
259 : 2 : return (false);
260 : : }
261 : :
262 : 2 : Py_INCREF(&requestloader_type);
263 : :
264 : 2 : return (true);
265 : : }
266 : : } // namespace python
267 : : } // namespace dns
268 : : } // namespace acl
269 : 0 : } // namespace isc
|