diff options
Diffstat (limited to 'plugins/FTPFileYM/curl/lib/easy.c')
-rw-r--r-- | plugins/FTPFileYM/curl/lib/easy.c | 509 |
1 files changed, 453 insertions, 56 deletions
diff --git a/plugins/FTPFileYM/curl/lib/easy.c b/plugins/FTPFileYM/curl/lib/easy.c index c27deffdae..1e718abcba 100644 --- a/plugins/FTPFileYM/curl/lib/easy.c +++ b/plugins/FTPFileYM/curl/lib/easy.c @@ -22,6 +22,14 @@ #include "curl_setup.h" +/* + * See comment in curl_memory.h for the explanation of this sanity check. + */ + +#ifdef CURLX_NO_MEMORY_CALLBACKS +#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" +#endif + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -42,6 +50,11 @@ #include <sys/param.h> #endif +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && defined(USE_OPENSSL) +#define SIGPIPE_IGNORE 1 +#include <signal.h> +#endif + #include "strequal.h" #include "urldata.h" #include <curl/curl.h> @@ -61,10 +74,10 @@ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "amigaos.h" -#include "curl_rand.h" #include "non-ascii.h" #include "warnless.h" #include "conncache.h" +#include "multiif.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -72,6 +85,56 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifdef SIGPIPE_IGNORE +struct sigpipe_ignore { + struct sigaction old_pipe_act; + bool no_signal; +}; + +#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x + +/* + * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl + * internals, and then sigpipe_restore() will restore the situation when we + * return from libcurl again. + */ +static void sigpipe_ignore(struct SessionHandle *data, + struct sigpipe_ignore *ig) +{ + /* get a local copy of no_signal because the SessionHandle might not be + around when we restore */ + ig->no_signal = data->set.no_signal; + if(!data->set.no_signal) { + struct sigaction action; + /* first, extract the existing situation */ + memset(&ig->old_pipe_act, 0, sizeof(struct sigaction)); + sigaction(SIGPIPE, NULL, &ig->old_pipe_act); + action = ig->old_pipe_act; + /* ignore this signal */ + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + } +} + +/* + * sigpipe_restore() puts back the outside world's opinion of signal handler + * and SIGPIPE handling. It MUST only be called after a corresponding + * sigpipe_ignore() was used. + */ +static void sigpipe_restore(struct sigpipe_ignore *ig) +{ + if(!ig->no_signal) + /* restore the outside state */ + sigaction(SIGPIPE, &ig->old_pipe_act, NULL); +} + +#else +/* for systems without sigaction */ +#define sigpipe_ignore(x,y) Curl_nop_stmt +#define sigpipe_restore(x) Curl_nop_stmt +#define SIGPIPE_VARIABLE(x) +#endif + /* win32_cleanup() is for win32 socket cleanup functionality, the opposite of win32_init() */ static void win32_cleanup(void) @@ -189,6 +252,9 @@ curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) +curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif #else /* * Symbian OS doesn't support initialization to code in writeable static data. @@ -220,6 +286,9 @@ CURLcode curl_global_init(long flags) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) + Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif if(flags & CURL_GLOBAL_SSL) if(!Curl_ssl_init()) { @@ -262,11 +331,10 @@ CURLcode curl_global_init(long flags) } #endif - init_flags = flags; - - /* Preset pseudo-random number sequence. */ + if(flags & CURL_GLOBAL_ACK_EINTR) + Curl_ack_eintr = 1; - Curl_srand(); + init_flags = flags; return CURLE_OK; } @@ -385,8 +453,328 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) return ret; } +#ifdef CURLDEBUG + +struct socketmonitor { + struct socketmonitor *next; /* the next node in the list or NULL */ + struct pollfd socket; /* socket info of what to monitor */ +}; + +struct events { + long ms; /* timeout, run the timeout function when reached */ + bool msbump; /* set TRUE when timeout is set by callback */ + int num_sockets; /* number of nodes in the monitor list */ + struct socketmonitor *list; /* list of sockets to monitor */ + int running_handles; /* store the returned number */ +}; + +/* events_timer + * + * Callback that gets called with a new value when the timeout should be + * updated. + */ + +static int events_timer(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp) /* private callback pointer */ +{ + struct events *ev = userp; + (void)multi; + if(timeout_ms == -1) + /* timeout removed */ + timeout_ms = 0; + else if(timeout_ms == 0) + /* timeout is already reached! */ + timeout_ms = 1; /* trigger asap */ + + ev->ms = timeout_ms; + ev->msbump = TRUE; + return 0; +} + + +/* poll2cselect + * + * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones + */ +static int poll2cselect(int pollmask) +{ + int omask=0; + if(pollmask & POLLIN) + omask |= CURL_CSELECT_IN; + if(pollmask & POLLOUT) + omask |= CURL_CSELECT_OUT; + if(pollmask & POLLERR) + omask |= CURL_CSELECT_ERR; + return omask; +} + + +/* socketcb2poll + * + * convert from libcurl' CURL_POLL_* bit definitions to poll()'s + */ +static short socketcb2poll(int pollmask) +{ + short omask=0; + if(pollmask & CURL_POLL_IN) + omask |= POLLIN; + if(pollmask & CURL_POLL_OUT) + omask |= POLLOUT; + return omask; +} + +/* events_socket + * + * Callback that gets called with information about socket activity to + * monitor. + */ +static int events_socket(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp) /* private socket + pointer */ +{ + struct events *ev = userp; + struct socketmonitor *m; + struct socketmonitor *prev=NULL; + (void)socketp; + + m = ev->list; + while(m) { + if(m->socket.fd == s) { + + if(what == CURL_POLL_REMOVE) { + struct socketmonitor *nxt = m->next; + /* remove this node from the list of monitored sockets */ + if(prev) + prev->next = nxt; + else + ev->list = nxt; + free(m); + m = nxt; + infof(easy, "socket cb: socket %d REMOVED\n", s); + } + else { + /* The socket 's' is already being monitored, update the activity + mask. Convert from libcurl bitmask to the poll one. */ + m->socket.events = socketcb2poll(what); + infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } + break; + } + prev = m; + m = m->next; /* move to next node */ + } + if(!m) { + if(what == CURL_POLL_REMOVE) { + /* this happens a bit too often, libcurl fix perhaps? */ + /* fprintf(stderr, + "%s: socket %d asked to be REMOVED but not present!\n", + __func__, s); */ + } + else { + m = malloc(sizeof(struct socketmonitor)); + m->next = ev->list; + m->socket.fd = s; + m->socket.events = socketcb2poll(what); + m->socket.revents = 0; + ev->list = m; + infof(easy, "socket cb: socket %d ADDED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } + } + + return 0; +} + + /* - * curl_easy_perform() is the external interface that performs a blocking + * events_setup() + * + * Do the multi handle setups that only event-based transfers need. + */ +static void events_setup(CURLM *multi, struct events *ev) +{ + /* timer callback */ + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); + curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); + + /* socket callback */ + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); + curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); +} + + +/* wait_or_timeout() + * + * waits for activity on any of the given sockets, or the timeout to trigger. + */ + +static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) +{ + bool done = FALSE; + CURLMcode mcode; + CURLcode rc = CURLE_OK; + + while(!done) { + CURLMsg *msg; + struct socketmonitor *m; + struct pollfd *f; + struct pollfd fds[4]; + int numfds=0; + int pollrc; + int i; + struct timeval before; + struct timeval after; + + /* populate the fds[] array */ + for(m = ev->list, f=&fds[0]; m; m = m->next) { + f->fd = m->socket.fd; + f->events = m->socket.events; + f->revents = 0; + /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ + f++; + numfds++; + } + + /* get the time stamp to use to figure out how long poll takes */ + before = curlx_tvnow(); + + /* wait for activity or timeout */ + pollrc = Curl_poll(fds, numfds, (int)ev->ms); + + after = curlx_tvnow(); + + ev->msbump = FALSE; /* reset here */ + + if(0 == pollrc) { + /* timeout! */ + ev->ms = 0; + /* fprintf(stderr, "call curl_multi_socket_action( TIMEOUT )\n"); */ + mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, + &ev->running_handles); + } + else if(pollrc > 0) { + /* loop over the monitored sockets to see which ones had activity */ + for(i = 0; i< numfds; i++) { + if(fds[i].revents) { + /* socket activity, tell libcurl */ + int act = poll2cselect(fds[i].revents); /* convert */ + infof(multi->easyp, "call curl_multi_socket_action( socket %d )\n", + fds[i].fd); + mcode = curl_multi_socket_action(multi, fds[i].fd, act, + &ev->running_handles); + } + } + + if(!ev->msbump) + /* If nothing updated the timeout, we decrease it by the spent time. + * If it was updated, it has the new timeout time stored already. + */ + ev->ms += curlx_tvdiff(after, before); + + } + if(mcode) + return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */ + + /* we don't really care about the "msgs_in_queue" value returned in the + second argument */ + msg = curl_multi_info_read(multi, &pollrc); + if(msg) { + rc = msg->data.result; + done = TRUE; + } + } + + return rc; +} + + +/* easy_events() + * + * Runs a transfer in a blocking manner using the events-based API + */ +static CURLcode easy_events(CURLM *multi) +{ + struct events evs= {2, FALSE, 0, NULL, 0}; + + /* if running event-based, do some further multi inits */ + events_setup(multi, &evs); + + return wait_or_timeout(multi, &evs); +} +#else /* CURLDEBUG */ +/* when not built with debug, this function doesn't exist */ +#define easy_events(x) CURLE_NOT_BUILT_IN +#endif + +static CURLcode easy_transfer(CURLM *multi) +{ + bool done = FALSE; + CURLMcode mcode = CURLM_OK; + CURLcode code = CURLE_OK; + struct timeval before; + int without_fds = 0; /* count number of consecutive returns from + curl_multi_wait() without any filedescriptors */ + + while(!done && !mcode) { + int still_running; + int ret; + + before = curlx_tvnow(); + mcode = curl_multi_wait(multi, NULL, 0, 1000, &ret); + + if(mcode == CURLM_OK) { + if(ret == -1) { + /* poll() failed not on EINTR, indicate a network problem */ + code = CURLE_RECV_ERROR; + break; + } + else if(ret == 0) { + struct timeval after = curlx_tvnow(); + /* If it returns without any filedescriptor instantly, we need to + avoid busy-looping during periods where it has nothing particular + to wait for */ + if(curlx_tvdiff(after, before) <= 10) { + without_fds++; + if(without_fds > 2) { + int sleep_ms = without_fds < 10 ? (1 << (without_fds-1)): 1000; + Curl_wait_ms(sleep_ms); + } + } + else + /* it wasn't "instant", restart counter */ + without_fds = 0; + } + else + /* got file descriptor, restart counter */ + without_fds = 0; + + mcode = curl_multi_perform(multi, &still_running); + } + + /* only read 'still_running' if curl_multi_perform() return OK */ + if((mcode == CURLM_OK) && !still_running) { + int rc; + CURLMsg *msg = curl_multi_info_read(multi, &rc); + if(msg) { + code = msg->data.result; + done = TRUE; + } + } + } + return code; +} + + +/* + * easy_perform() is the external interface that performs a blocking * transfer as previously setup. * * CONCEPT: This function creates a multi handle, adds the easy handle to it, @@ -398,18 +786,18 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be re-used so that the same pools and * caches can be used. + * + * DEBUG: if 'events' is set TRUE, this function will use a replacement engine + * instead of curl_multi_perform() and use curl_multi_socket_action(). */ -CURLcode curl_easy_perform(CURL *easy) +static CURLcode easy_perform(struct SessionHandle *data, bool events) { CURLM *multi; CURLMcode mcode; CURLcode code = CURLE_OK; - CURLMsg *msg; - bool done = FALSE; - int rc; - struct SessionHandle *data = easy; + SIGPIPE_VARIABLE(pipe_st); - if(!easy) + if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; if(data->multi) { @@ -420,13 +808,18 @@ CURLcode curl_easy_perform(CURL *easy) if(data->multi_easy) multi = data->multi_easy; else { - multi = curl_multi_init(); + /* this multi handle will only ever have a single easy handled attached + to it, so make it use minimal hashes */ + multi = Curl_multi_handle(1, 3); if(!multi) return CURLE_OUT_OF_MEMORY; data->multi_easy = multi; } - mcode = curl_multi_add_handle(multi, easy); + /* Copy the MAXCONNECTS option to the multi handle */ + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + + mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); if(mcode == CURLM_OUT_OF_MEMORY) @@ -435,64 +828,62 @@ CURLcode curl_easy_perform(CURL *easy) return CURLE_FAILED_INIT; } + sigpipe_ignore(data, &pipe_st); + /* assign this after curl_multi_add_handle() since that function checks for it and rejects this handle otherwise */ data->multi = multi; - while(!done && !mcode) { - int still_running; - - mcode = curl_multi_wait(multi, NULL, 0, 1000, NULL); - - if(mcode == CURLM_OK) - mcode = curl_multi_perform(multi, &still_running); - - /* only read 'still_running' if curl_multi_perform() return OK */ - if((mcode == CURLM_OK) && !still_running) { - msg = curl_multi_info_read(multi, &rc); - if(msg) { - code = msg->data.result; - done = TRUE; - } - } - } + /* run the transfer */ + code = events ? easy_events(multi) : easy_transfer(multi); /* ignoring the return code isn't nice, but atm we can't really handle a failure here, room for future improvement! */ - (void)curl_multi_remove_handle(multi, easy); + (void)curl_multi_remove_handle(multi, data); + + sigpipe_restore(&pipe_st); /* The multi handle is kept alive, owned by the easy handle */ return code; } + /* - * curl_easy_cleanup() is the external interface to cleaning/freeing the given - * easy handle. + * curl_easy_perform() is the external interface that performs a blocking + * transfer as previously setup. */ -void curl_easy_cleanup(CURL *curl) +CURLcode curl_easy_perform(CURL *easy) { - struct SessionHandle *data = (struct SessionHandle *)curl; - - if(!data) - return; - - Curl_close(data); + return easy_perform(easy, FALSE); } +#ifdef CURLDEBUG /* - * Store a pointed to the multi handle within the easy handle's data struct. + * curl_easy_perform_ev() is the external interface that performs a blocking + * transfer using the event-based API internally. */ -void Curl_easy_addmulti(struct SessionHandle *data, - void *multi) +CURLcode curl_easy_perform_ev(CURL *easy) { - data->multi = multi; + return easy_perform(easy, TRUE); } -void Curl_easy_initHandleData(struct SessionHandle *data) +#endif + +/* + * curl_easy_cleanup() is the external interface to cleaning/freeing the given + * easy handle. + */ +void curl_easy_cleanup(CURL *curl) { - memset(&data->req, 0, sizeof(struct SingleRequest)); + struct SessionHandle *data = (struct SessionHandle *)curl; + SIGPIPE_VARIABLE(pipe_st); - data->req.maxdownload = -1; + if(!data) + return; + + sigpipe_ignore(data, &pipe_st); + Curl_close(data); + sigpipe_restore(&pipe_st); } /* @@ -504,12 +895,16 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; + CURLcode ret; struct SessionHandle *data = (struct SessionHandle *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - return Curl_getinfo(data, info, paramp); + ret = Curl_getinfo(data, info, paramp); + + va_end(arg); + return ret; } /* @@ -587,8 +982,6 @@ CURL *curl_easy_duphandle(CURL *incurl) Curl_convert_setup(outcurl); - Curl_easy_initHandleData(outcurl); - outcurl->magic = CURLEASY_MAGIC_NUMBER; /* we reach this point and thus we are OK */ @@ -622,7 +1015,7 @@ void curl_easy_reset(CURL *curl) data->state.path = NULL; - Curl_safefree(data->state.proto.generic); + Curl_free_request_state(data); /* zero out UserDefined data: */ Curl_freeset(data); @@ -632,9 +1025,6 @@ void curl_easy_reset(CURL *curl) /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); - /* init Handle data */ - Curl_easy_initHandleData(data); - data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ } @@ -696,7 +1086,7 @@ CURLcode curl_easy_pause(CURL *curl, int action) do { chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize; - result = Curl_client_write(data->state.current_conn, + result = Curl_client_write(data->easy_conn, temptype, tempwrite, chunklen); if(result) /* failures abort the loop at once */ @@ -738,6 +1128,13 @@ CURLcode curl_easy_pause(CURL *curl, int action) free(freewrite); /* this is unconditionally no longer used */ } + /* if there's no error and we're not pausing both directions, we want + to have this handle checked soon */ + if(!result && + ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) + Curl_expire(data, 1); /* get this handle going again */ + return result; } |