blob: 20861d6e37832bd4559ccbeff943ef86e5100b93 [file] [log] [blame]
Thierry FOURNIER00a02252018-02-25 20:59:57 +01001/* spoa-server: processing Python
2 *
3 * Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +00004 * Copyright (C) 2020 Gilchrist Dadaglo <gilchrist@dadaglo.com>
Thierry FOURNIER00a02252018-02-25 20:59:57 +01005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000011 * This program is provided in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18/*
19 * Define PY_SSIZE_T_CLEAN before including Python.h
20 * as per https://docs.python.org/3/c-api/arg.html and https://docs.python.org/2/c-api/arg.html
Thierry FOURNIER00a02252018-02-25 20:59:57 +010021 */
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000022#define PY_SSIZE_T_CLEAN
Thierry FOURNIER00a02252018-02-25 20:59:57 +010023
24#include <Python.h>
25
26#include <arpa/inet.h>
27
28#include <errno.h>
29#include <string.h>
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000030#include <limits.h>
Thierry FOURNIER00a02252018-02-25 20:59:57 +010031
32#include "spoa.h"
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000033#include "ps_python.h"
Thierry FOURNIER00a02252018-02-25 20:59:57 +010034
35/* Embedding python documentation:
36 *
37 * https://docs.python.org/2/extending/embedding.html
38 * https://docs.python.org/2/extending/extending.html#extending-python-with-c-or-c
39 * https://docs.python.org/2/extending/extending.html#calling-python-functions-from-c
40 */
41
42static PyObject *module_ipaddress;
43static PyObject *ipv4_address;
44static PyObject *ipv6_address;
45static PyObject *spoa_error;
Gilchrist Dadaglo042f6972020-12-08 14:37:13 +000046static PyObject *empty_tuple;
Thierry FOURNIER00a02252018-02-25 20:59:57 +010047static struct worker *worker;
48
49static int ps_python_start_worker(struct worker *w);
50static int ps_python_load_file(struct worker *w, const char *file);
51static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
52
53static struct ps ps_python_bindings = {
54 .init_worker = ps_python_start_worker,
55 .load_file = ps_python_load_file,
56 .exec_message = ps_python_exec_message,
57 .ext = ".py",
58};
59
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000060static int ps_python_check_overflow(Py_ssize_t len)
61{
62 /* There might be an overflow when converting from Py_ssize_t to int.
63 * This function will catch those cases.
64 * Also, spoa "struct chunk" is limited to int size.
65 * We should not send data bigger than it can handle.
66 */
67 if (len >= (Py_ssize_t)INT_MAX) {
68 PyErr_Format(spoa_error,
William Dauchybbe18ac2020-08-01 16:28:51 +020069 "%zd is over 2GB. Please split in smaller pieces.", \
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000070 len);
71 return -1;
72 } else {
73 return Py_SAFE_DOWNCAST(len, Py_ssize_t, int);
74 }
75}
76
77#if IS_PYTHON_3K
78static PyObject *module_spoa;
79static PyObject *PyInit_spoa_module(void);
80#endif /* IS_PYTHON_3K */
81
Thierry FOURNIER00a02252018-02-25 20:59:57 +010082static PyObject *ps_python_register_message(PyObject *self, PyObject *args)
83{
84 const char *name;
85 PyObject *ref;
86
87 if (!PyArg_ParseTuple(args, "sO!", &name, &PyFunction_Type, &ref))
88 return NULL;
Ilya Shipitsince7b00f2020-03-23 22:28:40 +050089 Py_XINCREF(ref); /* because the function is internally referenced */
Thierry FOURNIER00a02252018-02-25 20:59:57 +010090
91 ps_register_message(&ps_python_bindings, name, (void *)ref);
92
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +000093 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +010094}
95
96static PyObject *ps_python_set_var_null(PyObject *self, PyObject *args)
97{
98 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000099 Py_ssize_t name_len;
100 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100101 int scope;
102
103 if (!PyArg_ParseTuple(args, "s#i", &name, &name_len, &scope))
104 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000105 name_len_i = ps_python_check_overflow(name_len);
106 if (name_len_i == -1)
107 return NULL;
108 if (!set_var_null(worker, name, name_len_i, scope)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000109 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100110 return NULL;
111 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000112 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100113}
114
115static PyObject *ps_python_set_var_boolean(PyObject *self, PyObject *args)
116{
117 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000118 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100119 int scope;
120 int value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000121 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100122
123 if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
124 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000125 name_len_i = ps_python_check_overflow(name_len);
126 if (name_len_i == -1)
127 return NULL;
128 if (!set_var_bool(worker, name, name_len_i, scope, value)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000129 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100130 return NULL;
131 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000132 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100133}
134
135static PyObject *ps_python_set_var_int32(PyObject *self, PyObject *args)
136{
137 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000138 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100139 int scope;
140 int32_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000141 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100142
143 if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
144 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000145 name_len_i = ps_python_check_overflow(name_len);
146 if (name_len_i == -1)
147 return NULL;
148 if (!set_var_int32(worker, name, name_len_i, scope, value)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000149 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100150 return NULL;
151 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000152 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100153}
154
155static PyObject *ps_python_set_var_uint32(PyObject *self, PyObject *args)
156{
157 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000158 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100159 int scope;
160 uint32_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000161 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100162
163 if (!PyArg_ParseTuple(args, "s#iI", &name, &name_len, &scope, &value))
164 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000165 name_len_i = ps_python_check_overflow(name_len);
166 if (name_len_i == -1)
167 return NULL;
168 if (!set_var_uint32(worker, name, name_len_i, scope, value)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000169 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100170 return NULL;
171 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000172 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100173}
174
175static PyObject *ps_python_set_var_int64(PyObject *self, PyObject *args)
176{
177 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000178 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100179 int scope;
180 int64_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000181 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100182
183 if (!PyArg_ParseTuple(args, "s#il", &name, &name_len, &scope, &value))
184 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000185 name_len_i = ps_python_check_overflow(name_len);
186 if (name_len_i == -1)
187 return NULL;
188 if (!set_var_int64(worker, name, name_len_i, scope, value)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000189 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100190 return NULL;
191 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000192 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100193}
194
195static PyObject *ps_python_set_var_uint64(PyObject *self, PyObject *args)
196{
197 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000198 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100199 int scope;
200 uint64_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000201 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100202
203 if (!PyArg_ParseTuple(args, "s#ik", &name, &name_len, &scope, &value))
204 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000205 name_len_i = ps_python_check_overflow(name_len);
206 if (name_len_i == -1)
207 return NULL;
208 if (!set_var_uint64(worker, name, name_len_i, scope, value)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000209 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100210 return NULL;
211 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000212 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100213}
214
215static PyObject *ps_python_set_var_ipv4(PyObject *self, PyObject *args)
216{
217 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000218 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100219 int scope;
220 PyObject *ipv4;
221 PyObject *value;
222 struct in_addr ip;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000223 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100224
225 if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv4))
226 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000227 name_len_i = ps_python_check_overflow(name_len);
228 if (name_len_i == -1)
229 return NULL;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100230 if (!PyObject_IsInstance(ipv4, ipv4_address)) {
231 PyErr_Format(spoa_error, "must be 'IPv4Address', not '%s'", ipv4->ob_type->tp_name);
232 return NULL;
233 }
234 /* Execute packed ... I think .. */
235 value = PyObject_GetAttrString(ipv4, "packed");
236 if (value == NULL)
237 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000238 if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
Gilchrist Dadaglo68847422020-12-08 14:37:08 +0000239 PyErr_Format(spoa_error, "IPv4 manipulation internal error");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100240 return NULL;
241 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000242 memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
243 if (!set_var_ipv4(worker, name, name_len_i, scope, &ip)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000244 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100245 return NULL;
246 }
Gilchrist Dadaglo9f0c9842020-08-24 19:21:32 +0000247 /* Once we set the IP value in the worker, we don't need it anymore... */
248 Py_XDECREF(value);
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000249 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100250}
251
252static PyObject *ps_python_set_var_ipv6(PyObject *self, PyObject *args)
253{
254 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000255 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100256 int scope;
257 PyObject *ipv6;
258 PyObject *value;
259 struct in6_addr ip;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000260 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100261
262 if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv6))
263 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000264 name_len_i = ps_python_check_overflow(name_len);
265 if (name_len_i == -1)
266 return NULL;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100267 if (!PyObject_IsInstance(ipv6, ipv6_address)) {
268 PyErr_Format(spoa_error, "must be 'IPv6Address', not '%s'", ipv6->ob_type->tp_name);
269 return NULL;
270 }
271 /* Execute packed ... I think .. */
272 value = PyObject_GetAttrString(ipv6, "packed");
273 if (value == NULL)
274 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000275 if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
Gilchrist Dadaglo68847422020-12-08 14:37:08 +0000276 PyErr_Format(spoa_error, "IPv6 manipulation internal error");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100277 return NULL;
278 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000279 memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
280 if (!set_var_ipv6(worker, name, name_len_i, scope, &ip)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000281 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100282 return NULL;
283 }
Gilchrist Dadaglo9f0c9842020-08-24 19:21:32 +0000284 /* Once we set the IP value in the worker, we don't need it anymore... */
285 Py_XDECREF(value);
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000286 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100287}
288
289static PyObject *ps_python_set_var_str(PyObject *self, PyObject *args)
290{
291 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000292 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100293 int scope;
294 const char *value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000295 Py_ssize_t value_len;
296 int name_len_i;
297 int value_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100298
299 if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
300 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000301 name_len_i = ps_python_check_overflow(name_len);
302 value_len_i = ps_python_check_overflow(value_len);
303 if (name_len_i == -1 || value_len_i == -1)
304 return NULL;
305 if (!set_var_string(worker, name, name_len_i, scope, value, value_len_i)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000306 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100307 return NULL;
308 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000309 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100310}
311
312static PyObject *ps_python_set_var_bin(PyObject *self, PyObject *args)
313{
314 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000315 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100316 int scope;
317 const char *value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000318 Py_ssize_t value_len;
319 int name_len_i;
320 int value_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100321
322 if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
323 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000324 name_len_i = ps_python_check_overflow(name_len);
325 value_len_i = ps_python_check_overflow(value_len);
326 if (name_len_i == -1 || value_len_i == -1)
327 return NULL;
328 if (!set_var_bin(worker, name, name_len_i, scope, value, value_len_i)) {
Gilchrist Dadaglo85b25822020-12-08 14:37:09 +0000329 PyErr_SetString(spoa_error, "No more memory space available");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100330 return NULL;
331 }
Gilchrist Dadaglod5c428e2020-12-08 14:37:07 +0000332 Py_RETURN_NONE;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100333}
334
335
336static PyMethodDef spoa_methods[] = {
337 {"register_message", ps_python_register_message, METH_VARARGS,
338 "Register binding for SPOA message."},
339 {"set_var_null", ps_python_set_var_null, METH_VARARGS,
340 "Set SPOA NULL variable"},
341 {"set_var_boolean", ps_python_set_var_boolean, METH_VARARGS,
342 "Set SPOA boolean variable"},
343 {"set_var_int32", ps_python_set_var_int32, METH_VARARGS,
344 "Set SPOA int32 variable"},
345 {"set_var_uint32", ps_python_set_var_uint32, METH_VARARGS,
346 "Set SPOA uint32 variable"},
347 {"set_var_int64", ps_python_set_var_int64, METH_VARARGS,
348 "Set SPOA int64 variable"},
349 {"set_var_uint64", ps_python_set_var_uint64, METH_VARARGS,
350 "Set SPOA uint64 variable"},
351 {"set_var_ipv4", ps_python_set_var_ipv4, METH_VARARGS,
352 "Set SPOA ipv4 variable"},
353 {"set_var_ipv6", ps_python_set_var_ipv6, METH_VARARGS,
354 "Set SPOA ipv6 variable"},
355 {"set_var_str", ps_python_set_var_str, METH_VARARGS,
356 "Set SPOA str variable"},
357 {"set_var_bin", ps_python_set_var_bin, METH_VARARGS,
358 "Set SPOA bin variable"},
359 { /* end */ }
360};
361
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000362#if IS_PYTHON_3K
363static struct PyModuleDef spoa_module_definition = {
364 PyModuleDef_HEAD_INIT, /* m_base */
365 "spoa", /* m_name */
366 "HAProxy SPOA module for python", /* m_doc */
367 -1, /* m_size */
368 spoa_methods, /* m_methods */
369 NULL, /* m_slots */
370 NULL, /* m_traverse */
371 NULL, /* m_clear */
372 NULL /* m_free */
373};
374
375static PyObject *PyInit_spoa_module(void)
376{
377 return module_spoa;
378}
379#endif /* IS_PYTHON_3K */
380
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100381static int ps_python_start_worker(struct worker *w)
382{
383 PyObject *m;
384 PyObject *module_name;
385 PyObject *value;
386 int ret;
387
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000388#if IS_PYTHON_27
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100389 Py_SetProgramName("spoa-server");
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000390#endif /* IS_PYTHON_27 */
391#if IS_PYTHON_3K
392 Py_SetProgramName(Py_DecodeLocale("spoa-server", NULL));
393 PyImport_AppendInittab("spoa", &PyInit_spoa_module);
394#endif /* IS_PYTHON_3K */
395
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100396 Py_Initialize();
397
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000398 module_name = PY_STRING_FROM_STRING("ipaddress");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100399 if (module_name == NULL) {
400 PyErr_Print();
401 return 0;
402 }
403
404 module_ipaddress = PyImport_Import(module_name);
405 Py_DECREF(module_name);
406 if (module_ipaddress == NULL) {
407 PyErr_Print();
408 return 0;
409 }
410
411 ipv4_address = PyObject_GetAttrString(module_ipaddress, "IPv4Address");
412 if (ipv4_address == NULL) {
Gilchrist Dadaglod1c0cf82020-12-08 14:37:12 +0000413 Py_DECREF(module_ipaddress);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100414 PyErr_Print();
415 return 0;
416 }
417
418 ipv6_address = PyObject_GetAttrString(module_ipaddress, "IPv6Address");
Gilchrist Dadaglod00ce062020-08-24 19:21:35 +0000419 if (ipv6_address == NULL) {
Gilchrist Dadaglod1c0cf82020-12-08 14:37:12 +0000420 Py_DECREF(ipv4_address);
421 Py_DECREF(module_ipaddress);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100422 PyErr_Print();
423 return 0;
424 }
425
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000426 PY_INIT_MODULE(m, "spoa", spoa_methods, &spoa_module_definition);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100427 if (m == NULL) {
Gilchrist Dadaglod1c0cf82020-12-08 14:37:12 +0000428 Py_DECREF(ipv4_address);
429 Py_DECREF(ipv6_address);
430 Py_DECREF(module_ipaddress);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100431 PyErr_Print();
432 return 0;
433 }
434
435 spoa_error = PyErr_NewException("spoa.error", NULL, NULL);
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000436 /* PyModule_AddObject will steal the reference to spoa_error
437 * in case of success only
438 * We need to increment the counters to continue using it
439 * but cleanup in case of failure
440 */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100441 Py_INCREF(spoa_error);
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000442 ret = PyModule_AddObject(m, "error", spoa_error);
443 if (ret == -1) {
444 Py_DECREF(m);
445 Py_DECREF(spoa_error);
446 PyErr_Print();
447 return 0;
448 }
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100449
450
451 value = PyLong_FromLong(SPOE_SCOPE_PROC);
452 if (value == NULL) {
453 PyErr_Print();
454 return 0;
455 }
456
457 ret = PyModule_AddObject(m, "scope_proc", value);
458 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000459 Py_DECREF(m);
460 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100461 PyErr_Print();
462 return 0;
463 }
464
465 value = PyLong_FromLong(SPOE_SCOPE_SESS);
466 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000467 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100468 PyErr_Print();
469 return 0;
470 }
471
472 ret = PyModule_AddObject(m, "scope_sess", value);
473 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000474 Py_DECREF(m);
475 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100476 PyErr_Print();
477 return 0;
478 }
479
480 value = PyLong_FromLong(SPOE_SCOPE_TXN);
481 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000482 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100483 PyErr_Print();
484 return 0;
485 }
486
487 ret = PyModule_AddObject(m, "scope_txn", value);
488 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000489 Py_DECREF(m);
490 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100491 PyErr_Print();
492 return 0;
493 }
494
495 value = PyLong_FromLong(SPOE_SCOPE_REQ);
496 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000497 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100498 PyErr_Print();
499 return 0;
500 }
501
502 ret = PyModule_AddObject(m, "scope_req", value);
503 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000504 Py_DECREF(m);
505 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100506 PyErr_Print();
507 return 0;
508 }
509
510 value = PyLong_FromLong(SPOE_SCOPE_RES);
511 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000512 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100513 PyErr_Print();
514 return 0;
515 }
516
517 ret = PyModule_AddObject(m, "scope_res", value);
518 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000519 Py_DECREF(m);
520 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100521 PyErr_Print();
522 return 0;
523 }
524
Gilchrist Dadaglo042f6972020-12-08 14:37:13 +0000525 empty_tuple = PyTuple_New(0);
526 if (empty_tuple == NULL) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100527 PyErr_Print();
528 return 0;
529 }
530
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000531#if IS_PYTHON_3K
532 module_spoa = m;
533#endif /* IS_PYTHON_3K */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100534 worker = w;
535 return 1;
536}
537
538static int ps_python_load_file(struct worker *w, const char *file)
539{
540 FILE *fp;
541 int ret;
542
543 fp = fopen(file, "r");
544 if (fp == NULL) {
545 LOG("python: Cannot read file \"%s\": %s", file, strerror(errno));
546 return 0;
547 }
548
549 ret = PyRun_SimpleFile(fp, file);
550 fclose(fp);
551 if (ret != 0) {
552 PyErr_Print();
553 return 0;
554 }
555
556 return 1;
557}
558
559static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
560{
561 int i;
562 PyObject *python_ref = ref;
563 PyObject *fkw;
564 PyObject *kw_args;
565 PyObject *result;
566 PyObject *ent;
567 PyObject *key;
568 PyObject *value;
569 PyObject *func;
570 int ret;
571 char ipbuf[64];
572 const char *p;
573 PyObject *ip_dict;
574 PyObject *ip_name;
575 PyObject *ip_value;
576
577 /* Dict containing arguments */
578
579 kw_args = PyList_New(0);
580 if (kw_args == NULL) {
581 PyErr_Print();
582 return 0;
583 }
584
585 for (i = 0; i < nargs; i++) {
586
587 /* New dict containing one argument */
588
589 ent = PyDict_New();
590 if (ent == NULL) {
591 Py_DECREF(kw_args);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100592 PyErr_Print();
593 return 0;
594 }
595
596 /* Create the name entry */
597
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000598 key = PY_STRING_FROM_STRING("name");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100599 if (key == NULL) {
600 Py_DECREF(kw_args);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000601 Py_DECREF(ent);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100602 PyErr_Print();
603 return 0;
604 }
605
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000606 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].name.str, args[i].name.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100607 if (value == NULL) {
608 Py_DECREF(kw_args);
609 Py_DECREF(ent);
610 Py_DECREF(key);
611 PyErr_Print();
612 return 0;
613 }
614
615 ret = PyDict_SetItem(ent, key, value);
616 Py_DECREF(key);
617 Py_DECREF(value);
618 if (ret == -1) {
619 Py_DECREF(kw_args);
620 Py_DECREF(ent);
621 PyErr_Print();
622 return 0;
623 }
624
Gilchrist Dadagloc7485ac2020-12-08 14:37:10 +0000625 /* Create the value entry */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100626
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000627 key = PY_STRING_FROM_STRING("value");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100628 if (key == NULL) {
629 Py_DECREF(kw_args);
630 Py_DECREF(ent);
631 PyErr_Print();
632 return 0;
633 }
634
635 switch (args[i].value.type) {
636 case SPOE_DATA_T_NULL:
637 value = Py_None;
638 break;
639 case SPOE_DATA_T_BOOL:
640 value = PyBool_FromLong(args[i].value.u.boolean);
641 break;
642 case SPOE_DATA_T_INT32:
643 value = PyLong_FromLong(args[i].value.u.sint32);
644 break;
645 case SPOE_DATA_T_UINT32:
646 value = PyLong_FromLong(args[i].value.u.uint32);
647 break;
648 case SPOE_DATA_T_INT64:
649 value = PyLong_FromLong(args[i].value.u.sint64);
650 break;
651 case SPOE_DATA_T_UINT64:
652 value = PyLong_FromUnsignedLong(args[i].value.u.uint64);
653 break;
654 case SPOE_DATA_T_IPV4:
655 case SPOE_DATA_T_IPV6:
656 if (args[i].value.type == SPOE_DATA_T_IPV4)
657 p = inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64);
658 else
659 p = inet_ntop(AF_INET6, &args[i].value.u.ipv6, ipbuf, 64);
660 if (!p)
661 strcpy(ipbuf, "0.0.0.0");
662
663 func = PyObject_GetAttrString(module_ipaddress, "ip_address");
664 if (func == NULL) {
665 Py_DECREF(kw_args);
666 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000667 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100668 PyErr_Print();
669 return 0;
670 }
671 ip_dict = PyDict_New();
672 if (ip_dict == NULL) {
673 Py_DECREF(kw_args);
674 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000675 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100676 Py_DECREF(func);
677 PyErr_Print();
678 return 0;
679 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000680 ip_name = PY_STRING_FROM_STRING("address");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100681 if (ip_name == NULL) {
682 Py_DECREF(kw_args);
683 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000684 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100685 Py_DECREF(func);
686 Py_DECREF(ip_dict);
687 PyErr_Print();
688 return 0;
689 }
690 ip_value = PyUnicode_FromString(ipbuf);
691 if (ip_value == NULL) {
692 Py_DECREF(kw_args);
693 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000694 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100695 Py_DECREF(func);
696 Py_DECREF(ip_dict);
697 Py_DECREF(ip_name);
698 PyErr_Print();
699 return 0;
700 }
701 ret = PyDict_SetItem(ip_dict, ip_name, ip_value);
702 Py_DECREF(ip_name);
703 Py_DECREF(ip_value);
704 if (ret == -1) {
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000705 Py_DECREF(kw_args);
706 Py_DECREF(ent);
707 Py_DECREF(key);
708 Py_DECREF(func);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100709 Py_DECREF(ip_dict);
710 PyErr_Print();
711 return 0;
712 }
Gilchrist Dadaglo042f6972020-12-08 14:37:13 +0000713 value = PyObject_Call(func, empty_tuple, ip_dict);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100714 Py_DECREF(func);
715 Py_DECREF(ip_dict);
716 break;
717
718 case SPOE_DATA_T_STR:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000719 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100720 break;
721 case SPOE_DATA_T_BIN:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000722 value = PY_BYTES_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100723 break;
724 default:
725 value = Py_None;
726 break;
727 }
728 if (value == NULL) {
729 Py_DECREF(kw_args);
730 Py_DECREF(ent);
731 Py_DECREF(key);
732 PyErr_Print();
733 return 0;
734 }
735
736 ret = PyDict_SetItem(ent, key, value);
737 Py_DECREF(key);
738 Py_DECREF(value);
739 if (ret == -1) {
740 Py_DECREF(kw_args);
741 Py_DECREF(ent);
742 PyErr_Print();
743 return 0;
744 }
745
746 /* Add dict to the list */
747
748 ret = PyList_Append(kw_args, ent);
749 Py_DECREF(ent);
750 if (ret == -1) {
751 Py_DECREF(kw_args);
752 PyErr_Print();
753 return 0;
754 }
755 }
756
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500757 /* Dictionary { args = <list-of-args> } for the function */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100758
759 fkw = PyDict_New();
760 if (fkw == NULL) {
761 Py_DECREF(kw_args);
762 PyErr_Print();
763 return 0;
764 }
765
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000766 key = PY_STRING_FROM_STRING("args");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100767 if (key == NULL) {
768 Py_DECREF(kw_args);
769 Py_DECREF(fkw);
770 PyErr_Print();
771 return 0;
772 }
773
774 ret = PyDict_SetItem(fkw, key, kw_args);
775 Py_DECREF(kw_args);
776 Py_DECREF(key);
777 if (ret == -1) {
778 Py_DECREF(fkw);
779 PyErr_Print();
780 return 0;
781 }
782
Gilchrist Dadaglo042f6972020-12-08 14:37:13 +0000783 result = PyObject_Call(python_ref, empty_tuple, fkw);
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000784 Py_DECREF(fkw);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100785 if (result == NULL) {
786 PyErr_Print();
787 return 0;
788 }
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000789 if (result != Py_None) {
790 Py_DECREF(result);
791 }
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100792
793 return 1;
794}
795
796__attribute__((constructor))
797static void __ps_python_init(void)
798{
799 ps_register(&ps_python_bindings);
800}