Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions lib/plat/windows/windows-spawn.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ lws_spawn_sul_reap(struct lws_sorted_usec_list *sul)
struct lws_spawn_piped *lsp = lws_container_of(sul,
struct lws_spawn_piped, sul_reap);

lwsl_notice("%s: reaping spawn after last stdpipe, tries left %d\n",
lwsl_info("%s: reaping spawn after last stdpipe, tries left %d\n",
__func__, lsp->reap_retry_budget);
if (!lws_spawn_reap(lsp) && !lsp->pipes_alive) {
if (--lsp->reap_retry_budget) {
Expand Down Expand Up @@ -269,15 +269,15 @@ windows_pipe_poll_hack(lws_sorted_usec_list_t *sul)
if (!PeekNamedPipe(lsp->pipe_fds[LWS_STDOUT][0], &c, 1, &br,
NULL, NULL)) {

lwsl_notice("%s: stdout pipe errored\n", __func__);
// lwsl_notice("%s: stdout pipe errored\n", __func__);
CloseHandle(lsp->stdwsi[LWS_STDOUT]->desc.filefd);
lsp->pipe_fds[LWS_STDOUT][0] = NULL;
lsp->stdwsi[LWS_STDOUT]->desc.filefd = NULL;
lsp->stdwsi[LWS_STDOUT] = NULL;
lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC);

if (lsp->stdwsi[LWS_STDIN]) {
lwsl_notice("%s: closing stdin from stdout close\n",
lwsl_info("%s: closing stdin from stdout close\n",
__func__);
CloseHandle(lsp->stdwsi[LWS_STDIN]->desc.filefd);
wsi = lsp->stdwsi[LWS_STDIN];
Expand Down Expand Up @@ -308,7 +308,7 @@ windows_pipe_poll_hack(lws_sorted_usec_list_t *sul)
if (!PeekNamedPipe(lsp->pipe_fds[LWS_STDERR][0], &c, 1, &br,
NULL, NULL)) {

lwsl_notice("%s: stderr pipe errored\n", __func__);
lwsl_info("%s: stderr pipe errored\n", __func__);
CloseHandle(wsi1->desc.filefd);
/*
* Assume is stderr still extant on entry, lsp can't
Expand Down Expand Up @@ -400,7 +400,7 @@ lws_spawn_piped(const struct lws_spawn_piped_info *i)

if (!SetHandleInformation(&lsp->pipe_fds[n][!n],
HANDLE_FLAG_INHERIT, 0)) {
lwsl_err("%s: SetHandleInformation() failed\n", __func__);
lwsl_info("%s: SetHandleInformation() failed\n", __func__);
//goto bail1;
}
}
Expand Down Expand Up @@ -452,10 +452,10 @@ lws_spawn_piped(const struct lws_spawn_piped_info *i)
i->opt_parent->child_list = lsp->stdwsi[n];
}

lwsl_notice("%s: pipe handles in %p, out %p, err %p\n", __func__,
lsp->stdwsi[LWS_STDIN]->desc.sockfd,
lsp->stdwsi[LWS_STDOUT]->desc.sockfd,
lsp->stdwsi[LWS_STDERR]->desc.sockfd);
// lwsl_notice("%s: pipe handles in %p, out %p, err %p\n", __func__,
// lsp->stdwsi[LWS_STDIN]->desc.sockfd,
// lsp->stdwsi[LWS_STDOUT]->desc.sockfd,
// lsp->stdwsi[LWS_STDERR]->desc.sockfd);

/*
* Windows nonblocking pipe handling is a mess that is unable
Expand Down Expand Up @@ -484,7 +484,9 @@ lws_spawn_piped(const struct lws_spawn_piped_info *i)
n++;
}

puts(cli);
if (p > cli && p[-1] == ' ')
*(--p) = '\0';
// puts(cli);

memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
Expand Down Expand Up @@ -525,6 +527,9 @@ lws_spawn_piped(const struct lws_spawn_piped_info *i)
lws_sul_schedule(context, i->tsi, &lsp->sul,
lws_spawn_timeout, i->timeout_us);

if (i->plsp)
*(i->plsp) = lsp;

return lsp;

bail3:
Expand Down Expand Up @@ -560,14 +565,21 @@ lws_spawn_stdwsi_closed(struct lws_spawn_piped *lsp, struct lws *wsi)

assert(lsp);
lsp->pipes_alive--;
lwsl_debug("%s: pipes alive %d\n", __func__, lsp->pipes_alive);
if (!lsp->pipes_alive)
lwsl_wsi_warn(wsi, "stdxxx down: pipes alive %d\n", lsp->pipes_alive);
if (!lsp->pipes_alive) {
lwsl_wsi_warn(wsi, "Scheduling reap");
lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi,
&lsp->sul_reap, lws_spawn_sul_reap, 1);
}

for (n = 0; n < 3; n++)
if (lsp->stdwsi[n] == wsi)
if (lsp->stdwsi[n] == wsi) {
lwsl_wsi_warn(wsi, "Identified stxxx wsi in lsp");
lsp->stdwsi[n] = NULL;
return;
}

lwsl_wsi_warn(wsi, "!!! unable to find stdwsi in lsp %p", lsp);
}

int
Expand Down
14 changes: 9 additions & 5 deletions lib/tls/mbedtls/wrapper/platform/ssl_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,18 @@ int ssl_pm_handshake(SSL *ssl)

/*
* OpenSSL return codes:
* 0 = did not complete, but may be retried
* 0 = The TLS/SSL handshake was not successful but was shut down
* controlled and by the specifications of the TLS/SSL protocol.
* 1 = successfully completed
* <0 = death
* <0 = The TLS/SSL handshake was not successful because a fatal error
* occurred either at the protocol level or a connection failure
* occurred.
*/
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
ssl->err = ret;
ssl->err = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? SSL_ERROR_WANT_READ :
SSL_ERROR_WANT_WRITE;
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret);
return 0; /* OpenSSL: did not complete but may be retried */
return -1;
}

if (ret == 0) { /* successful */
Expand All @@ -359,7 +363,7 @@ int ssl_pm_handshake(SSL *ssl)
lwsl_info("%s: ambiguous EAGAIN taken as WANT_READ\n", __func__);
ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ;

return 0;
return -1;
}

lwsl_info("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret);
Expand Down
23 changes: 23 additions & 0 deletions minimal-examples-lowlevel/api-tests/api-test-spawn/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
project(lws-api-test-spawn C)
cmake_minimum_required(VERSION 3.10)
find_package(libwebsockets CONFIG REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
include(CheckCSourceCompiles)
include(LwsCheckRequirements)

set(SRCS main.c)

set(requirements 1)
require_lws_config(LWS_WITH_SPAWN 1 requirements)

if (requirements)

add_executable(${PROJECT_NAME} ${SRCS})

if (websockets_shared)
target_link_libraries(${PROJECT_NAME} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
add_dependencies(${PROJECT_NAME} websockets_shared)
else()
target_link_libraries(${PROJECT_NAME} websockets ${LIBWEBSOCKETS_DEP_LIBS})
endif()
endif()
181 changes: 181 additions & 0 deletions minimal-examples-lowlevel/api-tests/api-test-spawn/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* lws-api-test-spawn
*
* Written in 2010-2022 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The test spawns a child process and captures the stdout, which is checked
* to not be empty.
*/

#include <libwebsockets.h>
#include <string.h>
#include <stdio.h>

static int interrupted, result = 1;
static struct lws_context *context;

struct spawn_test {
lws_sorted_usec_list_t sul_timeout;
struct lws_spawn_piped *lsp;
};

#if defined(WIN32)
static const char * const exec_array[] = { "cmd.exe", "/c", "echo lws-test-spawn-data", NULL };
static const char *expected_stdout = "lws-test-spawn-data\r\n";
#else
static const char * const exec_array[] = { "/bin/sh", "-c", "echo lws-test-spawn-data", NULL };
static const char *expected_stdout = "lws-test-spawn-data\n";
#endif

static char captured_stdout[128];
static size_t captured_stdout_len;

static void
timeout_cb(lws_sorted_usec_list_t *sul)
{
struct spawn_test *st = lws_container_of(sul, struct spawn_test, sul_timeout);
lwsl_err("%s: test timed out\n", __func__);
/* lsp may be NULL if the spawn failed */
if (st->lsp)
lws_spawn_piped_kill_child_process(st->lsp);
interrupted = 1;
lws_cancel_service(context);
}

static void
reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si, int we_killed_him)
{
lwsl_user("%s: child process exited\n", __func__);

if (captured_stdout_len != strlen(expected_stdout) ||
strncmp(captured_stdout, expected_stdout, captured_stdout_len)) {
lwsl_err("Captured stdout mismatch. Got:\n");
lwsl_hexdump_err(captured_stdout, captured_stdout_len);
lwsl_err("Expected:\n");
lwsl_hexdump_err(expected_stdout, strlen(expected_stdout));
} else {
lwsl_user("Captured expected stdout\n");
result = 0; /* PASS */
}

interrupted = 1;
lws_cancel_service(context);
}

static int
protocol_test_spawn_cb(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct spawn_test *st = lws_get_opaque_user_data(wsi);
char buf[4096];
ssize_t ilen;

switch (reason) {
case LWS_CALLBACK_RAW_RX_FILE:

#if defined(WIN32)
{
DWORD rb;
if (!ReadFile((HANDLE)lws_get_socket_fd(wsi), buf, sizeof(buf), &rb, NULL)) {
lwsl_debug("%s: read on stdwsi failed\n", __func__);
return -1;
}
ilen = rb;
}
#else
ilen = read((int)(intptr_t)lws_get_socket_fd(wsi), buf, sizeof(buf));
if (ilen < 1) {
lwsl_debug("%s: read on stdwsi failed\n", __func__);
return -1;
}
#endif


if (ilen > 0 && captured_stdout_len < sizeof(captured_stdout) - 1) {
size_t avail = sizeof(captured_stdout) - 1 - captured_stdout_len;
if (len > avail)
len = avail;
memcpy(captured_stdout + captured_stdout_len, buf, (size_t)ilen);
captured_stdout_len += (size_t)ilen;
captured_stdout[captured_stdout_len] = '\0';
}
break;

case LWS_CALLBACK_RAW_CLOSE_FILE:
lws_spawn_stdwsi_closed(st->lsp, wsi);
break;

default:
break;
}

return 0;
}

static struct lws_protocols protocols[] = {
{
.name = "lws-test-spawn",
.callback = protocol_test_spawn_cb,
},
LWS_PROTOCOL_LIST_TERM
};

int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_spawn_piped_info pinfo;
struct spawn_test st;
const char *env[] = {
"PATH=/usr/local/bin:/usr/bin:/bin",
"LANG=en_US.UTF-8",
NULL
};

memset(&pinfo, 0, sizeof(pinfo));
memset(&info, 0, sizeof info);
memset(&st, 0, sizeof st);
lws_cmdline_option_handle_builtin(argc, argv, &info);

lwsl_user("LWS API selftest: spawn\n");

info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;

context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}

pinfo.env_array = env;
pinfo.exec_array = exec_array;
pinfo.protocol_name = protocols[0].name;
pinfo.reap_cb = reap_cb;
pinfo.plsp = &st.lsp;
pinfo.timeout_us = 10 * LWS_US_PER_SEC;
pinfo.tsi = 0;
pinfo.vh = lws_get_vhost_by_name(context, "default");
pinfo.opaque = &st;

st.lsp = lws_spawn_piped(&pinfo);
if (!st.lsp) {
lwsl_err("lws_spawn_piped failed\n");
goto bail;
}

lws_sul_schedule(context, 0, &st.sul_timeout, timeout_cb, 15 * LWS_US_PER_SEC);

while (lws_service(context, 0) >= 0 && !interrupted)
;

bail:
lws_sul_cancel(&st.sul_timeout);
lws_context_destroy(context);

lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");

return result;
}
Loading