cshutdn.c 17 KB

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