From 687aabd30b3433c70ef698dc967f54da3d844779 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 25 Apr 2014 09:27:34 -0700 Subject: [PATCH 1/9] plugins/alarm_curl: turn on SMTP protocol Compiler ignores SMTP support because enums CURLOPT_MAIL_RCPT and CURLPROTO_SMTP are misused as macros for conditional compilation. Replace the enums with real macro CURLPROTO_SMTP so compiler can detect them. --- plugins/alarm_curl/alarm_curl_plugin.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index bd04d49d..ca436ba0 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -17,7 +17,7 @@ struct uwsgi_alarm_curl_opt { }; -#ifdef CURLOPT_MAIL_RCPT +#ifdef CURLPROTO_SMTP static void uwsgi_alarm_curl_to(CURL *curl, CURLoption option, char *arg, struct uwsgi_alarm_curl_config *uacc) { uacc->to = arg; struct curl_slist *list = NULL; @@ -46,10 +46,8 @@ static void uwsgi_alarm_curl_set_subject(CURL *curl, CURLoption option, char *ar static struct uwsgi_alarm_curl_opt uaco[] = { {"url", CURLOPT_URL, NULL}, -#ifdef CURLOPT_MAIL_RCPT - {"mail_to", CURLOPT_MAIL_RCPT, uwsgi_alarm_curl_to }, -#endif -#ifdef CURLOPT_MAIL_FROM +#ifdef CURLPROTO_SMTP + {"mail_to", CURLOPT_MAIL_RCPT, uwsgi_alarm_curl_to}, {"mail_from", CURLOPT_MAIL_FROM, NULL}, #endif {"subject", 0, uwsgi_alarm_curl_set_subject}, From 6664e7120b10535b0f3e4f94b1b8cd7f154a1852 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 04:34:06 -0700 Subject: [PATCH 2/9] plugins/alarm_curl: exit when requested protocol is not support When SMTP is not supported by libcurl, or user doesn't provide required URL for libcurl, throw an error message and then exit. --- plugins/alarm_curl/alarm_curl_plugin.c | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index ca436ba0..d8225f37 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -4,7 +4,7 @@ extern struct uwsgi_server uwsgi; struct uwsgi_alarm_curl_config { - int first; + char *url; char *arg; char *subject; char *to; @@ -44,8 +44,20 @@ static void uwsgi_alarm_curl_set_subject(CURL *curl, CURLoption option, char *ar uacc->subject = arg; } +static void uwsgi_alarm_curl_url(CURL *curl, CURLoption option, char *arg, struct uwsgi_alarm_curl_config *uacc) +{ +#ifndef CURLPROTO_SMTP + if (!uwsgi_strnicmp(arg, 4, "smtp", 4)) { + uwsgi_error("Please update libcurl to use SMTP protocol.\n"); + exit(1); + } +#endif + uacc->url = arg; + curl_easy_setopt(curl, option, arg); +} + static struct uwsgi_alarm_curl_opt uaco[] = { - {"url", CURLOPT_URL, NULL}, + {"url", CURLOPT_URL, uwsgi_alarm_curl_url}, #ifdef CURLPROTO_SMTP {"mail_to", CURLOPT_MAIL_RCPT, uwsgi_alarm_curl_to}, {"mail_from", CURLOPT_MAIL_FROM, NULL}, @@ -63,14 +75,10 @@ static struct uwsgi_alarm_curl_opt uaco[] = { static void uwsgi_alarm_curl_setopt(CURL *curl, char *opt, struct uwsgi_alarm_curl_config *uacc) { struct uwsgi_alarm_curl_opt *o = uaco; char *equal = strchr(opt,'='); - if (!equal) { - if (!uacc->first) { - curl_easy_setopt(curl, CURLOPT_URL, opt); - uacc->first = 1; - } + if (!equal && !uacc->url) { + uwsgi_alarm_curl_url(curl, CURLOPT_URL, opt, uacc); return; } - uacc->first = 1; *equal = 0; while(o->name) { if (!strcmp(o->name, opt)) { @@ -165,6 +173,11 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { p = strtok_r(NULL, ";", &ctx); } + if (!uacc->url) { + uwsgi_error("An URL is required to trigger curl-based alarm.\n"); + exit(1); + } + for(;;) { int ret = event_queue_wait(ut->queue, -1, &interesting_fd); if (ret < 0) return; From 7e80927317d63197b403873d4d364055e58da354 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 04:37:10 -0700 Subject: [PATCH 3/9] plugins/alarm_curl: free memory used during parsing args --- plugins/alarm_curl/alarm_curl_plugin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index d8225f37..0fc67eeb 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -29,6 +29,7 @@ static void uwsgi_alarm_curl_to(CURL *curl, CURLoption option, char *arg, struct p = strtok_r(NULL, ",", &ctx); } curl_easy_setopt(curl, option, list); + free(items); } #endif @@ -178,6 +179,8 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { exit(1); } + free(opts); + for(;;) { int ret = event_queue_wait(ut->queue, -1, &interesting_fd); if (ret < 0) return; From 9d21c99183b2193a6a202a0885600cff539c157c Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 04:40:37 -0700 Subject: [PATCH 4/9] plugins/alarm_curl: use CRLF instead of LF for line break in MIME Though most programs accept LF, MIME RFC explicitly requires CRLF. --- plugins/alarm_curl/alarm_curl_plugin.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index 0fc67eeb..a726b949 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -107,16 +107,17 @@ static size_t uwsgi_alarm_curl_read_callback(void *ptr, size_t size, size_t nmem if (ut->custom0 == 0) { size_t newline = 0; - size_t required = 1; + size_t required = 2; char *addr = ptr; - if (uacc->to) required += 4 + strlen(uacc->to) + 1; - if (uacc->subject) required += 9 + strlen(uacc->subject) + 1; + if (uacc->to) required += 4 + strlen(uacc->to) + 2; + if (uacc->subject) required += 9 + strlen(uacc->subject) + 2; if (required > full_size) goto skip; if (uacc->to) { memcpy(addr, "To: ", 4); addr+=4; memcpy(addr, uacc->to, strlen(uacc->to)); addr += strlen(uacc->to); + *addr++ = '\r'; *addr ++= '\n'; newline = 1; } @@ -124,11 +125,13 @@ static size_t uwsgi_alarm_curl_read_callback(void *ptr, size_t size, size_t nmem if (uacc->subject) { memcpy(addr, "Subject: ", 9); addr+=9; memcpy(addr, uacc->subject, strlen(uacc->subject)); addr += strlen(uacc->subject); + *addr++ = '\r'; *addr ++= '\n'; newline = 1; } skip: if (newline > 0) { + *addr++ = '\r'; *addr = '\n'; } ut->custom0 = 1; From e008ce5d56c7446ccbc75c07d5c13e492a5c1420 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 04:50:53 -0700 Subject: [PATCH 5/9] plugins/alarm_curl: add SSL option to skip certificate verification The new option ssl_insecure is equivalent to curl command line option --insecure or -k, which is useful for posting alarms via HTTPS to a server with self-generated certificate installed. --- plugins/alarm_curl/alarm_curl_plugin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index a726b949..d1dbd49a 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -57,6 +57,12 @@ static void uwsgi_alarm_curl_url(CURL *curl, CURLoption option, char *arg, struc curl_easy_setopt(curl, option, arg); } +static void uwsgi_alarm_curl_ssl_insecure(CURL *curl, CURLoption option, char *arg, struct uwsgi_alarm_curl_config *uacc) +{ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); +} + static struct uwsgi_alarm_curl_opt uaco[] = { {"url", CURLOPT_URL, uwsgi_alarm_curl_url}, #ifdef CURLPROTO_SMTP @@ -65,6 +71,7 @@ static struct uwsgi_alarm_curl_opt uaco[] = { #endif {"subject", 0, uwsgi_alarm_curl_set_subject}, {"ssl", CURLOPT_USE_SSL, uwsgi_alarm_curl_ssl}, + {"ssl_insecure", 0, uwsgi_alarm_curl_ssl_insecure}, {"auth_user", CURLOPT_USERNAME, NULL}, {"auth_pass", CURLOPT_PASSWORD, NULL}, {"method", CURLOPT_CUSTOMREQUEST, NULL}, From d566469c63a8be5e916dbf1e050feab5b84fc3ea Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 05:05:12 -0700 Subject: [PATCH 6/9] plugins/alarm_curl: fix read callback function for libcurl The read callback for libcurl shouldn't return size that is larger than the requested size when it's too small to hold email header. Otherwise curl will abort the operation. And we shouldn't return 1 (which is the current behavior) while there is no header at all. The expected size of the infile set by option CURLOPT_INFILESIZE or CURLOPT_INFILESIZE_LARGE should include both header and body sizes. The infile size is used when server supports the optional "SIZE" parameter of "MAIL FROM" command. libcurl hangs when wrong size is passed in. Since email header remains unchanged for each alarm_curl instance, we don't have to generate it per alarm request. Based on this, the solution is quite simple: we generate the header once during initialization and use it for all alarms. --- plugins/alarm_curl/alarm_curl_plugin.c | 121 +++++++++++++++---------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index d1dbd49a..6e597a60 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -3,6 +3,14 @@ extern struct uwsgi_server uwsgi; +struct uwsgi_alarm_curl { + int pos; + int blen; + char *body; + int hlen; + char hdr[]; +}; + struct uwsgi_alarm_curl_config { char *url; char *arg; @@ -96,63 +104,77 @@ static void uwsgi_alarm_curl_setopt(CURL *curl, char *opt, struct uwsgi_alarm_cu else { curl_easy_setopt(curl, o->option, equal+1); } - goto end; + break; } o++; } -end: - *equal = '='; } static size_t uwsgi_alarm_curl_read_callback(void *ptr, size_t size, size_t nmemb, void *userp) { - struct uwsgi_thread *ut = (struct uwsgi_thread *) userp; + struct uwsgi_alarm_curl *uac = userp; size_t full_size = size * nmemb; - size_t remains = ut->len - ut->pos; - struct uwsgi_alarm_curl_config *uacc = ut->data; + int remains = full_size; - if (remains == 0) return 0; - - if (ut->custom0 == 0) { - size_t newline = 0; - size_t required = 2; - char *addr = ptr; - if (uacc->to) required += 4 + strlen(uacc->to) + 2; - if (uacc->subject) required += 9 + strlen(uacc->subject) + 2; - if (required > full_size) goto skip; - - - if (uacc->to) { - memcpy(addr, "To: ", 4); addr+=4; - memcpy(addr, uacc->to, strlen(uacc->to)); addr += strlen(uacc->to); - *addr++ = '\r'; - *addr ++= '\n'; - newline = 1; + if (uac->pos < uac->hlen) { + if (remains > uac->hlen - uac->pos) { + memcpy(ptr, uac->hdr + uac->pos, uac->hlen - uac->pos); + ptr += uac->hlen - uac->pos; + remains -= uac->hlen - uac->pos; + uac->pos = uac->hlen; + } else { + memcpy(ptr, uac->hdr + uac->pos, remains); + uac->pos += remains; + return full_size; } - - if (uacc->subject) { - memcpy(addr, "Subject: ", 9); addr+=9; - memcpy(addr, uacc->subject, strlen(uacc->subject)); addr += strlen(uacc->subject); - *addr++ = '\r'; - *addr ++= '\n'; - newline = 1; - } -skip: - if (newline > 0) { - *addr++ = '\r'; - *addr = '\n'; - } - ut->custom0 = 1; - return required; } - if (full_size < remains) { - remains = full_size; + if (remains > uac->blen + uac->hlen - uac->pos) { + memcpy(ptr, uac->body + uac->pos - uac->hlen, uac->blen + uac->hlen - uac->pos); + remains -= uac->blen + uac->hlen - uac->pos; + uac->pos = uac->blen + uac->hlen; + return full_size - remains; } - memcpy(ptr, ut->buf + ut->pos, remains); - ut->pos += remains; + memcpy(ptr, uac->body + uac->pos - uac->hlen, remains); + uac->pos += remains; + return full_size; +} - return remains; +static struct uwsgi_alarm_curl *uwsgi_alarm_curl_alloc(struct uwsgi_alarm_curl_config *uacc) +{ + char *addr; + struct uwsgi_alarm_curl *uac; + size_t required = 0; + + if (uacc->to) required += 4 + strlen(uacc->to) + 2; + if (uacc->subject) required += 9 + strlen(uacc->subject) + 2; + if (required) + required += 2; /* newline between MIME header and body */ + + uac = uwsgi_malloc(sizeof(*uac) + required); + uac->hlen = required; + addr = uac->hdr; + + if (uacc->to) { + memcpy(addr, "To: ", 4); addr += 4; + memcpy(addr, uacc->to, strlen(uacc->to)); addr += strlen(uacc->to); + *addr++ = '\r'; + *addr++ = '\n'; + } + + if (uacc->subject) { + memcpy(addr, "Subject: ", 9); addr += 9; + memcpy(addr, uacc->subject, strlen(uacc->subject)); addr += strlen(uacc->subject); + *addr++ = '\r'; + *addr++ = '\n'; + } + + if (required) { + *addr++ = '\r'; + *addr = '\n'; + } + + return uac; } static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { @@ -166,7 +188,6 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, uwsgi.socket_timeout); curl_easy_setopt(curl, CURLOPT_TIMEOUT, uwsgi.socket_timeout); curl_easy_setopt(curl, CURLOPT_READFUNCTION, uwsgi_alarm_curl_read_callback); - curl_easy_setopt(curl, CURLOPT_READDATA, ut); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_POST, 1L); struct curl_slist *expect = NULL; expect = curl_slist_append(expect, "Expect:"); @@ -180,7 +201,7 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { char *ctx = NULL; char *p = strtok_r(opts, ";", &ctx); while(p) { - uwsgi_alarm_curl_setopt(curl, uwsgi_str(p), uacc); + uwsgi_alarm_curl_setopt(curl, p, uacc); p = strtok_r(NULL, ";", &ctx); } @@ -189,6 +210,8 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { exit(1); } + struct uwsgi_alarm_curl *uac = uwsgi_alarm_curl_alloc(uacc); + curl_easy_setopt(curl, CURLOPT_READDATA, uac); free(opts); for(;;) { @@ -198,10 +221,10 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { if (interesting_fd != ut->pipe[1]) continue; ssize_t rlen = read(ut->pipe[1], ut->buf, uwsgi.log_master_bufsize); if (rlen <= 0) continue; - ut->pos = 0; - ut->len = (size_t) rlen; - ut->custom0 = 0; - curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) ut->len); + uac->pos = 0; + uac->blen = (size_t) rlen; + uac->body = ut->buf; + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(uac->hlen + uac->blen)); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { uwsgi_log_alarm("-curl] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); From eff23382c2aa80e455398eaf4e5b965b14c0ddcf Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 14:28:59 -0700 Subject: [PATCH 7/9] plugins/{airbrake,alarm_curl,alarm_xmpp}: fix race condition A thread may run immediately after it's created. Trying to pass parameter to thread using uwsgi_thread->data without preventing the thread from accessing it first is a race condition. If the thread accesses uwsgi_thread->data first, uwsgi gets killed by SIGSEGV. This patch creates a new function uwsgi_thread_new_with_data() to assign user provided data to uwsgi_thread->data before the thread is created. --- core/utils.c | 7 ++++++- plugins/airbrake/airbrake_plugin.c | 7 +++---- plugins/alarm_curl/alarm_curl_plugin.c | 7 +++---- plugins/alarm_xmpp/alarm_xmpp_plugin.c | 3 +-- uwsgi.h | 1 + 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/core/utils.c b/core/utils.c index 7bc359e0..c0976a1f 100644 --- a/core/utils.c +++ b/core/utils.c @@ -3752,7 +3752,7 @@ static void *uwsgi_thread_run(void *arg) { return NULL; } -struct uwsgi_thread *uwsgi_thread_new(void (*func) (struct uwsgi_thread *)) { +struct uwsgi_thread *uwsgi_thread_new_with_data(void (*func) (struct uwsgi_thread *), void *data) { struct uwsgi_thread *ut = uwsgi_calloc(sizeof(struct uwsgi_thread)); @@ -3769,6 +3769,7 @@ struct uwsgi_thread *uwsgi_thread_new(void (*func) (struct uwsgi_thread *)) { uwsgi_socket_nb(ut->pipe[1]); ut->func = func; + ut->data = data; pthread_attr_init(&ut->tattr); pthread_attr_setdetachstate(&ut->tattr, PTHREAD_CREATE_DETACHED); @@ -3788,6 +3789,10 @@ error: return NULL; } +struct uwsgi_thread *uwsgi_thread_new(void (*func) (struct uwsgi_thread *)) { + return uwsgi_thread_new_with_data(func, NULL); +} + int uwsgi_kvlist_parse(char *src, size_t len, char list_separator, char kv_separator, ...) { size_t i; va_list ap; diff --git a/plugins/airbrake/airbrake_plugin.c b/plugins/airbrake/airbrake_plugin.c index ee54b72a..69d368c7 100644 --- a/plugins/airbrake/airbrake_plugin.c +++ b/plugins/airbrake/airbrake_plugin.c @@ -271,12 +271,11 @@ static void uwsgi_airbrake_loop(struct uwsgi_thread *ut) { } static void uwsgi_airbrake_init(struct uwsgi_alarm_instance *uai) { - struct uwsgi_thread *ut = uwsgi_thread_new(uwsgi_airbrake_loop); - if (!ut) return; - uai->data_ptr = ut; struct uwsgi_airbrake_config *uacc = uwsgi_calloc(sizeof(struct uwsgi_airbrake_config)); uacc->arg = uai->arg; - ut->data = uacc; + struct uwsgi_thread *ut = uwsgi_thread_new_with_data(uwsgi_airbrake_loop, uacc); + if (!ut) return; + uai->data_ptr = ut; } static void uwsgi_airbrake_func(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index 6e597a60..52e16076 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -234,12 +234,11 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { } static void uwsgi_alarm_curl_init(struct uwsgi_alarm_instance *uai) { - struct uwsgi_thread *ut = uwsgi_thread_new(uwsgi_alarm_curl_loop); - if (!ut) return; - uai->data_ptr = ut; struct uwsgi_alarm_curl_config *uacc = uwsgi_calloc(sizeof(struct uwsgi_alarm_curl_config)); uacc->arg = uai->arg; - ut->data = uacc; + struct uwsgi_thread *ut = uwsgi_thread_new_with_data(uwsgi_alarm_curl_loop, uacc); + if (!ut) return; + uai->data_ptr = ut; } // pipe the message into the thread; diff --git a/plugins/alarm_xmpp/alarm_xmpp_plugin.c b/plugins/alarm_xmpp/alarm_xmpp_plugin.c index 595465a8..784a9650 100644 --- a/plugins/alarm_xmpp/alarm_xmpp_plugin.c +++ b/plugins/alarm_xmpp/alarm_xmpp_plugin.c @@ -4,10 +4,9 @@ void uwsgi_alarm_xmpp_loop(struct uwsgi_thread *); static void uwsgi_alarm_xmpp_init(struct uwsgi_alarm_instance *uai) { - struct uwsgi_thread *ut = uwsgi_thread_new(uwsgi_alarm_xmpp_loop); + struct uwsgi_thread *ut = uwsgi_thread_new_with_data(uwsgi_alarm_xmpp_loop, uai->arg); if (!ut) return; uai->data_ptr = ut; - ut->data = uai->arg; } // pipe the message into the thread; diff --git a/uwsgi.h b/uwsgi.h index 48d01a2a..37a87b9d 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -4175,6 +4175,7 @@ struct uwsgi_thread { void (*func) (struct uwsgi_thread *); }; struct uwsgi_thread *uwsgi_thread_new(void (*)(struct uwsgi_thread *)); +struct uwsgi_thread *uwsgi_thread_new_with_data(void (*)(struct uwsgi_thread *), void *data); struct uwsgi_offload_request { // the request socket From c146275e9b1b32130c8f5f3cb1c646c8d9255ebc Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 15:03:09 -0700 Subject: [PATCH 8/9] plugins/alarm_curl: refactor code No need to calloc struct uwsgi_alarm_curl_config because all data we need is populated and stored in struct uwsgi_alarm_curl. Save some memory when there are hundreds of curl-based alarms. --- plugins/alarm_curl/alarm_curl_plugin.c | 65 +++++++++++++++----------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index 52e16076..ecda0382 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -4,6 +4,8 @@ extern struct uwsgi_server uwsgi; struct uwsgi_alarm_curl { + CURL *curl; + struct uwsgi_thread *ut; int pos; int blen; char *body; @@ -13,7 +15,6 @@ struct uwsgi_alarm_curl { struct uwsgi_alarm_curl_config { char *url; - char *arg; char *subject; char *to; }; @@ -177,13 +178,12 @@ static struct uwsgi_alarm_curl *uwsgi_alarm_curl_alloc(struct uwsgi_alarm_curl_c return uac; } -static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { - int interesting_fd; - ut->buf = uwsgi_malloc(uwsgi.log_master_bufsize); - +static struct uwsgi_alarm_curl *uwsgi_alarm_curl_init_curl(struct uwsgi_alarm_instance *uai) { CURL *curl = curl_easy_init(); - // ARGH !!! - if (!curl) return; + if (!curl) { + uwsgi_error("Failed to initialize libcurl.\n"); + exit(1); + } curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, uwsgi.socket_timeout); curl_easy_setopt(curl, CURLOPT_TIMEOUT, uwsgi.socket_timeout); @@ -194,26 +194,48 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, expect); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - struct uwsgi_alarm_curl_config *uacc = (struct uwsgi_alarm_curl_config *) ut->data; - char *opts = uwsgi_str(uacc->arg); + struct uwsgi_alarm_curl_config uacc; + memset(&uacc, 0, sizeof(uacc)); + char *opts = uwsgi_str(uai->arg); // fill curl options char *ctx = NULL; char *p = strtok_r(opts, ";", &ctx); while(p) { - uwsgi_alarm_curl_setopt(curl, p, uacc); + uwsgi_alarm_curl_setopt(curl, p, &uacc); p = strtok_r(NULL, ";", &ctx); } - if (!uacc->url) { + if (!uacc.url) { uwsgi_error("An URL is required to trigger curl-based alarm.\n"); exit(1); } - struct uwsgi_alarm_curl *uac = uwsgi_alarm_curl_alloc(uacc); + struct uwsgi_alarm_curl *uac = uwsgi_alarm_curl_alloc(&uacc); curl_easy_setopt(curl, CURLOPT_READDATA, uac); free(opts); + uac->curl = curl; + uai->data_ptr = uac; + return uac; +} + +static void uwsgi_alarm_curl_call_curl(struct uwsgi_alarm_curl *uac, char *msg, int len) +{ + uac->pos = 0; + uac->body = msg; + uac->blen = len; + curl_easy_setopt(uac->curl, CURLOPT_INFILESIZE, uac->hlen + uac->blen); + CURLcode res = curl_easy_perform(uac->curl); + if (res != CURLE_OK) + uwsgi_log_alarm("-curl] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); +} + +static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { + int interesting_fd; + struct uwsgi_alarm_curl *uac = uwsgi_alarm_curl_init_curl(ut->data); + uac->ut = ut; + ut->buf = uwsgi_malloc(uwsgi.log_master_bufsize); for(;;) { int ret = event_queue_wait(ut->queue, -1, &interesting_fd); if (ret < 0) return; @@ -221,29 +243,18 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { if (interesting_fd != ut->pipe[1]) continue; ssize_t rlen = read(ut->pipe[1], ut->buf, uwsgi.log_master_bufsize); if (rlen <= 0) continue; - uac->pos = 0; - uac->blen = (size_t) rlen; - uac->body = ut->buf; - curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(uac->hlen + uac->blen)); - CURLcode res = curl_easy_perform(curl); - if (res != CURLE_OK) { - uwsgi_log_alarm("-curl] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - + uwsgi_alarm_curl_call_curl(uac, ut->buf, rlen); } } static void uwsgi_alarm_curl_init(struct uwsgi_alarm_instance *uai) { - struct uwsgi_alarm_curl_config *uacc = uwsgi_calloc(sizeof(struct uwsgi_alarm_curl_config)); - uacc->arg = uai->arg; - struct uwsgi_thread *ut = uwsgi_thread_new_with_data(uwsgi_alarm_curl_loop, uacc); - if (!ut) return; - uai->data_ptr = ut; + uwsgi_thread_new_with_data(uwsgi_alarm_curl_loop, uai); } // pipe the message into the thread; static void uwsgi_alarm_curl_func(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { - struct uwsgi_thread *ut = (struct uwsgi_thread *) uai->data_ptr; + struct uwsgi_alarm_curl *uac = uai->data_ptr; + struct uwsgi_thread *ut = uac->ut; ut->rlen = write(ut->pipe[0], msg, len); } From 418d7841487406c04e3a6fcb2aa9f503df0cc392 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sun, 27 Apr 2014 15:29:00 -0700 Subject: [PATCH 9/9] plugins/alarm_curl: option for disabling dedicated alarm threads A new global option alarm-cheap allows users to disable creating thread per curl-based alarm. This option can be extended to other alarm plugins like xmpp. --- core/alarm.c | 45 +++++++++++++------------- core/uwsgi.c | 1 + plugins/alarm_curl/alarm_curl_plugin.c | 13 ++++++-- uwsgi.h | 2 +- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/core/alarm.c b/core/alarm.c index eb50ea7b..781c76c1 100644 --- a/core/alarm.c +++ b/core/alarm.c @@ -363,6 +363,19 @@ void uwsgi_alarm_thread_start() { } } +void uwsgi_alarm_trigger_uai(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { + struct iovec iov[2]; + iov[0].iov_base = &uai; + iov[0].iov_len = sizeof(long); + iov[1].iov_base = msg; + iov[1].iov_len = len; + + // now send the message to the alarm thread + if (writev(uwsgi.alarm_thread->pipe[0], iov, 2) != (ssize_t) (len+sizeof(long))) { + uwsgi_error("[uwsgi-alarm-error] uwsgi_alarm_trigger()/writev()"); + } +} + #ifdef UWSGI_PCRE // check if a log should raise an alarm void uwsgi_alarm_log_check(char *msg, size_t len) { @@ -372,7 +385,14 @@ void uwsgi_alarm_log_check(char *msg, size_t len) { while (ual) { if (uwsgi_regexp_match(ual->pattern, ual->pattern_extra, msg, len) >= 0) { if (!ual->negate) { - uwsgi_alarm_log_run(ual, msg, len); + struct uwsgi_alarm_ll *uall = ual->alarms; + while (uall) { + if (uwsgi.alarm_cheap) + uwsgi_alarm_trigger_uai(uall->alarm, msg, len); + else + uwsgi_alarm_run(uall->alarm, msg, len); + uall = uall->next; + } } else { break; @@ -397,18 +417,6 @@ void uwsgi_alarm_run(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { uai->last_msg_size = len; } -#ifdef UWSGI_PCRE -// call the alarms mapped to a log line -void uwsgi_alarm_log_run(struct uwsgi_alarm_log *ual, char *msg, size_t len) { - struct uwsgi_alarm_ll *uall = ual->alarms; - while (uall) { - uwsgi_alarm_run(uall->alarm, msg, len); - uall = uall->next; - } -} -#endif - - // this is the api function workers,mules and whatever you want can call from code void uwsgi_alarm_trigger(char *alarm_instance_name, char *msg, size_t len) { if (!uwsgi.alarm_thread) return; @@ -416,16 +424,7 @@ void uwsgi_alarm_trigger(char *alarm_instance_name, char *msg, size_t len) { struct uwsgi_alarm_instance *uai = uwsgi_alarm_get_instance(alarm_instance_name); if (!uai) return; - struct iovec iov[2]; - iov[0].iov_base = &uai; - iov[0].iov_len = sizeof(long); - iov[1].iov_base = msg; - iov[1].iov_len = len; - - // now send the message to the alarm thread - if (writev(uwsgi.alarm_thread->pipe[0], iov, 2) != (ssize_t) (len+sizeof(long))) { - uwsgi_error("[uwsgi-alarm-error] uwsgi_alarm_trigger()/writev()"); - } + uwsgi_alarm_trigger_uai(uai, msg, len); } struct uwsgi_alarm_fd *uwsgi_add_alarm_fd(int fd, char *alarm, size_t buf_len, char *msg, size_t msg_len) { diff --git a/core/uwsgi.c b/core/uwsgi.c index 1f0c8c46..b1145ebb 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -673,6 +673,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"use-abort", no_argument, 0, "call abort() on segfault/fpe, could be useful for generating a core dump", uwsgi_opt_true, &uwsgi.use_abort, 0}, {"alarm", required_argument, 0, "create a new alarm, syntax: ", uwsgi_opt_add_string_list, &uwsgi.alarm_list, UWSGI_OPT_MASTER}, + {"alarm-cheap", required_argument, 0, "use main alarm thread rather than create dedicated threads for curl-based alarms", uwsgi_opt_true, &uwsgi.alarm_cheap, 0}, {"alarm-freq", required_argument, 0, "tune the anti-loop alam system (default 3 seconds)", uwsgi_opt_set_int, &uwsgi.alarm_freq, 0}, {"alarm-fd", required_argument, 0, "raise the specified alarm when an fd is read for read (by default it reads 1 byte, set 8 for eventfd)", uwsgi_opt_add_string_list, &uwsgi.alarm_fd_list, UWSGI_OPT_MASTER}, {"alarm-segfault", required_argument, 0, "raise the specified alarm when the segmentation fault handler is executed", uwsgi_opt_add_string_list, &uwsgi.alarm_segfault, UWSGI_OPT_MASTER}, diff --git a/plugins/alarm_curl/alarm_curl_plugin.c b/plugins/alarm_curl/alarm_curl_plugin.c index ecda0382..0bb5f687 100644 --- a/plugins/alarm_curl/alarm_curl_plugin.c +++ b/plugins/alarm_curl/alarm_curl_plugin.c @@ -248,14 +248,21 @@ static void uwsgi_alarm_curl_loop(struct uwsgi_thread *ut) { } static void uwsgi_alarm_curl_init(struct uwsgi_alarm_instance *uai) { - uwsgi_thread_new_with_data(uwsgi_alarm_curl_loop, uai); + if (uwsgi.alarm_cheap) + uwsgi_alarm_curl_init_curl(uai); + else + uwsgi_thread_new_with_data(uwsgi_alarm_curl_loop, uai); } // pipe the message into the thread; static void uwsgi_alarm_curl_func(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { struct uwsgi_alarm_curl *uac = uai->data_ptr; - struct uwsgi_thread *ut = uac->ut; - ut->rlen = write(ut->pipe[0], msg, len); + if (uwsgi.alarm_cheap) + uwsgi_alarm_curl_call_curl(uac, msg, len); + else { + struct uwsgi_thread *ut = uac->ut; + ut->rlen = write(ut->pipe[0], msg, len); + } } static void uwsgi_alarm_curl_load(void) { diff --git a/uwsgi.h b/uwsgi.h index 37a87b9d..0dc855b6 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -2708,6 +2708,7 @@ struct uwsgi_server { char *subscription_notify_socket; int mule_reload_mercy; + int alarm_cheap; }; struct uwsgi_rpc { @@ -4149,7 +4150,6 @@ char *uwsgi_elf_section(char *, char *, size_t *); void uwsgi_alarm_log_check(char *, size_t); void uwsgi_alarm_run(struct uwsgi_alarm_instance *, char *, size_t); -void uwsgi_alarm_log_run(struct uwsgi_alarm_log *, char *, size_t); void uwsgi_register_alarm(char *, void (*)(struct uwsgi_alarm_instance *), void (*)(struct uwsgi_alarm_instance *, char *, size_t)); void uwsgi_register_embedded_alarms(); void uwsgi_alarms_init();