blob: 12953f38045804cc5c60b14fb263eae565bdae4e [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;
46static PyObject *empty_array;
47static 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) {
413 PyErr_Print();
414 return 0;
415 }
416
417 ipv6_address = PyObject_GetAttrString(module_ipaddress, "IPv6Address");
Gilchrist Dadaglod00ce062020-08-24 19:21:35 +0000418 if (ipv6_address == NULL) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100419 PyErr_Print();
420 return 0;
421 }
422
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000423 PY_INIT_MODULE(m, "spoa", spoa_methods, &spoa_module_definition);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100424 if (m == NULL) {
425 PyErr_Print();
426 return 0;
427 }
428
429 spoa_error = PyErr_NewException("spoa.error", NULL, NULL);
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000430 /* PyModule_AddObject will steal the reference to spoa_error
431 * in case of success only
432 * We need to increment the counters to continue using it
433 * but cleanup in case of failure
434 */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100435 Py_INCREF(spoa_error);
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000436 ret = PyModule_AddObject(m, "error", spoa_error);
437 if (ret == -1) {
438 Py_DECREF(m);
439 Py_DECREF(spoa_error);
440 PyErr_Print();
441 return 0;
442 }
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100443
444
445 value = PyLong_FromLong(SPOE_SCOPE_PROC);
446 if (value == NULL) {
447 PyErr_Print();
448 return 0;
449 }
450
451 ret = PyModule_AddObject(m, "scope_proc", value);
452 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000453 Py_DECREF(m);
454 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100455 PyErr_Print();
456 return 0;
457 }
458
459 value = PyLong_FromLong(SPOE_SCOPE_SESS);
460 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000461 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100462 PyErr_Print();
463 return 0;
464 }
465
466 ret = PyModule_AddObject(m, "scope_sess", value);
467 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000468 Py_DECREF(m);
469 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100470 PyErr_Print();
471 return 0;
472 }
473
474 value = PyLong_FromLong(SPOE_SCOPE_TXN);
475 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000476 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100477 PyErr_Print();
478 return 0;
479 }
480
481 ret = PyModule_AddObject(m, "scope_txn", value);
482 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000483 Py_DECREF(m);
484 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100485 PyErr_Print();
486 return 0;
487 }
488
489 value = PyLong_FromLong(SPOE_SCOPE_REQ);
490 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000491 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100492 PyErr_Print();
493 return 0;
494 }
495
496 ret = PyModule_AddObject(m, "scope_req", value);
497 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000498 Py_DECREF(m);
499 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100500 PyErr_Print();
501 return 0;
502 }
503
504 value = PyLong_FromLong(SPOE_SCOPE_RES);
505 if (value == NULL) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000506 Py_DECREF(m);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100507 PyErr_Print();
508 return 0;
509 }
510
511 ret = PyModule_AddObject(m, "scope_res", value);
512 if (ret == -1) {
Gilchrist Dadaglo132d8f62020-12-08 14:37:11 +0000513 Py_DECREF(m);
514 Py_DECREF(value);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100515 PyErr_Print();
516 return 0;
517 }
518
519 empty_array = PyDict_New();
520 if (empty_array == NULL) {
521 PyErr_Print();
522 return 0;
523 }
524
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000525#if IS_PYTHON_3K
526 module_spoa = m;
527#endif /* IS_PYTHON_3K */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100528 worker = w;
529 return 1;
530}
531
532static int ps_python_load_file(struct worker *w, const char *file)
533{
534 FILE *fp;
535 int ret;
536
537 fp = fopen(file, "r");
538 if (fp == NULL) {
539 LOG("python: Cannot read file \"%s\": %s", file, strerror(errno));
540 return 0;
541 }
542
543 ret = PyRun_SimpleFile(fp, file);
544 fclose(fp);
545 if (ret != 0) {
546 PyErr_Print();
547 return 0;
548 }
549
550 return 1;
551}
552
553static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
554{
555 int i;
556 PyObject *python_ref = ref;
557 PyObject *fkw;
558 PyObject *kw_args;
559 PyObject *result;
560 PyObject *ent;
561 PyObject *key;
562 PyObject *value;
563 PyObject *func;
564 int ret;
565 char ipbuf[64];
566 const char *p;
567 PyObject *ip_dict;
568 PyObject *ip_name;
569 PyObject *ip_value;
570
571 /* Dict containing arguments */
572
573 kw_args = PyList_New(0);
574 if (kw_args == NULL) {
575 PyErr_Print();
576 return 0;
577 }
578
579 for (i = 0; i < nargs; i++) {
580
581 /* New dict containing one argument */
582
583 ent = PyDict_New();
584 if (ent == NULL) {
585 Py_DECREF(kw_args);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100586 PyErr_Print();
587 return 0;
588 }
589
590 /* Create the name entry */
591
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000592 key = PY_STRING_FROM_STRING("name");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100593 if (key == NULL) {
594 Py_DECREF(kw_args);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000595 Py_DECREF(ent);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100596 PyErr_Print();
597 return 0;
598 }
599
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000600 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].name.str, args[i].name.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100601 if (value == NULL) {
602 Py_DECREF(kw_args);
603 Py_DECREF(ent);
604 Py_DECREF(key);
605 PyErr_Print();
606 return 0;
607 }
608
609 ret = PyDict_SetItem(ent, key, value);
610 Py_DECREF(key);
611 Py_DECREF(value);
612 if (ret == -1) {
613 Py_DECREF(kw_args);
614 Py_DECREF(ent);
615 PyErr_Print();
616 return 0;
617 }
618
Gilchrist Dadagloc7485ac2020-12-08 14:37:10 +0000619 /* Create the value entry */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100620
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000621 key = PY_STRING_FROM_STRING("value");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100622 if (key == NULL) {
623 Py_DECREF(kw_args);
624 Py_DECREF(ent);
625 PyErr_Print();
626 return 0;
627 }
628
629 switch (args[i].value.type) {
630 case SPOE_DATA_T_NULL:
631 value = Py_None;
632 break;
633 case SPOE_DATA_T_BOOL:
634 value = PyBool_FromLong(args[i].value.u.boolean);
635 break;
636 case SPOE_DATA_T_INT32:
637 value = PyLong_FromLong(args[i].value.u.sint32);
638 break;
639 case SPOE_DATA_T_UINT32:
640 value = PyLong_FromLong(args[i].value.u.uint32);
641 break;
642 case SPOE_DATA_T_INT64:
643 value = PyLong_FromLong(args[i].value.u.sint64);
644 break;
645 case SPOE_DATA_T_UINT64:
646 value = PyLong_FromUnsignedLong(args[i].value.u.uint64);
647 break;
648 case SPOE_DATA_T_IPV4:
649 case SPOE_DATA_T_IPV6:
650 if (args[i].value.type == SPOE_DATA_T_IPV4)
651 p = inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64);
652 else
653 p = inet_ntop(AF_INET6, &args[i].value.u.ipv6, ipbuf, 64);
654 if (!p)
655 strcpy(ipbuf, "0.0.0.0");
656
657 func = PyObject_GetAttrString(module_ipaddress, "ip_address");
658 if (func == NULL) {
659 Py_DECREF(kw_args);
660 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000661 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100662 PyErr_Print();
663 return 0;
664 }
665 ip_dict = PyDict_New();
666 if (ip_dict == NULL) {
667 Py_DECREF(kw_args);
668 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000669 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100670 Py_DECREF(func);
671 PyErr_Print();
672 return 0;
673 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000674 ip_name = PY_STRING_FROM_STRING("address");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100675 if (ip_name == NULL) {
676 Py_DECREF(kw_args);
677 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000678 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100679 Py_DECREF(func);
680 Py_DECREF(ip_dict);
681 PyErr_Print();
682 return 0;
683 }
684 ip_value = PyUnicode_FromString(ipbuf);
685 if (ip_value == NULL) {
686 Py_DECREF(kw_args);
687 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000688 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100689 Py_DECREF(func);
690 Py_DECREF(ip_dict);
691 Py_DECREF(ip_name);
692 PyErr_Print();
693 return 0;
694 }
695 ret = PyDict_SetItem(ip_dict, ip_name, ip_value);
696 Py_DECREF(ip_name);
697 Py_DECREF(ip_value);
698 if (ret == -1) {
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000699 Py_DECREF(kw_args);
700 Py_DECREF(ent);
701 Py_DECREF(key);
702 Py_DECREF(func);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100703 Py_DECREF(ip_dict);
704 PyErr_Print();
705 return 0;
706 }
707 value = PyObject_Call(func, empty_array, ip_dict);
708 Py_DECREF(func);
709 Py_DECREF(ip_dict);
710 break;
711
712 case SPOE_DATA_T_STR:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000713 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 +0100714 break;
715 case SPOE_DATA_T_BIN:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000716 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 +0100717 break;
718 default:
719 value = Py_None;
720 break;
721 }
722 if (value == NULL) {
723 Py_DECREF(kw_args);
724 Py_DECREF(ent);
725 Py_DECREF(key);
726 PyErr_Print();
727 return 0;
728 }
729
730 ret = PyDict_SetItem(ent, key, value);
731 Py_DECREF(key);
732 Py_DECREF(value);
733 if (ret == -1) {
734 Py_DECREF(kw_args);
735 Py_DECREF(ent);
736 PyErr_Print();
737 return 0;
738 }
739
740 /* Add dict to the list */
741
742 ret = PyList_Append(kw_args, ent);
743 Py_DECREF(ent);
744 if (ret == -1) {
745 Py_DECREF(kw_args);
746 PyErr_Print();
747 return 0;
748 }
749 }
750
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500751 /* Dictionary { args = <list-of-args> } for the function */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100752
753 fkw = PyDict_New();
754 if (fkw == NULL) {
755 Py_DECREF(kw_args);
756 PyErr_Print();
757 return 0;
758 }
759
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000760 key = PY_STRING_FROM_STRING("args");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100761 if (key == NULL) {
762 Py_DECREF(kw_args);
763 Py_DECREF(fkw);
764 PyErr_Print();
765 return 0;
766 }
767
768 ret = PyDict_SetItem(fkw, key, kw_args);
769 Py_DECREF(kw_args);
770 Py_DECREF(key);
771 if (ret == -1) {
772 Py_DECREF(fkw);
773 PyErr_Print();
774 return 0;
775 }
776
777 result = PyObject_Call(python_ref, empty_array, fkw);
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000778 Py_DECREF(fkw);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100779 if (result == NULL) {
780 PyErr_Print();
781 return 0;
782 }
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000783 if (result != Py_None) {
784 Py_DECREF(result);
785 }
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100786
787 return 1;
788}
789
790__attribute__((constructor))
791static void __ps_python_init(void)
792{
793 ps_register(&ps_python_bindings);
794}