Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> |
9 | | * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
10 | | * |
11 | | * This software is licensed as described in the file COPYING, which |
12 | | * you should have received as part of this distribution. The terms |
13 | | * are also available at https://curl.se/docs/copyright.html. |
14 | | * |
15 | | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
16 | | * copies of the Software, and permit persons to whom the Software is |
17 | | * furnished to do so, under the terms of the COPYING file. |
18 | | * |
19 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
20 | | * KIND, either express or implied. |
21 | | * |
22 | | * SPDX-License-Identifier: curl |
23 | | * |
24 | | ***************************************************************************/ |
25 | | #include "curl_setup.h" |
26 | | |
27 | | #include "urldata.h" |
28 | | #include "url.h" |
29 | | #include "cfilters.h" |
30 | | #include "progress.h" |
31 | | #include "multiif.h" |
32 | | #include "multi_ev.h" |
33 | | #include "curl_trc.h" |
34 | | #include "cshutdn.h" |
35 | | #include "sigpipe.h" |
36 | | #include "connect.h" |
37 | | #include "select.h" |
38 | | #include "curlx/strparse.h" |
39 | | |
40 | | |
41 | 21.7k | #define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000) |
42 | | |
43 | | void Curl_cshutdn_start_timer(struct Curl_easy *data, int8_t sockindex, |
44 | | int timeout_ms) |
45 | 21.7k | { |
46 | 21.7k | struct connectdata *conn = data->conn; |
47 | 21.7k | const struct curltime *pnow = Curl_pgrs_now(data); |
48 | | |
49 | 21.7k | DEBUGASSERT(conn); |
50 | 21.7k | conn->shutdown.start_ms[sockindex] = |
51 | 21.7k | curlx_ptimediff_ms(pnow, &conn->created); |
52 | 21.7k | conn->shutdown.timeout_ms = (timeout_ms > 0) ? |
53 | 0 | (timediff_t)timeout_ms : |
54 | 21.7k | ((data->set.shutdowntimeout > 0) ? |
55 | 21.7k | data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS); |
56 | | /* Set a timer, unless we operate on the admin handle */ |
57 | 21.7k | if(data->mid) |
58 | 0 | Curl_expire_set(data, EXPIRE_SHUTDOWN, conn->shutdown.timeout_ms, pnow); |
59 | 21.7k | CURL_TRC_M(data, "shutdown start on%s connection", |
60 | 21.7k | sockindex ? " secondary" : ""); |
61 | 21.7k | } |
62 | | |
63 | | timediff_t Curl_cshutdn_timeleft_ms(struct Curl_easy *data, |
64 | | struct connectdata *conn, |
65 | | int8_t sockindex) |
66 | 27.5k | { |
67 | 27.5k | timediff_t left_ms; |
68 | | |
69 | 27.5k | if(conn->shutdown.start_ms[sockindex] < 0) |
70 | 0 | return 0; /* not started or no limits */ |
71 | | |
72 | 27.5k | left_ms = conn->shutdown.timeout_ms - |
73 | 27.5k | (curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->created) - |
74 | 27.5k | conn->shutdown.start_ms[sockindex]); |
75 | 27.5k | return left_ms ? left_ms : -1; |
76 | 27.5k | } |
77 | | |
78 | | static timediff_t cshutdn_conn_timeleft(struct Curl_easy *data, |
79 | | struct connectdata *conn) |
80 | 2.84k | { |
81 | 2.84k | timediff_t left_ms = 0, ms; |
82 | 2.84k | int8_t i; |
83 | | |
84 | 8.53k | for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) { |
85 | 5.68k | if(conn->shutdown.start_ms[i] < 0) |
86 | 2.84k | continue; |
87 | 2.84k | ms = Curl_cshutdn_timeleft_ms(data, conn, i); |
88 | 2.84k | if(ms && (!left_ms || ms < left_ms)) |
89 | 2.84k | left_ms = ms; |
90 | 2.84k | } |
91 | 2.84k | return left_ms; |
92 | 2.84k | } |
93 | | |
94 | | void Curl_cshutdn_clear_timer(struct Curl_easy *data, int8_t sockindex) |
95 | 0 | { |
96 | 0 | data->conn->shutdown.start_ms[sockindex] = -1; |
97 | 0 | } |
98 | | |
99 | | static void cshutdn_run_conn_handler(struct Curl_easy *data, |
100 | | struct connectdata *conn) |
101 | 62.6k | { |
102 | 62.6k | if(!conn->bits.shutdown_handler) { |
103 | | |
104 | 37.9k | if(conn->scheme && conn->scheme->run->disconnect) { |
105 | | /* Some disconnect handlers do a blocking wait on server responses. |
106 | | * FTP/IMAP/SMTP and SFTP are among them. When using the internal |
107 | | * handle, set an overall short timeout so we do not hang for the |
108 | | * default 120 seconds. */ |
109 | 0 | if(data->state.internal) { |
110 | 0 | data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS; |
111 | 0 | Curl_pgrsTime(data, TIMER_STARTOP); |
112 | 0 | } |
113 | | |
114 | | /* This is set if protocol-specific cleanups should be made */ |
115 | 0 | DEBUGF(infof(data, "connection #%" FMT_OFF_T |
116 | 0 | ", shutdown protocol handler (aborted=%d)", |
117 | 0 | conn->connection_id, conn->bits.aborted)); |
118 | | /* There are protocol handlers that block on retrieving |
119 | | * server responses here (FTP). Set a short timeout. */ |
120 | 0 | conn->scheme->run->disconnect(data, conn, (bool)conn->bits.aborted); |
121 | 0 | } |
122 | | |
123 | 37.9k | conn->bits.shutdown_handler = TRUE; |
124 | 37.9k | } |
125 | 62.6k | } |
126 | | |
127 | | CURLcode Curl_cshutdn_try_once_idx(struct Curl_easy *data, |
128 | | int8_t sockindex, bool *done) |
129 | 24.6k | { |
130 | 24.6k | struct Curl_cfilter *cf; |
131 | 24.6k | CURLcode result = CURLE_OK; |
132 | 24.6k | timediff_t timeout_ms; |
133 | | |
134 | 24.6k | DEBUGASSERT(data->conn); |
135 | | |
136 | 24.6k | if(!CONN_SOCK_IDX_VALID(sockindex)) |
137 | 0 | return CURLE_BAD_FUNCTION_ARGUMENT; |
138 | | |
139 | | /* Get the first connected filter that is not shut down already. */ |
140 | 24.6k | cf = data->conn->cfilter[sockindex]; |
141 | 24.6k | while(cf && (!cf->connected || cf->shutdown)) |
142 | 0 | cf = cf->next; |
143 | | |
144 | 24.6k | if(!cf) { |
145 | 0 | *done = TRUE; |
146 | 0 | return CURLE_OK; |
147 | 0 | } |
148 | | |
149 | 24.6k | *done = FALSE; |
150 | 24.6k | if(!CURL_CONN_IN_SHUTDOWN(data->conn, sockindex)) { |
151 | 0 | Curl_cshutdn_start_timer(data, sockindex, 0); |
152 | 0 | } |
153 | 24.6k | else { |
154 | 24.6k | timeout_ms = Curl_cshutdn_timeleft_ms(data, data->conn, sockindex); |
155 | 24.6k | if(timeout_ms < 0) { |
156 | | /* info message, since this might be regarded as acceptable */ |
157 | 0 | infof(data, "shutdown timeout"); |
158 | 0 | return CURLE_OPERATION_TIMEDOUT; |
159 | 0 | } |
160 | 24.6k | } |
161 | | |
162 | 48.9k | while(cf) { |
163 | 27.4k | if(!cf->shutdown) { |
164 | 27.4k | bool cfdone = FALSE; |
165 | 27.4k | result = cf->cft->do_shutdown(cf, data, &cfdone); |
166 | 27.4k | if(result) { |
167 | 1 | CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result); |
168 | 1 | return result; |
169 | 1 | } |
170 | 27.4k | else if(!cfdone) { |
171 | 3.17k | CURL_TRC_CF(data, cf, "shut down not done yet"); |
172 | 3.17k | return CURLE_OK; |
173 | 3.17k | } |
174 | 24.2k | CURL_TRC_CF(data, cf, "shut down successfully"); |
175 | 24.2k | cf->shutdown = TRUE; |
176 | 24.2k | } |
177 | 24.2k | cf = cf->next; |
178 | 24.2k | } |
179 | 21.4k | *done = (!result); |
180 | 21.4k | return result; |
181 | 24.6k | } |
182 | | |
183 | | static void cshutdn_run_once(struct Curl_easy *data, |
184 | | struct connectdata *conn, |
185 | | bool *done) |
186 | 24.6k | { |
187 | 24.6k | CURLcode r1, r2; |
188 | 24.6k | bool done1, done2; |
189 | | |
190 | | /* We expect to be attached when called */ |
191 | 24.6k | DEBUGASSERT(data->conn == conn); |
192 | | |
193 | 24.6k | if(!CURL_CONN_IN_SHUTDOWN(conn, FIRSTSOCKET)) { |
194 | 21.7k | Curl_cshutdn_start_timer(data, FIRSTSOCKET, 0); |
195 | 21.7k | } |
196 | | |
197 | 24.6k | cshutdn_run_conn_handler(data, conn); |
198 | | |
199 | 24.6k | if(conn->bits.shutdown_filters) { |
200 | 0 | *done = TRUE; |
201 | 0 | return; |
202 | 0 | } |
203 | | |
204 | 24.6k | if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) |
205 | 24.6k | r1 = Curl_cshutdn_try_once_idx(data, FIRSTSOCKET, &done1); |
206 | 13 | else { |
207 | 13 | r1 = CURLE_OK; |
208 | 13 | done1 = TRUE; |
209 | 13 | } |
210 | | |
211 | 24.6k | if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) |
212 | 0 | r2 = Curl_cshutdn_try_once_idx(data, SECONDARYSOCKET, &done2); |
213 | 24.6k | else { |
214 | 24.6k | r2 = CURLE_OK; |
215 | 24.6k | done2 = TRUE; |
216 | 24.6k | } |
217 | | |
218 | | /* we are done when any failed or both report success */ |
219 | 24.6k | *done = (r1 || r2 || (done1 && done2)); |
220 | 24.6k | if(*done) |
221 | 21.5k | conn->bits.shutdown_filters = TRUE; |
222 | 24.6k | } |
223 | | |
224 | | void Curl_cshutdn_try_once(struct Curl_easy *admin, |
225 | | struct connectdata *conn, |
226 | | bool *done) |
227 | 24.6k | { |
228 | 24.6k | DEBUGASSERT(!admin->conn); |
229 | 24.6k | DEBUGASSERT(!admin->mid); |
230 | 24.6k | Curl_attach_connection(admin, conn, FALSE); |
231 | 24.6k | cshutdn_run_once(admin, conn, done); |
232 | 24.6k | CURL_TRC_M(admin, "[SHUTDOWN] shutdown, done=%d", *done); |
233 | 24.6k | Curl_detach_connection(admin); |
234 | 24.6k | } |
235 | | |
236 | | void Curl_cshutdn_terminate(struct Curl_easy *admin, |
237 | | struct connectdata *conn, |
238 | | bool do_shutdown) |
239 | 37.9k | { |
240 | 37.9k | bool done; |
241 | | |
242 | | /* there must be a connection to close */ |
243 | 37.9k | DEBUGASSERT(conn); |
244 | | /* it must be removed from the connection pool */ |
245 | 37.9k | DEBUGASSERT(conn->cpid == UINT32_MAX); |
246 | | /* the transfer must be detached from the connection */ |
247 | 37.9k | DEBUGASSERT(admin && !admin->conn); |
248 | 37.9k | DEBUGASSERT(!admin->mid); |
249 | | |
250 | 37.9k | Curl_attach_connection(admin, conn, FALSE); |
251 | | |
252 | 37.9k | cshutdn_run_conn_handler(admin, conn); |
253 | 37.9k | if(do_shutdown) { |
254 | | /* Make a last attempt to shutdown handlers and filters, if |
255 | | * not done so already. */ |
256 | 0 | cshutdn_run_once(admin, conn, &done); |
257 | 0 | } |
258 | 37.9k | CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T, |
259 | 37.9k | conn->bits.shutdown_filters ? "" : "force ", |
260 | 37.9k | conn->connection_id); |
261 | 37.9k | Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET); |
262 | 37.9k | Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); |
263 | 37.9k | Curl_detach_connection(admin); |
264 | | |
265 | 37.9k | if(admin->multi) |
266 | 37.9k | Curl_multi_ev_conn_done(admin->multi, admin, conn); |
267 | 37.9k | Curl_conn_free(admin, conn); |
268 | | |
269 | 37.9k | if(admin->multi) { |
270 | 37.9k | CURL_TRC_M(admin, "[SHUTDOWN] trigger multi connchanged"); |
271 | 37.9k | Curl_multi_connchanged(admin->multi); |
272 | 37.9k | } |
273 | 37.9k | } |
274 | | |
275 | | static size_t cshutdn_destroy_oldest(struct cshutdn *cshutdn, |
276 | | struct Curl_easy *admin, |
277 | | const char *destination, |
278 | | size_t count) |
279 | 0 | { |
280 | 0 | struct Curl_sigpipe_ctx sigpipe_ctx; |
281 | 0 | size_t removed = 0; |
282 | 0 | uint32_t i = 0; |
283 | |
|
284 | 0 | sigpipe_init(&sigpipe_ctx); |
285 | 0 | while((removed < count) && (i < CURL_PTRARRAY_COUNT(&cshutdn->conns))) { |
286 | 0 | struct connectdata *conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
287 | 0 | if(!destination || !strcmp(destination, conn->destination)) { |
288 | 0 | Curl_ptrarray_remove(&cshutdn->conns, i); |
289 | 0 | sigpipe_apply(admin, &sigpipe_ctx); |
290 | 0 | Curl_cshutdn_terminate(admin, conn, FALSE); |
291 | 0 | ++removed; |
292 | 0 | } |
293 | 0 | else |
294 | 0 | ++i; |
295 | 0 | } |
296 | 0 | sigpipe_restore(&sigpipe_ctx); |
297 | 0 | return removed; |
298 | 0 | } |
299 | | |
300 | | size_t Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, |
301 | | struct Curl_easy *admin, |
302 | | const char *destination, |
303 | | size_t count) |
304 | 0 | { |
305 | 0 | if(cshutdn) { |
306 | 0 | return cshutdn_destroy_oldest(cshutdn, admin, destination, count); |
307 | 0 | } |
308 | 0 | return 0; |
309 | 0 | } |
310 | | |
311 | 0 | #define NUM_POLLS_ON_STACK 10 |
312 | | |
313 | | static CURLcode cshutdn_wait(struct cshutdn *cshutdn, |
314 | | struct Curl_easy *admin, |
315 | | int timeout_ms) |
316 | 0 | { |
317 | 0 | struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; |
318 | 0 | struct curl_pollfds cpfds; |
319 | 0 | CURLcode result; |
320 | |
|
321 | 0 | Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); |
322 | |
|
323 | 0 | result = Curl_cshutdn_add_pollfds(cshutdn, admin, &cpfds); |
324 | 0 | if(result) |
325 | 0 | goto out; |
326 | | |
327 | 0 | Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000)); |
328 | |
|
329 | 0 | out: |
330 | 0 | Curl_pollfds_cleanup(&cpfds); |
331 | 0 | return result; |
332 | 0 | } |
333 | | |
334 | | static void cshutdn_perform(struct cshutdn *cshutdn, |
335 | | struct Curl_easy *admin, |
336 | | struct Curl_sigpipe_ctx *sigpipe_ctx) |
337 | 178k | { |
338 | 178k | struct connectdata *conn; |
339 | 178k | timediff_t next_expire_ms = 0, ms; |
340 | 178k | uint32_t i = 0; |
341 | 178k | bool done; |
342 | | |
343 | 178k | if(!CURL_PTRARRAY_COUNT(&cshutdn->conns)) |
344 | 176k | return; |
345 | | |
346 | 2.16k | CURL_TRC_M(admin, "[SHUTDOWN] perform on %u connections", |
347 | 2.16k | CURL_PTRARRAY_COUNT(&cshutdn->conns)); |
348 | 2.16k | sigpipe_apply(admin, sigpipe_ctx); |
349 | 5.12k | while(i < CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
350 | 2.95k | conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
351 | 2.95k | Curl_cshutdn_try_once(admin, conn, &done); |
352 | 2.95k | if(done) { |
353 | 111 | Curl_ptrarray_remove(&cshutdn->conns, i); |
354 | 111 | Curl_cshutdn_terminate(admin, conn, FALSE); |
355 | 111 | } |
356 | 2.84k | else { |
357 | 2.84k | ++i; |
358 | | /* idata has one timer list, but maybe more than one connection. |
359 | | * Set EXPIRE_SHUTDOWN to the smallest time left for all. */ |
360 | 2.84k | ms = cshutdn_conn_timeleft(admin, conn); |
361 | 2.84k | if(ms && (!next_expire_ms || (ms < next_expire_ms))) |
362 | 2.08k | next_expire_ms = ms; |
363 | 2.84k | } |
364 | 2.95k | } |
365 | | |
366 | 2.16k | if(next_expire_ms) |
367 | 2.08k | Curl_expire(admin, next_expire_ms, EXPIRE_SHUTDOWN); |
368 | 2.16k | } |
369 | | |
370 | | static void cshutdn_terminate_all(struct cshutdn *cshutdn, |
371 | | struct Curl_easy *admin, |
372 | | int timeout_ms) |
373 | 136 | { |
374 | 136 | struct curltime started = *Curl_pgrs_now(admin); |
375 | 136 | struct Curl_sigpipe_ctx sigpipe_ctx; |
376 | | |
377 | 136 | CURL_TRC_M(admin, "[SHUTDOWN] shutdown all"); |
378 | 136 | sigpipe_init(&sigpipe_ctx); |
379 | | |
380 | 136 | while(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
381 | 136 | timediff_t spent_ms; |
382 | 136 | int remain_ms; |
383 | | |
384 | 136 | cshutdn_perform(cshutdn, admin, &sigpipe_ctx); |
385 | | |
386 | 136 | if(!CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
387 | 0 | CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished cleanly"); |
388 | 0 | break; |
389 | 0 | } |
390 | | |
391 | | /* wait for activity, timeout or "nothing" */ |
392 | 136 | spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(admin), &started); |
393 | 136 | if(spent_ms >= (timediff_t)timeout_ms) { |
394 | 136 | CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, %s", |
395 | 136 | (timeout_ms > 0) ? "timeout" : "best effort done"); |
396 | 136 | break; |
397 | 136 | } |
398 | | |
399 | 0 | remain_ms = timeout_ms - (int)spent_ms; |
400 | 0 | if(cshutdn_wait(cshutdn, admin, remain_ms)) { |
401 | 0 | CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, aborted"); |
402 | 0 | break; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | /* Terminate any remaining. */ |
407 | 354 | while(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
408 | 218 | struct connectdata *conn = Curl_ptrarray_remove(&cshutdn->conns, 0); |
409 | 218 | Curl_cshutdn_terminate(admin, conn, FALSE); |
410 | 218 | } |
411 | 136 | DEBUGASSERT(!CURL_PTRARRAY_COUNT(&cshutdn->conns)); |
412 | | |
413 | 136 | sigpipe_restore(&sigpipe_ctx); |
414 | 136 | } |
415 | | |
416 | | void Curl_cshutdn_init(struct cshutdn *cshutdn) |
417 | 28.0k | { |
418 | 28.0k | Curl_ptrarray_init(&cshutdn->conns); |
419 | 28.0k | } |
420 | | |
421 | | void Curl_cshutdn_destroy(struct cshutdn *cshutdn, |
422 | | struct Curl_easy *admin) |
423 | 28.0k | { |
424 | 28.0k | if(admin) { |
425 | 28.0k | int timeout_ms = 0; |
426 | | /* for testing, run graceful shutdown */ |
427 | 28.0k | #ifdef DEBUGBUILD |
428 | 28.0k | { |
429 | 28.0k | const char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); |
430 | 28.0k | if(p) { |
431 | 0 | curl_off_t l; |
432 | 0 | if(!curlx_str_number(&p, &l, INT_MAX)) |
433 | 0 | timeout_ms = (int)l; |
434 | 0 | } |
435 | 28.0k | } |
436 | 28.0k | #endif |
437 | 28.0k | if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
438 | 136 | CURL_TRC_M(admin, "[SHUTDOWN] destroy, %u connections, timeout=%dms", |
439 | 136 | CURL_PTRARRAY_COUNT(&cshutdn->conns), timeout_ms); |
440 | 136 | cshutdn_terminate_all(cshutdn, admin, timeout_ms); |
441 | 136 | } |
442 | 28.0k | } |
443 | 28.0k | } |
444 | | |
445 | | size_t Curl_cshutdn_count(struct cshutdn *cshutdn) |
446 | 0 | { |
447 | 0 | if(cshutdn) { |
448 | 0 | return CURL_PTRARRAY_COUNT(&cshutdn->conns); |
449 | 0 | } |
450 | 0 | return 0; |
451 | 0 | } |
452 | | |
453 | | size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, |
454 | | const char *destination) |
455 | 0 | { |
456 | 0 | if(cshutdn) { |
457 | 0 | size_t n = 0; |
458 | 0 | uint32_t i = 0; |
459 | 0 | for(i = 0; i < CURL_PTRARRAY_COUNT(&cshutdn->conns); ++i) { |
460 | 0 | struct connectdata *conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
461 | 0 | if(!strcmp(destination, conn->destination)) |
462 | 0 | ++n; |
463 | 0 | } |
464 | 0 | return n; |
465 | 0 | } |
466 | 0 | return 0; |
467 | 0 | } |
468 | | |
469 | | static CURLMcode cshutdn_update_ev(struct Curl_multi *multi, |
470 | | struct connectdata *conn) |
471 | 0 | { |
472 | 0 | CURLMcode mresult; |
473 | 0 | Curl_attach_connection(multi->admin, conn, FALSE); |
474 | 0 | mresult = Curl_multi_ev_assess_conn(multi, multi->admin, conn); |
475 | 0 | Curl_detach_connection(multi->admin); |
476 | 0 | return mresult; |
477 | 0 | } |
478 | | |
479 | | void Curl_cshutdn_add(struct cshutdn *cshutdn, |
480 | | struct Curl_multi *multi, |
481 | | struct connectdata *conn, |
482 | | size_t max_shutdowns) |
483 | 330 | { |
484 | 330 | struct Curl_easy *admin = multi->admin; |
485 | | |
486 | | /* Add the connection to our shutdown list for non-blocking shutdown |
487 | | * during multi processing. */ |
488 | 330 | if(max_shutdowns <= CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
489 | 0 | CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection " |
490 | 0 | "due to shutdown limit of %zu", max_shutdowns); |
491 | 0 | cshutdn_destroy_oldest(cshutdn, admin, NULL, 1); |
492 | 0 | } |
493 | | |
494 | 330 | if(multi->socket_cb && cshutdn_update_ev(multi, conn)) { |
495 | 0 | CURL_TRC_M(admin, "[SHUTDOWN] update events failed, discarding #%" |
496 | 0 | FMT_OFF_T, conn->connection_id); |
497 | 0 | Curl_cshutdn_terminate(admin, conn, FALSE); |
498 | 0 | return; |
499 | 0 | } |
500 | | |
501 | 330 | if(Curl_ptrarray_add(&cshutdn->conns, conn)) { |
502 | 1 | CURL_TRC_M(admin, "[SHUTDOWN] adding failed, discarding #%" |
503 | 1 | FMT_OFF_T, conn->connection_id); |
504 | 1 | Curl_cshutdn_terminate(admin, conn, FALSE); |
505 | 1 | return; |
506 | 1 | } |
507 | 329 | CURL_TRC_M(admin, "[SHUTDOWN] added #%" FMT_OFF_T |
508 | 329 | " to shutdowns, now %u conns in shutdown", |
509 | 329 | conn->connection_id, CURL_PTRARRAY_COUNT(&cshutdn->conns)); |
510 | 329 | } |
511 | | |
512 | | void Curl_cshutdn_perform(struct cshutdn *cshutdn, |
513 | | struct Curl_easy *admin, |
514 | | struct Curl_sigpipe_ctx *sigpipe_ctx) |
515 | 178k | { |
516 | 178k | cshutdn_perform(cshutdn, admin, sigpipe_ctx); |
517 | 178k | } |
518 | | |
519 | | /* return fd_set info about the shutdown connections */ |
520 | | void Curl_cshutdn_setfds(struct cshutdn *cshutdn, |
521 | | struct Curl_easy *admin, |
522 | | fd_set *read_fd_set, fd_set *write_fd_set, |
523 | | int *maxfd) |
524 | 37.2k | { |
525 | 37.2k | if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
526 | 0 | struct easy_pollset ps; |
527 | 0 | uint32_t i; |
528 | |
|
529 | 0 | Curl_pollset_init(&ps); |
530 | 0 | for(i = 0; i < CURL_PTRARRAY_COUNT(&cshutdn->conns); ++i) { |
531 | 0 | unsigned int j; |
532 | 0 | struct connectdata *conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
533 | 0 | CURLcode result; |
534 | |
|
535 | 0 | Curl_pollset_reset(&ps); |
536 | 0 | Curl_attach_connection(admin, conn, FALSE); |
537 | 0 | result = Curl_conn_adjust_pollset(admin, conn, &ps); |
538 | 0 | Curl_detach_connection(admin); |
539 | |
|
540 | 0 | if(result) |
541 | 0 | continue; |
542 | | |
543 | 0 | for(j = 0; j < ps.n; j++) { |
544 | 0 | curl_socket_t sock = ps.sockets[j]; |
545 | 0 | if(!FDSET_SOCK(sock)) |
546 | 0 | continue; |
547 | 0 | if(ps.actions[j] & CURL_POLL_IN) |
548 | 0 | FD_SET(sock, read_fd_set); |
549 | 0 | if(ps.actions[j] & CURL_POLL_OUT) |
550 | 0 | FD_SET(sock, write_fd_set); |
551 | 0 | if((ps.actions[j] & (CURL_POLL_OUT | CURL_POLL_IN)) && |
552 | 0 | ((int)sock > *maxfd)) |
553 | 0 | *maxfd = (int)sock; |
554 | 0 | } |
555 | 0 | } |
556 | 0 | Curl_pollset_cleanup(&ps); |
557 | 0 | } |
558 | 37.2k | } |
559 | | |
560 | | /* return information about the shutdown connections */ |
561 | | unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, |
562 | | struct Curl_easy *admin, |
563 | | struct Curl_waitfds *cwfds) |
564 | 0 | { |
565 | 0 | unsigned int need = 0; |
566 | |
|
567 | 0 | if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
568 | 0 | struct easy_pollset ps; |
569 | 0 | struct connectdata *conn; |
570 | 0 | uint32_t i; |
571 | 0 | CURLcode result; |
572 | |
|
573 | 0 | Curl_pollset_init(&ps); |
574 | 0 | for(i = 0; i < CURL_PTRARRAY_COUNT(&cshutdn->conns); ++i) { |
575 | 0 | conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
576 | 0 | Curl_pollset_reset(&ps); |
577 | 0 | Curl_attach_connection(admin, conn, FALSE); |
578 | 0 | result = Curl_conn_adjust_pollset(admin, conn, &ps); |
579 | 0 | Curl_detach_connection(admin); |
580 | |
|
581 | 0 | if(!result) |
582 | 0 | need += Curl_waitfds_add_ps(cwfds, &ps); |
583 | 0 | } |
584 | 0 | Curl_pollset_cleanup(&ps); |
585 | 0 | } |
586 | 0 | return need; |
587 | 0 | } |
588 | | |
589 | | CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, |
590 | | struct Curl_easy *admin, |
591 | | struct curl_pollfds *cpfds) |
592 | 1.36k | { |
593 | 1.36k | CURLcode result = CURLE_OK; |
594 | | |
595 | 1.36k | if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) { |
596 | 31 | struct easy_pollset ps; |
597 | 31 | struct connectdata *conn; |
598 | 31 | uint32_t i; |
599 | | |
600 | 31 | Curl_pollset_init(&ps); |
601 | 70 | for(i = 0; i < CURL_PTRARRAY_COUNT(&cshutdn->conns); ++i) { |
602 | 39 | conn = CURL_PTRARRAY_GET(&cshutdn->conns, i); |
603 | 39 | Curl_pollset_reset(&ps); |
604 | 39 | Curl_attach_connection(admin, conn, FALSE); |
605 | 39 | result = Curl_conn_adjust_pollset(admin, conn, &ps); |
606 | 39 | Curl_detach_connection(admin); |
607 | | |
608 | 39 | if(!result) |
609 | 39 | result = Curl_pollfds_add_ps(cpfds, &ps); |
610 | 39 | if(result) { |
611 | 0 | Curl_pollset_cleanup(&ps); |
612 | 0 | Curl_pollfds_cleanup(cpfds); |
613 | 0 | goto out; |
614 | 0 | } |
615 | 39 | } |
616 | 31 | Curl_pollset_cleanup(&ps); |
617 | 31 | } |
618 | 1.36k | out: |
619 | 1.36k | return result; |
620 | 1.36k | } |