Coverage Report

Created: 2026-10-15 06:17

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
632
#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
632
{
46
632
  struct connectdata *conn = data->conn;
47
632
  const struct curltime *pnow = Curl_pgrs_now(data);
48
49
632
  DEBUGASSERT(conn);
50
632
  conn->shutdown.start_ms[sockindex] =
51
632
    curlx_ptimediff_ms(pnow, &conn->created);
52
632
  conn->shutdown.timeout_ms = (timeout_ms > 0) ?
53
0
    (timediff_t)timeout_ms :
54
632
    ((data->set.shutdowntimeout > 0) ?
55
632
     data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
56
  /* Set a timer, unless we operate on the admin handle */
57
632
  if(data->mid)
58
0
    Curl_expire_set(data, EXPIRE_SHUTDOWN, conn->shutdown.timeout_ms, pnow);
59
632
  CURL_TRC_M(data, "shutdown start on%s connection",
60
632
             sockindex ? " secondary" : "");
61
632
}
62
63
timediff_t Curl_cshutdn_timeleft_ms(struct Curl_easy *data,
64
                                    struct connectdata *conn,
65
                                    int8_t sockindex)
66
632
{
67
632
  timediff_t left_ms;
68
69
632
  if(conn->shutdown.start_ms[sockindex] < 0)
70
0
    return 0; /* not started or no limits */
71
72
632
  left_ms = conn->shutdown.timeout_ms -
73
632
            (curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->created) -
74
632
             conn->shutdown.start_ms[sockindex]);
75
632
  return left_ms ? left_ms : -1;
76
632
}
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
static void cshutdn_run_conn_handler(struct Curl_easy *data,
100
                                     struct connectdata *conn)
101
2.77k
{
102
2.77k
  if(!conn->bits.shutdown_handler) {
103
104
2.14k
    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
2.14k
    conn->bits.shutdown_handler = TRUE;
124
2.14k
  }
125
2.77k
}
126
127
CURLcode Curl_cshutdn_try_once_idx(struct Curl_easy *data,
128
                                   int8_t sockindex, bool *done)
129
632
{
130
632
  struct Curl_cfilter *cf;
131
632
  CURLcode result = CURLE_OK;
132
632
  timediff_t timeout_ms;
133
134
632
  DEBUGASSERT(data->conn);
135
136
632
  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
632
  cf = data->conn->cfilter[sockindex];
141
632
  while(cf && (!cf->connected || cf->shutdown))
142
0
    cf = cf->next;
143
144
632
  if(!cf) {
145
0
    *done = TRUE;
146
0
    return CURLE_OK;
147
0
  }
148
149
632
  *done = FALSE;
150
632
  if(!CURL_CONN_IN_SHUTDOWN(data->conn, sockindex)) {
151
0
    Curl_cshutdn_start_timer(data, sockindex, 0);
152
0
  }
153
632
  else {
154
632
    timeout_ms = Curl_cshutdn_timeleft_ms(data, data->conn, sockindex);
155
632
    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
632
  }
161
162
1.26k
  while(cf) {
163
632
    if(!cf->shutdown) {
164
632
      bool cfdone = FALSE;
165
632
      result = cf->cft->do_shutdown(cf, data, &cfdone);
166
632
      if(result) {
167
0
        CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result);
168
0
        return result;
169
0
      }
170
632
      else if(!cfdone) {
171
0
        CURL_TRC_CF(data, cf, "shut down not done yet");
172
0
        return CURLE_OK;
173
0
      }
174
632
      CURL_TRC_CF(data, cf, "shut down successfully");
175
632
      cf->shutdown = TRUE;
176
632
    }
177
632
    cf = cf->next;
178
632
  }
179
632
  *done = (!result);
180
632
  return result;
181
632
}
182
183
static void cshutdn_run_once(struct Curl_easy *data,
184
                             struct connectdata *conn,
185
                             bool *done)
