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