void lws_cgi_remove_and_kill(struct lws *wsi) { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; struct lws_cgi **pcgi = &pt->http.cgi_list; /* remove us from the cgi list */ lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi); while (*pcgi) { if (*pcgi == wsi->http.cgi) { /* drop us from the pt cgi list */ *pcgi = (*pcgi)->cgi_list; break; } pcgi = &(*pcgi)->cgi_list; } if (wsi->http.cgi->headers_buf) { lwsl_debug("close: freed cgi headers\n"); lws_free_set_NULL(wsi->http.cgi->headers_buf); } /* we have a cgi going, we must kill it */ wsi->http.cgi->being_closed = 1; lws_cgi_kill(wsi); }
void lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) { struct lws_context_per_thread *pt; struct lws **pwsi, *wsi1, *wsi2; struct lws_context *context; struct lws_tokens eff_buf; int n, m, ret; if (!wsi) return; context = wsi->context; pt = &context->pt[(int)wsi->tsi]; /* if we have children, close them first */ if (wsi->child_list) { wsi2 = wsi->child_list; while (wsi2) { //lwsl_notice("%s: closing %p: close child %p\n", // __func__, wsi, wsi2); wsi1 = wsi2->sibling_list; //lwsl_notice("%s: closing %p: next sibling %p\n", // __func__, wsi2, wsi1); wsi2->parent = NULL; /* stop it doing shutdown processing */ wsi2->socket_is_permanently_unusable = 1; lws_close_free_wsi(wsi2, reason); wsi2 = wsi1; } wsi->child_list = NULL; } #ifdef LWS_WITH_CGI if (wsi->mode == LWSCM_CGI) { /* we are not a network connection, but a handler for CGI io */ if (wsi->parent && wsi->parent->cgi) /* end the binding between us and master */ wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; wsi->socket_is_permanently_unusable = 1; goto just_kill_connection; } if (wsi->cgi) { /* we have a cgi going, we must kill it */ wsi->cgi->being_closed = 1; lws_cgi_kill(wsi); } #endif if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED && wsi->u.http.fd != LWS_INVALID_FILE) { lws_plat_file_close(wsi, wsi->u.http.fd); wsi->u.http.fd = LWS_INVALID_FILE; context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); } if (wsi->socket_is_permanently_unusable || reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || wsi->state == LWSS_SHUTDOWN) goto just_kill_connection; wsi->state_pre_close = wsi->state; switch (wsi->state_pre_close) { case LWSS_DEAD_SOCKET: return; /* we tried the polite way... */ case LWSS_AWAITING_CLOSE_ACK: goto just_kill_connection; case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE: if (wsi->trunc_len) { lws_callback_on_writable(wsi); return; } lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); goto just_kill_connection; default: if (wsi->trunc_len) { lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); return; } break; } if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT || wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) goto just_kill_connection; if (wsi->mode == LWSCM_HTTP_SERVING) context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); if (wsi->mode == LWSCM_HTTP_CLIENT) context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP, wsi->user_space, NULL, 0); /* * are his extensions okay with him closing? Eg he might be a mux * parent and just his ch1 aspect is closing? */ if (lws_ext_cb_active(wsi, LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { lwsl_ext("extension vetoed close\n"); return; } /* * flush any tx pending from extensions, since we may send close packet * if there are problems with send, just nuke the connection */ do { ret = 0; eff_buf.token = NULL; eff_buf.token_len = 0; /* show every extension the new incoming data */ m = lws_ext_cb_active(wsi, LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0); if (m < 0) { lwsl_ext("Extension reports fatal error\n"); goto just_kill_connection; } if (m) /* * at least one extension told us he has more * to spill, so we will go around again after */ ret = 1; /* assuming they left us something to send, send it */ if (eff_buf.token_len) if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, eff_buf.token_len) != eff_buf.token_len) { lwsl_debug("close: ext spill failed\n"); goto just_kill_connection; } } while (ret); /* * signal we are closing, lws_write will * add any necessary version-specific stuff. If the write fails, * no worries we are closing anyway. If we didn't initiate this * close, then our state has been changed to * LWSS_RETURNED_CLOSE_ALREADY and we will skip this. * * Likewise if it's a second call to close this connection after we * sent the close indication to the peer already, we are in state * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time. */ if (wsi->state_pre_close == LWSS_ESTABLISHED && (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */ (reason != LWS_CLOSE_STATUS_NOSTATUS && (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) { lwsl_debug("sending close indication...\n"); /* if no prepared close reason, use 1000 and no aux data */ if (!wsi->u.ws.close_in_ping_buffer_len) { wsi->u.ws.close_in_ping_buffer_len = 2; wsi->u.ws.ping_payload_buf[LWS_PRE] = (reason >> 16) & 0xff; wsi->u.ws.ping_payload_buf[LWS_PRE + 1] = reason & 0xff; }
LWS_EXTERN int lws_cgi_kill_terminated(struct lws_context_per_thread *pt) { struct lws_cgi **pcgi, *cgi = NULL; int status, n = 1; while (n > 0) { /* find finished guys but don't reap yet */ n = waitpid(-1, &status, WNOHANG); if (n <= 0) continue; lwsl_debug("%s: observed PID %d terminated\n", __func__, n); pcgi = &pt->http.cgi_list; /* check all the subprocesses on the cgi list */ while (*pcgi) { /* get the next one first as list may change */ cgi = *pcgi; pcgi = &(*pcgi)->cgi_list; if (cgi->pid <= 0) continue; /* finish sending cached headers */ if (cgi->headers_buf) continue; /* wait for stdout to be drained */ if (cgi->content_length > cgi->content_length_seen) continue; if (cgi->content_length) { lwsl_debug("%s: wsi %p: expected content " "length seen: %lld\n", __func__, cgi->wsi, (unsigned long long)cgi->content_length_seen); } /* reap it */ waitpid(n, &status, WNOHANG); /* * he's already terminated so no need for kill() * but we should do the terminated cgi callback * and close him if he's not already closing */ if (n == cgi->pid) { lwsl_debug("%s: found PID %d on cgi list\n", __func__, n); if (!cgi->content_length) { /* * well, if he sends chunked... * give him 2s after the * cgi terminated to send buffered */ cgi->chunked_grace++; continue; } /* defeat kill() */ cgi->pid = 0; lws_cgi_kill(cgi->wsi); break; } cgi = NULL; } /* if not found on the cgi list, as he's one of ours, reap */ if (!cgi) { lwsl_debug("%s: reading PID %d although no cgi match\n", __func__, n); waitpid(n, &status, WNOHANG); } } pcgi = &pt->http.cgi_list; /* check all the subprocesses on the cgi list */ while (*pcgi) { /* get the next one first as list may change */ cgi = *pcgi; pcgi = &(*pcgi)->cgi_list; if (cgi->pid <= 0) continue; /* we deferred killing him after reaping his PID */ if (cgi->chunked_grace) { cgi->chunked_grace++; if (cgi->chunked_grace < 2) continue; goto finish_him; } /* finish sending cached headers */ if (cgi->headers_buf) continue; /* wait for stdout to be drained */ if (cgi->content_length > cgi->content_length_seen) continue; if (cgi->content_length) lwsl_debug("%s: wsi %p: expected " "content len seen: %lld\n", __func__, cgi->wsi, (unsigned long long)cgi->content_length_seen); /* reap it */ if (waitpid(cgi->pid, &status, WNOHANG) > 0) { if (!cgi->content_length) { /* * well, if he sends chunked... * give him 2s after the * cgi terminated to send buffered */ cgi->chunked_grace++; continue; } finish_him: lwsl_debug("%s: found PID %d on cgi list\n", __func__, cgi->pid); /* defeat kill() */ cgi->pid = 0; lws_cgi_kill(cgi->wsi); break; } } return 0; }