option(LWS_WITH_HTTP2 "Compile with support for http2" OFF)
option(LWS_MBED3 "Platform is MBED3" OFF)
option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
+option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
install(FILES ${TEST_SERVER_DATA}
DESTINATION share/libwebsockets-test-server
COMPONENT examples)
+if (LWS_WITH_CGI)
+ set(CGI_TEST_SCRIPT "${PROJECT_SOURCE_DIR}/test-server/lws-cgi-test.sh")
+ install(FILES ${CGI_TEST_SCRIPT}
+ PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ
+ DESTINATION share/libwebsockets-test-server
+ COMPONENT examples)
+ endif()
endif()
# Install the LibwebsocketsConfig.cmake and LibwebsocketsConfigVersion.cmake
message(" LWS_MBED3 = ${LWS_MBED3}")
message(" LWS_SSL_SERVER_WITH_ECDH_CERT = ${LWS_SSL_SERVER_WITH_ECDH_CERT}")
message(" LWS_MAX_SMP = ${LWS_MAX_SMP}")
+message(" LWS_WITH_CGI = ${LWS_WITH_CGI}")
message("---------------------------------------------------------------------")
# These will be available to parent projects including libwebsockets using add_subdirectory()
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
const char *readbuf, size_t len);
+3) MINOR NEWAPI CGI type "network io" subprocess execution is now possible from
+a simple api.
+
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_kill(struct lws *wsi);
+
+To use it, you must first set the cmake option
+
+$ cmake .. -DLWS_WITH_CGI=1
+
+See test-server-http.c and test server path
+
+http://localhost:7681/cgitest
+
+stdin gets http body, you can test it with wget
+
+$ echo hello > hello.txt
+$ wget http://localhost:7681/cgitest --post-file=hello.txt -O- --quiet
+lwstest script
+read="hello"
+
+
v1.7.0
======
body_chunk_len = min(wsi->u.http.content_remain,len);
wsi->u.http.content_remain -= body_chunk_len;
len -= body_chunk_len;
+#ifdef LWS_WITH_CGI
+ if (wsi->cgi) {
+ struct lws_cgi_args args;
- n = wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_BODY, wsi->user_space,
- buf, body_chunk_len);
- if (n)
- goto bail;
+ args.ch = LWS_STDIN;
+ args.stdwsi = &wsi->cgi->stdwsi[0];
+ args.data = buf;
+ args.len = body_chunk_len;
- buf += body_chunk_len;
+ /* returns how much used */
+ n = user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_CGI_STDIN_DATA,
+ wsi->user_space,
+ (void *)&args, 0);
+ if (n < 0)
+ goto bail;
+ } else {
+#endif
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+ buf, body_chunk_len);
+ if (n)
+ goto bail;
+ n = body_chunk_len;
+#ifdef LWS_WITH_CGI
+ }
+#endif
+ buf += n;
if (wsi->u.http.content_remain) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
/* he sent all the content in time */
postbody_completion:
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- n = wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_BODY_COMPLETION,
- wsi->user_space, NULL, 0);
- if (n)
- goto bail;
+#ifdef LWS_WITH_CGI
+ if (!wsi->cgi)
+#endif
+ {
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY_COMPLETION,
+ wsi->user_space, NULL, 0);
+ if (n)
+ goto bail;
+ }
goto http_complete;
}
*/
#include "private-libwebsockets.h"
+#include <sys/types.h>
+#if defined(WIN32) || defined(_WIN32)
+#else
+#include <sys/wait.h>
+#endif
int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr;
*wsi->timeout_list_prev = wsi;
}
+ lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs);
wsi->pending_timeout_limit = now + secs;
wsi->pending_timeout = reason;
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
+#ifdef LWS_WITH_CGI
+ if (wsi->mode == LWSCM_CGI) {
+ /* we are not a network connection, but a handler for CGI io */
+ assert(wsi->master);
+ assert(wsi->master->cgi);
+ assert(wsi->master->cgi->stdwsi[(int)wsi->cgi_channel] == wsi);
+ /* end the binding between us and master */
+ wsi->master->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
+ wsi->master = NULL;
+ wsi->socket_is_permanently_unusable = 1;
+
+ goto just_kill_connection;
+ }
+
+ if (wsi->cgi) {
+ /* we have a cgi going, we must kill it and close the
+ * related stdin/out/err wsis first
+ */
+ wsi->cgi->being_closed = 1;
+ lws_cgi_kill(wsi);
+ }
+#endif
+
if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
wsi->u.http.fd != LWS_INVALID_FILE) {
lwsl_debug("closing http file\n");
}
#endif
+LWS_VISIBLE LWS_EXTERN int
+lws_is_cgi(struct lws *wsi) {
+#ifdef LWS_WITH_CGI
+ return !!wsi->cgi;
+#else
+ return 0;
+#endif
+}
+
+#ifdef LWS_WITH_CGI
+
+static struct lws *
+lws_create_basic_wsi(struct lws_context *context, int tsi)
+{
+ struct lws *new_wsi;
+
+ if ((unsigned int)context->pt[tsi].fds_count ==
+ context->fd_limit_per_thread - 1) {
+ lwsl_err("no space for new conn\n");
+ return NULL;
+ }
+
+ new_wsi = lws_zalloc(sizeof(struct lws));
+ if (new_wsi == NULL) {
+ lwsl_err("Out of memory for new connection\n");
+ return NULL;
+ }
+
+ new_wsi->tsi = tsi;
+ new_wsi->context = context;
+ new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
+ new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+ /* intialize the instance struct */
+
+ new_wsi->state = LWSS_CGI;
+ new_wsi->mode = LWSCM_CGI;
+ new_wsi->hdr_parsing_completed = 0;
+ new_wsi->position_in_fds_table = -1;
+
+ /*
+ * these can only be set once the protocol is known
+ * we set an unestablished connection's protocol pointer
+ * to the start of the supported list, so it can look
+ * for matching ones during the handshake
+ */
+ new_wsi->protocol = context->protocols;
+ new_wsi->user_space = NULL;
+ new_wsi->ietf_spec_revision = 0;
+ new_wsi->sock = LWS_SOCK_INVALID;
+ context->count_wsi_allocated++;
+
+ return new_wsi;
+}
+
+/**
+ * lws_cgi: spawn network-connected cgi process
+ *
+ * @wsi: connection to own the process
+ * @exec_array: array of "exec-name" "arg1" ... "argn" NULL
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ char *env_array[30], cgi_path[400];
+ struct lws_cgi *cgi;
+ int n;
+
+ /*
+ * give the master wsi a cgi struct
+ */
+
+ wsi->cgi = lws_zalloc(sizeof(*wsi->cgi));
+ if (!wsi->cgi) {
+ lwsl_err("%s: OOM\n", __func__);
+ return -1;
+ }
+
+ cgi = wsi->cgi;
+ cgi->wsi = wsi; /* set cgi's owning wsi */
+
+ /* create pipes for [stdin|stdout] and [stderr] */
+
+ for (n = 0; n < 3; n++)
+ if (pipe(cgi->pipe_fds[n]) == -1)
+ goto bail1;
+
+ /* create cgi wsis for each stdin/out/err fd */
+
+ for (n = 0; n < 3; n++) {
+ cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi);
+ if (!cgi->stdwsi[n])
+ goto bail2;
+ cgi->stdwsi[n]->cgi_channel = n;
+ /* read side is 0, stdin we want the write side, others read */
+ cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)];
+ fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK);
+ }
+
+ for (n = 0; n < 3; n++) {
+ cgi->stdwsi[n]->master = wsi;
+ if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
+ goto bail3;
+ }
+
+ lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT);
+ lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN);
+ lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN);
+
+ lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
+ cgi->stdwsi[LWS_STDIN]->sock,
+ cgi->stdwsi[LWS_STDOUT]->sock,
+ cgi->stdwsi[LWS_STDERR]->sock);
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
+
+ /* add us to the pt list of active cgis */
+ cgi->cgi_list = pt->cgi_list;
+ pt->cgi_list = cgi;
+
+ /* prepare his CGI env */
+
+ n = 0;
+ if (wsi->u.hdr.ah) {
+ snprintf(cgi_path, sizeof(cgi_path) - 1, "PATH_INFO=%s",
+ lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
+ cgi_path[sizeof(cgi_path) - 1] = '\0';
+ env_array[n++] = cgi_path;
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+ env_array[n++] = "REQUEST_METHOD=POST";
+ else
+ env_array[n++] = "REQUEST_METHOD=GET";
+ }
+ if (lws_is_ssl(wsi))
+ env_array[n++] = "HTTPS=ON";
+ env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
+ env_array[n++] = "PATH=/bin:/usr/bin:/usrlocal/bin";
+ env_array[n] = NULL;
+
+ /* we are ready with the redirection pipes... run the thing */
+#ifdef LWS_HAVE_VFORK
+ cgi->pid = vfork();
+#else
+ cgi->pid = fork();
+#endif
+ if (cgi->pid < 0) {
+ lwsl_err("fork failed, errno %d", errno);
+ goto bail3;
+ }
+
+ if (cgi->pid)
+ /* we are the parent process */
+ return 0;
+
+ /* We are the forked process, redirect and kill inherited things.
+ *
+ * Because of vfork(), we cannot do anything that changes pages in
+ * the parent environment. Stuff that changes kernel state for the
+ * process is OK. Stuff that happens after the execvpe() is OK.
+ */
+
+ for (n = 0; n < 3; n++) {
+ if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
+ lwsl_err("%s: stdin dup2 failed\n", __func__);
+ goto bail3;
+ }
+ close(cgi->pipe_fds[n][!(n == 0)]);
+ }
+
+ execvpe(exec_array[0], &exec_array[0], &env_array[0]);
+ exit(1);
+
+bail3:
+ /* drop us from the pt cgi list */
+ pt->cgi_list = cgi->cgi_list;
+
+ while (--n >= 0)
+ remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]);
+bail2:
+ for (n = 0; n < 3; n++)
+ if (wsi->cgi->stdwsi[n])
+ lws_free_wsi(cgi->stdwsi[n]);
+
+bail1:
+ for (n = 0; n < 3; n++) {
+ if (cgi->pipe_fds[n][0])
+ close(cgi->pipe_fds[n][0]);
+ if (cgi->pipe_fds[n][1])
+ close(cgi->pipe_fds[n][1]);
+ }
+
+ lws_free_set_NULL(wsi->cgi);
+
+ lwsl_err("%s: failed\n", __func__);
+
+ return -1;
+}
+
+/**
+ * lws_cgi_kill: terminate cgi process associated with wsi
+ *
+ * @wsi: connection to own the process
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_kill(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws_cgi **pcgi = &pt->cgi_list;
+ struct lws_cgi_args args;
+ int n, status, do_close = 0;
+
+ if (!wsi->cgi)
+ return 0;
+
+ lwsl_notice("%s: wsi %p\n", __func__, wsi);
+
+ assert(wsi->cgi);
+
+ if (wsi->cgi->pid > 0) {
+ /* kill the process */
+ n = kill(wsi->cgi->pid, SIGTERM);
+ if (n < 0) {
+ lwsl_err("%s: failed\n", __func__);
+ return 1;
+ }
+ waitpid(wsi->cgi->pid, &status, 0); /* !!! may hang !!! */
+ }
+
+ args.stdwsi = &wsi->cgi->stdwsi[0];
+
+ if (wsi->cgi->pid != -1 && user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_CGI_TERMINATED,
+ wsi->user_space,
+ (void *)&args, 0)) {
+ wsi->cgi->pid = -1;
+ do_close = !wsi->cgi->being_closed;
+ }
+
+ /* remove us from the cgi list */
+ while (*pcgi) {
+ if (*pcgi == wsi->cgi) {
+ /* drop us from the pt cgi list */
+ *pcgi = (*pcgi)->cgi_list;
+ break;
+ }
+ pcgi = &(*pcgi)->cgi_list;
+ }
+
+ for (n = 0 ; n < 3; n++) {
+ if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) {
+ close(wsi->cgi->pipe_fds[n][!!(n == 0)]);
+ wsi->cgi->pipe_fds[n][!!(n == 0)] = -1;
+
+ lws_close_free_wsi(wsi->cgi->stdwsi[n], 0);
+ }
+ }
+
+ lws_free_set_NULL(wsi->cgi);
+
+ if (do_close)
+ lws_close_free_wsi(wsi, 0);
+
+ return 0;
+}
+
+LWS_EXTERN int
+lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
+{
+ struct lws_cgi **pcgi = &pt->cgi_list, *cgi;
+ int status;
+
+ /* check all the subprocesses on the cgi list for termination */
+ while (*pcgi) {
+ /* get the next one because we may close current one next */
+ cgi = *pcgi;
+ pcgi = &(*pcgi)->cgi_list;
+
+ if (cgi->pid > 0 &&
+ waitpid(cgi->pid, &status, WNOHANG) > 0) {
+ cgi->pid = 0;
+ lws_cgi_kill(cgi->wsi);
+ pcgi = &pt->cgi_list;
+ }
+ }
+
+ return 0;
+}
+#endif
LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
+ LWS_CALLBACK_CGI = 40,
+ LWS_CALLBACK_CGI_TERMINATED = 41,
+ LWS_CALLBACK_CGI_STDIN_DATA = 42,
+ LWS_CALLBACK_CGI_STDIN_COMPLETED = 43,
+
/****** add new things just above ---^ ******/
LWS_CALLBACK_USER = 1000, /* user code can use any including / above */
PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11,
PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12,
PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13,
+ PENDING_TIMEOUT_CGI = 14,
/****** add new things just above ---^ ******/
};
LWS_VISIBLE LWS_EXTERN int
lws_is_ssl(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_is_cgi(struct lws *wsi);
+
#ifdef LWS_SHA1_USE_OPENSSL_NAME
#define lws_SHA1 SHA1
#else
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_get_count_threads(struct lws_context *context);
+#ifdef LWS_WITH_CGI
+enum lws_enum_stdinouterr {
+ LWS_STDIN = 0,
+ LWS_STDOUT = 1,
+ LWS_STDERR = 2,
+};
+
+struct lws_cgi_args {
+ struct lws **stdwsi; /* get fd with lws_get_socket_fd() */
+ enum lws_enum_stdinouterr ch;
+ unsigned char *data; /* for messages with payload */
+ int len;
+};
+
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_kill(struct lws *wsi);
+#endif
+
/*
* Wsi-associated File Operations access helpers
*
#include "lws_config.h"
#include "lws_config_private.h"
+
+#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK)
+#define _GNU_SOURCE
+#endif
+
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
LWSS_HTTP2_AWAIT_CLIENT_PREFACE,
LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS,
LWSS_HTTP2_ESTABLISHED,
+
+ LWSS_CGI
};
enum http_version {
/* special internal types */
LWSCM_SERVER_LISTENER,
+ LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */
};
enum {
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
struct lws *timeout_list;
+#ifdef LWS_WITH_CGI
+ struct lws_cgi *cgi_list;
+#endif
void *http_header_data;
struct allocated_headers *ah_pool;
struct lws *ah_wait_list;
unsigned int tx_draining_ext:1;
};
+#ifdef LWS_WITH_CGI
+
+/* wsi who is master of the cgi points to an lws_cgi */
+
+struct lws_cgi {
+ struct lws_cgi *cgi_list;
+ struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
+ struct lws *wsi; /* owner */
+ int pipe_fds[3][2];
+ int pid;
+
+ unsigned int being_closed:1;
+};
+#endif
+
struct lws {
/* structs */
/* pointers */
struct lws_context *context;
+#ifdef LWS_WITH_CGI
+ struct lws_cgi *cgi; /* wsi being cgi master have one of these */
+ struct lws *master; /* for stdin/out/err wsi to point to cgi master */
+#endif
const struct lws_protocols *protocol;
struct lws *timeout_list;
struct lws **timeout_list_prev;
char pending_timeout; /* enum pending_timeout */
char pps; /* enum lws_pending_protocol_send */
char tsi; /* thread service index we belong to */
+#ifdef LWS_WITH_CGI
+ char cgi_channel; /* which of stdin/out/err */
+#endif
};
LWS_EXTERN int log_level;
lws_get_addresses(struct lws_context *context, void *ads, char *name,
int name_len, char *rip, int rip_len);
+LWS_EXTERN int
+lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
+
/*
* custom allocator
*/
return 1;
}
#endif
- lwsl_parser("accepted v%02d connection\n", wsi->ietf_spec_revision);
+ lwsl_parser("accepted v%02d connection\n",
+ wsi->ietf_spec_revision);
return 0;
} /* while all chars are handled */
int n = 0, hit = -1;
for (; n < context->count_threads; n++) {
- if ((unsigned int)context->pt[n].fds_count != context->fd_limit_per_thread - 1 &&
+ if ((unsigned int)context->pt[n].fds_count !=
+ context->fd_limit_per_thread - 1 &&
(unsigned int)context->pt[n].fds_count < lowest) {
lowest = context->pt[n].fds_count;
hit = n;
}
wsi = wsi1;
}
+#ifdef LWS_WITH_CGI
+ lws_cgi_kill_terminated(pt);
+#endif
#if 0
{
char s[300], *p = s;
*/
#if LWS_POSIX
-
/* handle session socket closed */
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
#endif
+ lwsl_debug("fd=%d, revents=%d\n", pollfd->fd, pollfd->revents);
+
/* okay, what we came here to do... */
switch (wsi->mode) {
wsi->u.ws.tx_draining_ext = 0;
}
- if (wsi->u.ws.tx_draining_ext) {
+ if (wsi->u.ws.tx_draining_ext)
/* we cannot deal with new RX until the TX ext
* path has been drained. It's because new
* rx will, eg, crap on the wsi rx buf that
* to avoid blocking.
*/
break;
- }
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
/* We cannot deal with any kind of new RX
} while (more);
if (wsi->u.hdr.ah) {
- lwsl_err("%s: %p: detaching inherited used ah\n", __func__, wsi);
+ lwsl_err("%s: %p: detaching inherited used ah\n",
+ __func__, wsi);
/* show we used all the pending rx up */
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
/* we can run the normal ah detach flow despite
}
break;
+#ifdef LWS_WITH_CGI
+ case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data...
+ * do the callback on our master wsi
+ */
+ {
+ struct lws_cgi_args args;
+
+ if (wsi->cgi_channel >= LWS_STDOUT &&
+ !(pollfd->revents & pollfd->events & LWS_POLLIN))
+ break;
+ if (wsi->cgi_channel == LWS_STDIN &&
+ !(pollfd->revents & pollfd->events & LWS_POLLOUT))
+ break;
+
+ if (wsi->cgi_channel == LWS_STDIN)
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_info("failed at set pollfd\n");
+ return 1;
+ }
+
+ args.ch = wsi->cgi_channel;
+ args.stdwsi = &wsi->master->cgi->stdwsi[0];
+ if (user_callback_handle_rxflow(
+ wsi->master->protocol->callback,
+ wsi->master, LWS_CALLBACK_CGI,
+ wsi->master->user_space,
+ (void *)&args, 0))
+ return 1;
+
+ break;
+ }
+#endif
default:
#ifdef LWS_NO_CLIENT
break;
/* SSL server using ECDH certificate */
#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
+/* CGI apis */
+#cmakedefine LWS_WITH_CGI
+
/* Maximum supported service threads */
#define LWS_MAX_SMP ${LWS_MAX_SMP}
--- /dev/null
+#!/bin/sh
+
+echo "lwstest script stdout"
+>&2 echo "lwstest script stderr"
+
+echo "REQUEST_METHOD=$REQUEST_METHOD"
+
+if [ "$REQUEST_METHOD" = "POST" ] ; then
+ read line
+ echo "read=\"$line\""
+fi
+
+echo "done"
+
+exit 0
+
char buf[256];
char b64[64];
int n, m;
-
#ifdef EXTERNAL_POLL
struct lws_pollargs *pa = (struct lws_pollargs *)in;
#endif
goto try_to_reuse;
}
+#ifdef LWS_WITH_CGI
+ if (!strcmp(in, "/cgitest")) {
+ static char *cmd[] = {
+ "/bin/sh",
+ "-c",
+ INSTALL_DATADIR"/libwebsockets-test-server/lws-cgi-test.sh",
+ NULL
+ };
+
+ lwsl_notice("%s: cgitest\n", __func__);
+ n = lws_cgi(wsi, cmd, 5);
+ if (n) {
+ lwsl_err("%s: cgi failed\n");
+ return -1;
+ }
+ p = buffer + LWS_PRE;
+ end = p + sizeof(buffer) - LWS_PRE;
+
+ if (lws_add_http_header_status(wsi, 200, &p, end))
+ return 1;
+ if (lws_add_http_header_by_token(wsi,
+ WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)"text/plain",
+ 10, &p, end))
+ return 1;
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
+ (unsigned char *)"close", 5, &p, end))
+ return 1;
+ if (lws_finalize_http_header(wsi, &p, end))
+ return 1;
+ n = lws_write(wsi, buffer + LWS_PRE,
+ p - (buffer + LWS_PRE),
+ LWS_WRITE_HTTP_HEADERS);
+ break;
+ }
+#endif
+
/* if a legal POST URL, let it continue and accept data */
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
return 0;
*p = '\0';
lwsl_info("%s\n", buffer + LWS_PRE);
- n = lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE),
+ n = lws_write(wsi, buffer + LWS_PRE,
+ p - (buffer + LWS_PRE),
LWS_WRITE_HTTP_HEADERS);
if (n < 0) {
lws_plat_file_close(wsi, pss->fd);
if (pss->fd == LWS_INVALID_FILE)
goto try_to_reuse;
-
+#ifdef LWS_WITH_CGI
+ if (pss->reason_bf) {
+ lwsl_debug("%s: stdout\n", __func__);
+ n = read(lws_get_socket_fd(pss->args.stdwsi[LWS_STDOUT]),
+ buf + LWS_PRE, sizeof(buf) - LWS_PRE);
+ //lwsl_notice("read %d (errno %d)\n", n, errno);
+ if (n < 0 && errno != EAGAIN)
+ return -1;
+ if (n > 0) {
+ m = lws_write(wsi, (unsigned char *)buf + LWS_PRE, n,
+ LWS_WRITE_HTTP);
+ //lwsl_notice("write %d\n", m);
+ if (m < 0)
+ goto bail;
+ pss->reason_bf = 0;
+ }
+ break;
+ }
+#endif
/*
* we can send more of whatever it is we were sending
*/
* connection continue.
*/
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-
/* if we returned non-zero from here, we kill the connection */
break;
+#ifdef LWS_WITH_CGI
+ /* CGI IO events (POLLIN/OUT) appear here our demo user code policy is
+ *
+ * - POST data goes on subprocess stdin
+ * - subprocess stdout goes on http via writeable callback
+ * - subprocess stderr goes to the logs
+ */
+ case LWS_CALLBACK_CGI:
+ pss->args = *((struct lws_cgi_args *)in);
+ //lwsl_notice("LWS_CALLBACK_CGI: ch %d\n", pss->args.ch);
+ switch (pss->args.ch) { /* which of stdin/out/err ? */
+ case LWS_STDIN:
+ /* TBD stdin rx flow control */
+ break;
+ case LWS_STDOUT:
+ pss->reason_bf |= 1 << pss->args.ch;
+ /* when writing to MASTER would not block */
+ lws_callback_on_writable(wsi);
+ break;
+ case LWS_STDERR:
+ n = read(lws_get_socket_fd(pss->args.stdwsi[LWS_STDERR]),
+ buf, 127);
+ //lwsl_notice("stderr reads %d\n", n);
+ if (n > 0) {
+ if (buf[n - 1] != '\n')
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ lwsl_notice("CGI-stderr: %s\n", buf);
+ }
+ break;
+ }
+ break;
+
+ case LWS_CALLBACK_CGI_TERMINATED:
+ lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
+ /* because we sent on openended http, close the connection */
+ return -1;
+
+ case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
+ lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
+ pss->args = *((struct lws_cgi_args *)in);
+ n = write(lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]),
+ pss->args.data, pss->args.len);
+ lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
+ if (n < pss->args.len)
+ lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: sent %d only %d went",
+ n, pss->args.len);
+ return n;
+#endif
+
/*
* callbacks for managing the external poll() array appear in
* protocol 0 callback
struct per_session_data__http {
lws_filefd_type fd;
+#ifdef LWS_WITH_CGI
+ struct lws_cgi_args args;
+ int reason_bf;
+#endif
};
/*