186
632
{
187
632
  CURLcode r1, r2;
188
632
  bool done1, done2;
189
190
  /* We expect to be attached when called */
191
632
  DEBUGASSERT(data->conn == conn);
192
193
632
  if(!CURL_CONN_IN_SHUTDOWN(conn, FIRSTSOCKET)) {
194
632
    Curl_cshutdn_start_timer(data, FIRSTSOCKET, 0);
195
632
  }
196
197
632
  cshutdn_run_conn_handler(data, conn);
198
199
632
  if(conn->bits.shutdown_filters) {
200
0
    *done = TRUE;
201
0
    return;
202
0
  }
203
204
632
  if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
205
632
    r1 = Curl_cshutdn_try_once_idx(data, FIRSTSOCKET, &done1);
206
0
  else {
207
0
    r1 = CURLE_OK;
208
0
    done1 = TRUE;
209
0
  }
210
211
632
  if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
212
0
    r2 = Curl_cshutdn_try_once_idx(data, SECONDARYSOCKET, &done2);
213
632
  else {
214
632
    r2 = CURLE_OK;
215
632
    done2 = TRUE;
216
632
  }
217
218
  /* we are done when any failed or both report success */
219
632
  *done = (r1 || r2 || (done1 && done2));
220
632
  if(*done)
221
632
    conn->bits.shutdown_filters = TRUE;
222
632
}
223
224
void Curl_cshutdn_try_once(struct Curl_easy *admin,
225
                           struct connectdata *conn,
226
                           bool *done)
227
632
{
228
632
  DEBUGASSERT(!admin->conn);
229
632
  DEBUGASSERT(!admin->mid);
230
632
  Curl_attach_connection(admin, conn, FALSE);
231
632
  cshutdn_run_once(admin, conn, done);
232
632
  CURL_TRC_M(admin, "[SHUTDOWN] shutdown, done=%d", *done);
233
632
  Curl_detach_connection(admin);
234
632
}
235
236
void Curl_cshutdn_terminate(struct Curl_easy *admin,
237
                            struct connectdata *conn,
238
                            bool do_shutdown)
239
2.14k
{
240
2.14k
  bool done;
241
242
  /* there must be a connection to close */
243
2.14k
  DEBUGASSERT(conn);
244
  /* it must be removed from the connection pool */
245
2.14k
  DEBUGASSERT(conn->cpid == UINT32_MAX);
246
  /* the transfer must be detached from the connection */
247
2.14k
  DEBUGASSERT(admin && !admin->conn);
248
2.14k
  DEBUGASSERT(!admin->mid);
249
250
2.14k
  Curl_attach_connection(admin, conn, FALSE);
251
252
2.14k
  cshutdn_run_conn_handler(admin, conn);
253
2.14k
  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
2.14k
  CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
259
2.14k
             conn->bits.shutdown_filters ? "" : "force ",
260
2.14k
             conn->connection_id);
261
2.14k
  Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET);
262
2.14k
  Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET);
263
2.14k
  Curl_detach_connection(admin);
264
265
2.14k
  if(admin->multi)
266
2.14k
    Curl_multi_ev_conn_done(admin->multi, admin, conn);
267
2.14k
  Curl_conn_free(admin, conn);
268
269
2.14k
  if(admin->multi) {
270
2.14k
    CURL_TRC_M(admin, "[SHUTDOWN] trigger multi connchanged");
271
2.14k
    Curl_multi_connchanged(admin->multi);
272
2.14k
  }
273
2.14k
}
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
14.2k
{
338
14.2k
  struct connectdata *conn;
339
14.2k
  timediff_t next_expire_ms = 0, ms;
340
14.2k
  uint32_t i = 0;
341
14.2k
  bool done;
342
343
14.2k
  if(!CURL_PTRARRAY_COUNT(&cshutdn->conns))
344
14.2k
    return;
345
346
0
  CURL_TRC_M(admin, "[SHUTDOWN] perform on %u connections",
347
0
             CURL_PTRARRAY_COUNT(&cshutdn->conns));
348
0
  sigpipe_apply(admin, sigpipe_ctx);
349
0
  while(i < CURL_PTRARRAY_COUNT(&cshutdn->conns)) {
350
0
    conn = CURL_PTRARRAY_GET(&cshutdn->conns, i);
351
0
    Curl_cshutdn_try_once(admin, conn, &done);
352
0
    if(done) {
353
0
      Curl_ptrarray_remove(&cshutdn->conns, i);
354
0
      Curl_cshutdn_terminate(admin, conn, FALSE);
355
0
    }
356
0
    else {
357
0
      ++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
0
      ms = cshutdn_conn_timeleft(admin, conn);
361
0
      if(ms && (!next_expire_ms || (ms < next_expire_ms)))
362
0
        next_expire_ms = ms;
363
0
    }
364
0
  }
365
366
0
  if(next_expire_ms)
367
0
    Curl_expire(admin, next_expire_ms, EXPIRE_SHUTDOWN);
368
0
}
369
370
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
371
                                  struct Curl_easy *admin,
372
                                  int timeout_ms)
