kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit bbcdec82fc3c26a1b6ed39b7f4f20d412ff6a891
parent 57840a8366d61604ac845de5b21a645bef528f20
Author: Joris Vink <joris@coders.se>
Date:   Thu, 12 Jan 2017 23:38:51 +0100

Add initial python support.

Based on work done by Stanislav Yudin.

Diffstat:
Makefile | 9++++++++-
includes/http.h | 4++++
includes/kore.h | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
includes/python_api.h | 32++++++++++++++++++++++++++++++++
includes/python_methods.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/config.c | 28+++++++++++++++++++++++++++-
src/connection.c | 4++--
src/http.c | 11++++++-----
src/kore.c | 26+++++++++++++++++---------
src/module.c | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
src/python.c | 667+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/runtime.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/validator.c | 13+++++++------
src/worker.c | 24+++++++++++++++++-------
14 files changed, 1223 insertions(+), 103 deletions(-)

diff --git a/Makefile b/Makefile @@ -9,7 +9,8 @@ INCLUDE_DIR=$(PREFIX)/include/kore S_SRC= src/kore.c src/buf.c src/cli.c src/config.c src/connection.c \ src/domain.c src/mem.c src/msg.c src/module.c src/net.c \ - src/pool.c src/timer.c src/utils.c src/worker.c src/keymgr.c + src/pool.c src/runtime.c src/timer.c src/utils.c src/worker.c \ + src/keymgr.c CFLAGS+=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes CFLAGS+=-Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual @@ -63,6 +64,12 @@ ifneq ("$(JSONRPC)", "") CFLAGS+=-DKORE_USE_JSONRPC endif +ifneq ("$(PYTHON)", "") + S_SRC+=src/python.c + LDFLAGS+=$(shell python3-config --ldflags) + CFLAGS+=$(shell python3-config --includes) -DKORE_USE_PYTHON +endif + OSNAME=$(shell uname -s | sed -e 's/[-_].*//g' | tr A-Z a-z) ifeq ("$(OSNAME)", "darwin") CFLAGS+=-I/opt/local/include/ -I/usr/local/opt/openssl/include diff --git a/includes/http.h b/includes/http.h @@ -186,6 +186,10 @@ struct http_request { char *query_string; struct kore_module_handle *hdlr; +#if defined(KORE_USE_PYTHON) + void *py_object; +#endif + LIST_HEAD(, kore_task) tasks; LIST_HEAD(, kore_pgsql) pgsqls; diff --git a/includes/kore.h b/includes/kore.h @@ -207,11 +207,32 @@ TAILQ_HEAD(connection_list, connection); extern struct connection_list connections; extern struct connection_list disconnected; +#define KORE_RUNTIME_NATIVE 0 +#define KORE_RUNTIME_PYTHON 1 + +struct kore_runtime { + int type; +#if !defined(KORE_NO_HTTP) + int (*http_request)(void *, struct http_request *); + int (*validator)(void *, struct http_request *, void *); +#endif + int (*execute)(void *, void *); + int (*onload)(void *, int); + void (*connect)(void *, struct connection *); +}; + +struct kore_runtime_call { + void *addr; + struct kore_runtime *runtime; +}; + +extern struct kore_runtime kore_native_runtime; + struct listener { - u_int8_t type; - u_int8_t addrtype; - int fd; - void (*connect)(struct connection *); + u_int8_t type; + u_int8_t addrtype; + int fd; + struct kore_runtime_call *connect; union { struct sockaddr_in ipv4; @@ -250,32 +271,48 @@ struct kore_auth { #define HANDLER_TYPE_STATIC 1 #define HANDLER_TYPE_DYNAMIC 2 -#endif +#endif /* !KORE_NO_HTTP */ #define KORE_MODULE_LOAD 1 #define KORE_MODULE_UNLOAD 2 +#define KORE_MODULE_NATIVE 0 +#define KORE_MODULE_PYTHON 1 + +struct kore_module; + +struct kore_module_functions { + void (*free)(struct kore_module *); + void (*reload)(struct kore_module *); + int (*callback)(struct kore_module *, int); + void (*load)(struct kore_module *, const char *); + void *(*getsym)(struct kore_module *, const char *); +}; + struct kore_module { - void *handle; - char *path; - char *onload; - int (*ocb)(int); + void *handle; + char *path; + char *onload; + int type; + time_t mtime; + struct kore_runtime_call *ocb; - time_t mtime; + struct kore_module_functions *fun; + struct kore_runtime *runtime; TAILQ_ENTRY(kore_module) list; }; struct kore_module_handle { - char *path; - char *func; - void *addr; - int type; - int errors; - regex_t rctx; - struct kore_domain *dom; + char *path; + char *func; + int type; + int errors; + regex_t rctx; + struct kore_domain *dom; + struct kore_runtime_call *rcall; #if !defined(KORE_NO_HTTP) - struct kore_auth *auth; + struct kore_auth *auth; TAILQ_HEAD(, kore_handler_params) params; #endif TAILQ_ENTRY(kore_module_handle) list; @@ -313,15 +350,15 @@ TAILQ_HEAD(kore_domain_h, kore_domain); #define KORE_VALIDATOR_TYPE_FUNCTION 2 struct kore_validator { - u_int8_t type; - char *name; - char *arg; - regex_t rctx; - int (*func)(struct http_request *, char *); + u_int8_t type; + char *name; + char *arg; + regex_t rctx; + struct kore_runtime_call *rcall; TAILQ_ENTRY(kore_validator) list; }; -#endif +#endif /* !KORE_NO_HTTP */ #define KORE_BUF_OWNER_API 0x0001 @@ -568,16 +605,27 @@ void kore_module_reload(int); void kore_module_onload(void); int kore_module_loaded(void); void kore_domain_closelogs(void); -void *kore_module_getsym(const char *); +void *kore_module_getsym(const char *, struct kore_runtime **); void kore_domain_load_crl(void); void kore_domain_keymgr_init(void); -void kore_module_load(const char *, const char *); void kore_domain_sslstart(struct kore_domain *); +void kore_module_load(const char *, const char *, int); void kore_domain_callback(void (*cb)(struct kore_domain *)); int kore_module_handler_new(const char *, const char *, const char *, const char *, int); void kore_module_handler_free(struct kore_module_handle *); +struct kore_runtime_call *kore_runtime_getcall(const char *); + +int kore_runtime_onload(struct kore_runtime_call *, int); +void kore_runtime_connect(struct kore_runtime_call *, struct connection *); +#if !defined(KORE_NO_HTTP) +int kore_runtime_http_request(struct kore_runtime_call *, + struct http_request *); +int kore_runtime_validator(struct kore_runtime_call *, + struct http_request *, void *); +#endif + struct kore_domain *kore_domain_lookup(const char *); struct kore_module_handle *kore_module_handler_find(const char *, const char *); diff --git a/includes/python_api.h b/includes/python_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 Stanislav Yudin <stan@endlessinsomnia.com> + * Copyright (c) 2017 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __H_PYTHON_H +#define __H_PYTHON_H + +#include <Python.h> + +void kore_python_init(void); +void kore_python_cleanup(void); +void kore_python_path(const char *); + +PyObject *kore_python_callable(PyObject *, const char *); + +extern struct kore_module_functions kore_python_module; +extern struct kore_runtime kore_python_runtime; + +#endif diff --git a/includes/python_methods.h b/includes/python_methods.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +static PyObject *python_exported_log(PyObject *, PyObject *); + +#define METHOD(n, c, a) { n, (PyCFunction)c, a, NULL } +#define GETTER(n, g) { n, (getter)g, NULL, NULL, NULL } +#define SETTER(n, s) { n, NULL, (setter)g, NULL, NULL } +#define GETSET(n, g, s) { n, (getter)g, (setter)s, NULL, NULL } + +static struct PyMethodDef pykore_methods[] = { + METHOD("log", python_exported_log, METH_VARARGS), + { NULL, NULL, 0, NULL } +}; + +static struct PyModuleDef pykore_module = { + PyModuleDef_HEAD_INIT, "kore", NULL, -1, pykore_methods +}; + +struct pyconnection { + PyObject_HEAD + struct connection *c; +}; + +static PyMethodDef pyconnection_methods[] = { + METHOD(NULL, NULL, -1), +}; + +static PyGetSetDef pyconnection_getset[] = { + GETTER(NULL, NULL), +}; + +static void pyconnection_dealloc(struct pyconnection *); + +static PyTypeObject pyconnection_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "kore.connection", + .tp_doc = "struct connection", + .tp_getset = pyconnection_getset, + .tp_methods = pyconnection_methods, + .tp_basicsize = sizeof(struct pyconnection), + .tp_dealloc = (destructor)pyconnection_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, +}; + +#if !defined(KORE_NO_HTTP) +struct pyhttp_request { + PyObject_HEAD + struct http_request *req; +}; + +static void pyhttp_dealloc(struct pyhttp_request *); + +static PyObject *pyhttp_response(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_body_read(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_populate_get(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_populate_post(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_request_header(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_response_header(struct pyhttp_request *, PyObject *); + +static PyMethodDef pyhttp_request_methods[] = { + METHOD("response", pyhttp_response, METH_VARARGS), + METHOD("body_read", pyhttp_body_read, METH_VARARGS), + METHOD("populate_get", pyhttp_populate_get, METH_NOARGS), + METHOD("populate_post", pyhttp_populate_post, METH_NOARGS), + METHOD("request_header", pyhttp_request_header, METH_VARARGS), + METHOD("response_header", pyhttp_response_header, METH_VARARGS), + METHOD(NULL, NULL, -1) +}; + +static int pyhttp_set_state(struct pyhttp_request *, PyObject *, void *); + +static PyObject *pyhttp_get_host(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_path(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_body(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_agent(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_state(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_method(struct pyhttp_request *, void *); +static PyObject *pyhttp_get_connection(struct pyhttp_request *, void *); + +static PyGetSetDef pyhttp_request_getset[] = { + GETTER("host", pyhttp_get_host), + GETTER("path", pyhttp_get_path), + GETTER("body", pyhttp_get_body), + GETTER("agent", pyhttp_get_agent), + GETTER("method", pyhttp_get_method), + GETTER("connection", pyhttp_get_connection), + GETSET("state", pyhttp_get_state, pyhttp_set_state), + GETTER(NULL, NULL) +}; + +static PyTypeObject pyhttp_request_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "kore.http_request", + .tp_doc = "struct http_request", + .tp_getset = pyhttp_request_getset, + .tp_methods = pyhttp_request_methods, + .tp_dealloc = (destructor)pyhttp_dealloc, + .tp_basicsize = sizeof(struct pyhttp_request), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, +}; +#endif diff --git a/src/config.c b/src/config.c @@ -35,6 +35,10 @@ #include "tasks.h" #endif +#if defined(KORE_USE_PYTHON) +#include "python_api.h" +#endif + /* XXX - This is becoming a clusterfuck. Fix it. */ #if !defined(KORE_SINGLE_BINARY) @@ -99,6 +103,10 @@ static int configure_pgsql_conn_max(char *); static int configure_task_threads(char *); #endif +#if defined(KORE_USE_PYTHON) +static int configure_python_import(char *); +#endif + static void domain_sslstart(void); static void kore_parse_config_file(const char *); @@ -111,6 +119,9 @@ static struct { #if !defined(KORE_SINGLE_BINARY) { "load", configure_load }, #endif +#if defined(KORE_USE_PYTHON) + { "python_import", configure_python_import }, +#endif { "domain", configure_domain }, { "chroot", configure_chroot }, { "runas", configure_runas }, @@ -313,7 +324,7 @@ configure_load(char *options) if (argv[0] == NULL) return (KORE_RESULT_ERROR); - kore_module_load(argv[0], argv[1]); + kore_module_load(argv[0], argv[1], KORE_MODULE_NATIVE); return (KORE_RESULT_OK); } #else @@ -1073,3 +1084,18 @@ configure_task_threads(char *option) return (KORE_RESULT_OK); } #endif + +#if defined(KORE_USE_PYTHON) +static int +configure_python_import(char *module) +{ + char *argv[3]; + + kore_split_string(module, " ", argv, 3); + if (argv[0] == NULL) + return (KORE_RESULT_ERROR); + + kore_module_load(argv[0], argv[1], KORE_MODULE_PYTHON); + return (KORE_RESULT_OK); +} +#endif diff --git a/src/connection.c b/src/connection.c @@ -128,7 +128,7 @@ kore_connection_accept(struct listener *listener, struct connection **out) c->read = net_read; if (listener->connect != NULL) { - listener->connect(c); + kore_runtime_connect(listener->connect, c); } else { #if !defined(KORE_NO_HTTP) c->proto = CONN_PROTO_HTTP; @@ -265,7 +265,7 @@ kore_connection_handle(struct connection *c) if (c->owner != NULL) { listener = (struct listener *)c->owner; if (listener->connect != NULL) { - listener->connect(c); + kore_runtime_connect(listener->connect, c); return (KORE_RESULT_OK); } } diff --git a/src/http.c b/src/http.c @@ -204,6 +204,10 @@ http_request_new(struct connection *c, const char *host, req->http_body_offset = 0; req->http_body_path = NULL; +#if defined(KORE_USE_PYTHON) + req->py_object = NULL; +#endif + req->host = kore_pool_get(&http_host_pool); memcpy(req->host, host, hostlen); req->host[hostlen] = '\0'; @@ -298,7 +302,7 @@ http_process(void) void http_process_request(struct http_request *req) { - int r, (*cb)(struct http_request *); + int r; kore_debug("http_process_request: %p->%p (%s)", req->owner, req, req->path); @@ -314,10 +318,7 @@ http_process_request(struct http_request *req) switch (r) { case KORE_RESULT_OK: - *(void **)&(cb) = req->hdlr->addr; - worker->active_hdlr = req->hdlr; - r = cb(req); - worker->active_hdlr = NULL; + r = kore_runtime_http_request(req->hdlr->rcall, req); break; case KORE_RESULT_RETRY: break; diff --git a/src/kore.c b/src/kore.c @@ -30,6 +30,10 @@ #include "http.h" #endif +#if defined(KORE_USE_PYTHON) +#include "python_api.h" +#endif + volatile sig_atomic_t sig_recv; struct listener_head listeners; @@ -175,6 +179,9 @@ main(int argc, char *argv[]) LIST_INIT(&listeners); kore_log_init(); +#if defined(KORE_USE_PYTHON) + kore_python_init(); +#endif #if !defined(KORE_NO_HTTP) kore_auth_init(); kore_validator_init(); @@ -187,7 +194,7 @@ main(int argc, char *argv[]) if (config_file == NULL) usage(); #else - kore_module_load(NULL, NULL); + kore_module_load(NULL, NULL, KORE_MODULE_NATIVE); #endif kore_parse_config(); @@ -340,8 +347,7 @@ kore_server_bind(const char *ip, const char *port, const char *ccb) } if (ccb != NULL) { - *(void **)&(l->connect) = kore_module_getsym(ccb); - if (l->connect == NULL) { + if ((l->connect = kore_runtime_getcall(ccb)) == NULL) { printf("no such callback: '%s'\n", ccb); close(l->fd); kore_free(l); @@ -398,10 +404,10 @@ kore_server_sslstart(void) static void kore_server_start(void) { - u_int32_t tmp; - int quit; + u_int32_t tmp; + int quit; #if defined(KORE_SINGLE_BINARY) - void (*preload)(void); + struct kore_runtime_call *rcall; #endif if (foreground == 0 && daemon(1, 1) == -1) @@ -422,9 +428,11 @@ kore_server_start(void) kore_log(LOG_NOTICE, "jsonrpc built-in enabled"); #endif #if defined(KORE_SINGLE_BINARY) - *(void **)&(preload) = kore_module_getsym("kore_preload"); - if (preload != NULL) - preload(); + rcall = kore_runtime_getcall("kore_preload"); + if (rcall != NULL) { + rcall->runtime->execute(rcall->addr, NULL); + kore_free(rcall); + } #endif kore_platform_proctitle("kore [parent]"); diff --git a/src/module.c b/src/module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> + * Copyright (c) 2013-2017 Joris Vink <joris@coders.se> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,8 +20,28 @@ #include "kore.h" +#if !defined(KORE_NO_HTTP) +#include "http.h" +#endif + +#if defined(KORE_USE_PYTHON) +#include "python_api.h" +#endif + static TAILQ_HEAD(, kore_module) modules; +static void native_free(struct kore_module *); +static void native_reload(struct kore_module *); +static void native_load(struct kore_module *, const char *); +static void *native_getsym(struct kore_module *, const char *); + +struct kore_module_functions kore_native_module = { + .free = native_free, + .load = native_load, + .getsym = native_getsym, + .reload = native_reload, +}; + void kore_module_init(void) { @@ -36,15 +56,12 @@ kore_module_cleanup(void) for (module = TAILQ_FIRST(&modules); module != NULL; module = next) { next = TAILQ_NEXT(module, list); TAILQ_REMOVE(&modules, module, list); - - kore_free(module->path); - (void)dlclose(module->handle); - kore_free(module); + module->fun->free(module); } } void -kore_module_load(const char *path, const char *onload) +kore_module_load(const char *path, const char *onload, int type) { #if !defined(KORE_SINGLE_BINARY) struct stat st; @@ -54,8 +71,10 @@ kore_module_load(const char *path, const char *onload) kore_debug("kore_module_load(%s, %s)", path, onload); module = kore_malloc(sizeof(struct kore_module)); - module->onload = NULL; module->ocb = NULL; + module->type = type; + module->onload = NULL; + module->handle = NULL; #if !defined(KORE_SINGLE_BINARY) if (stat(path, &st) == -1) @@ -68,18 +87,34 @@ kore_module_load(const char *path, const char *onload) module->mtime = 0; #endif - module->handle = dlopen(module->path, RTLD_NOW | RTLD_GLOBAL); - if (module->handle == NULL) - fatal("%s: %s", path, dlerror()); - - if (onload != NULL) { - module->onload = kore_strdup(onload); - *(void **)&(module->ocb) = dlsym(module->handle, onload); - if (module->ocb == NULL) - fatal("%s: onload '%s' not present", path, onload); + switch (module->type) { + case KORE_MODULE_NATIVE: + module->fun = &kore_native_module; + module->runtime = &kore_native_runtime; + break; +#if defined(KORE_USE_PYTHON) + case KORE_MODULE_PYTHON: + module->fun = &kore_python_module; + module->runtime = &kore_python_runtime; + break; +#endif + default: + fatal("kore_module_load: unknown type %d", type); } + module->fun->load(module, onload); TAILQ_INSERT_TAIL(&modules, module, list); + + if (onload != NULL) { + module->ocb = kore_malloc(sizeof(*module->ocb)); + module->ocb->runtime = module->runtime; + module->ocb->addr = module->fun->getsym(module, onload); + + if (module->ocb->addr == NULL) { + fatal("%s: onload '%s' not present", + module->path, onload); + } + } } void @@ -92,7 +127,7 @@ kore_module_onload(void) if (module->ocb == NULL) continue; - (void)module->ocb(KORE_MODULE_LOAD); + kore_runtime_onload(module->ocb, KORE_MODULE_LOAD); } #endif } @@ -102,6 +137,7 @@ kore_module_reload(int cbs) { #if !defined(KORE_SINGLE_BINARY) struct stat st; + int ret; struct kore_domain *dom; struct kore_module_handle *hdlr; struct kore_module *module; @@ -113,11 +149,15 @@ kore_module_reload(int cbs) continue; } - if (module->mtime == st.st_mtime) + if (module->mtime == st.st_mtime) { + kore_log(LOG_NOTICE, "not reloading %s", module->path); continue; + } if (module->ocb != NULL && cbs == 1) { - if (!module->ocb(KORE_MODULE_UNLOAD)) { + ret = kore_runtime_onload(module->ocb, + KORE_MODULE_UNLOAD); + if (ret == KORE_RESULT_ERROR) { kore_log(LOG_NOTICE, "not reloading %s", module->path); continue; @@ -125,32 +165,31 @@ kore_module_reload(int cbs) } module->mtime = st.st_mtime; - if (dlclose(module->handle)) - fatal("cannot close existing module: %s", dlerror()); - - module->handle = dlopen(module->path, RTLD_NOW | RTLD_GLOBAL); - if (module->handle == NULL) - fatal("kore_module_reload(): %s", dlerror()); + module->fun->reload(module); if (module->onload != NULL) { - *(void **)&(module->ocb) = - dlsym(module->handle, module->onload); - if (module->ocb == NULL) { + kore_free(module->ocb); + module->ocb = kore_malloc(sizeof(*module->ocb)); + module->ocb->runtime = module->runtime; + module->ocb->addr = + module->fun->getsym(module, module->onload); + if (module->ocb->addr == NULL) { fatal("%s: onload '%s' not present", module->path, module->onload); } - - if (cbs) - (void)module->ocb(KORE_MODULE_LOAD); } + if (module->ocb != NULL && cbs == 1) + kore_runtime_onload(module->ocb, KORE_MODULE_LOAD); + kore_log(LOG_NOTICE, "reloaded '%s' module", module->path); } TAILQ_FOREACH(dom, &domains, list) { TAILQ_FOREACH(hdlr, &(dom->handlers), list) { - hdlr->addr = kore_module_getsym(hdlr->func); - if (hdlr->func == NULL) + kore_free(hdlr->rcall); + hdlr->rcall = kore_runtime_getcall(hdlr->func); + if (hdlr->rcall == NULL) fatal("no function '%s' found", hdlr->func); hdlr->errors = 0; } @@ -177,19 +216,12 @@ kore_module_handler_new(const char *path, const char *domain, const char *func, const char *auth, int type) { struct kore_auth *ap; - void *addr; struct kore_domain *dom; struct kore_module_handle *hdlr; kore_debug("kore_module_handler_new(%s, %s, %s, %s, %d)", path, domain, func, auth, type); - addr = kore_module_getsym(func); - if (addr == NULL) { - kore_debug("function '%s' not found", func); - return (KORE_RESULT_ERROR); - } - if ((dom = kore_domain_lookup(domain)) == NULL) return (KORE_RESULT_ERROR); @@ -204,12 +236,18 @@ kore_module_handler_new(const char *path, const char *domain, hdlr->auth = ap; hdlr->dom = dom; hdlr->errors = 0; - hdlr->addr = addr; hdlr->type = type; - TAILQ_INIT(&(hdlr->params)); hdlr->path = kore_strdup(path); hdlr->func = kore_strdup(func); + TAILQ_INIT(&(hdlr->params)); + + if ((hdlr->rcall = kore_runtime_getcall(func)) == NULL) { + kore_module_handler_free(hdlr); + kore_log(LOG_ERR, "function '%s' not found", func); + return (KORE_RESULT_ERROR); + } + if (hdlr->type == HANDLER_TYPE_DYNAMIC) { if (regcomp(&(hdlr->rctx), hdlr->path, REG_EXTENDED | REG_NOSUB)) { @@ -270,20 +308,55 @@ kore_module_handler_find(const char *domain, const char *path) return (NULL); } - #endif /* !KORE_NO_HTTP */ void * -kore_module_getsym(const char *symbol) +kore_module_getsym(const char *symbol, struct kore_runtime **runtime) { void *ptr; struct kore_module *module; + if (runtime != NULL) + *runtime = NULL; + TAILQ_FOREACH(module, &modules, list) { - ptr = dlsym(module->handle, symbol); - if (ptr != NULL) + ptr = module->fun->getsym(module, symbol); + if (ptr != NULL) { + if (runtime != NULL) + *runtime = module->runtime; return (ptr); + } } return (NULL); } + +static void * +native_getsym(struct kore_module *module, const char *symbol) +{ + return (dlsym(module->handle, symbol)); +} + +static void +native_free(struct kore_module *module) +{ + kore_free(module->path); + (void)dlclose(module->handle); + kore_free(module); +} + +static void +native_reload(struct kore_module *module) +{ + if (dlclose(module->handle)) + fatal("cannot close existing module: %s", dlerror()); + module->fun->load(module, module->onload); +} + +static void +native_load(struct kore_module *module, const char *onload) +{ + module->handle = dlopen(module->path, RTLD_NOW | RTLD_GLOBAL); + if (module->handle == NULL) + fatal("%s: %s", module->path, dlerror()); +} diff --git a/src/python.c b/src/python.c @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2016 Stanislav Yudin <stan@endlessinsomnia.com> + * Copyright (c) 2017 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> + +#include <libgen.h> + +#include "kore.h" + +#if !defined(KORE_NO_HTTP) +#include "http.h" +#endif + +#include "python_api.h" +#include "python_methods.h" + +static PyMODINIT_FUNC python_module_init(void); +static PyObject *python_import(const char *); +static void python_log_error(const char *); +static PyObject *pyconnection_alloc(struct connection *); +static PyObject *python_callable(PyObject *, const char *); + +#if !defined(KORE_NO_HTTP) +static PyObject *pyhttp_request_alloc(struct http_request *); +#endif + +static void python_append_path(const char *); +static void python_push_integer(PyObject *, const char *, long); +static void python_push_type(const char *, PyObject *, PyTypeObject *); + +#if !defined(KORE_NO_HTTP) +static int python_runtime_http_request(void *, struct http_request *); +static int python_runtime_validator(void *, struct http_request *, void *); +#endif +static int python_runtime_onload(void *, int); +static void python_runtime_connect(void *, struct connection *); + +static void python_module_free(struct kore_module *); +static void python_module_reload(struct kore_module *); +static void python_module_load(struct kore_module *, const char *); +static void *python_module_getsym(struct kore_module *, const char *); + +struct kore_module_functions kore_python_module = { + .free = python_module_free, + .load = python_module_load, + .getsym = python_module_getsym, + .reload = python_module_reload +}; + +struct kore_runtime kore_python_runtime = { + KORE_RUNTIME_PYTHON, +#if !defined(KORE_NO_HTTP) + .http_request = python_runtime_http_request, + .validator = python_runtime_validator, +#endif + .onload = python_runtime_onload, + .connect = python_runtime_connect +}; + +void +kore_python_init(void) +{ + if (PyImport_AppendInittab("kore", &python_module_init) == -1) + fatal("kore_python_init: failed to add new module"); + + Py_Initialize(); +} + +void +kore_python_cleanup(void) +{ + if (Py_IsInitialized()) + Py_Finalize(); +} + +static void +python_log_error(const char *function) +{ + PyObject *type, *value, *traceback; + + if (!PyErr_Occurred()) + return; + + PyErr_Fetch(&type, &value, &traceback); + + if (type == NULL || value == NULL || traceback == NULL) + fatal("python_log_error(): exception but no error trace?"); + + kore_log(LOG_ERR, + "python exception in '%s' - type:%s - value:%s - trace:%s", + function, + PyUnicode_AsUTF8AndSize(type, NULL), + PyUnicode_AsUTF8AndSize(value, NULL), + PyUnicode_AsUTF8AndSize(traceback, NULL)); + + Py_DECREF(type); + Py_DECREF(value); + Py_DECREF(traceback); +} + +static void +python_module_free(struct kore_module *module) +{ + kore_free(module->path); + Py_DECREF(module->handle); + kore_free(module); +} + +static void +python_module_reload(struct kore_module *module) +{ + /* Calls through to kore_python_module_load() below. */ + module->fun->load(module, module->onload); +} + +static void +python_module_load(struct kore_module *module, const char *onload) +{ + if (module->handle != NULL) + Py_DECREF(module->handle); + + kore_python_cleanup(); + kore_python_init(); + + module->handle = python_import(module->path); + if (module->handle == NULL) + fatal("%s: failed to import module", module->path); +} + +static void * +python_module_getsym(struct kore_module *module, const char *symbol) +{ + return (python_callable(module->handle, symbol)); +} + +static void pyhttp_dealloc(struct pyhttp_request *pyreq) +{ + printf("pyreq %p goes byebye\n", (void *)pyreq); + PyObject_Del((PyObject *)pyreq); +} + +static void pyconnection_dealloc(struct pyconnection *pyc) +{ + printf("pyc %p goes byebye\n", (void *)pyc); + PyObject_Del((PyObject *)pyc); +} + +#if !defined(KORE_NO_HTTP) +static int +python_runtime_http_request(void *addr, struct http_request *req) +{ + int ret; + PyObject *pyret, *pyreq, *args, *callable; + + callable = (PyObject *)addr; + + pyreq = pyhttp_request_alloc(req); + if (pyreq == NULL) { + kore_log(LOG_ERR, "cannot create new pyhttp_request"); + http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0); + return (KORE_RESULT_OK); + } + + if ((args = PyTuple_New(1)) == NULL) + fatal("python_runtime_http_request: PyTuple_New failed"); + + printf(" args tuple: %p\n", (void *)args); + + if (PyTuple_SetItem(args, 0, pyreq) != 0) + fatal("python_runtime_http_request: PyTuple_SetItem failed"); + + PyErr_Clear(); + pyret = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + + if (pyret == NULL) { + Py_XDECREF(req->py_object); + python_log_error("python_runtime_http_request"); + http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0); + return (KORE_RESULT_OK); + } + + if (!PyLong_Check(pyret)) + fatal("python_runtime_http_request: unexpected return type"); + + ret = (int)PyLong_AsLong(pyret); + if (ret != KORE_RESULT_RETRY) + Py_XDECREF(req->py_object); + + Py_DECREF(pyret); + + return (ret); +} + +static int +python_runtime_validator(void *addr, struct http_request *req, void *data) +{ + printf("python_runtime_validator: XXX %s\n", req->path); + return (KORE_RESULT_OK); +} +#endif + +static int +python_runtime_onload(void *addr, int action) +{ + int ret; + PyObject *pyret, *args, *pyact, *callable; + + callable = (PyObject *)addr; + + if ((pyact = PyLong_FromLong(action)) == NULL) + fatal("python_runtime_onload: PyLong_FromLong failed"); + + if ((args = PyTuple_New(1)) == NULL) + fatal("python_runtime_onload: PyTuple_New failed"); + + if (PyTuple_SetItem(args, 0, pyact) != 0) + fatal("python_runtime_onload: PyTuple_SetItem failed"); + + pyret = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + + if (pyret == NULL) { + python_log_error("python_runtime_onload"); + return (KORE_RESULT_ERROR); + } + + if (!PyLong_Check(pyret)) + fatal("python_runtime_onload: unexpected return type"); + + ret = (int)PyLong_AsLong(pyret); + Py_DECREF(pyret); + + return (ret); +} + +static void +python_runtime_connect(void *addr, struct connection *c) +{ + PyObject *pyc, *pyret, *args, *callable; + + callable = (PyObject *)addr; + + if ((pyc = pyconnection_alloc(c)) == NULL) { + kore_log(LOG_ERR, "cannot create new pyconnection"); + kore_connection_disconnect(c); + return; + } + + if ((args = PyTuple_New(1)) == NULL) + fatal("python_runtime_connect: PyTuple_New failed"); + + if (PyTuple_SetItem(args, 0, pyc) != 0) + fatal("python_runtime_connect: PyTuple_SetItem failed"); + + pyret = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + + if (pyret == NULL) { + python_log_error("python_runtime_connect"); + kore_connection_disconnect(c); + } + + Py_DECREF(pyret); +} + +static PyMODINIT_FUNC +python_module_init(void) +{ + PyObject *pykore; + + if ((pykore = PyModule_Create(&pykore_module)) == NULL) + fatal("python_module_init: failed to setup pykore module"); + + python_push_type("pyconnection", pykore, &pyconnection_type); + + python_push_integer(pykore, "RESULT_OK", KORE_RESULT_OK); + python_push_integer(pykore, "RESULT_RETRY", KORE_RESULT_RETRY); + python_push_integer(pykore, "RESULT_ERROR", KORE_RESULT_ERROR); + python_push_integer(pykore, "MODULE_LOAD", KORE_MODULE_LOAD); + python_push_integer(pykore, "MODULE_UNLOAD", KORE_MODULE_UNLOAD); + + python_push_integer(pykore, "LOG_ERR", LOG_ERR); + python_push_integer(pykore, "LOG_INFO", LOG_INFO); + python_push_integer(pykore, "LOG_NOTICE", LOG_NOTICE); + +#if !defined(KORE_NO_HTTP) + python_push_type("pyhttp_request", pykore, &pyhttp_request_type); + + python_push_integer(pykore, "METHOD_GET", HTTP_METHOD_GET); + python_push_integer(pykore, "METHOD_PUT", HTTP_METHOD_PUT); + python_push_integer(pykore, "METHOD_HEAD", HTTP_METHOD_HEAD); + python_push_integer(pykore, "METHOD_POST", HTTP_METHOD_POST); + python_push_integer(pykore, "METHOD_DELETE", HTTP_METHOD_DELETE); + python_push_integer(pykore, + "WEBSOCKET_BROADCAST_LOCAL", WEBSOCKET_BROADCAST_LOCAL); + python_push_integer(pykore, + "WEBSOCKET_BROADCAST_GLOBAL", WEBSOCKET_BROADCAST_GLOBAL); +#endif + + return (pykore); +} + +static void +python_append_path(const char *path) +{ + PyObject *mpath, *spath; + + if ((mpath = PyUnicode_FromString(path)) == NULL) + fatal("python_append_path: PyUnicode_FromString failed"); + + if ((spath = PySys_GetObject("path")) == NULL) + fatal("python_append_path: PySys_GetObject failed"); + + PyList_Append(spath, mpath); + Py_DECREF(mpath); +} + +static void +python_push_type(const char *name, PyObject *module, PyTypeObject *type) +{ + if (PyType_Ready(type) == -1) + fatal("python_push_type: failed to ready %s", name); + + Py_INCREF(type); + + if (PyModule_AddObject(module, name, (PyObject *)type) == -1) + fatal("python_push_type: failed to push %s", name); +} + +static void +python_push_integer(PyObject *module, const char *name, long value) +{ + int ret; + + if ((ret = PyModule_AddIntConstant(module, name, value)) == -1) + fatal("python_push_integer: failed to add %s", name); +} + +static PyObject * +python_exported_log(PyObject *self, PyObject *args) +{ + int prio; + const char *message; + + if (!PyArg_ParseTuple(args, "is", &prio, &message)) { + PyErr_SetString(PyExc_TypeError, "invalid parameters"); + return (NULL); + } + + kore_log(prio, "%s", message); + + Py_RETURN_TRUE; +} + +static PyObject * +python_import(const char *path) +{ + PyObject *module; + char *dir, *file, *copy, *p; + + copy = kore_strdup(path); + + if ((file = basename(copy)) == NULL) + fatal("basename: %s: %s", path, errno_s); + if ((dir = dirname(copy)) == NULL) + fatal("dirname: %s: %s", path, errno_s); + + if ((p = strrchr(file, '.')) != NULL) + *p = '\0'; + + python_append_path(dir); + module = PyImport_ImportModule(file); + if (module == NULL) + PyErr_Print(); + + kore_free(copy); + + return (module); +} + +static PyObject * +python_callable(PyObject *module, const char *symbol) +{ + PyObject *obj; + + if ((obj = PyObject_GetAttrString(module, symbol)) == NULL) + return (NULL); + + if (!PyCallable_Check(obj)) { + Py_DECREF(obj); + return (NULL); + } + + return (obj); +} + +static PyObject * +pyconnection_alloc(struct connection *c) +{ + struct pyconnection *pyc; + + pyc = PyObject_New(struct pyconnection, &pyconnection_type); + if (pyc == NULL) + return (NULL); + + printf(" pyc: %p\n", (void *)pyc); + pyc->c = c; + + return ((PyObject *)pyc); +} + +#if !defined(KORE_NO_HTTP) +static PyObject * +pyhttp_request_alloc(struct http_request *req) +{ + struct pyhttp_request *pyreq; + + pyreq = PyObject_New(struct pyhttp_request, &pyhttp_request_type); + if (pyreq == NULL) + return (NULL); + + pyreq->req = req; + + return ((PyObject *)pyreq); +} + +static PyObject * +pyhttp_response(struct pyhttp_request *pyreq, PyObject *args) +{ + Py_buffer body; + int status; + + if (!PyArg_ParseTuple(args, "iy*", &status, &body)) { + PyErr_SetString(PyExc_TypeError, "invalid parameters"); + return (NULL); + } + + http_response(pyreq->req, status, body.buf, body.len); + PyBuffer_Release(&body); + + Py_RETURN_TRUE; +} + +static PyObject * +pyhttp_response_header(struct pyhttp_request *pyreq, PyObject *args) +{ + const char *header, *value; + + if (!PyArg_ParseTuple(args, "ss", &header, &value)) { + PyErr_SetString(PyExc_TypeError, "invalid parameters"); + return (NULL); + } + + http_response_header(pyreq->req, header, value); + + Py_RETURN_TRUE; +} + +static PyObject * +pyhttp_request_header(struct pyhttp_request *pyreq, PyObject *args) +{ + char *value; + const char *header; + PyObject *result; + + if (!PyArg_ParseTuple(args, "s", &header)) { + PyErr_SetString(PyExc_TypeError, "invalid parameters"); + return (NULL); + } + + if (!http_request_header(pyreq->req, header, &value)) { + Py_RETURN_NONE; + } + + if ((result = PyUnicode_FromString(value)) == NULL) + return (PyErr_NoMemory()); + + return (result); +} + +static PyObject * +pyhttp_body_read(struct pyhttp_request *pyreq, PyObject *args) +{ + ssize_t ret; + size_t len; + Py_ssize_t pylen; + PyObject *result; + u_int8_t buf[1024]; + + if (!PyArg_ParseTuple(args, "n", &pylen) || pylen < 0) { + PyErr_SetString(PyExc_TypeError, "invalid parameters"); + return (NULL); + } + + len = (size_t)pylen; + if (len > sizeof(buf)) { + PyErr_SetString(PyExc_RuntimeError, "len > sizeof(buf)"); + return (NULL); + } + + ret = http_body_read(pyreq->req, buf, len); + if (ret == -1) { + PyErr_SetString(PyExc_RuntimeError, "http_body_read() failed"); + return (NULL); + } + + if (ret > INT_MAX) { + PyErr_SetString(PyExc_RuntimeError, "ret > INT_MAX"); + return (NULL); + } + + result = Py_BuildValue("ny#", ret, buf, (int)ret); + if (result == NULL) + return (PyErr_NoMemory()); + + return (result); +} + +static PyObject * +pyhttp_populate_get(struct pyhttp_request *pyreq, PyObject *args) +{ + http_populate_get(pyreq->req); + Py_RETURN_TRUE; +} + +static PyObject * +pyhttp_populate_post(struct pyhttp_request *pyreq, PyObject *args) +{ + http_populate_post(pyreq->req); + Py_RETURN_TRUE; +} + +static PyObject * +pyhttp_get_host(struct pyhttp_request *pyreq, void *closure) +{ + PyObject *host; + + if ((host = PyUnicode_FromString(pyreq->req->host)) == NULL) + return (PyErr_NoMemory()); + + return (host); +} + +static PyObject * +pyhttp_get_path(struct pyhttp_request *pyreq, void *closure) +{ + PyObject *path; + + if ((path = PyUnicode_FromString(pyreq->req->path)) == NULL) + return (PyErr_NoMemory()); + + return (path); +} + +static PyObject * +pyhttp_get_body(struct pyhttp_request *pyreq, void *closure) +{ + ssize_t ret; + struct kore_buf buf; + PyObject *body; + u_int8_t data[BUFSIZ]; + + kore_buf_init(&buf, 1024); + + for (;;) { + ret = http_body_read(pyreq->req, data, sizeof(data)); + if (ret == -1) { + kore_buf_cleanup(&buf); + PyErr_SetString(PyExc_RuntimeError, + "http_body_read() failed"); + return (NULL); + } + + if (ret == 0) + break; + + kore_buf_append(&buf, data, (size_t)ret); + } + + body = PyBytes_FromStringAndSize((char *)buf.data, buf.offset); + kore_buf_free(&buf); + + if (body == NULL) + return (PyErr_NoMemory()); + + return (body); +} + +static PyObject * +pyhttp_get_agent(struct pyhttp_request *pyreq, void *closure) +{ + PyObject *agent; + + if (pyreq->req->agent == NULL) { + Py_RETURN_NONE; + } + + if ((agent = PyUnicode_FromString(pyreq->req->path)) == NULL) + return (PyErr_NoMemory()); + + return (agent); +} + +static int +pyhttp_set_state(struct pyhttp_request *pyreq, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "pyhttp_set_state: value is NULL"); + return (-1); + } + + Py_XDECREF(pyreq->req->py_object); + pyreq->req->py_object = value; + Py_INCREF(pyreq->req->py_object); + + return (0); +} + +static PyObject * +pyhttp_get_state(struct pyhttp_request *pyreq, void *closure) +{ + if (pyreq->req->py_object == NULL) + Py_RETURN_NONE; + + Py_INCREF(pyreq->req->py_object); + + return (pyreq->req->py_object); +} + +static PyObject * +pyhttp_get_method(struct pyhttp_request *pyreq, void *closure) +{ + PyObject *method; + + if ((method = PyLong_FromUnsignedLong(pyreq->req->method)) == NULL) + return (PyErr_NoMemory()); + + return (method); +} + +static PyObject * +pyhttp_get_connection(struct pyhttp_request *pyreq, void *closure) +{ + PyObject *pyc; + + if ((pyc = pyconnection_alloc(pyreq->req->owner)) == NULL) + return (PyErr_NoMemory()); + + return (pyc); +} +#endif diff --git a/src/runtime.c b/src/runtime.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> + +#include "kore.h" + +#if !defined(KORE_NO_HTTP) +#include "http.h" +#endif + +#if defined(KORE_USE_PYTHON) +#include "python_api.h" +#endif + +static int native_runtime_onload(void *, int); +static void native_runtime_connect(void *, struct connection *); +#if !defined(KORE_NO_HTTP) +static int native_runtime_http_request(void *, struct http_request *); +static int native_runtime_validator(void *, struct http_request *, void *); +#endif + +struct kore_runtime kore_native_runtime = { + KORE_RUNTIME_NATIVE, +#if !defined(KORE_NO_HTTP) + .http_request = native_runtime_http_request, + .validator = native_runtime_validator, +#endif + .onload = native_runtime_onload, + .connect = native_runtime_connect +}; + +struct kore_runtime_call * +kore_runtime_getcall(const char *symbol) +{ + void *ptr; + struct kore_runtime_call *rcall; + struct kore_runtime *runtime; + + ptr = kore_module_getsym(symbol, &runtime); + if (ptr == NULL) + return (NULL); + + rcall = kore_malloc(sizeof(*rcall)); + rcall->addr = ptr; + rcall->runtime = runtime; + + return (rcall); +} + +int +kore_runtime_onload(struct kore_runtime_call *rcall, int action) +{ + return (rcall->runtime->onload(rcall->addr, action)); +} + +void +kore_runtime_connect(struct kore_runtime_call *rcall, struct connection *c) +{ + rcall->runtime->connect(rcall->addr, c); +} + +#if !defined(KORE_NO_HTTP) +int +kore_runtime_http_request(struct kore_runtime_call *rcall, + struct http_request *req) +{ + return (rcall->runtime->http_request(rcall->addr, req)); +} + +int +kore_runtime_validator(struct kore_runtime_call *rcall, + struct http_request *req, void *data) +{ + return (rcall->runtime->validator(rcall->addr, req, data)); +} +#endif + +static void +native_runtime_connect(void *addr, struct connection *c) +{ + void (*cb)(struct connection *); + + *(void **)&(cb) = addr; + cb(c); +} + +static int +native_runtime_onload(void *addr, int action) +{ + int (*cb)(int); + + *(void **)&(cb) = addr; + return (cb(action)); +} + +#if !defined(KORE_NO_HTTP) +static int +native_runtime_http_request(void *addr, struct http_request *req) +{ + int (*cb)(struct http_request *); + + *(void **)&(cb) = addr; + return (cb(req)); +} + +static int +native_runtime_validator(void *addr, struct http_request *req, void *data) +{ + int (*cb)(struct http_request *, void *); + + *(void **)&(cb) = addr; + return (cb(req, data)); +} +#endif diff --git a/src/validator.c b/src/validator.c @@ -42,8 +42,8 @@ kore_validator_add(const char *name, u_int8_t type, const char *arg) } break; case KORE_VALIDATOR_TYPE_FUNCTION: - *(void **)(&val->func) = kore_module_getsym(arg); - if (val->func == NULL) { + val->rcall = kore_runtime_getcall(arg); + if (val->rcall == NULL) { kore_free(val); kore_log(LOG_NOTICE, "validator %s has undefined callback %s", @@ -92,7 +92,7 @@ kore_validator_check(struct http_request *req, struct kore_validator *val, r = KORE_RESULT_ERROR; break; case KORE_VALIDATOR_TYPE_FUNCTION: - r = val->func(req, data); + r = kore_runtime_validator(val->rcall, req, data); break; default: r = KORE_RESULT_ERROR; @@ -113,9 +113,10 @@ kore_validator_reload(void) if (val->type != KORE_VALIDATOR_TYPE_FUNCTION) continue; - *(void **)&(val->func) = kore_module_getsym(val->arg); - if (val->func == NULL) - fatal("no function for validator %s found", val->name); + kore_free(val->rcall); + val->rcall = kore_runtime_getcall(val->arg); + if (val->rcall == NULL) + fatal("no function for validator %s found", val->arg); } } diff --git a/src/worker.c b/src/worker.c @@ -40,6 +40,10 @@ #include "tasks.h" #endif +#if defined(KORE_USE_PYTHON) +#include "python_api.h" +#endif + #if defined(WORKER_DEBUG) #define worker_debug(fmt, ...) printf(fmt, ##__VA_ARGS__) #else @@ -262,11 +266,11 @@ kore_worker_privdrop(void) void kore_worker_entry(struct kore_worker *kw) { - char buf[16]; - int quit, had_lock, r; - u_int64_t now, next_lock, netwait; + char buf[16]; + int quit, had_lock, r; + u_int64_t now, next_lock, netwait; #if defined(KORE_SINGLE_BINARY) - void (*onload)(void); + struct kore_runtime_call *rcall; #endif worker = kw; @@ -332,9 +336,11 @@ kore_worker_entry(struct kore_worker *kw) kore_log(LOG_NOTICE, "worker %d started (cpu#%d)", kw->id, kw->cpu); #if defined(KORE_SINGLE_BINARY) - *(void **)&(onload) = kore_module_getsym("kore_onload"); - if (onload != NULL) - onload(); + rcall = kore_runtime_getcall("kore_onload"); + if (rcall != NULL) { + rcall->runtime->execute(rcall->addr, NULL); + kore_free(rcall); + } #else kore_module_onload(); #endif @@ -406,6 +412,10 @@ kore_worker_entry(struct kore_worker *kw) #endif net_cleanup(); +#if defined(KORE_USE_PYTHON) + kore_python_cleanup(); +#endif + kore_debug("worker %d shutting down", kw->id); exit(0); }