blob: 5cb7ca86e230be47feb13b9bf52de84f3593852a [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
93 return Py_None;
94}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100109 PyErr_SetString(spoa_error, "No space left available");
110 return NULL;
111 }
112 return Py_None;
113}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100129 PyErr_SetString(spoa_error, "No space left available");
130 return NULL;
131 }
132 return Py_None;
133}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100149 PyErr_SetString(spoa_error, "No space left available");
150 return NULL;
151 }
152 return Py_None;
153}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100169 PyErr_SetString(spoa_error, "No space left available");
170 return NULL;
171 }
172 return Py_None;
173}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100189 PyErr_SetString(spoa_error, "No space left available");
190 return NULL;
191 }
192 return Py_None;
193}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100209 PyErr_SetString(spoa_error, "No space left available");
210 return NULL;
211 }
212 return Py_None;
213}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100239 PyErr_Format(spoa_error, "UPv6 manipulation internal error");
240 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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100244 PyErr_SetString(spoa_error, "No space left available");
245 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);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100249 return Py_None;
250}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100276 PyErr_Format(spoa_error, "UPv6 manipulation internal error");
277 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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100281 PyErr_SetString(spoa_error, "No space left available");
282 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);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100286 return Py_None;
287}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100306 PyErr_SetString(spoa_error, "No space left available");
307 return NULL;
308 }
309 return Py_None;
310}
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)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100329 PyErr_SetString(spoa_error, "No space left available");
330 return NULL;
331 }
332 return Py_None;
333}
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);
430 Py_INCREF(spoa_error);
431 PyModule_AddObject(m, "error", spoa_error);
432
433
434 value = PyLong_FromLong(SPOE_SCOPE_PROC);
435 if (value == NULL) {
436 PyErr_Print();
437 return 0;
438 }
439
440 ret = PyModule_AddObject(m, "scope_proc", value);
441 if (ret == -1) {
442 PyErr_Print();
443 return 0;
444 }
445
446 value = PyLong_FromLong(SPOE_SCOPE_SESS);
447 if (value == NULL) {
448 PyErr_Print();
449 return 0;
450 }
451
452 ret = PyModule_AddObject(m, "scope_sess", value);
453 if (ret == -1) {
454 PyErr_Print();
455 return 0;
456 }
457
458 value = PyLong_FromLong(SPOE_SCOPE_TXN);
459 if (value == NULL) {
460 PyErr_Print();
461 return 0;
462 }
463
464 ret = PyModule_AddObject(m, "scope_txn", value);
465 if (ret == -1) {
466 PyErr_Print();
467 return 0;
468 }
469
470 value = PyLong_FromLong(SPOE_SCOPE_REQ);
471 if (value == NULL) {
472 PyErr_Print();
473 return 0;
474 }
475
476 ret = PyModule_AddObject(m, "scope_req", value);
477 if (ret == -1) {
478 PyErr_Print();
479 return 0;
480 }
481
482 value = PyLong_FromLong(SPOE_SCOPE_RES);
483 if (value == NULL) {
484 PyErr_Print();
485 return 0;
486 }
487
488 ret = PyModule_AddObject(m, "scope_res", value);
489 if (ret == -1) {
490 PyErr_Print();
491 return 0;
492 }
493
494 empty_array = PyDict_New();
495 if (empty_array == NULL) {
496 PyErr_Print();
497 return 0;
498 }
499
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000500#if IS_PYTHON_3K
501 module_spoa = m;
502#endif /* IS_PYTHON_3K */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100503 worker = w;
504 return 1;
505}
506
507static int ps_python_load_file(struct worker *w, const char *file)
508{
509 FILE *fp;
510 int ret;
511
512 fp = fopen(file, "r");
513 if (fp == NULL) {
514 LOG("python: Cannot read file \"%s\": %s", file, strerror(errno));
515 return 0;
516 }
517
518 ret = PyRun_SimpleFile(fp, file);
519 fclose(fp);
520 if (ret != 0) {
521 PyErr_Print();
522 return 0;
523 }
524
525 return 1;
526}
527
528static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
529{
530 int i;
531 PyObject *python_ref = ref;
532 PyObject *fkw;
533 PyObject *kw_args;
534 PyObject *result;
535 PyObject *ent;
536 PyObject *key;
537 PyObject *value;
538 PyObject *func;
539 int ret;
540 char ipbuf[64];
541 const char *p;
542 PyObject *ip_dict;
543 PyObject *ip_name;
544 PyObject *ip_value;
545
546 /* Dict containing arguments */
547
548 kw_args = PyList_New(0);
549 if (kw_args == NULL) {
550 PyErr_Print();
551 return 0;
552 }
553
554 for (i = 0; i < nargs; i++) {
555
556 /* New dict containing one argument */
557
558 ent = PyDict_New();
559 if (ent == NULL) {
560 Py_DECREF(kw_args);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100561 PyErr_Print();
562 return 0;
563 }
564
565 /* Create the name entry */
566
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000567 key = PY_STRING_FROM_STRING("name");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100568 if (key == NULL) {
569 Py_DECREF(kw_args);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000570 Py_DECREF(ent);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100571 PyErr_Print();
572 return 0;
573 }
574
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000575 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].name.str, args[i].name.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100576 if (value == NULL) {
577 Py_DECREF(kw_args);
578 Py_DECREF(ent);
579 Py_DECREF(key);
580 PyErr_Print();
581 return 0;
582 }
583
584 ret = PyDict_SetItem(ent, key, value);
585 Py_DECREF(key);
586 Py_DECREF(value);
587 if (ret == -1) {
588 Py_DECREF(kw_args);
589 Py_DECREF(ent);
590 PyErr_Print();
591 return 0;
592 }
593
594 /* Create th value entry */
595
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000596 key = PY_STRING_FROM_STRING("value");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100597 if (key == NULL) {
598 Py_DECREF(kw_args);
599 Py_DECREF(ent);
600 PyErr_Print();
601 return 0;
602 }
603
604 switch (args[i].value.type) {
605 case SPOE_DATA_T_NULL:
606 value = Py_None;
607 break;
608 case SPOE_DATA_T_BOOL:
609 value = PyBool_FromLong(args[i].value.u.boolean);
610 break;
611 case SPOE_DATA_T_INT32:
612 value = PyLong_FromLong(args[i].value.u.sint32);
613 break;
614 case SPOE_DATA_T_UINT32:
615 value = PyLong_FromLong(args[i].value.u.uint32);
616 break;
617 case SPOE_DATA_T_INT64:
618 value = PyLong_FromLong(args[i].value.u.sint64);
619 break;
620 case SPOE_DATA_T_UINT64:
621 value = PyLong_FromUnsignedLong(args[i].value.u.uint64);
622 break;
623 case SPOE_DATA_T_IPV4:
624 case SPOE_DATA_T_IPV6:
625 if (args[i].value.type == SPOE_DATA_T_IPV4)
626 p = inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64);
627 else
628 p = inet_ntop(AF_INET6, &args[i].value.u.ipv6, ipbuf, 64);
629 if (!p)
630 strcpy(ipbuf, "0.0.0.0");
631
632 func = PyObject_GetAttrString(module_ipaddress, "ip_address");
633 if (func == NULL) {
634 Py_DECREF(kw_args);
635 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000636 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100637 PyErr_Print();
638 return 0;
639 }
640 ip_dict = PyDict_New();
641 if (ip_dict == NULL) {
642 Py_DECREF(kw_args);
643 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000644 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100645 Py_DECREF(func);
646 PyErr_Print();
647 return 0;
648 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000649 ip_name = PY_STRING_FROM_STRING("address");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100650 if (ip_name == NULL) {
651 Py_DECREF(kw_args);
652 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000653 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100654 Py_DECREF(func);
655 Py_DECREF(ip_dict);
656 PyErr_Print();
657 return 0;
658 }
659 ip_value = PyUnicode_FromString(ipbuf);
660 if (ip_value == NULL) {
661 Py_DECREF(kw_args);
662 Py_DECREF(ent);
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000663 Py_DECREF(key);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100664 Py_DECREF(func);
665 Py_DECREF(ip_dict);
666 Py_DECREF(ip_name);
667 PyErr_Print();
668 return 0;
669 }
670 ret = PyDict_SetItem(ip_dict, ip_name, ip_value);
671 Py_DECREF(ip_name);
672 Py_DECREF(ip_value);
673 if (ret == -1) {
Gilchrist Dadaglo2417ebc2020-08-24 19:21:34 +0000674 Py_DECREF(kw_args);
675 Py_DECREF(ent);
676 Py_DECREF(key);
677 Py_DECREF(func);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100678 Py_DECREF(ip_dict);
679 PyErr_Print();
680 return 0;
681 }
682 value = PyObject_Call(func, empty_array, ip_dict);
683 Py_DECREF(func);
684 Py_DECREF(ip_dict);
685 break;
686
687 case SPOE_DATA_T_STR:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000688 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 +0100689 break;
690 case SPOE_DATA_T_BIN:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000691 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 +0100692 break;
693 default:
694 value = Py_None;
695 break;
696 }
697 if (value == NULL) {
698 Py_DECREF(kw_args);
699 Py_DECREF(ent);
700 Py_DECREF(key);
701 PyErr_Print();
702 return 0;
703 }
704
705 ret = PyDict_SetItem(ent, key, value);
706 Py_DECREF(key);
707 Py_DECREF(value);
708 if (ret == -1) {
709 Py_DECREF(kw_args);
710 Py_DECREF(ent);
711 PyErr_Print();
712 return 0;
713 }
714
715 /* Add dict to the list */
716
717 ret = PyList_Append(kw_args, ent);
718 Py_DECREF(ent);
719 if (ret == -1) {
720 Py_DECREF(kw_args);
721 PyErr_Print();
722 return 0;
723 }
724 }
725
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500726 /* Dictionary { args = <list-of-args> } for the function */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100727
728 fkw = PyDict_New();
729 if (fkw == NULL) {
730 Py_DECREF(kw_args);
731 PyErr_Print();
732 return 0;
733 }
734
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000735 key = PY_STRING_FROM_STRING("args");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100736 if (key == NULL) {
737 Py_DECREF(kw_args);
738 Py_DECREF(fkw);
739 PyErr_Print();
740 return 0;
741 }
742
743 ret = PyDict_SetItem(fkw, key, kw_args);
744 Py_DECREF(kw_args);
745 Py_DECREF(key);
746 if (ret == -1) {
747 Py_DECREF(fkw);
748 PyErr_Print();
749 return 0;
750 }
751
752 result = PyObject_Call(python_ref, empty_array, fkw);
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000753 Py_DECREF(fkw);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100754 if (result == NULL) {
755 PyErr_Print();
756 return 0;
757 }
Gilchrist Dadaglo222f0602020-08-24 19:21:31 +0000758 if (result != Py_None) {
759 Py_DECREF(result);
760 }
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100761
762 return 1;
763}
764
765__attribute__((constructor))
766static void __ps_python_init(void)
767{
768 ps_register(&ps_python_bindings);
769}