373
0
{
374
0
  struct curltime started = *Curl_pgrs_now(admin);
375
0
  struct Curl_sigpipe_ctx sigpipe_ctx;
376
377
0
  CURL_TRC_M(admin, "[SHUTDOWN] shutdown all");
378
0
  sigpipe_init(&sigpipe_ctx);
379
380
0
  while(CURL_PTRARRAY_COUNT(&cshutdn->conns)) {
381
0
    timediff_t spent_ms;
382
0
    int remain_ms;
383
384
0
    cshutdn_perform(cshutdn, admin, &sigpipe_ctx);
385
386
0
    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
0
    spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(admin), &started);
393
0
    if(spent_ms >= (timediff_t)timeout_ms) {
394
0
      CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, %s",
395
0
                 (timeout_ms > 0) ? "timeout" : "best effort done");
396
0
      break;
397
0
    }
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
0
  while(CURL_PTRARRAY_COUNT(&cshutdn->conns)) {
408
0
    struct connectdata *conn = Curl_ptrarray_remove(&cshutdn->conns, 0);
409
0
    Curl_cshutdn_terminate(admin, conn, FALSE);
410
0
  }
411
0
  DEBUGASSERT(!CURL_PTRARRAY_COUNT(&cshutdn->conns));
412
413
0
  sigpipe_restore(&sigpipe_ctx);
414
0
}
415
416
void Curl_cshutdn_init(struct cshutdn *cshutdn)
417
2.44k
{
418
2.44k
  Curl_ptrarray_init(&cshutdn->conns);
419
2.44k
}
420
421
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
422
                          struct Curl_easy *admin)
423
2.44k
{
424
2.44k
  if(admin) {
425
2.44k
    int timeout_ms = 0;
426
    /* for testing, run graceful shutdown */
427
2.44k
#ifdef DEBUGBUILD
428
2.44k
    {
429
2.44k
      const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
430
2.44k
      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
2.44k
    }
436
2.44k
#endif
437
2.44k
    if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) {
438
0
      CURL_TRC_M(admin, "[SHUTDOWN] destroy, %u connections, timeout=%dms",
439
0
                 CURL_PTRARRAY_COUNT(&cshutdn->conns), timeout_ms);
440
0
      cshutdn_terminate_all(cshutdn, admin, timeout_ms);
441
0
    }
442
2.44k
  }
443
2.44k
}
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
0
{
484
0
  struct Curl_easy *admin = multi->admin;
485
486
  /* Add the connection to our shutdown list for non-blocking shutdown
487
   * during multi processing. */
488
0
  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
0
  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
0
  if(Curl_ptrarray_add(&cshutdn->conns, conn)) {
502
0
    CURL_TRC_M(admin, "[SHUTDOWN] adding failed, discarding #%"
503
0
               FMT_OFF_T, conn->connection_id);
504
0
    Curl_cshutdn_terminate(admin, conn, FALSE);
505
0
    return;
506
0
  }
507
0
  CURL_TRC_M(admin, "[SHUTDOWN] added #%" FMT_OFF_T
508
0
             " to shutdowns, now %u conns in shutdown",
509
0
             conn->connection_id, CURL_PTRARRAY_COUNT(&cshutdn->conns));
510
0
}
511
512
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
513
                          struct Curl_easy *admin,
514
                          struct Curl_sigpipe_ctx *sigpipe_ctx)
515
14.2k
{
516
14.2k
  cshutdn_perform(cshutdn, admin, sigpipe_ctx);
517
14.2k
}
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
0
{
525
0
  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
0
}
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
0
{
593
0
  CURLcode result = CURLE_OK;
594
595
0
  if(CURL_PTRARRAY_COUNT(&cshutdn->conns)) {
596
0
    struct easy_pollset ps;
597
0
    struct connectdata *conn;
598
0
    uint32_t i;
599
600
0
    Curl_pollset_init(&ps);
601
0
    for(i = 0; i < CURL_PTRARRAY_COUNT(&cshutdn->conns); ++i) {
602
0
      conn = CURL_PTRARRAY_GET(&cshutdn->conns, i);
603
0
      Curl_pollset_reset(&ps);
604
0
      Curl_attach_connection(admin, conn, FALSE);
605
0
      result = Curl_conn_adjust_pollset(admin, conn, &ps);
606
0
      Curl_detach_connection(admin);
607
608
0
      if(!result)
609
0
        result = Curl_pollfds_add_ps(cpfds, &ps);
610
0
      if(result) {
611
0
        Curl_pollset_cleanup(&ps);
612
0
        Curl_pollfds_cleanup(cpfds);
613
0
        goto out;
614
0
      }
615
0
    }
616
0
    Curl_pollset_cleanup(&ps);
617
0
  }
618
0
out:
619
0
  return result;
620
0
}