diff --git a/configure b/configure index 3cd02ea3..00d302a7 100755 --- a/configure +++ b/configure @@ -165,6 +165,9 @@ fi # if enabled_or_auto curl; then if check_pkg libcurl; then + if check_pkg nspr; then + enable nspr + fi enable curl elif enabled curl; then die "Curl development support not foun (use --disable-curl)" diff --git a/src/http.h b/src/http.h index 120dda8f..eb3264ce 100644 --- a/src/http.h +++ b/src/http.h @@ -158,4 +158,6 @@ http_connect ( const url_t *url, void *p ); void http_close ( http_client_t *hc ); +void curl_done ( void ); + #endif /* HTTP_H_ */ diff --git a/src/http/http_client.c b/src/http/http_client.c index 84b64c9c..3cba6459 100644 --- a/src/http/http_client.c +++ b/src/http/http_client.c @@ -31,6 +31,7 @@ #include #include #include +#include /* @@ -41,6 +42,7 @@ struct http_client CURL *hc_curl; int hc_fd; url_t hc_url; + int hc_init; int hc_begin; /* Callbacks */ @@ -55,10 +57,12 @@ struct http_client /* * Global state */ +static int http_running; static tvhpoll_t *http_poll; static TAILQ_HEAD(,http_client) http_clients; static pthread_mutex_t http_lock; static CURLM *http_curlm; +static th_pipe_t http_pipe; /* * Disable @@ -109,6 +113,7 @@ http_curl_socket ( CURL *c, int fd, int a, void *u, void *s ) if (a & CURL_POLL_OUT) ev.events |= TVHPOLL_OUT; ev.data.fd = fd; + ev.data.u64 = (uint64_t)hc; hc->hc_fd = fd; tvhpoll_add(http_poll, &ev, 1); } @@ -138,27 +143,41 @@ http_curl_data ( void *buf, size_t len, size_t n, void *p ) static void * http_thread ( void *p ) { - int n, e, run = 0; + int n, run = 0; tvhpoll_event_t ev; http_client_t *hc; + char c; - while (tvheadend_running) { + while (http_running) { n = tvhpoll_wait(http_poll, &ev, 1, -1); if (n < 0) { if (tvheadend_running) tvherror("http_client", "tvhpoll_wait() error"); - break; - } else { + } else if (n > 0) { + if ((uint64_t)&http_pipe == ev.data.u64) { + if (read(http_pipe.rd, &c, 1) == 1) { + if (c == 'n') { + pthread_mutex_lock(&http_lock); + TAILQ_FOREACH(hc, &http_clients, hc_link) { + if (hc->hc_init == 0) + continue; + hc->hc_init = 0; + curl_multi_socket_action(http_curlm, hc->hc_fd, 0, &run); + } + pthread_mutex_unlock(&http_lock); + } else { + /* end-of-task */ + break; + } + } + continue; + } pthread_mutex_lock(&http_lock); TAILQ_FOREACH(hc, &http_clients, hc_link) - if (hc->hc_fd == ev.data.fd) + if ((uint64_t)hc == ev.data.u64) break; - if (hc && (ev.events & (TVHPOLL_IN | TVHPOLL_OUT))) { - e = 0; - if (ev.events & TVHPOLL_IN) e |= CURL_POLL_IN; - if (ev.events & TVHPOLL_OUT) e |= CURL_POLL_OUT; - curl_multi_socket_action(http_curlm, ev.data.fd, 0, &run); - } + if (hc && (ev.events & (TVHPOLL_IN | TVHPOLL_OUT))) + curl_multi_socket_action(http_curlm, hc->hc_fd, 0, &run); pthread_mutex_unlock(&http_lock); } } @@ -177,8 +196,6 @@ http_connect http_client_fail_cb fail_cb, void *p ) { - int run; - /* Setup structure */ http_client_t *hc = calloc(1, sizeof(http_client_t)); hc->hc_curl = curl_easy_init(); @@ -187,6 +204,7 @@ http_connect hc->hc_data = data_cb; hc->hc_fail = fail_cb; hc->hc_opaque = p; + hc->hc_init = 1; /* Store */ pthread_mutex_lock(&http_lock); @@ -198,9 +216,10 @@ http_connect curl_easy_setopt(hc->hc_curl, CURLOPT_WRITEFUNCTION, http_curl_data); curl_easy_setopt(hc->hc_curl, CURLOPT_WRITEDATA, hc); curl_multi_add_handle(http_curlm, hc->hc_curl); - curl_multi_socket_action(http_curlm, CURL_SOCKET_TIMEOUT, 0, &run); pthread_mutex_unlock(&http_lock); + tvh_write(http_pipe.wr, "n", 1); + return hc; } @@ -224,6 +243,8 @@ pthread_t http_client_tid; void http_client_init ( void ) { + tvhpoll_event_t ev = { 0 }; + /* Setup list */ pthread_mutex_init(&http_lock, NULL); TAILQ_INIT(&http_clients); @@ -233,21 +254,48 @@ http_client_init ( void ) http_curlm = curl_multi_init(); curl_multi_setopt(http_curlm, CURLMOPT_SOCKETFUNCTION, http_curl_socket); + /* Setup pipe */ + tvh_pipe(O_NONBLOCK, &http_pipe); + /* Setup poll */ - http_poll = tvhpoll_create(10); + http_poll = tvhpoll_create(10); + ev.fd = http_pipe.rd; + ev.events = TVHPOLL_IN; + ev.data.u64 = (uint64_t)&http_pipe; + tvhpoll_add(http_poll, &ev, 1); /* Setup thread */ + http_running = 1; tvhthread_create(&http_client_tid, NULL, http_thread, NULL, 0); } void http_client_done ( void ) { - pthread_kill(http_client_tid, SIGTERM); + http_running = 0; + tvh_write(http_pipe.wr, "", 1); pthread_join(http_client_tid, NULL); + assert(TAILQ_FIRST(&http_clients) == NULL); + tvh_pipe_close(&http_pipe); tvhpoll_destroy(http_poll); curl_multi_cleanup(http_curlm); +} + + +void +curl_done ( void ) +{ +#if ENABLE_NSPR + void PR_Cleanup( void ); +#endif curl_global_cleanup(); +#if ENABLE_NSPR + /* + * Note: Curl depends on the NSPR library. + * The PR_Cleanup() call is mandatory to free NSPR resources. + */ + PR_Cleanup(); +#endif } #else /* ENABLE_CURL */ @@ -262,4 +310,9 @@ http_client_done ( void ) { } +void +curl_done ( void ) +{ +} + #endif /* ENABLE_CURL */ diff --git a/src/main.c b/src/main.c index 2245ecde..534e99cc 100644 --- a/src/main.c +++ b/src/main.c @@ -748,6 +748,8 @@ main(int argc, char **argv) imagecache_init(); + http_client_init(); + service_init(); #if ENABLE_MPEGTS @@ -764,7 +766,6 @@ main(int argc, char **argv) timeshift_init(); #endif - http_client_init(); tcp_server_init(opt_ipv6); http_server_init(opt_bindaddr); webui_init(); @@ -823,11 +824,11 @@ main(int argc, char **argv) tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); - tvhftrace("main", http_client_done); tvhftrace("main", fsmonitor_done); #if ENABLE_MPEGTS tvhftrace("main", mpegts_done); #endif + tvhftrace("main", http_client_done); // Note: the locking is obviously a bit redundant, but without // we need to disable the gtimer_arm call in epg_save() @@ -867,6 +868,8 @@ main(int argc, char **argv) free(opt_tsfile.str); + curl_done(); + return 0; }