Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmcurl/lib/cshutdn.c
5012 views
1
/***************************************************************************
2
* _ _ ____ _
3
* Project ___| | | | _ \| |
4
* / __| | | | |_) | |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
7
*
8
* Copyright (C) Linus Nielsen Feltzing, <[email protected]>
9
* Copyright (C) Daniel Stenberg, <[email protected]>, 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
static void cshutdn_run_conn_handler(struct Curl_easy *data,
42
struct connectdata *conn)
43
{
44
if(!conn->bits.shutdown_handler) {
45
46
if(conn->handler && conn->handler->disconnect) {
47
/* Some disconnect handlers do a blocking wait on server responses.
48
* FTP/IMAP/SMTP and SFTP are among them. When using the internal
49
* handle, set an overall short timeout so we do not hang for the
50
* default 120 seconds. */
51
if(data->state.internal) {
52
data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
53
(void)Curl_pgrsTime(data, TIMER_STARTOP);
54
}
55
56
/* This is set if protocol-specific cleanups should be made */
57
DEBUGF(infof(data, "connection #%" FMT_OFF_T
58
", shutdown protocol handler (aborted=%d)",
59
conn->connection_id, conn->bits.aborted));
60
/* There are protocol handlers that block on retrieving
61
* server responses here (FTP). Set a short timeout. */
62
conn->handler->disconnect(data, conn, conn->bits.aborted);
63
}
64
65
conn->bits.shutdown_handler = TRUE;
66
}
67
}
68
69
static void cshutdn_run_once(struct Curl_easy *data,
70
struct connectdata *conn,
71
bool *done)
72
{
73
CURLcode r1, r2;
74
bool done1, done2;
75
76
/* We expect to be attached when called */
77
DEBUGASSERT(data->conn == conn);
78
79
cshutdn_run_conn_handler(data, conn);
80
81
if(conn->bits.shutdown_filters) {
82
*done = TRUE;
83
return;
84
}
85
86
if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
87
r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
88
else {
89
r1 = CURLE_OK;
90
done1 = TRUE;
91
}
92
93
if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
94
r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
95
else {
96
r2 = CURLE_OK;
97
done2 = TRUE;
98
}
99
100
/* we are done when any failed or both report success */
101
*done = (r1 || r2 || (done1 && done2));
102
if(*done)
103
conn->bits.shutdown_filters = TRUE;
104
}
105
106
void Curl_cshutdn_run_once(struct Curl_easy *data,
107
struct connectdata *conn,
108
bool *done)
109
{
110
DEBUGASSERT(!data->conn);
111
Curl_attach_connection(data, conn);
112
cshutdn_run_once(data, conn, done);
113
CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
114
Curl_detach_connection(data);
115
}
116
117
void Curl_cshutdn_terminate(struct Curl_easy *data,
118
struct connectdata *conn,
119
bool do_shutdown)
120
{
121
struct Curl_easy *admin = data;
122
bool done;
123
124
/* there must be a connection to close */
125
DEBUGASSERT(conn);
126
/* it must be removed from the connection pool */
127
DEBUGASSERT(!conn->bits.in_cpool);
128
/* the transfer must be detached from the connection */
129
DEBUGASSERT(data && !data->conn);
130
131
/* If we can obtain an internal admin handle, use that to attach
132
* and terminate the connection. Some protocol will try to mess with
133
* `data` during shutdown and we do not want that with a `data` from
134
* the application. */
135
if(data->multi && data->multi->admin)
136
admin = data->multi->admin;
137
138
Curl_attach_connection(admin, conn);
139
140
cshutdn_run_conn_handler(admin, conn);
141
if(do_shutdown) {
142
/* Make a last attempt to shutdown handlers and filters, if
143
* not done so already. */
144
cshutdn_run_once(admin, conn, &done);
145
}
146
CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
147
conn->bits.shutdown_filters ? "" : "force ",
148
conn->connection_id);
149
Curl_conn_close(admin, SECONDARYSOCKET);
150
Curl_conn_close(admin, FIRSTSOCKET);
151
Curl_detach_connection(admin);
152
153
if(data->multi)
154
Curl_multi_ev_conn_done(data->multi, data, conn);
155
Curl_conn_free(admin, conn);
156
157
if(data->multi) {
158
CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
159
Curl_multi_connchanged(data->multi);
160
}
161
}
162
163
static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
164
struct Curl_easy *data,
165
const char *destination)
166
{
167
struct Curl_llist_node *e;
168
struct connectdata *conn;
169
170
e = Curl_llist_head(&cshutdn->list);
171
while(e) {
172
conn = Curl_node_elem(e);
173
if(!destination || !strcmp(destination, conn->destination))
174
break;
175
e = Curl_node_next(e);
176
}
177
178
if(e) {
179
SIGPIPE_VARIABLE(pipe_st);
180
conn = Curl_node_elem(e);
181
Curl_node_remove(e);
182
sigpipe_init(&pipe_st);
183
sigpipe_apply(data, &pipe_st);
184
Curl_cshutdn_terminate(data, conn, FALSE);
185
sigpipe_restore(&pipe_st);
186
return TRUE;
187
}
188
return FALSE;
189
}
190
191
bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
192
const char *destination)
193
{
194
if(data && data->multi) {
195
struct cshutdn *csd = &data->multi->cshutdn;
196
return cshutdn_destroy_oldest(csd, data, destination);
197
}
198
return FALSE;
199
}
200
201
#define NUM_POLLS_ON_STACK 10
202
203
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
204
struct Curl_easy *data,
205
int timeout_ms)
206
{
207
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
208
struct curl_pollfds cpfds;
209
CURLcode result;
210
211
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
212
213
result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
214
if(result)
215
goto out;
216
217
Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
218
219
out:
220
Curl_pollfds_cleanup(&cpfds);
221
return result;
222
}
223
224
static void cshutdn_perform(struct cshutdn *cshutdn,
225
struct Curl_easy *data)
226
{
227
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
228
struct Curl_llist_node *enext;
229
struct connectdata *conn;
230
timediff_t next_expire_ms = 0, ms;
231
bool done;
232
233
if(!e)
234
return;
235
236
CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
237
Curl_llist_count(&cshutdn->list));
238
while(e) {
239
enext = Curl_node_next(e);
240
conn = Curl_node_elem(e);
241
Curl_cshutdn_run_once(data, conn, &done);
242
if(done) {
243
Curl_node_remove(e);
244
Curl_cshutdn_terminate(data, conn, FALSE);
245
}
246
else {
247
/* idata has one timer list, but maybe more than one connection.
248
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
249
ms = Curl_conn_shutdown_timeleft(data, conn);
250
if(ms && ms < next_expire_ms)
251
next_expire_ms = ms;
252
}
253
e = enext;
254
}
255
256
if(next_expire_ms)
257
Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN);
258
}
259
260
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
261
struct Curl_easy *data,
262
int timeout_ms)
263
{
264
struct curltime started = *Curl_pgrs_now(data);
265
struct Curl_llist_node *e;
266
SIGPIPE_VARIABLE(pipe_st);
267
268
DEBUGASSERT(cshutdn);
269
DEBUGASSERT(data);
270
271
CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
272
sigpipe_init(&pipe_st);
273
sigpipe_apply(data, &pipe_st);
274
275
while(Curl_llist_head(&cshutdn->list)) {
276
timediff_t spent_ms;
277
int remain_ms;
278
279
cshutdn_perform(cshutdn, data);
280
281
if(!Curl_llist_head(&cshutdn->list)) {
282
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
283
break;
284
}
285
286
/* wait for activity, timeout or "nothing" */
287
spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &started);
288
if(spent_ms >= (timediff_t)timeout_ms) {
289
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
290
(timeout_ms > 0) ? "timeout" : "best effort done");
291
break;
292
}
293
294
remain_ms = timeout_ms - (int)spent_ms;
295
if(cshutdn_wait(cshutdn, data, remain_ms)) {
296
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
297
break;
298
}
299
}
300
301
/* Terminate any remaining. */
302
e = Curl_llist_head(&cshutdn->list);
303
while(e) {
304
struct connectdata *conn = Curl_node_elem(e);
305
Curl_node_remove(e);
306
Curl_cshutdn_terminate(data, conn, FALSE);
307
e = Curl_llist_head(&cshutdn->list);
308
}
309
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
310
311
sigpipe_restore(&pipe_st);
312
}
313
314
int Curl_cshutdn_init(struct cshutdn *cshutdn,
315
struct Curl_multi *multi)
316
{
317
DEBUGASSERT(multi);
318
cshutdn->multi = multi;
319
Curl_llist_init(&cshutdn->list, NULL);
320
cshutdn->initialised = TRUE;
321
return 0; /* good */
322
}
323
324
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
325
struct Curl_easy *data)
326
{
327
if(cshutdn->initialised && data) {
328
int timeout_ms = 0;
329
/* Just for testing, run graceful shutdown */
330
#ifdef DEBUGBUILD
331
{
332
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
333
if(p) {
334
curl_off_t l;
335
if(!curlx_str_number(&p, &l, INT_MAX))
336
timeout_ms = (int)l;
337
}
338
}
339
#endif
340
341
CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
342
Curl_llist_count(&cshutdn->list), timeout_ms);
343
cshutdn_terminate_all(cshutdn, data, timeout_ms);
344
}
345
cshutdn->multi = NULL;
346
}
347
348
size_t Curl_cshutdn_count(struct Curl_easy *data)
349
{
350
if(data && data->multi) {
351
struct cshutdn *csd = &data->multi->cshutdn;
352
return Curl_llist_count(&csd->list);
353
}
354
return 0;
355
}
356
357
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
358
const char *destination)
359
{
360
if(data && data->multi) {
361
struct cshutdn *csd = &data->multi->cshutdn;
362
size_t n = 0;
363
struct Curl_llist_node *e = Curl_llist_head(&csd->list);
364
while(e) {
365
struct connectdata *conn = Curl_node_elem(e);
366
if(!strcmp(destination, conn->destination))
367
++n;
368
e = Curl_node_next(e);
369
}
370
return n;
371
}
372
return 0;
373
}
374
375
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
376
struct Curl_easy *data,
377
struct connectdata *conn)
378
{
379
CURLMcode mresult;
380
381
DEBUGASSERT(cshutdn);
382
DEBUGASSERT(cshutdn->multi->socket_cb);
383
384
Curl_attach_connection(data, conn);
385
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
386
Curl_detach_connection(data);
387
return mresult;
388
}
389
390
void Curl_cshutdn_add(struct cshutdn *cshutdn,
391
struct connectdata *conn,
392
size_t conns_in_pool)
393
{
394
struct Curl_easy *data = cshutdn->multi->admin;
395
size_t max_total = cshutdn->multi->max_total_connections;
396
397
/* Add the connection to our shutdown list for non-blocking shutdown
398
* during multi processing. */
399
if(max_total > 0 && (max_total <=
400
(conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
401
CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
402
"due to connection limit of %zu", max_total);
403
cshutdn_destroy_oldest(cshutdn, data, NULL);
404
}
405
406
if(cshutdn->multi->socket_cb) {
407
if(cshutdn_update_ev(cshutdn, data, conn)) {
408
CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
409
FMT_OFF_T, conn->connection_id);
410
Curl_cshutdn_terminate(data, conn, FALSE);
411
return;
412
}
413
}
414
415
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
416
CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
417
" to shutdowns, now %zu conns in shutdown",
418
conn->connection_id, Curl_llist_count(&cshutdn->list));
419
}
420
421
static void cshutdn_multi_socket(struct cshutdn *cshutdn,
422
struct Curl_easy *data,
423
curl_socket_t s)
424
{
425
struct Curl_llist_node *e;
426
struct connectdata *conn;
427
bool done;
428
429
DEBUGASSERT(cshutdn->multi->socket_cb);
430
e = Curl_llist_head(&cshutdn->list);
431
while(e) {
432
conn = Curl_node_elem(e);
433
if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
434
Curl_cshutdn_run_once(data, conn, &done);
435
if(done || cshutdn_update_ev(cshutdn, data, conn)) {
436
Curl_node_remove(e);
437
Curl_cshutdn_terminate(data, conn, FALSE);
438
}
439
break;
440
}
441
e = Curl_node_next(e);
442
}
443
}
444
445
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
446
struct Curl_easy *data,
447
curl_socket_t s)
448
{
449
if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
450
cshutdn_perform(cshutdn, data);
451
else
452
cshutdn_multi_socket(cshutdn, data, s);
453
}
454
455
/* return fd_set info about the shutdown connections */
456
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
457
struct Curl_easy *data,
458
fd_set *read_fd_set, fd_set *write_fd_set,
459
int *maxfd)
460
{
461
if(Curl_llist_head(&cshutdn->list)) {
462
struct Curl_llist_node *e;
463
struct easy_pollset ps;
464
465
Curl_pollset_init(&ps);
466
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
467
unsigned int i;
468
struct connectdata *conn = Curl_node_elem(e);
469
CURLcode result;
470
471
Curl_pollset_reset(&ps);
472
Curl_attach_connection(data, conn);
473
result = Curl_conn_adjust_pollset(data, conn, &ps);
474
Curl_detach_connection(data);
475
476
if(result)
477
continue;
478
479
for(i = 0; i < ps.n; i++) {
480
curl_socket_t sock = ps.sockets[i];
481
if(!FDSET_SOCK(sock))
482
continue;
483
#ifdef __DJGPP__
484
#pragma GCC diagnostic push
485
#pragma GCC diagnostic ignored "-Warith-conversion"
486
#endif
487
if(ps.actions[i] & CURL_POLL_IN)
488
FD_SET(sock, read_fd_set);
489
if(ps.actions[i] & CURL_POLL_OUT)
490
FD_SET(sock, write_fd_set);
491
#ifdef __DJGPP__
492
#pragma GCC diagnostic pop
493
#endif
494
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
495
((int)sock > *maxfd))
496
*maxfd = (int)sock;
497
}
498
}
499
Curl_pollset_cleanup(&ps);
500
}
501
}
502
503
/* return information about the shutdown connections */
504
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
505
struct Curl_easy *data,
506
struct Curl_waitfds *cwfds)
507
{
508
unsigned int need = 0;
509
510
if(Curl_llist_head(&cshutdn->list)) {
511
struct Curl_llist_node *e;
512
struct easy_pollset ps;
513
struct connectdata *conn;
514
CURLcode result;
515
516
Curl_pollset_init(&ps);
517
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
518
conn = Curl_node_elem(e);
519
Curl_pollset_reset(&ps);
520
Curl_attach_connection(data, conn);
521
result = Curl_conn_adjust_pollset(data, conn, &ps);
522
Curl_detach_connection(data);
523
524
if(!result)
525
need += Curl_waitfds_add_ps(cwfds, &ps);
526
}
527
Curl_pollset_cleanup(&ps);
528
}
529
return need;
530
}
531
532
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
533
struct Curl_easy *data,
534
struct curl_pollfds *cpfds)
535
{
536
CURLcode result = CURLE_OK;
537
538
if(Curl_llist_head(&cshutdn->list)) {
539
struct Curl_llist_node *e;
540
struct easy_pollset ps;
541
struct connectdata *conn;
542
543
Curl_pollset_init(&ps);
544
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
545
conn = Curl_node_elem(e);
546
Curl_pollset_reset(&ps);
547
Curl_attach_connection(data, conn);
548
result = Curl_conn_adjust_pollset(data, conn, &ps);
549
Curl_detach_connection(data);
550
551
if(!result)
552
result = Curl_pollfds_add_ps(cpfds, &ps);
553
if(result) {
554
Curl_pollset_cleanup(&ps);
555
Curl_pollfds_cleanup(cpfds);
556
goto out;
557
}
558
}
559
Curl_pollset_cleanup(&ps);
560
}
561
out:
562
return result;
563
}
564
565