cshutdn.c 17 KB

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