Coverage Report

Created: 2026-09-14 07:05

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