mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-06-16 02:15:48 +00:00
499 lines
16 KiB
C
499 lines
16 KiB
C
#include <uwsgi.h>
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
/*
|
|
|
|
Exceptions management
|
|
|
|
generally exceptions are printed in the logs, but if you enable
|
|
an exception manager they will be stored in a (relatively big) uwsgi packet
|
|
with the following structure.
|
|
|
|
"vars" -> keyval of request vars
|
|
"backtrace" -> list of backtrace lines. Each line is a list of 5 elements filename,line,function,text,custom
|
|
"unix" -> seconds since the epoch
|
|
"class" -> the exception class
|
|
"msg" -> a text message mapped to the extension
|
|
"repr" -> a text message mapped to the extension in language-specific gergo
|
|
"wid" -> worker id
|
|
"core" -> the core generating the exception
|
|
"pid" -> pid of the worker
|
|
"node" -> hostname
|
|
|
|
Other vars can be added, but you cannot be sure they will be used by exceptions handler.
|
|
|
|
The exception-uwsgi packet is passed "as is" to the exception handler
|
|
|
|
Exceptions hooks:
|
|
|
|
a request plugin can export that hooks:
|
|
|
|
struct uwsgi_buffer *backtrace(struct wsgi_request *);
|
|
struct uwsgi_buffer *exception_class(struct wsgi_request *);
|
|
struct uwsgi_buffer *exception_msg(struct wsgi_request *);
|
|
struct uwsgi_buffer *exception_repr(struct wsgi_request *);
|
|
void exception_log(struct wsgi_request *);
|
|
|
|
Remember to reset the exception status (if possibile) after each call
|
|
|
|
Exceptions catcher:
|
|
|
|
This is a special development-mode in which exceptions are printed
|
|
to the HTTP client.
|
|
|
|
*/
|
|
|
|
struct uwsgi_buffer *uwsgi_exception_handler_object(struct wsgi_request *wsgi_req) {
|
|
struct uwsgi_buffer *ub = uwsgi_buffer_new(4096);
|
|
if (uwsgi_buffer_append_keyval(ub, "vars", 4, wsgi_req->buffer, wsgi_req->len)) goto error;
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->backtrace) {
|
|
struct uwsgi_buffer *bt = uwsgi.p[wsgi_req->uh->modifier1]->backtrace(wsgi_req);
|
|
if (bt) {
|
|
if (uwsgi_buffer_append_keyval(ub, "backtrace", 9, bt->buf, bt->pos)) {
|
|
uwsgi_buffer_destroy(bt);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(bt);
|
|
}
|
|
}
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
|
|
struct uwsgi_buffer *ec = uwsgi.p[wsgi_req->uh->modifier1]->exception_class(wsgi_req);
|
|
if (ec) {
|
|
if (uwsgi_buffer_append_keyval(ub, "class", 5, ec->buf, ec->pos)) {
|
|
uwsgi_buffer_destroy(ec);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(ec);
|
|
}
|
|
}
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
|
|
struct uwsgi_buffer *em = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
|
|
if (em) {
|
|
if (uwsgi_buffer_append_keyval(ub, "msg", 3, em->buf, em->pos)) {
|
|
uwsgi_buffer_destroy(em);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(em);
|
|
}
|
|
}
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
|
|
struct uwsgi_buffer *er = uwsgi.p[wsgi_req->uh->modifier1]->exception_repr(wsgi_req);
|
|
if (er) {
|
|
if (uwsgi_buffer_append_keyval(ub, "repr", 4, er->buf, er->pos)) {
|
|
uwsgi_buffer_destroy(er);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(er);
|
|
}
|
|
}
|
|
|
|
if (uwsgi_buffer_append_keynum(ub, "unix", 4, uwsgi_now())) goto error;
|
|
if (uwsgi_buffer_append_keynum(ub, "wid", 3, uwsgi.mywid)) goto error;
|
|
if (uwsgi_buffer_append_keynum(ub, "pid", 3, uwsgi.mypid)) goto error;
|
|
if (uwsgi_buffer_append_keynum(ub, "core", 4, wsgi_req->async_id)) goto error;
|
|
if (uwsgi_buffer_append_keyval(ub, "node", 4, uwsgi.hostname, uwsgi.hostname_len)) goto error;
|
|
|
|
return ub;
|
|
|
|
error:
|
|
uwsgi_buffer_destroy(ub);
|
|
return NULL;
|
|
}
|
|
|
|
static void append_vars_to_ubuf(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) {
|
|
struct uwsgi_buffer *ub = (struct uwsgi_buffer *) data;
|
|
|
|
if (uwsgi_buffer_append(ub, key, keylen)) return;
|
|
if (uwsgi_buffer_append(ub, " = ", 3)) return;
|
|
if (uwsgi_buffer_append(ub, val, vallen)) return;
|
|
if (uwsgi_buffer_append(ub, "\n", 1)) return;
|
|
}
|
|
|
|
static void append_backtrace_to_ubuf(uint16_t pos, char *value, uint16_t len, void *data) {
|
|
struct uwsgi_buffer *ub = (struct uwsgi_buffer *) data;
|
|
|
|
uint16_t item = 0;
|
|
if (pos > 0) {
|
|
item = pos % 5;
|
|
}
|
|
|
|
switch(item) {
|
|
// filename
|
|
case 0:
|
|
if (uwsgi_buffer_append(ub, "filename: \"", 11)) return;
|
|
if (uwsgi_buffer_append(ub, value, len)) return;
|
|
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
|
|
break;
|
|
// lineno
|
|
case 1:
|
|
if (uwsgi_buffer_append(ub, "line: ", 6)) return;
|
|
if (uwsgi_buffer_append(ub, value, len)) return;
|
|
if (uwsgi_buffer_append(ub, " ", 1)) return;
|
|
break;
|
|
// function
|
|
case 2:
|
|
if (uwsgi_buffer_append(ub, "function: \"", 11)) return;
|
|
if (uwsgi_buffer_append(ub, value, len)) return;
|
|
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
|
|
break;
|
|
// text
|
|
case 3:
|
|
if (len > 0) {
|
|
if (uwsgi_buffer_append(ub, "text/code: \"", 12)) return;
|
|
if (uwsgi_buffer_append(ub, value, len)) return;
|
|
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
|
|
}
|
|
break;
|
|
// custom
|
|
case 4:
|
|
if (len > 0) {
|
|
if (uwsgi_buffer_append(ub, "custom: \"", 9)) return;
|
|
if (uwsgi_buffer_append(ub, value, len)) return;
|
|
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
|
|
}
|
|
if (uwsgi_buffer_append(ub, "\n", 1)) return;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int uwsgi_exceptions_catch(struct wsgi_request *wsgi_req) {
|
|
|
|
if (uwsgi_response_prepare_headers(wsgi_req, "500 Internal Server Error", 25)) {
|
|
return -1;
|
|
}
|
|
|
|
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) {
|
|
return -1;
|
|
}
|
|
|
|
struct uwsgi_buffer *ub = uwsgi_buffer_new(4096);
|
|
if (uwsgi_buffer_append(ub, "uWSGI exceptions catcher for \"", 30)) goto error;
|
|
if (uwsgi_buffer_append(ub, wsgi_req->method, wsgi_req->method_len)) goto error;
|
|
if (uwsgi_buffer_append(ub, " ", 1)) goto error;
|
|
if (uwsgi_buffer_append(ub, wsgi_req->uri, wsgi_req->uri_len)) goto error;
|
|
if (uwsgi_buffer_append(ub, "\" (request plugin: \"", 20)) goto error;
|
|
if (uwsgi_buffer_append(ub, (char *) uwsgi.p[wsgi_req->uh->modifier1]->name, strlen(uwsgi.p[wsgi_req->uh->modifier1]->name))) goto error;
|
|
if (uwsgi_buffer_append(ub, "\", modifier1: ", 14 )) goto error;
|
|
if (uwsgi_buffer_num64(ub, wsgi_req->uh->modifier1)) goto error;
|
|
if (uwsgi_buffer_append(ub, ")\n\n", 3)) goto error;
|
|
|
|
if (uwsgi_buffer_append(ub, "Exception: ", 11)) goto error;
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
|
|
struct uwsgi_buffer *ub_exc_repr = uwsgi.p[wsgi_req->uh->modifier1]->exception_repr(wsgi_req);
|
|
if (ub_exc_repr) {
|
|
if (uwsgi_buffer_append(ub, ub_exc_repr->buf, ub_exc_repr->pos)) {
|
|
uwsgi_buffer_destroy(ub_exc_repr);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(ub_exc_repr);
|
|
}
|
|
else {
|
|
goto notavail3;
|
|
}
|
|
}
|
|
else {
|
|
notavail3:
|
|
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
|
|
}
|
|
|
|
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
|
|
|
|
if (uwsgi_buffer_append(ub, "Exception class: ", 17)) goto error;
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
|
|
struct uwsgi_buffer *ub_exc_class = uwsgi.p[wsgi_req->uh->modifier1]->exception_class(wsgi_req);
|
|
if (ub_exc_class) {
|
|
if (uwsgi_buffer_append(ub, ub_exc_class->buf, ub_exc_class->pos)) {
|
|
uwsgi_buffer_destroy(ub_exc_class);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(ub_exc_class);
|
|
}
|
|
else {
|
|
goto notavail;
|
|
}
|
|
}
|
|
else {
|
|
notavail:
|
|
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
|
|
}
|
|
|
|
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
|
|
|
|
if (uwsgi_buffer_append(ub, "Exception message: ", 19)) goto error;
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
|
|
struct uwsgi_buffer *ub_exc_msg = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
|
|
if (ub_exc_msg) {
|
|
if (uwsgi_buffer_append(ub, ub_exc_msg->buf, ub_exc_msg->pos)) {
|
|
uwsgi_buffer_destroy(ub_exc_msg);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(ub_exc_msg);
|
|
}
|
|
else {
|
|
goto notavail2;
|
|
}
|
|
}
|
|
else {
|
|
notavail2:
|
|
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
|
|
}
|
|
|
|
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
|
|
|
|
if (uwsgi_buffer_append(ub, "Backtrace:\n", 11)) goto error;
|
|
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->backtrace) {
|
|
struct uwsgi_buffer *ub_exc_bt = uwsgi.p[wsgi_req->uh->modifier1]->backtrace(wsgi_req);
|
|
if (ub_exc_bt) {
|
|
struct uwsgi_buffer *parsed_bt = uwsgi_buffer_new(4096);
|
|
if (uwsgi_hooked_parse_array(ub_exc_bt->buf, ub_exc_bt->pos, append_backtrace_to_ubuf, parsed_bt)) {
|
|
uwsgi_buffer_destroy(ub_exc_bt);
|
|
uwsgi_buffer_destroy(parsed_bt);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(ub_exc_bt);
|
|
if (uwsgi_buffer_append(ub, parsed_bt->buf, parsed_bt->pos)) {
|
|
uwsgi_buffer_destroy(parsed_bt);
|
|
goto error;
|
|
}
|
|
uwsgi_buffer_destroy(parsed_bt);
|
|
}
|
|
else {
|
|
goto notavail4;
|
|
}
|
|
}
|
|
else {
|
|
notavail4:
|
|
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
|
|
}
|
|
|
|
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
|
|
|
|
if (uwsgi_hooked_parse(wsgi_req->buffer, wsgi_req->len, append_vars_to_ubuf, ub)) {
|
|
goto error;
|
|
}
|
|
|
|
if (uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos)) {
|
|
goto error;
|
|
}
|
|
|
|
uwsgi_buffer_destroy(ub);
|
|
return 0;
|
|
|
|
error:
|
|
uwsgi_buffer_destroy(ub);
|
|
return -1;
|
|
|
|
}
|
|
|
|
static void uwsgi_exception_run_handlers(struct uwsgi_buffer *ub) {
|
|
struct uwsgi_string_list *usl = uwsgi.exception_handlers_instance;
|
|
struct iovec iov[2];
|
|
iov[1].iov_base = ub->buf;
|
|
iov[1].iov_len = ub->pos;
|
|
while(usl) {
|
|
struct uwsgi_exception_handler_instance *uehi = (struct uwsgi_exception_handler_instance *) usl->custom_ptr;
|
|
iov[0].iov_base = &uehi;
|
|
iov[0].iov_len = sizeof(long);
|
|
// now send the message to the exception handler thread
|
|
if (writev(uwsgi.exception_handler_thread->pipe[0], iov, 2) != (ssize_t) (ub->pos+sizeof(long))) {
|
|
uwsgi_error("[uwsgi-exception-handler-error] uwsgi_exception_run_handlers()/writev()");
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
}
|
|
|
|
void uwsgi_manage_exception(struct wsgi_request *wsgi_req,int catch) {
|
|
|
|
int do_exit = uwsgi.reload_on_exception;
|
|
|
|
if (!wsgi_req) goto log2;
|
|
|
|
if (do_exit) goto check_catch;
|
|
|
|
if (uwsgi.exception_handlers_instance) {
|
|
struct uwsgi_buffer *ehi = uwsgi_exception_handler_object(wsgi_req);
|
|
if (ehi) {
|
|
uwsgi_exception_run_handlers(ehi);
|
|
uwsgi_buffer_destroy(ehi);
|
|
}
|
|
}
|
|
|
|
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].exceptions++;
|
|
uwsgi_apps[wsgi_req->app_id].exceptions++;
|
|
|
|
if (uwsgi.reload_on_exception_type && uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
|
|
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
|
|
if (ub) {
|
|
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_type;
|
|
while (usl) {
|
|
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
|
|
do_exit = 1;
|
|
uwsgi_buffer_destroy(ub);
|
|
goto check_catch;
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_buffer_destroy(ub);
|
|
}
|
|
}
|
|
|
|
if (uwsgi.reload_on_exception_value && uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
|
|
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
|
|
if (ub) {
|
|
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_value;
|
|
while (usl) {
|
|
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
|
|
do_exit = 1;
|
|
uwsgi_buffer_destroy(ub);
|
|
goto check_catch;
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_buffer_destroy(ub);
|
|
}
|
|
}
|
|
|
|
if (uwsgi.reload_on_exception_repr && uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
|
|
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
|
|
if (ub) {
|
|
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_repr;
|
|
while (usl) {
|
|
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
|
|
do_exit = 1;
|
|
uwsgi_buffer_destroy(ub);
|
|
goto check_catch;
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_buffer_destroy(ub);
|
|
}
|
|
}
|
|
|
|
check_catch:
|
|
if (catch && wsgi_req) {
|
|
if (uwsgi_exceptions_catch(wsgi_req)) {
|
|
// for now, just goto, new features could be added
|
|
goto log;
|
|
}
|
|
}
|
|
|
|
log:
|
|
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_log) {
|
|
uwsgi.p[wsgi_req->uh->modifier1]->exception_log(wsgi_req);
|
|
}
|
|
|
|
log2:
|
|
if (do_exit) {
|
|
exit(UWSGI_EXCEPTION_CODE);
|
|
}
|
|
|
|
}
|
|
|
|
struct uwsgi_exception_handler *uwsgi_register_exception_handler(char *name, int (*func)(struct uwsgi_exception_handler_instance *, char *, size_t)) {
|
|
struct uwsgi_exception_handler *old_ueh = NULL, *ueh = uwsgi.exception_handlers;
|
|
while(ueh) {
|
|
if (!strcmp(name, ueh->name)) {
|
|
return NULL;
|
|
}
|
|
old_ueh = ueh;
|
|
ueh = ueh->next;
|
|
}
|
|
|
|
ueh = uwsgi_calloc(sizeof(struct uwsgi_exception_handler));
|
|
ueh->name = name;
|
|
ueh->func = func;
|
|
|
|
if (old_ueh) {
|
|
old_ueh->next = ueh;
|
|
}
|
|
else {
|
|
uwsgi.exception_handlers = ueh;
|
|
}
|
|
|
|
return ueh;
|
|
}
|
|
|
|
struct uwsgi_exception_handler *uwsgi_exception_handler_by_name(char *name) {
|
|
struct uwsgi_exception_handler *ueh = uwsgi.exception_handlers;
|
|
while(ueh) {
|
|
if (!strcmp(name, ueh->name)) {
|
|
return ueh;
|
|
}
|
|
ueh = ueh->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void uwsgi_exception_handler_thread_loop(struct uwsgi_thread *ut) {
|
|
char *buf = uwsgi_malloc(uwsgi.exception_handler_msg_size + sizeof(long));
|
|
for (;;) {
|
|
int interesting_fd = -1;
|
|
int ret = event_queue_wait(ut->queue, -1, &interesting_fd);
|
|
if (ret > 0) {
|
|
ssize_t len = read(ut->pipe[1], buf, uwsgi.exception_handler_msg_size + sizeof(long));
|
|
if (len > (ssize_t)(sizeof(long) + 1)) {
|
|
size_t msg_size = len - sizeof(long);
|
|
char *msg = buf + sizeof(long);
|
|
long ptr = 0;
|
|
memcpy(&ptr, buf, sizeof(long));
|
|
struct uwsgi_exception_handler_instance *uehi = (struct uwsgi_exception_handler_instance *) ptr;
|
|
if (!uehi)
|
|
break;
|
|
if (uehi->handler->func(uehi, msg, msg_size)) {
|
|
uwsgi_log("[uwsgi-exception] error running the handler \"%s\" args: \"%s\"\n", uehi->handler->name, uehi->arg ? uehi->arg : "");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(buf);
|
|
}
|
|
|
|
void uwsgi_exception_setup_handlers() {
|
|
|
|
struct uwsgi_string_list *usl = uwsgi.exception_handlers_instance;
|
|
while(usl) {
|
|
// do not free handler !!!
|
|
char *handler = uwsgi_str(usl->value);
|
|
char *colon = strchr(handler, ':');
|
|
if (colon) {
|
|
*colon = 0;
|
|
}
|
|
struct uwsgi_exception_handler *ueh = uwsgi_exception_handler_by_name(handler);
|
|
if (!ueh) {
|
|
uwsgi_log("unable to find exception handler: %s\n", handler);
|
|
exit(1);
|
|
}
|
|
|
|
struct uwsgi_exception_handler_instance *uehi = uwsgi_calloc(sizeof(struct uwsgi_exception_handler_instance));
|
|
uehi->handler = ueh;
|
|
if (colon) {
|
|
uehi->arg = colon+1;
|
|
}
|
|
usl->custom_ptr = uehi;
|
|
usl = usl->next;
|
|
}
|
|
}
|
|
|
|
void uwsgi_exceptions_handler_thread_start() {
|
|
if (!uwsgi.exception_handlers_instance) return;
|
|
// start the exception_handler_thread
|
|
uwsgi.exception_handler_thread = uwsgi_thread_new(uwsgi_exception_handler_thread_loop);
|
|
if (!uwsgi.exception_handler_thread) {
|
|
uwsgi_log("unable to spawn exception handler thread\n");
|
|
exit(1);
|
|
}
|
|
|
|
}
|