#include extern struct uwsgi_server uwsgi; struct uwsgi_symcall { struct uwsgi_string_list *symcall_function_name; int (*symcall_function)(struct wsgi_request *); struct uwsgi_string_list *rpc; struct uwsgi_string_list *post_fork; int use_rtld_next; void *dlsym_handle; } usym; struct uwsgi_plugin symcall_plugin; static struct uwsgi_option uwsgi_symcall_options[] = { {"symcall", required_argument, 0, "load the specified C symbol as the symcall request handler (supports too)", uwsgi_opt_add_string_list, &usym.symcall_function_name, 0}, #ifdef RTLD_NEXT {"symcall-use-next", no_argument, 0, "use RTLD_NEXT when searching for symbols", uwsgi_opt_true, &usym.use_rtld_next, 0}, #endif {"symcall-register-rpc", required_argument, 0, "load the specified C symbol as an RPC function (syntax: name function)", uwsgi_opt_add_string_list, &usym.rpc, 0}, {"symcall-post-fork", required_argument, 0, "call the specified C symbol after each fork()", uwsgi_opt_add_string_list, &usym.post_fork, 0}, {0, 0, 0, 0}, }; static void uwsgi_symcall_init(){ #ifdef RTLD_NEXT if (usym.use_rtld_next) { usym.dlsym_handle = RTLD_NEXT; } #endif struct uwsgi_string_list *usl = NULL; int has_mountpoints = 0; uwsgi_foreach(usl, usym.symcall_function_name) { char *func = usl->value, *mountpoint = ""; char *equal = strchr(usl->value, '='); if (equal) { *equal = 0; func = equal+1; mountpoint = usl->value; has_mountpoints = 1; } usl->custom_ptr = dlsym(usym.dlsym_handle, func); if (!usl->custom_ptr) { uwsgi_log("unable to find symbol \"%s\" in process address space\n", func); exit(1); } int id = uwsgi_apps_cnt; struct uwsgi_app *ua = uwsgi_add_app(id, symcall_plugin.modifier1, mountpoint, strlen(mountpoint), usl->custom_ptr, NULL); uwsgi_log("symcall app %d (mountpoint: \"%.*s\") mapped to function ptr: %p\n", id, ua->mountpoint_len, ua->mountpoint, usl->custom_ptr); if (equal) *equal = '='; } if (!has_mountpoints && usym.symcall_function_name) { usym.symcall_function = usym.symcall_function_name->custom_ptr; } uwsgi_foreach(usl, usym.rpc) { char *space = strchr(usl->value, ' '); if (!space) { uwsgi_log("invalid symcall RPC syntax, must be: rpcname symbol\n"); exit(1); } *space = 0; void *func = dlsym(usym.dlsym_handle, space+1); if (!func) { uwsgi_log("unable to find symbol \"%s\" in process address space\n", space+1); exit(1); } if (uwsgi_register_rpc(usl->value, &symcall_plugin, 0, func)) { uwsgi_log("unable to register rpc function"); exit(1); } *space = ' '; } } static int uwsgi_symcall_request(struct wsgi_request *wsgi_req) { if (usym.symcall_function) { return usym.symcall_function(wsgi_req); } if (uwsgi_parse_vars(wsgi_req)) return -1; wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, symcall_plugin.modifier1); if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { if (uwsgi_apps[uwsgi.default_app].modifier1 == symcall_plugin.modifier1) { wsgi_req->app_id = uwsgi.default_app; } } if (wsgi_req->app_id == -1) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; if (ua->interpreter) { int (*func)(struct wsgi_request *) = (int (*)(struct wsgi_request *)) ua->interpreter; return func(wsgi_req); } return UWSGI_OK; } static void uwsgi_symcall_after_request(struct wsgi_request *wsgi_req) { log_request(wsgi_req); } static uint64_t uwsgi_symcall_rpc(void *func, uint8_t argc, char **argv, uint16_t argvs[], char **buffer) { uint64_t (*casted_func)(uint8_t, char **, uint16_t *, char **) = (uint64_t (*)(uint8_t, char **, uint16_t *, char **)) func; return casted_func(argc, argv, argvs, buffer); } static void uwsgi_symcall_post_fork() { void (*func)(void); struct uwsgi_string_list *usl = usym.post_fork; while(usl) { func = dlsym(usym.dlsym_handle, usl->value); if (!func) { uwsgi_log("unable to find symbol \"%s\" in process address space\n", usl->value); exit(1); } func(); usl = usl->next; } } static int uwsgi_symcall_mule(char *opt) { if (uwsgi_endswith(opt, "()")) { char *func_name = uwsgi_concat2n(opt, strlen(opt)-2, "", 0); void (*func)() = dlsym(usym.dlsym_handle, func_name); if (!func) { uwsgi_log("unable to find symbol \"%s\" in process address space\n", func_name); exit(1); } free(func_name); func(); return 1; } return 0; } #ifdef UWSGI_ROUTING static int symcall_route(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { char **subject = (char **) (((char *)(wsgi_req))+ur->subject); uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len); struct uwsgi_buffer *ub = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data, ur->data_len); if (!ub) return UWSGI_ROUTE_BREAK; int (*func)(struct wsgi_request *) = (int (*)(struct wsgi_request *)) dlsym(usym.dlsym_handle, ub->buf); uwsgi_buffer_destroy(ub); if (func) { wsgi_req->async_status = func(wsgi_req); } else { if (ur->custom) return UWSGI_ROUTE_NEXT; uwsgi_404(wsgi_req); } return UWSGI_ROUTE_BREAK; } static int uwsgi_router_symcall(struct uwsgi_route *ur, char *args) { ur->func = symcall_route; ur->data = args; ur->data_len = strlen(args); return 0; } static int uwsgi_router_symcall_next(struct uwsgi_route *ur, char *args) { ur->custom = 1; return uwsgi_router_symcall(ur, args); } #endif static void uwsgi_symcall_register() { usym.dlsym_handle = RTLD_DEFAULT; #ifdef UWSGI_ROUTING uwsgi_register_router("symcall", uwsgi_router_symcall); uwsgi_register_router("symcall-next", uwsgi_router_symcall_next); #endif } struct uwsgi_plugin symcall_plugin = { .name = "symcall", .modifier1 = 18, .options = uwsgi_symcall_options, .init_apps = uwsgi_symcall_init, .request = uwsgi_symcall_request, .after_request = uwsgi_symcall_after_request, .rpc = uwsgi_symcall_rpc, .post_fork = uwsgi_symcall_post_fork, .mule = uwsgi_symcall_mule, .on_load = uwsgi_symcall_register, };