1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#if !defined(_GNU_SOURCE)
26d4afb5ceSopenharmony_ci#define _GNU_SOURCE
27d4afb5ceSopenharmony_ci#endif
28d4afb5ceSopenharmony_ci
29d4afb5ceSopenharmony_ci#include "private-lib-core.h"
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_ci#if defined(WIN32) || defined(_WIN32)
32d4afb5ceSopenharmony_ci#else
33d4afb5ceSopenharmony_ci#include <sys/wait.h>
34d4afb5ceSopenharmony_ci#endif
35d4afb5ceSopenharmony_ci
36d4afb5ceSopenharmony_cistatic const char *hex = "0123456789ABCDEF";
37d4afb5ceSopenharmony_ci
38d4afb5ceSopenharmony_civoid
39d4afb5ceSopenharmony_cilws_cgi_sul_cb(lws_sorted_usec_list_t *sul);
40d4afb5ceSopenharmony_ci
41d4afb5ceSopenharmony_cistatic int
42d4afb5ceSopenharmony_ciurlencode(const char *in, int inlen, char *out, int outlen)
43d4afb5ceSopenharmony_ci{
44d4afb5ceSopenharmony_ci	char *start = out, *end = out + outlen;
45d4afb5ceSopenharmony_ci
46d4afb5ceSopenharmony_ci	while (inlen-- && out < end - 4) {
47d4afb5ceSopenharmony_ci		if ((*in >= 'A' && *in <= 'Z') ||
48d4afb5ceSopenharmony_ci		    (*in >= 'a' && *in <= 'z') ||
49d4afb5ceSopenharmony_ci		    (*in >= '0' && *in <= '9') ||
50d4afb5ceSopenharmony_ci		    *in == '-' ||
51d4afb5ceSopenharmony_ci		    *in == '_' ||
52d4afb5ceSopenharmony_ci		    *in == '.' ||
53d4afb5ceSopenharmony_ci		    *in == '~') {
54d4afb5ceSopenharmony_ci			*out++ = *in++;
55d4afb5ceSopenharmony_ci			continue;
56d4afb5ceSopenharmony_ci		}
57d4afb5ceSopenharmony_ci		if (*in == ' ') {
58d4afb5ceSopenharmony_ci			*out++ = '+';
59d4afb5ceSopenharmony_ci			in++;
60d4afb5ceSopenharmony_ci			continue;
61d4afb5ceSopenharmony_ci		}
62d4afb5ceSopenharmony_ci		*out++ = '%';
63d4afb5ceSopenharmony_ci		*out++ = hex[(*in) >> 4];
64d4afb5ceSopenharmony_ci		*out++ = hex[(*in++) & 15];
65d4afb5ceSopenharmony_ci	}
66d4afb5ceSopenharmony_ci	*out = '\0';
67d4afb5ceSopenharmony_ci
68d4afb5ceSopenharmony_ci	if (out >= end - 4)
69d4afb5ceSopenharmony_ci		return -1;
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_ci	return lws_ptr_diff(out, start);
72d4afb5ceSopenharmony_ci}
73d4afb5ceSopenharmony_ci
74d4afb5ceSopenharmony_cistatic void
75d4afb5ceSopenharmony_cilws_cgi_grace(lws_sorted_usec_list_t *sul)
76d4afb5ceSopenharmony_ci{
77d4afb5ceSopenharmony_ci	struct lws_cgi *cgi = lws_container_of(sul, struct lws_cgi, sul_grace);
78d4afb5ceSopenharmony_ci
79d4afb5ceSopenharmony_ci	/* act on the reap cb from earlier */
80d4afb5ceSopenharmony_ci
81d4afb5ceSopenharmony_ci	if (!cgi->wsi->http.cgi->post_in_expected)
82d4afb5ceSopenharmony_ci		cgi->wsi->http.cgi->cgi_transaction_over = 1;
83d4afb5ceSopenharmony_ci
84d4afb5ceSopenharmony_ci	lws_callback_on_writable(cgi->wsi);
85d4afb5ceSopenharmony_ci}
86d4afb5ceSopenharmony_ci
87d4afb5ceSopenharmony_ci
88d4afb5ceSopenharmony_cistatic void
89d4afb5ceSopenharmony_cilws_cgi_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si,
90d4afb5ceSopenharmony_ci		 int we_killed_him)
91d4afb5ceSopenharmony_ci{
92d4afb5ceSopenharmony_ci	struct lws *wsi = (struct lws *)opaque;
93d4afb5ceSopenharmony_ci
94d4afb5ceSopenharmony_ci	/*
95d4afb5ceSopenharmony_ci	 * The cgi has come to an end, by itself or with a signal...
96d4afb5ceSopenharmony_ci	 */
97d4afb5ceSopenharmony_ci
98d4afb5ceSopenharmony_ci	if (wsi->http.cgi)
99d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, "post_in_expected %d",
100d4afb5ceSopenharmony_ci			   (int)wsi->http.cgi->post_in_expected);
101d4afb5ceSopenharmony_ci
102d4afb5ceSopenharmony_ci	/*
103d4afb5ceSopenharmony_ci	 * Grace period to handle the incoming stdout
104d4afb5ceSopenharmony_ci	 */
105d4afb5ceSopenharmony_ci
106d4afb5ceSopenharmony_ci	if (wsi->http.cgi)
107d4afb5ceSopenharmony_ci		lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->http.cgi->sul_grace,
108d4afb5ceSopenharmony_ci			 lws_cgi_grace, 1 * LWS_US_PER_SEC);
109d4afb5ceSopenharmony_ci}
110d4afb5ceSopenharmony_ci
111d4afb5ceSopenharmony_ciint
112d4afb5ceSopenharmony_cilws_cgi(struct lws *wsi, const char * const *exec_array,
113d4afb5ceSopenharmony_ci	int script_uri_path_len, int timeout_secs,
114d4afb5ceSopenharmony_ci	const struct lws_protocol_vhost_options *mp_cgienv)
115d4afb5ceSopenharmony_ci{
116d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
117d4afb5ceSopenharmony_ci	struct lws_spawn_piped_info info;
118d4afb5ceSopenharmony_ci	char *env_array[30], cgi_path[500], e[1024], *p = e,
119d4afb5ceSopenharmony_ci	     *end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend;
120d4afb5ceSopenharmony_ci	struct lws_cgi *cgi;
121d4afb5ceSopenharmony_ci	int n, m = 0, i, uritok = -1, c;
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_ci	/*
124d4afb5ceSopenharmony_ci	 * give the cgi stream wsi a cgi struct
125d4afb5ceSopenharmony_ci	 */
126d4afb5ceSopenharmony_ci
127d4afb5ceSopenharmony_ci	wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi");
128d4afb5ceSopenharmony_ci	if (!wsi->http.cgi) {
129d4afb5ceSopenharmony_ci		lwsl_wsi_err(wsi, "OOM");
130d4afb5ceSopenharmony_ci		return -1;
131d4afb5ceSopenharmony_ci	}
132d4afb5ceSopenharmony_ci
133d4afb5ceSopenharmony_ci	wsi->http.cgi->response_code = HTTP_STATUS_OK;
134d4afb5ceSopenharmony_ci
135d4afb5ceSopenharmony_ci	cgi = wsi->http.cgi;
136d4afb5ceSopenharmony_ci	cgi->wsi = wsi; /* set cgi's owning wsi */
137d4afb5ceSopenharmony_ci	sum = cgi->summary;
138d4afb5ceSopenharmony_ci	sumend = sum + strlen(cgi->summary) - 1;
139d4afb5ceSopenharmony_ci
140d4afb5ceSopenharmony_ci	if (timeout_secs)
141d4afb5ceSopenharmony_ci		lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
142d4afb5ceSopenharmony_ci
143d4afb5ceSopenharmony_ci	/* the cgi stdout is always sending us http1.x header data first */
144d4afb5ceSopenharmony_ci	wsi->hdr_state = LCHS_HEADER;
145d4afb5ceSopenharmony_ci
146d4afb5ceSopenharmony_ci	/* add us to the pt list of active cgis */
147d4afb5ceSopenharmony_ci	lwsl_wsi_debug(wsi, "adding cgi %p to list", wsi->http.cgi);
148d4afb5ceSopenharmony_ci	cgi->cgi_list = pt->http.cgi_list;
149d4afb5ceSopenharmony_ci	pt->http.cgi_list = cgi;
150d4afb5ceSopenharmony_ci
151d4afb5ceSopenharmony_ci	/* if it's not already running, start the cleanup timer */
152d4afb5ceSopenharmony_ci	if (!pt->sul_cgi.list.owner)
153d4afb5ceSopenharmony_ci		lws_sul_schedule(pt->context, (int)(pt - pt->context->pt), &pt->sul_cgi,
154d4afb5ceSopenharmony_ci				 lws_cgi_sul_cb, 3 * LWS_US_PER_SEC);
155d4afb5ceSopenharmony_ci
156d4afb5ceSopenharmony_ci	sum += lws_snprintf(sum, lws_ptr_diff_size_t(sumend, sum), "%s ", exec_array[0]);
157d4afb5ceSopenharmony_ci
158d4afb5ceSopenharmony_ci	if (0) {
159d4afb5ceSopenharmony_ci		char *pct = lws_hdr_simple_ptr(wsi,
160d4afb5ceSopenharmony_ci				WSI_TOKEN_HTTP_CONTENT_ENCODING);
161d4afb5ceSopenharmony_ci
162d4afb5ceSopenharmony_ci		if (pct && !strcmp(pct, "gzip"))
163d4afb5ceSopenharmony_ci			wsi->http.cgi->gzip_inflate = 1;
164d4afb5ceSopenharmony_ci	}
165d4afb5ceSopenharmony_ci
166d4afb5ceSopenharmony_ci	/* prepare his CGI env */
167d4afb5ceSopenharmony_ci
168d4afb5ceSopenharmony_ci	n = 0;
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_ci	if (lws_is_ssl(wsi)) {
171d4afb5ceSopenharmony_ci		env_array[n++] = p;
172d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTPS=ON");
173d4afb5ceSopenharmony_ci		p++;
174d4afb5ceSopenharmony_ci	}
175d4afb5ceSopenharmony_ci
176d4afb5ceSopenharmony_ci	if (wsi->http.ah) {
177d4afb5ceSopenharmony_ci		static const unsigned char meths[] = {
178d4afb5ceSopenharmony_ci			WSI_TOKEN_GET_URI,
179d4afb5ceSopenharmony_ci			WSI_TOKEN_POST_URI,
180d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
181d4afb5ceSopenharmony_ci			WSI_TOKEN_OPTIONS_URI,
182d4afb5ceSopenharmony_ci			WSI_TOKEN_PUT_URI,
183d4afb5ceSopenharmony_ci			WSI_TOKEN_PATCH_URI,
184d4afb5ceSopenharmony_ci			WSI_TOKEN_DELETE_URI,
185d4afb5ceSopenharmony_ci#endif
186d4afb5ceSopenharmony_ci			WSI_TOKEN_CONNECT,
187d4afb5ceSopenharmony_ci			WSI_TOKEN_HEAD_URI,
188d4afb5ceSopenharmony_ci		#ifdef LWS_WITH_HTTP2
189d4afb5ceSopenharmony_ci			WSI_TOKEN_HTTP_COLON_PATH,
190d4afb5ceSopenharmony_ci		#endif
191d4afb5ceSopenharmony_ci		};
192d4afb5ceSopenharmony_ci		static const char * const meth_names[] = {
193d4afb5ceSopenharmony_ci			"GET", "POST",
194d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
195d4afb5ceSopenharmony_ci			"OPTIONS", "PUT", "PATCH", "DELETE",
196d4afb5ceSopenharmony_ci#endif
197d4afb5ceSopenharmony_ci			"CONNECT", "HEAD", ":path"
198d4afb5ceSopenharmony_ci		};
199d4afb5ceSopenharmony_ci
200d4afb5ceSopenharmony_ci		if (script_uri_path_len >= 0)
201d4afb5ceSopenharmony_ci			for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++)
202d4afb5ceSopenharmony_ci				if (lws_hdr_total_length(wsi, meths[m]) >=
203d4afb5ceSopenharmony_ci						script_uri_path_len) {
204d4afb5ceSopenharmony_ci					uritok = meths[m];
205d4afb5ceSopenharmony_ci					break;
206d4afb5ceSopenharmony_ci				}
207d4afb5ceSopenharmony_ci
208d4afb5ceSopenharmony_ci		if (script_uri_path_len < 0 && uritok < 0)
209d4afb5ceSopenharmony_ci			goto bail;
210d4afb5ceSopenharmony_ci//		if (script_uri_path_len < 0)
211d4afb5ceSopenharmony_ci//			uritok = 0;
212d4afb5ceSopenharmony_ci
213d4afb5ceSopenharmony_ci		if (m >= 0) {
214d4afb5ceSopenharmony_ci			env_array[n++] = p;
215d4afb5ceSopenharmony_ci			if (m < (int)LWS_ARRAY_SIZE(meths) - 1) {
216d4afb5ceSopenharmony_ci				p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
217d4afb5ceSopenharmony_ci						  "REQUEST_METHOD=%s",
218d4afb5ceSopenharmony_ci						  meth_names[m]);
219d4afb5ceSopenharmony_ci				sum += lws_snprintf(sum, lws_ptr_diff_size_t(sumend, sum), "%s ",
220d4afb5ceSopenharmony_ci						    meth_names[m]);
221d4afb5ceSopenharmony_ci#if defined(LWS_ROLE_H2)
222d4afb5ceSopenharmony_ci			} else {
223d4afb5ceSopenharmony_ci				p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
224d4afb5ceSopenharmony_ci						  "REQUEST_METHOD=%s",
225d4afb5ceSopenharmony_ci			  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
226d4afb5ceSopenharmony_ci				sum += lws_snprintf(sum, lws_ptr_diff_size_t(sumend, sum), "%s ",
227d4afb5ceSopenharmony_ci					lws_hdr_simple_ptr(wsi,
228d4afb5ceSopenharmony_ci						  WSI_TOKEN_HTTP_COLON_METHOD));
229d4afb5ceSopenharmony_ci#endif
230d4afb5ceSopenharmony_ci			}
231d4afb5ceSopenharmony_ci			p++;
232d4afb5ceSopenharmony_ci		}
233d4afb5ceSopenharmony_ci
234d4afb5ceSopenharmony_ci		if (uritok >= 0)
235d4afb5ceSopenharmony_ci			sum += lws_snprintf(sum, lws_ptr_diff_size_t(sumend, sum), "%s ",
236d4afb5ceSopenharmony_ci					    lws_hdr_simple_ptr(wsi, (enum lws_token_indexes)uritok));
237d4afb5ceSopenharmony_ci
238d4afb5ceSopenharmony_ci		env_array[n++] = p;
239d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "QUERY_STRING=");
240d4afb5ceSopenharmony_ci		/* dump the individual URI Arg parameters */
241d4afb5ceSopenharmony_ci		m = 0;
242d4afb5ceSopenharmony_ci		while (script_uri_path_len >= 0) {
243d4afb5ceSopenharmony_ci			i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
244d4afb5ceSopenharmony_ci					     WSI_TOKEN_HTTP_URI_ARGS, m);
245d4afb5ceSopenharmony_ci			if (i < 0)
246d4afb5ceSopenharmony_ci				break;
247d4afb5ceSopenharmony_ci			t = tok;
248d4afb5ceSopenharmony_ci			while (*t && *t != '=' && p < end - 4)
249d4afb5ceSopenharmony_ci				*p++ = *t++;
250d4afb5ceSopenharmony_ci			if (*t == '=')
251d4afb5ceSopenharmony_ci				*p++ = *t++;
252d4afb5ceSopenharmony_ci			i = urlencode(t, i - lws_ptr_diff(t, tok), p, lws_ptr_diff(end, p));
253d4afb5ceSopenharmony_ci			if (i > 0) {
254d4afb5ceSopenharmony_ci				p += i;
255d4afb5ceSopenharmony_ci				*p++ = '&';
256d4afb5ceSopenharmony_ci			}
257d4afb5ceSopenharmony_ci			m++;
258d4afb5ceSopenharmony_ci		}
259d4afb5ceSopenharmony_ci		if (m)
260d4afb5ceSopenharmony_ci			p--;
261d4afb5ceSopenharmony_ci		*p++ = '\0';
262d4afb5ceSopenharmony_ci
263d4afb5ceSopenharmony_ci		if (uritok >= 0) {
264d4afb5ceSopenharmony_ci			strcpy(cgi_path, "REQUEST_URI=");
265d4afb5ceSopenharmony_ci			c = lws_hdr_copy(wsi, cgi_path + 12,
266d4afb5ceSopenharmony_ci					 sizeof(cgi_path) - 12, (enum lws_token_indexes)uritok);
267d4afb5ceSopenharmony_ci			if (c < 0)
268d4afb5ceSopenharmony_ci				goto bail;
269d4afb5ceSopenharmony_ci
270d4afb5ceSopenharmony_ci			cgi_path[sizeof(cgi_path) - 1] = '\0';
271d4afb5ceSopenharmony_ci			env_array[n++] = cgi_path;
272d4afb5ceSopenharmony_ci		}
273d4afb5ceSopenharmony_ci
274d4afb5ceSopenharmony_ci		sum += lws_snprintf(sum, lws_ptr_diff_size_t(sumend, sum), "%s", env_array[n - 1]);
275d4afb5ceSopenharmony_ci
276d4afb5ceSopenharmony_ci		if (script_uri_path_len >= 0) {
277d4afb5ceSopenharmony_ci			env_array[n++] = p;
278d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "PATH_INFO=%s",
279d4afb5ceSopenharmony_ci				      cgi_path + 12 + script_uri_path_len);
280d4afb5ceSopenharmony_ci			p++;
281d4afb5ceSopenharmony_ci		}
282d4afb5ceSopenharmony_ci	}
283d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
284d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
285d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
286d4afb5ceSopenharmony_ci		env_array[n++] = p;
287d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_REFERER=%s",
288d4afb5ceSopenharmony_ci			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
289d4afb5ceSopenharmony_ci		p++;
290d4afb5ceSopenharmony_ci	}
291d4afb5ceSopenharmony_ci#endif
292d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
293d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
294d4afb5ceSopenharmony_ci		env_array[n++] = p;
295d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_HOST=%s",
296d4afb5ceSopenharmony_ci			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
297d4afb5ceSopenharmony_ci		p++;
298d4afb5ceSopenharmony_ci	}
299d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
300d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
301d4afb5ceSopenharmony_ci		env_array[n++] = p;
302d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_COOKIE=");
303d4afb5ceSopenharmony_ci		m = lws_hdr_copy(wsi, p, lws_ptr_diff(end, p), WSI_TOKEN_HTTP_COOKIE);
304d4afb5ceSopenharmony_ci		if (m > 0)
305d4afb5ceSopenharmony_ci			p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
306d4afb5ceSopenharmony_ci		*p++ = '\0';
307d4afb5ceSopenharmony_ci	}
308d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
309d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
310d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
311d4afb5ceSopenharmony_ci		env_array[n++] = p;
312d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_USER_AGENT=%s",
313d4afb5ceSopenharmony_ci			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
314d4afb5ceSopenharmony_ci		p++;
315d4afb5ceSopenharmony_ci	}
316d4afb5ceSopenharmony_ci#endif
317d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
318d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) {
319d4afb5ceSopenharmony_ci		env_array[n++] = p;
320d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_CONTENT_ENCODING=%s",
321d4afb5ceSopenharmony_ci		      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING));
322d4afb5ceSopenharmony_ci		p++;
323d4afb5ceSopenharmony_ci	}
324d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
325d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) {
326d4afb5ceSopenharmony_ci		env_array[n++] = p;
327d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_ACCEPT=%s",
328d4afb5ceSopenharmony_ci			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT));
329d4afb5ceSopenharmony_ci		p++;
330d4afb5ceSopenharmony_ci	}
331d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
332d4afb5ceSopenharmony_ci	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) {
333d4afb5ceSopenharmony_ci		env_array[n++] = p;
334d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "HTTP_ACCEPT_ENCODING=%s",
335d4afb5ceSopenharmony_ci		      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING));
336d4afb5ceSopenharmony_ci		p++;
337d4afb5ceSopenharmony_ci	}
338d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0 &&
339d4afb5ceSopenharmony_ci	    uritok == WSI_TOKEN_POST_URI) {
340d4afb5ceSopenharmony_ci		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
341d4afb5ceSopenharmony_ci			env_array[n++] = p;
342d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "CONTENT_TYPE=%s",
343d4afb5ceSopenharmony_ci			  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
344d4afb5ceSopenharmony_ci			p++;
345d4afb5ceSopenharmony_ci		}
346d4afb5ceSopenharmony_ci		if (!wsi->http.cgi->gzip_inflate &&
347d4afb5ceSopenharmony_ci		    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
348d4afb5ceSopenharmony_ci			env_array[n++] = p;
349d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "CONTENT_LENGTH=%s",
350d4afb5ceSopenharmony_ci					  lws_hdr_simple_ptr(wsi,
351d4afb5ceSopenharmony_ci					  WSI_TOKEN_HTTP_CONTENT_LENGTH));
352d4afb5ceSopenharmony_ci			p++;
353d4afb5ceSopenharmony_ci		}
354d4afb5ceSopenharmony_ci
355d4afb5ceSopenharmony_ci		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
356d4afb5ceSopenharmony_ci			wsi->http.cgi->post_in_expected = (lws_filepos_t)
357d4afb5ceSopenharmony_ci				atoll(lws_hdr_simple_ptr(wsi,
358d4afb5ceSopenharmony_ci						WSI_TOKEN_HTTP_CONTENT_LENGTH));
359d4afb5ceSopenharmony_ci	}
360d4afb5ceSopenharmony_ci
361d4afb5ceSopenharmony_ci
362d4afb5ceSopenharmony_ci	env_array[n++] = p;
363d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin");
364d4afb5ceSopenharmony_ci	p++;
365d4afb5ceSopenharmony_ci
366d4afb5ceSopenharmony_ci	env_array[n++] = p;
367d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "SCRIPT_PATH=%s", exec_array[0]);
368d4afb5ceSopenharmony_ci	p++;
369d4afb5ceSopenharmony_ci
370d4afb5ceSopenharmony_ci	while (mp_cgienv) {
371d4afb5ceSopenharmony_ci		env_array[n++] = p;
372d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s=%s", mp_cgienv->name,
373d4afb5ceSopenharmony_ci			      mp_cgienv->value);
374d4afb5ceSopenharmony_ci		if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) {
375d4afb5ceSopenharmony_ci			wsi->http.cgi->implied_chunked = 1;
376d4afb5ceSopenharmony_ci			wsi->http.cgi->explicitly_chunked = 1;
377d4afb5ceSopenharmony_ci		}
378d4afb5ceSopenharmony_ci		lwsl_info("   Applying mount-specific cgi env '%s'\n",
379d4afb5ceSopenharmony_ci			   env_array[n - 1]);
380d4afb5ceSopenharmony_ci		p++;
381d4afb5ceSopenharmony_ci		mp_cgienv = mp_cgienv->next;
382d4afb5ceSopenharmony_ci	}
383d4afb5ceSopenharmony_ci
384d4afb5ceSopenharmony_ci	env_array[n++] = p;
385d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "SERVER_SOFTWARE=lws");
386d4afb5ceSopenharmony_ci	p++;
387d4afb5ceSopenharmony_ci
388d4afb5ceSopenharmony_ci	env_array[n] = NULL;
389d4afb5ceSopenharmony_ci
390d4afb5ceSopenharmony_ci#if 0
391d4afb5ceSopenharmony_ci	for (m = 0; m < n; m++)
392d4afb5ceSopenharmony_ci		lwsl_notice("    %s\n", env_array[m]);
393d4afb5ceSopenharmony_ci#endif
394d4afb5ceSopenharmony_ci
395d4afb5ceSopenharmony_ci	memset(&info, 0, sizeof(info));
396d4afb5ceSopenharmony_ci	info.env_array = (const char **)env_array;
397d4afb5ceSopenharmony_ci	info.exec_array = exec_array;
398d4afb5ceSopenharmony_ci	info.max_log_lines = 20000;
399d4afb5ceSopenharmony_ci	info.opt_parent = wsi;
400d4afb5ceSopenharmony_ci	info.timeout_us = 5 * 60 * LWS_US_PER_SEC;
401d4afb5ceSopenharmony_ci	info.tsi = wsi->tsi;
402d4afb5ceSopenharmony_ci	info.vh = wsi->a.vhost;
403d4afb5ceSopenharmony_ci	info.ops = &role_ops_cgi;
404d4afb5ceSopenharmony_ci	info.plsp = &wsi->http.cgi->lsp;
405d4afb5ceSopenharmony_ci	info.opaque = wsi;
406d4afb5ceSopenharmony_ci	info.reap_cb = lws_cgi_reap_cb;
407d4afb5ceSopenharmony_ci
408d4afb5ceSopenharmony_ci	/*
409d4afb5ceSopenharmony_ci	 * Actually having made the env, as a cgi we don't need the ah
410d4afb5ceSopenharmony_ci	 * any more
411d4afb5ceSopenharmony_ci	 */
412d4afb5ceSopenharmony_ci	if (script_uri_path_len >= 0) {
413d4afb5ceSopenharmony_ci		lws_header_table_detach(wsi, 0);
414d4afb5ceSopenharmony_ci		info.disable_ctrlc = 1;
415d4afb5ceSopenharmony_ci	}
416d4afb5ceSopenharmony_ci
417d4afb5ceSopenharmony_ci	wsi->http.cgi->lsp = lws_spawn_piped(&info);
418d4afb5ceSopenharmony_ci	if (!wsi->http.cgi->lsp) {
419d4afb5ceSopenharmony_ci		lwsl_err("%s: spawn failed\n", __func__);
420d4afb5ceSopenharmony_ci		goto bail;
421d4afb5ceSopenharmony_ci	}
422d4afb5ceSopenharmony_ci
423d4afb5ceSopenharmony_ci	/* we are the parent process */
424d4afb5ceSopenharmony_ci
425d4afb5ceSopenharmony_ci	wsi->a.context->count_cgi_spawned++;
426d4afb5ceSopenharmony_ci
427d4afb5ceSopenharmony_ci	/* inform cgi owner of the child PID */
428d4afb5ceSopenharmony_ci	n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
429d4afb5ceSopenharmony_ci				    LWS_CALLBACK_CGI_PROCESS_ATTACH,
430d4afb5ceSopenharmony_ci				    wsi->user_space, NULL, (unsigned int)cgi->lsp->child_pid);
431d4afb5ceSopenharmony_ci	(void)n;
432d4afb5ceSopenharmony_ci
433d4afb5ceSopenharmony_ci	return 0;
434d4afb5ceSopenharmony_ci
435d4afb5ceSopenharmony_cibail:
436d4afb5ceSopenharmony_ci	lws_sul_cancel(&wsi->http.cgi->sul_grace);
437d4afb5ceSopenharmony_ci	lws_free_set_NULL(wsi->http.cgi);
438d4afb5ceSopenharmony_ci
439d4afb5ceSopenharmony_ci	lwsl_err("%s: failed\n", __func__);
440d4afb5ceSopenharmony_ci
441d4afb5ceSopenharmony_ci	return -1;
442d4afb5ceSopenharmony_ci}
443d4afb5ceSopenharmony_ci
444d4afb5ceSopenharmony_ci/* we have to parse out these headers in the CGI output */
445d4afb5ceSopenharmony_ci
446d4afb5ceSopenharmony_cistatic const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
447d4afb5ceSopenharmony_ci	"content-length: ",
448d4afb5ceSopenharmony_ci	"location: ",
449d4afb5ceSopenharmony_ci	"status: ",
450d4afb5ceSopenharmony_ci	"transfer-encoding: chunked",
451d4afb5ceSopenharmony_ci	"content-encoding: gzip",
452d4afb5ceSopenharmony_ci};
453d4afb5ceSopenharmony_ci
454d4afb5ceSopenharmony_cienum header_recode {
455d4afb5ceSopenharmony_ci	HR_NAME,
456d4afb5ceSopenharmony_ci	HR_WHITESPACE,
457d4afb5ceSopenharmony_ci	HR_ARG,
458d4afb5ceSopenharmony_ci	HR_CRLF,
459d4afb5ceSopenharmony_ci};
460d4afb5ceSopenharmony_ci
461d4afb5ceSopenharmony_ciint
462d4afb5ceSopenharmony_cilws_cgi_write_split_stdout_headers(struct lws *wsi)
463d4afb5ceSopenharmony_ci{
464d4afb5ceSopenharmony_ci	int n, m, cmd;
465d4afb5ceSopenharmony_ci	unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start,
466d4afb5ceSopenharmony_ci			*end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
467d4afb5ceSopenharmony_ci			*value = NULL;
468d4afb5ceSopenharmony_ci	char c, hrs;
469d4afb5ceSopenharmony_ci
470d4afb5ceSopenharmony_ci	if (!wsi->http.cgi)
471d4afb5ceSopenharmony_ci		return -1;
472d4afb5ceSopenharmony_ci
473d4afb5ceSopenharmony_ci	while (wsi->hdr_state != LHCS_PAYLOAD) {
474d4afb5ceSopenharmony_ci		/*
475d4afb5ceSopenharmony_ci		 * We have to separate header / finalize and payload chunks,
476d4afb5ceSopenharmony_ci		 * since they need to be handled separately
477d4afb5ceSopenharmony_ci		 */
478d4afb5ceSopenharmony_ci		switch (wsi->hdr_state) {
479d4afb5ceSopenharmony_ci		case LHCS_RESPONSE:
480d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "LHCS_RESPONSE: iss response %d",
481d4afb5ceSopenharmony_ci					    wsi->http.cgi->response_code);
482d4afb5ceSopenharmony_ci			if (lws_add_http_header_status(wsi,
483d4afb5ceSopenharmony_ci						   (unsigned int)wsi->http.cgi->response_code,
484d4afb5ceSopenharmony_ci						       &p, end))
485d4afb5ceSopenharmony_ci				return 1;
486d4afb5ceSopenharmony_ci			if (!wsi->http.cgi->explicitly_chunked &&
487d4afb5ceSopenharmony_ci			    !wsi->http.cgi->content_length &&
488d4afb5ceSopenharmony_ci				lws_add_http_header_by_token(wsi,
489d4afb5ceSopenharmony_ci					WSI_TOKEN_HTTP_TRANSFER_ENCODING,
490d4afb5ceSopenharmony_ci					(unsigned char *)"chunked", 7, &p, end))
491d4afb5ceSopenharmony_ci				return 1;
492d4afb5ceSopenharmony_ci			if (!(wsi->mux_substream))
493d4afb5ceSopenharmony_ci				if (lws_add_http_header_by_token(wsi,
494d4afb5ceSopenharmony_ci						WSI_TOKEN_CONNECTION,
495d4afb5ceSopenharmony_ci						(unsigned char *)"close", 5,
496d4afb5ceSopenharmony_ci						&p, end))
497d4afb5ceSopenharmony_ci					return 1;
498d4afb5ceSopenharmony_ci			n = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
499d4afb5ceSopenharmony_ci				      LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);
500d4afb5ceSopenharmony_ci
501d4afb5ceSopenharmony_ci			/*
502d4afb5ceSopenharmony_ci			 * so we have a bunch of http/1 style ascii headers
503d4afb5ceSopenharmony_ci			 * starting from wsi->http.cgi->headers_buf through
504d4afb5ceSopenharmony_ci			 * wsi->http.cgi->headers_pos.  These are OK for http/1
505d4afb5ceSopenharmony_ci			 * connections, but they're no good for http/2 conns.
506d4afb5ceSopenharmony_ci			 *
507d4afb5ceSopenharmony_ci			 * Let's redo them at headers_pos forward using the
508d4afb5ceSopenharmony_ci			 * correct coding for http/1 or http/2
509d4afb5ceSopenharmony_ci			 */
510d4afb5ceSopenharmony_ci			if (!wsi->mux_substream)
511d4afb5ceSopenharmony_ci				goto post_hpack_recode;
512d4afb5ceSopenharmony_ci
513d4afb5ceSopenharmony_ci			p = wsi->http.cgi->headers_start;
514d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_start =
515d4afb5ceSopenharmony_ci					wsi->http.cgi->headers_pos;
516d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_dumped =
517d4afb5ceSopenharmony_ci					wsi->http.cgi->headers_start;
518d4afb5ceSopenharmony_ci			hrs = HR_NAME;
519d4afb5ceSopenharmony_ci			name = buf;
520d4afb5ceSopenharmony_ci
521d4afb5ceSopenharmony_ci			while (p < wsi->http.cgi->headers_start) {
522d4afb5ceSopenharmony_ci				switch (hrs) {
523d4afb5ceSopenharmony_ci				case HR_NAME:
524d4afb5ceSopenharmony_ci					/*
525d4afb5ceSopenharmony_ci					 * in http/2 upper-case header names
526d4afb5ceSopenharmony_ci					 * are illegal.  So convert to lower-
527d4afb5ceSopenharmony_ci					 * case.
528d4afb5ceSopenharmony_ci					 */
529d4afb5ceSopenharmony_ci					if (name - buf > 64)
530d4afb5ceSopenharmony_ci						return -1;
531d4afb5ceSopenharmony_ci					if (*p != ':') {
532d4afb5ceSopenharmony_ci						if (*p >= 'A' && *p <= 'Z')
533d4afb5ceSopenharmony_ci							*name++ = (unsigned char)((*p++) +
534d4afb5ceSopenharmony_ci								  ('a' - 'A'));
535d4afb5ceSopenharmony_ci						else
536d4afb5ceSopenharmony_ci							*name++ = *p++;
537d4afb5ceSopenharmony_ci					} else {
538d4afb5ceSopenharmony_ci						p++;
539d4afb5ceSopenharmony_ci						*name++ = '\0';
540d4afb5ceSopenharmony_ci						value = name;
541d4afb5ceSopenharmony_ci						hrs = HR_WHITESPACE;
542d4afb5ceSopenharmony_ci					}
543d4afb5ceSopenharmony_ci					break;
544d4afb5ceSopenharmony_ci				case HR_WHITESPACE:
545d4afb5ceSopenharmony_ci					if (*p == ' ') {
546d4afb5ceSopenharmony_ci						p++;
547d4afb5ceSopenharmony_ci						break;
548d4afb5ceSopenharmony_ci					}
549d4afb5ceSopenharmony_ci					hrs = HR_ARG;
550d4afb5ceSopenharmony_ci					/* fallthru */
551d4afb5ceSopenharmony_ci				case HR_ARG:
552d4afb5ceSopenharmony_ci					if (name > end - 64)
553d4afb5ceSopenharmony_ci						return -1;
554d4afb5ceSopenharmony_ci
555d4afb5ceSopenharmony_ci					if (*p != '\x0a' && *p != '\x0d') {
556d4afb5ceSopenharmony_ci						*name++ = *p++;
557d4afb5ceSopenharmony_ci						break;
558d4afb5ceSopenharmony_ci					}
559d4afb5ceSopenharmony_ci					hrs = HR_CRLF;
560d4afb5ceSopenharmony_ci					/* fallthru */
561d4afb5ceSopenharmony_ci				case HR_CRLF:
562d4afb5ceSopenharmony_ci					if ((*p != '\x0a' && *p != '\x0d') ||
563d4afb5ceSopenharmony_ci					    p + 1 == wsi->http.cgi->headers_start) {
564d4afb5ceSopenharmony_ci						*name = '\0';
565d4afb5ceSopenharmony_ci						if ((strcmp((const char *)buf,
566d4afb5ceSopenharmony_ci							    "transfer-encoding")
567d4afb5ceSopenharmony_ci						)) {
568d4afb5ceSopenharmony_ci							lwsl_debug("+ %s: %s\n",
569d4afb5ceSopenharmony_ci								   buf, value);
570d4afb5ceSopenharmony_ci							if (
571d4afb5ceSopenharmony_ci					lws_add_http_header_by_name(wsi, buf,
572d4afb5ceSopenharmony_ci					(unsigned char *)value, lws_ptr_diff(name, value),
573d4afb5ceSopenharmony_ci					(unsigned char **)&wsi->http.cgi->headers_pos,
574d4afb5ceSopenharmony_ci					(unsigned char *)wsi->http.cgi->headers_end))
575d4afb5ceSopenharmony_ci								return 1;
576d4afb5ceSopenharmony_ci							hrs = HR_NAME;
577d4afb5ceSopenharmony_ci							name = buf;
578d4afb5ceSopenharmony_ci							break;
579d4afb5ceSopenharmony_ci						}
580d4afb5ceSopenharmony_ci					}
581d4afb5ceSopenharmony_ci					p++;
582d4afb5ceSopenharmony_ci					break;
583d4afb5ceSopenharmony_ci				}
584d4afb5ceSopenharmony_ci			}
585d4afb5ceSopenharmony_cipost_hpack_recode:
586d4afb5ceSopenharmony_ci			/* finalize cached headers before dumping them */
587d4afb5ceSopenharmony_ci			if (lws_finalize_http_header(wsi,
588d4afb5ceSopenharmony_ci			      (unsigned char **)&wsi->http.cgi->headers_pos,
589d4afb5ceSopenharmony_ci			      (unsigned char *)wsi->http.cgi->headers_end)) {
590d4afb5ceSopenharmony_ci
591d4afb5ceSopenharmony_ci				lwsl_notice("finalize failed\n");
592d4afb5ceSopenharmony_ci				return -1;
593d4afb5ceSopenharmony_ci			}
594d4afb5ceSopenharmony_ci
595d4afb5ceSopenharmony_ci			wsi->hdr_state = LHCS_DUMP_HEADERS;
596d4afb5ceSopenharmony_ci			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS;
597d4afb5ceSopenharmony_ci			lws_callback_on_writable(wsi);
598d4afb5ceSopenharmony_ci			/* back to the loop for writeability again */
599d4afb5ceSopenharmony_ci			return 0;
600d4afb5ceSopenharmony_ci
601d4afb5ceSopenharmony_ci		case LHCS_DUMP_HEADERS:
602d4afb5ceSopenharmony_ci
603d4afb5ceSopenharmony_ci			n = (int)(wsi->http.cgi->headers_pos -
604d4afb5ceSopenharmony_ci			    wsi->http.cgi->headers_dumped);
605d4afb5ceSopenharmony_ci			if (n > 512)
606d4afb5ceSopenharmony_ci				n = 512;
607d4afb5ceSopenharmony_ci
608d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "LHCS_DUMP_HEADERS: %d", n);
609d4afb5ceSopenharmony_ci
610d4afb5ceSopenharmony_ci			cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
611d4afb5ceSopenharmony_ci			if (wsi->http.cgi->headers_dumped + n !=
612d4afb5ceSopenharmony_ci						wsi->http.cgi->headers_pos) {
613d4afb5ceSopenharmony_ci				lwsl_notice("adding no fin flag\n");
614d4afb5ceSopenharmony_ci				cmd |= LWS_WRITE_NO_FIN;
615d4afb5ceSopenharmony_ci			}
616d4afb5ceSopenharmony_ci
617d4afb5ceSopenharmony_ci			m = lws_write(wsi,
618d4afb5ceSopenharmony_ci				 (unsigned char *)wsi->http.cgi->headers_dumped,
619d4afb5ceSopenharmony_ci				      (unsigned int)n, (enum lws_write_protocol)cmd);
620d4afb5ceSopenharmony_ci			if (m < 0) {
621d4afb5ceSopenharmony_ci				lwsl_wsi_debug(wsi, "write says %d", m);
622d4afb5ceSopenharmony_ci				return -1;
623d4afb5ceSopenharmony_ci			}
624d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_dumped += n;
625d4afb5ceSopenharmony_ci			if (wsi->http.cgi->headers_dumped ==
626d4afb5ceSopenharmony_ci			    wsi->http.cgi->headers_pos) {
627d4afb5ceSopenharmony_ci				wsi->hdr_state = LHCS_PAYLOAD;
628d4afb5ceSopenharmony_ci				lws_free_set_NULL(wsi->http.cgi->headers_buf);
629d4afb5ceSopenharmony_ci				lwsl_wsi_debug(wsi, "freed cgi headers");
630d4afb5ceSopenharmony_ci
631d4afb5ceSopenharmony_ci				if (wsi->http.cgi->post_in_expected) {
632d4afb5ceSopenharmony_ci					lwsl_wsi_info(wsi, "post data still "
633d4afb5ceSopenharmony_ci							   "expected, asking "
634d4afb5ceSopenharmony_ci							   "for writeable");
635d4afb5ceSopenharmony_ci					lws_callback_on_writable(wsi);
636d4afb5ceSopenharmony_ci				}
637d4afb5ceSopenharmony_ci
638d4afb5ceSopenharmony_ci			} else {
639d4afb5ceSopenharmony_ci				wsi->reason_bf |=
640d4afb5ceSopenharmony_ci					LWS_CB_REASON_AUX_BF__CGI_HEADERS;
641d4afb5ceSopenharmony_ci				lws_callback_on_writable(wsi);
642d4afb5ceSopenharmony_ci			}
643d4afb5ceSopenharmony_ci
644d4afb5ceSopenharmony_ci			/*
645d4afb5ceSopenharmony_ci			 * writeability becomes uncertain now we wrote
646d4afb5ceSopenharmony_ci			 * something, we must return to the event loop
647d4afb5ceSopenharmony_ci			 */
648d4afb5ceSopenharmony_ci			return 0;
649d4afb5ceSopenharmony_ci		}
650d4afb5ceSopenharmony_ci
651d4afb5ceSopenharmony_ci		if (!wsi->http.cgi->headers_buf) {
652d4afb5ceSopenharmony_ci			/* if we don't already have a headers buf, cook one */
653d4afb5ceSopenharmony_ci			n = 2048;
654d4afb5ceSopenharmony_ci			if (wsi->mux_substream)
655d4afb5ceSopenharmony_ci				n = 4096;
656d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_buf = lws_malloc((unsigned int)n + LWS_PRE,
657d4afb5ceSopenharmony_ci							   "cgi hdr buf");
658d4afb5ceSopenharmony_ci			if (!wsi->http.cgi->headers_buf) {
659d4afb5ceSopenharmony_ci				lwsl_wsi_err(wsi, "OOM");
660d4afb5ceSopenharmony_ci				return -1;
661d4afb5ceSopenharmony_ci			}
662d4afb5ceSopenharmony_ci
663d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "allocated cgi hdrs");
664d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_start =
665d4afb5ceSopenharmony_ci					wsi->http.cgi->headers_buf + LWS_PRE;
666d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start;
667d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos;
668d4afb5ceSopenharmony_ci			wsi->http.cgi->headers_end =
669d4afb5ceSopenharmony_ci					wsi->http.cgi->headers_buf + n - 1;
670d4afb5ceSopenharmony_ci
671d4afb5ceSopenharmony_ci			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
672d4afb5ceSopenharmony_ci				wsi->http.cgi->match[n] = 0;
673d4afb5ceSopenharmony_ci				wsi->http.cgi->lp = 0;
674d4afb5ceSopenharmony_ci			}
675d4afb5ceSopenharmony_ci		}
676d4afb5ceSopenharmony_ci
677d4afb5ceSopenharmony_ci		n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
678d4afb5ceSopenharmony_ci		if (n < 0)
679d4afb5ceSopenharmony_ci			return -1;
680d4afb5ceSopenharmony_ci		n = (int)read(n, &c, 1);
681d4afb5ceSopenharmony_ci		if (n < 0) {
682d4afb5ceSopenharmony_ci			if (errno != EAGAIN) {
683d4afb5ceSopenharmony_ci				lwsl_wsi_debug(wsi, "read says %d", n);
684d4afb5ceSopenharmony_ci				return -1;
685d4afb5ceSopenharmony_ci			}
686d4afb5ceSopenharmony_ci			else
687d4afb5ceSopenharmony_ci				n = 0;
688d4afb5ceSopenharmony_ci
689d4afb5ceSopenharmony_ci			if (wsi->http.cgi->headers_pos >=
690d4afb5ceSopenharmony_ci					wsi->http.cgi->headers_end - 4) {
691d4afb5ceSopenharmony_ci				lwsl_wsi_notice(wsi, "CGI hdrs > buf size");
692d4afb5ceSopenharmony_ci
693d4afb5ceSopenharmony_ci				return -1;
694d4afb5ceSopenharmony_ci			}
695d4afb5ceSopenharmony_ci		}
696d4afb5ceSopenharmony_ci		if (!n)
697d4afb5ceSopenharmony_ci			goto agin;
698d4afb5ceSopenharmony_ci
699d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "-- 0x%02X %c %d %d", (unsigned char)c, c,
700d4afb5ceSopenharmony_ci				    wsi->http.cgi->match[1], wsi->hdr_state);
701d4afb5ceSopenharmony_ci		if (!c)
702d4afb5ceSopenharmony_ci			return -1;
703d4afb5ceSopenharmony_ci		switch (wsi->hdr_state) {
704d4afb5ceSopenharmony_ci		case LCHS_HEADER:
705d4afb5ceSopenharmony_ci			hdr:
706d4afb5ceSopenharmony_ci			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
707d4afb5ceSopenharmony_ci				/*
708d4afb5ceSopenharmony_ci				 * significant headers with
709d4afb5ceSopenharmony_ci				 * numeric decimal payloads
710d4afb5ceSopenharmony_ci				 */
711d4afb5ceSopenharmony_ci				if (!significant_hdr[n][wsi->http.cgi->match[n]] &&
712d4afb5ceSopenharmony_ci				    (c >= '0' && c <= '9') &&
713d4afb5ceSopenharmony_ci				    wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) {
714d4afb5ceSopenharmony_ci					wsi->http.cgi->l[wsi->http.cgi->lp++] = c;
715d4afb5ceSopenharmony_ci					wsi->http.cgi->l[wsi->http.cgi->lp] = '\0';
716d4afb5ceSopenharmony_ci					switch (n) {
717d4afb5ceSopenharmony_ci					case SIGNIFICANT_HDR_CONTENT_LENGTH:
718d4afb5ceSopenharmony_ci						wsi->http.cgi->content_length =
719d4afb5ceSopenharmony_ci							(lws_filepos_t)atoll(wsi->http.cgi->l);
720d4afb5ceSopenharmony_ci						break;
721d4afb5ceSopenharmony_ci					case SIGNIFICANT_HDR_STATUS:
722d4afb5ceSopenharmony_ci						wsi->http.cgi->response_code =
723d4afb5ceSopenharmony_ci							atoi(wsi->http.cgi->l);
724d4afb5ceSopenharmony_ci						lwsl_wsi_debug(wsi, "Status set to %d",
725d4afb5ceSopenharmony_ci								wsi->http.cgi->response_code);
726d4afb5ceSopenharmony_ci						break;
727d4afb5ceSopenharmony_ci					default:
728d4afb5ceSopenharmony_ci						break;
729d4afb5ceSopenharmony_ci					}
730d4afb5ceSopenharmony_ci				}
731d4afb5ceSopenharmony_ci				/* hits up to the NUL are sticky until next hdr */
732d4afb5ceSopenharmony_ci				if (significant_hdr[n][wsi->http.cgi->match[n]]) {
733d4afb5ceSopenharmony_ci					if (tolower(c) ==
734d4afb5ceSopenharmony_ci					    significant_hdr[n][wsi->http.cgi->match[n]])
735d4afb5ceSopenharmony_ci						wsi->http.cgi->match[n]++;
736d4afb5ceSopenharmony_ci					else
737d4afb5ceSopenharmony_ci						wsi->http.cgi->match[n] = 0;
738d4afb5ceSopenharmony_ci				}
739d4afb5ceSopenharmony_ci			}
740d4afb5ceSopenharmony_ci
741d4afb5ceSopenharmony_ci			/* some cgi only send us \x0a for EOL */
742d4afb5ceSopenharmony_ci			if (c == '\x0a') {
743d4afb5ceSopenharmony_ci				wsi->hdr_state = LCHS_SINGLE_0A;
744d4afb5ceSopenharmony_ci				*wsi->http.cgi->headers_pos++ = '\x0d';
745d4afb5ceSopenharmony_ci			}
746d4afb5ceSopenharmony_ci			*wsi->http.cgi->headers_pos++ = (unsigned char)c;
747d4afb5ceSopenharmony_ci			if (c == '\x0d')
748d4afb5ceSopenharmony_ci				wsi->hdr_state = LCHS_LF1;
749d4afb5ceSopenharmony_ci
750d4afb5ceSopenharmony_ci			if (wsi->hdr_state != LCHS_HEADER &&
751d4afb5ceSopenharmony_ci			    !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING]
752d4afb5ceSopenharmony_ci				    [wsi->http.cgi->match[
753d4afb5ceSopenharmony_ci					 SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
754d4afb5ceSopenharmony_ci				lwsl_wsi_info(wsi, "cgi produced chunked");
755d4afb5ceSopenharmony_ci				wsi->http.cgi->explicitly_chunked = 1;
756d4afb5ceSopenharmony_ci			}
757d4afb5ceSopenharmony_ci
758d4afb5ceSopenharmony_ci			/* presence of Location: mandates 302 retcode */
759d4afb5ceSopenharmony_ci			if (wsi->hdr_state != LCHS_HEADER &&
760d4afb5ceSopenharmony_ci			    !significant_hdr[SIGNIFICANT_HDR_LOCATION][
761d4afb5ceSopenharmony_ci			      wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
762d4afb5ceSopenharmony_ci				lwsl_wsi_debug(wsi, "CGI: Location hdr seen");
763d4afb5ceSopenharmony_ci				wsi->http.cgi->response_code = 302;
764d4afb5ceSopenharmony_ci			}
765d4afb5ceSopenharmony_ci			break;
766d4afb5ceSopenharmony_ci		case LCHS_LF1:
767d4afb5ceSopenharmony_ci			*wsi->http.cgi->headers_pos++ = (unsigned char)c;
768d4afb5ceSopenharmony_ci			if (c == '\x0a') {
769d4afb5ceSopenharmony_ci				wsi->hdr_state = LCHS_CR2;
770d4afb5ceSopenharmony_ci				break;
771d4afb5ceSopenharmony_ci			}
772d4afb5ceSopenharmony_ci			/* we got \r[^\n]... it's unreasonable */
773d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "funny CRLF 0x%02X",
774d4afb5ceSopenharmony_ci					    (unsigned char)c);
775d4afb5ceSopenharmony_ci			return -1;
776d4afb5ceSopenharmony_ci
777d4afb5ceSopenharmony_ci		case LCHS_CR2:
778d4afb5ceSopenharmony_ci			if (c == '\x0d') {
779d4afb5ceSopenharmony_ci				/* drop the \x0d */
780d4afb5ceSopenharmony_ci				wsi->hdr_state = LCHS_LF2;
781d4afb5ceSopenharmony_ci				break;
782d4afb5ceSopenharmony_ci			}
783d4afb5ceSopenharmony_ci			wsi->hdr_state = LCHS_HEADER;
784d4afb5ceSopenharmony_ci			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
785d4afb5ceSopenharmony_ci				wsi->http.cgi->match[n] = 0;
786d4afb5ceSopenharmony_ci			wsi->http.cgi->lp = 0;
787d4afb5ceSopenharmony_ci			goto hdr;
788d4afb5ceSopenharmony_ci
789d4afb5ceSopenharmony_ci		case LCHS_LF2:
790d4afb5ceSopenharmony_ci		case LCHS_SINGLE_0A:
791d4afb5ceSopenharmony_ci			m = wsi->hdr_state;
792d4afb5ceSopenharmony_ci			if (c == '\x0a') {
793d4afb5ceSopenharmony_ci				lwsl_wsi_debug(wsi, "Content-Length: %lld",
794d4afb5ceSopenharmony_ci					(unsigned long long)
795d4afb5ceSopenharmony_ci					wsi->http.cgi->content_length);
796d4afb5ceSopenharmony_ci				wsi->hdr_state = LHCS_RESPONSE;
797d4afb5ceSopenharmony_ci				/*
798d4afb5ceSopenharmony_ci				 * drop the \0xa ... finalize
799d4afb5ceSopenharmony_ci				 * will add it if needed (HTTP/1)
800d4afb5ceSopenharmony_ci				 */
801d4afb5ceSopenharmony_ci				break;
802d4afb5ceSopenharmony_ci			}
803d4afb5ceSopenharmony_ci			if (m == LCHS_LF2)
804d4afb5ceSopenharmony_ci				/* we got \r\n\r[^\n]... unreasonable */
805d4afb5ceSopenharmony_ci				return -1;
806d4afb5ceSopenharmony_ci			/* we got \x0anext header, it's reasonable */
807d4afb5ceSopenharmony_ci			*wsi->http.cgi->headers_pos++ = (unsigned char)c;
808d4afb5ceSopenharmony_ci			wsi->hdr_state = LCHS_HEADER;
809d4afb5ceSopenharmony_ci			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
810d4afb5ceSopenharmony_ci				wsi->http.cgi->match[n] = 0;
811d4afb5ceSopenharmony_ci			wsi->http.cgi->lp = 0;
812d4afb5ceSopenharmony_ci			break;
813d4afb5ceSopenharmony_ci		case LHCS_PAYLOAD:
814d4afb5ceSopenharmony_ci			break;
815d4afb5ceSopenharmony_ci		}
816d4afb5ceSopenharmony_ci
817d4afb5ceSopenharmony_ciagin:
818d4afb5ceSopenharmony_ci		/* ran out of input, ended the hdrs, or filled up the hdrs buf */
819d4afb5ceSopenharmony_ci		if (!n || wsi->hdr_state == LHCS_PAYLOAD)
820d4afb5ceSopenharmony_ci			return 0;
821d4afb5ceSopenharmony_ci	}
822d4afb5ceSopenharmony_ci
823d4afb5ceSopenharmony_ci	/* payload processing */
824d4afb5ceSopenharmony_ci
825d4afb5ceSopenharmony_ci	m = !wsi->http.cgi->implied_chunked && !wsi->mux_substream &&
826d4afb5ceSopenharmony_ci	//    !wsi->http.cgi->explicitly_chunked &&
827d4afb5ceSopenharmony_ci	    !wsi->http.cgi->content_length;
828d4afb5ceSopenharmony_ci	n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
829d4afb5ceSopenharmony_ci	if (n < 0)
830d4afb5ceSopenharmony_ci		return -1;
831d4afb5ceSopenharmony_ci	n = (int)read(n, start, sizeof(buf) - LWS_PRE);
832d4afb5ceSopenharmony_ci
833d4afb5ceSopenharmony_ci	if (n < 0 && errno != EAGAIN) {
834d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "stdout read says %d", n);
835d4afb5ceSopenharmony_ci		return -1;
836d4afb5ceSopenharmony_ci	}
837d4afb5ceSopenharmony_ci	if (n > 0) {
838d4afb5ceSopenharmony_ci		// lwsl_hexdump_notice(buf, n);
839d4afb5ceSopenharmony_ci
840d4afb5ceSopenharmony_ci		if (!wsi->mux_substream && m) {
841d4afb5ceSopenharmony_ci			char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
842d4afb5ceSopenharmony_ci			m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
843d4afb5ceSopenharmony_ci					 "%X\x0d\x0a", n);
844d4afb5ceSopenharmony_ci			memmove(start + m, start, (unsigned int)n);
845d4afb5ceSopenharmony_ci			memcpy(start, chdr, (unsigned int)m);
846d4afb5ceSopenharmony_ci			memcpy(start + m + n, "\x0d\x0a", 2);
847d4afb5ceSopenharmony_ci			n += m + 2;
848d4afb5ceSopenharmony_ci		}
849d4afb5ceSopenharmony_ci
850d4afb5ceSopenharmony_ci
851d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP2)
852d4afb5ceSopenharmony_ci		if (wsi->mux_substream) {
853d4afb5ceSopenharmony_ci			struct lws *nwsi = lws_get_network_wsi(wsi);
854d4afb5ceSopenharmony_ci
855d4afb5ceSopenharmony_ci			__lws_set_timeout(wsi,
856d4afb5ceSopenharmony_ci				PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
857d4afb5ceSopenharmony_ci
858d4afb5ceSopenharmony_ci			if (!nwsi->immortal_substream_count)
859d4afb5ceSopenharmony_ci				__lws_set_timeout(nwsi,
860d4afb5ceSopenharmony_ci					PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
861d4afb5ceSopenharmony_ci		}
862d4afb5ceSopenharmony_ci#endif
863d4afb5ceSopenharmony_ci
864d4afb5ceSopenharmony_ci		cmd = LWS_WRITE_HTTP;
865d4afb5ceSopenharmony_ci		if (wsi->http.cgi->content_length_seen + (unsigned int)n ==
866d4afb5ceSopenharmony_ci						wsi->http.cgi->content_length)
867d4afb5ceSopenharmony_ci			cmd = LWS_WRITE_HTTP_FINAL;
868d4afb5ceSopenharmony_ci
869d4afb5ceSopenharmony_ci		m = lws_write(wsi, (unsigned char *)start, (unsigned int)n, (enum lws_write_protocol)cmd);
870d4afb5ceSopenharmony_ci		//lwsl_notice("write %d\n", m);
871d4afb5ceSopenharmony_ci		if (m < 0) {
872d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "stdout write says %d\n", m);
873d4afb5ceSopenharmony_ci			return -1;
874d4afb5ceSopenharmony_ci		}
875d4afb5ceSopenharmony_ci		wsi->http.cgi->content_length_seen += (unsigned int)n;
876d4afb5ceSopenharmony_ci	} else {
877d4afb5ceSopenharmony_ci
878d4afb5ceSopenharmony_ci		if (!wsi->mux_substream && m) {
879d4afb5ceSopenharmony_ci			uint8_t term[LWS_PRE + 6];
880d4afb5ceSopenharmony_ci
881d4afb5ceSopenharmony_ci			lwsl_wsi_info(wsi, "sent trailer");
882d4afb5ceSopenharmony_ci			memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
883d4afb5ceSopenharmony_ci
884d4afb5ceSopenharmony_ci			if (lws_write(wsi, term + LWS_PRE, 5,
885d4afb5ceSopenharmony_ci				      LWS_WRITE_HTTP_FINAL) != 5)
886d4afb5ceSopenharmony_ci				return -1;
887d4afb5ceSopenharmony_ci
888d4afb5ceSopenharmony_ci			wsi->http.cgi->cgi_transaction_over = 1;
889d4afb5ceSopenharmony_ci
890d4afb5ceSopenharmony_ci			return 0;
891d4afb5ceSopenharmony_ci		}
892d4afb5ceSopenharmony_ci
893d4afb5ceSopenharmony_ci		if (wsi->cgi_stdout_zero_length) {
894d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "stdout is POLLHUP'd");
895d4afb5ceSopenharmony_ci			if (wsi->mux_substream)
896d4afb5ceSopenharmony_ci				m = lws_write(wsi, (unsigned char *)start, 0,
897d4afb5ceSopenharmony_ci					      LWS_WRITE_HTTP_FINAL);
898d4afb5ceSopenharmony_ci			else
899d4afb5ceSopenharmony_ci				return -1;
900d4afb5ceSopenharmony_ci			return 1;
901d4afb5ceSopenharmony_ci		}
902d4afb5ceSopenharmony_ci		wsi->cgi_stdout_zero_length = 1;
903d4afb5ceSopenharmony_ci	}
904d4afb5ceSopenharmony_ci	return 0;
905d4afb5ceSopenharmony_ci}
906d4afb5ceSopenharmony_ci
907d4afb5ceSopenharmony_ciint
908d4afb5ceSopenharmony_cilws_cgi_kill(struct lws *wsi)
909d4afb5ceSopenharmony_ci{
910d4afb5ceSopenharmony_ci	struct lws_cgi_args args;
911d4afb5ceSopenharmony_ci	pid_t pid;
912d4afb5ceSopenharmony_ci	int n, m = 0;
913d4afb5ceSopenharmony_ci
914d4afb5ceSopenharmony_ci	if (!wsi->http.cgi || !wsi->http.cgi->lsp)
915d4afb5ceSopenharmony_ci		return 0;
916d4afb5ceSopenharmony_ci
917d4afb5ceSopenharmony_ci	pid = wsi->http.cgi->lsp->child_pid;
918d4afb5ceSopenharmony_ci
919d4afb5ceSopenharmony_ci	args.stdwsi = &wsi->http.cgi->lsp->stdwsi[0];
920d4afb5ceSopenharmony_ci	lws_spawn_piped_kill_child_process(wsi->http.cgi->lsp);
921d4afb5ceSopenharmony_ci	/* that has invalidated and NULL'd wsi->http.cgi->lsp */
922d4afb5ceSopenharmony_ci
923d4afb5ceSopenharmony_ci	if (pid != -1) {
924d4afb5ceSopenharmony_ci		if (wsi->http.cgi)
925d4afb5ceSopenharmony_ci			m = wsi->http.cgi->being_closed;
926d4afb5ceSopenharmony_ci		n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
927d4afb5ceSopenharmony_ci						LWS_CALLBACK_CGI_TERMINATED,
928d4afb5ceSopenharmony_ci						wsi->user_space, (void *)&args,
929d4afb5ceSopenharmony_ci						(unsigned int)pid);
930d4afb5ceSopenharmony_ci		if (n && !m)
931d4afb5ceSopenharmony_ci			lws_close_free_wsi(wsi, 0, "lws_cgi_kill");
932d4afb5ceSopenharmony_ci	}
933d4afb5ceSopenharmony_ci
934d4afb5ceSopenharmony_ci	return 0;
935d4afb5ceSopenharmony_ci}
936d4afb5ceSopenharmony_ci
937d4afb5ceSopenharmony_ciint
938d4afb5ceSopenharmony_cilws_cgi_kill_terminated(struct lws_context_per_thread *pt)
939d4afb5ceSopenharmony_ci{
940d4afb5ceSopenharmony_ci	struct lws_cgi **pcgi, *cgi = NULL;
941d4afb5ceSopenharmony_ci	int status, n = 1;
942d4afb5ceSopenharmony_ci
943d4afb5ceSopenharmony_ci	while (n > 0) {
944d4afb5ceSopenharmony_ci		/* find finished guys but don't reap yet */
945d4afb5ceSopenharmony_ci		n = waitpid(-1, &status, WNOHANG);
946d4afb5ceSopenharmony_ci		if (n <= 0)
947d4afb5ceSopenharmony_ci			continue;
948d4afb5ceSopenharmony_ci		lwsl_cx_debug(pt->context, "observed PID %d terminated", n);
949d4afb5ceSopenharmony_ci
950d4afb5ceSopenharmony_ci		pcgi = &pt->http.cgi_list;
951d4afb5ceSopenharmony_ci
952d4afb5ceSopenharmony_ci		/* check all the subprocesses on the cgi list */
953d4afb5ceSopenharmony_ci		while (*pcgi) {
954d4afb5ceSopenharmony_ci			/* get the next one first as list may change */
955d4afb5ceSopenharmony_ci			cgi = *pcgi;
956d4afb5ceSopenharmony_ci			pcgi = &(*pcgi)->cgi_list;
957d4afb5ceSopenharmony_ci
958d4afb5ceSopenharmony_ci			if (cgi->lsp->child_pid <= 0)
959d4afb5ceSopenharmony_ci				continue;
960d4afb5ceSopenharmony_ci
961d4afb5ceSopenharmony_ci			/* finish sending cached headers */
962d4afb5ceSopenharmony_ci			if (cgi->headers_buf)
963d4afb5ceSopenharmony_ci				continue;
964d4afb5ceSopenharmony_ci
965d4afb5ceSopenharmony_ci			/* wait for stdout to be drained */
966d4afb5ceSopenharmony_ci			if (cgi->content_length > cgi->content_length_seen)
967d4afb5ceSopenharmony_ci				continue;
968d4afb5ceSopenharmony_ci
969d4afb5ceSopenharmony_ci			if (cgi->content_length) {
970d4afb5ceSopenharmony_ci				lwsl_cx_debug(pt->context, "expected content "
971d4afb5ceSopenharmony_ci							   "length seen: %lld",
972d4afb5ceSopenharmony_ci				(unsigned long long)cgi->content_length_seen);
973d4afb5ceSopenharmony_ci			}
974d4afb5ceSopenharmony_ci
975d4afb5ceSopenharmony_ci			/* reap it */
976d4afb5ceSopenharmony_ci			waitpid(n, &status, WNOHANG);
977d4afb5ceSopenharmony_ci			/*
978d4afb5ceSopenharmony_ci			 * he's already terminated so no need for kill()
979d4afb5ceSopenharmony_ci			 * but we should do the terminated cgi callback
980d4afb5ceSopenharmony_ci			 * and close him if he's not already closing
981d4afb5ceSopenharmony_ci			 */
982d4afb5ceSopenharmony_ci			if (n == cgi->lsp->child_pid) {
983d4afb5ceSopenharmony_ci
984d4afb5ceSopenharmony_ci				if (!cgi->content_length) {
985d4afb5ceSopenharmony_ci					/*
986d4afb5ceSopenharmony_ci					 * well, if he sends chunked...
987d4afb5ceSopenharmony_ci					 * give him 2s after the
988d4afb5ceSopenharmony_ci					 * cgi terminated to send buffered
989d4afb5ceSopenharmony_ci					 */
990d4afb5ceSopenharmony_ci					cgi->chunked_grace++;
991d4afb5ceSopenharmony_ci					continue;
992d4afb5ceSopenharmony_ci				}
993d4afb5ceSopenharmony_ci
994d4afb5ceSopenharmony_ci				/* defeat kill() */
995d4afb5ceSopenharmony_ci				cgi->lsp->child_pid = 0;
996d4afb5ceSopenharmony_ci				lws_cgi_kill(cgi->wsi);
997d4afb5ceSopenharmony_ci
998d4afb5ceSopenharmony_ci				break;
999d4afb5ceSopenharmony_ci			}
1000d4afb5ceSopenharmony_ci			cgi = NULL;
1001d4afb5ceSopenharmony_ci		}
1002d4afb5ceSopenharmony_ci		/* if not found on the cgi list, as he's one of ours, reap */
1003d4afb5ceSopenharmony_ci		if (!cgi)
1004d4afb5ceSopenharmony_ci			waitpid(n, &status, WNOHANG);
1005d4afb5ceSopenharmony_ci
1006d4afb5ceSopenharmony_ci	}
1007d4afb5ceSopenharmony_ci
1008d4afb5ceSopenharmony_ci	pcgi = &pt->http.cgi_list;
1009d4afb5ceSopenharmony_ci
1010d4afb5ceSopenharmony_ci	/* check all the subprocesses on the cgi list */
1011d4afb5ceSopenharmony_ci	while (*pcgi) {
1012d4afb5ceSopenharmony_ci		/* get the next one first as list may change */
1013d4afb5ceSopenharmony_ci		cgi = *pcgi;
1014d4afb5ceSopenharmony_ci		pcgi = &(*pcgi)->cgi_list;
1015d4afb5ceSopenharmony_ci
1016d4afb5ceSopenharmony_ci		if (!cgi || !cgi->lsp || cgi->lsp->child_pid <= 0)
1017d4afb5ceSopenharmony_ci			continue;
1018d4afb5ceSopenharmony_ci
1019d4afb5ceSopenharmony_ci		/* we deferred killing him after reaping his PID */
1020d4afb5ceSopenharmony_ci		if (cgi->chunked_grace) {
1021d4afb5ceSopenharmony_ci			cgi->chunked_grace++;
1022d4afb5ceSopenharmony_ci			if (cgi->chunked_grace < 2)
1023d4afb5ceSopenharmony_ci				continue;
1024d4afb5ceSopenharmony_ci			goto finish_him;
1025d4afb5ceSopenharmony_ci		}
1026d4afb5ceSopenharmony_ci
1027d4afb5ceSopenharmony_ci		/* finish sending cached headers */
1028d4afb5ceSopenharmony_ci		if (cgi->headers_buf)
1029d4afb5ceSopenharmony_ci			continue;
1030d4afb5ceSopenharmony_ci
1031d4afb5ceSopenharmony_ci		/* wait for stdout to be drained */
1032d4afb5ceSopenharmony_ci		if (cgi->content_length > cgi->content_length_seen)
1033d4afb5ceSopenharmony_ci			continue;
1034d4afb5ceSopenharmony_ci
1035d4afb5ceSopenharmony_ci		if (cgi->content_length)
1036d4afb5ceSopenharmony_ci			lwsl_wsi_debug(cgi->wsi, "expected cont len seen: %lld",
1037d4afb5ceSopenharmony_ci				  (unsigned long long)cgi->content_length_seen);
1038d4afb5ceSopenharmony_ci
1039d4afb5ceSopenharmony_ci		/* reap it */
1040d4afb5ceSopenharmony_ci		if (waitpid(cgi->lsp->child_pid, &status, WNOHANG) > 0) {
1041d4afb5ceSopenharmony_ci
1042d4afb5ceSopenharmony_ci			if (!cgi->content_length) {
1043d4afb5ceSopenharmony_ci				/*
1044d4afb5ceSopenharmony_ci				 * well, if he sends chunked...
1045d4afb5ceSopenharmony_ci				 * give him 2s after the
1046d4afb5ceSopenharmony_ci				 * cgi terminated to send buffered
1047d4afb5ceSopenharmony_ci				 */
1048d4afb5ceSopenharmony_ci				cgi->chunked_grace++;
1049d4afb5ceSopenharmony_ci				continue;
1050d4afb5ceSopenharmony_ci			}
1051d4afb5ceSopenharmony_cifinish_him:
1052d4afb5ceSopenharmony_ci			lwsl_cx_debug(pt->context, "found PID %d on cgi list",
1053d4afb5ceSopenharmony_ci						   cgi->lsp->child_pid);
1054d4afb5ceSopenharmony_ci
1055d4afb5ceSopenharmony_ci			/* defeat kill() */
1056d4afb5ceSopenharmony_ci			cgi->lsp->child_pid = 0;
1057d4afb5ceSopenharmony_ci			lws_cgi_kill(cgi->wsi);
1058d4afb5ceSopenharmony_ci
1059d4afb5ceSopenharmony_ci			break;
1060d4afb5ceSopenharmony_ci		}
1061d4afb5ceSopenharmony_ci	}
1062d4afb5ceSopenharmony_ci
1063d4afb5ceSopenharmony_ci	return 0;
1064d4afb5ceSopenharmony_ci}
1065d4afb5ceSopenharmony_ci
1066d4afb5ceSopenharmony_cistruct lws *
1067d4afb5ceSopenharmony_cilws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch)
1068d4afb5ceSopenharmony_ci{
1069d4afb5ceSopenharmony_ci	if (!wsi->http.cgi)
1070d4afb5ceSopenharmony_ci		return NULL;
1071d4afb5ceSopenharmony_ci
1072d4afb5ceSopenharmony_ci	return wsi->http.cgi->lsp->stdwsi[ch];
1073d4afb5ceSopenharmony_ci}
1074d4afb5ceSopenharmony_ci
1075d4afb5ceSopenharmony_civoid
1076d4afb5ceSopenharmony_cilws_cgi_remove_and_kill(struct lws *wsi)
1077d4afb5ceSopenharmony_ci{
1078d4afb5ceSopenharmony_ci	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
1079d4afb5ceSopenharmony_ci	struct lws_cgi **pcgi = &pt->http.cgi_list;
1080d4afb5ceSopenharmony_ci
1081d4afb5ceSopenharmony_ci	/* remove us from the cgi list */
1082d4afb5ceSopenharmony_ci
1083d4afb5ceSopenharmony_ci	while (*pcgi) {
1084d4afb5ceSopenharmony_ci		if (*pcgi == wsi->http.cgi) {
1085d4afb5ceSopenharmony_ci			/* drop us from the pt cgi list */
1086d4afb5ceSopenharmony_ci			*pcgi = (*pcgi)->cgi_list;
1087d4afb5ceSopenharmony_ci			break;
1088d4afb5ceSopenharmony_ci		}
1089d4afb5ceSopenharmony_ci		pcgi = &(*pcgi)->cgi_list;
1090d4afb5ceSopenharmony_ci	}
1091d4afb5ceSopenharmony_ci	if (wsi->http.cgi->headers_buf)
1092d4afb5ceSopenharmony_ci		lws_free_set_NULL(wsi->http.cgi->headers_buf);
1093d4afb5ceSopenharmony_ci
1094d4afb5ceSopenharmony_ci	/* we have a cgi going, we must kill it */
1095d4afb5ceSopenharmony_ci	wsi->http.cgi->being_closed = 1;
1096d4afb5ceSopenharmony_ci	lws_cgi_kill(wsi);
1097d4afb5ceSopenharmony_ci
1098d4afb5ceSopenharmony_ci	if (!pt->http.cgi_list)
1099d4afb5ceSopenharmony_ci		lws_sul_cancel(&pt->sul_cgi);
1100d4afb5ceSopenharmony_ci}
1101