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