1
0
Fork 0

Adding upstream version 1.7.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-04-22 17:25:44 +02:00
parent b1ccf37dc2
commit afa05943b9
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
8 changed files with 165 additions and 90 deletions

View file

@ -47,4 +47,34 @@ jobs:
upload_url: ${{ needs.release.outputs.upload_url }} upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: build/ttyd asset_path: build/ttyd
asset_name: ttyd.${{ matrix.target }} asset_name: ttyd.${{ matrix.target }}
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
win10:
runs-on: windows-latest
needs: release
steps:
- uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >-
make
mingw64/mingw-w64-x86_64-gcc
mingw64/mingw-w64-x86_64-cmake
mingw64/mingw-w64-x86_64-zlib
mingw64/mingw-w64-x86_64-libuv
mingw64/mingw-w64-x86_64-mbedtls
mingw64/mingw-w64-x86_64-json-c
update: true
- name: Build ttyd
shell: msys2 {0}
run: ./scripts/mingw-build.sh
- name: Upload assets
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: build/ttyd.exe
asset_name: ttyd.win10.exe
asset_content_type: application/octet-stream

View file

@ -1,7 +1,15 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
project(ttyd C) if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
set(PROJECT_VERSION "1.7.0") project(ttyd C)
set(PROJECT_VERSION_MAJOR "1")
set(PROJECT_VERSION_MINOR "7")
set(PROJECT_VERSION_PATCH "1")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
else()
cmake_policy(SET CMP0048 NEW)
project(ttyd VERSION 1.7.1 LANGUAGES C)
endif()
find_package(Git) find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
@ -72,6 +80,8 @@ endif()
if(WIN32) if(WIN32)
list(APPEND LINK_LIBS shell32 ws2_32) list(APPEND LINK_LIBS shell32 ws2_32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app.rc.in ${CMAKE_CURRENT_BINARY_DIR}/app.rc @ONLY)
list(APPEND SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/app.rc)
elseif(NOT APPLE) elseif(NOT APPLE)
list(APPEND LINK_LIBS util) list(APPEND LINK_LIBS util)
endif() endif()

View file

@ -66,7 +66,7 @@ USAGE:
ttyd [options] <command> [<arguments...>] ttyd [options] <command> [<arguments...>]
VERSION: VERSION:
1.7.0 1.7.1
OPTIONS: OPTIONS:
-p, --port Port to listen (default: 7681, use `0` for random port) -p, --port Port to listen (default: 7681, use `0` for random port)

32
app.rc.in Normal file
View file

@ -0,0 +1,32 @@
#include <winver.h>
#define VERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0
#define VERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.0\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION
PRODUCTVERSION VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "Share your terminal over the web\0"
VALUE "ProductName", "ttyd\0"
VALUE "ProductVersion", VERSION_STR
VALUE "FileVersion", VERSION_STR
VALUE "InternalName", "ttyd\0"
VALUE "OriginalFilename", "ttyd.exe\0"
VALUE "LegalCopyright", "Copyright (C) 2016-2022 Shuanglei Tao\0"
VALUE "Comment", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View file

@ -51,9 +51,7 @@ static bool check_host_origin(struct lws *wsi) {
char buf[256]; char buf[256];
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
int len = lws_hdr_copy(wsi, buf, (int)sizeof(buf), WSI_TOKEN_ORIGIN); int len = lws_hdr_copy(wsi, buf, (int)sizeof(buf), WSI_TOKEN_ORIGIN);
if (len <= 0) { if (len <= 0) return false;
return false;
}
const char *prot, *address, *path; const char *prot, *address, *path;
int port; int port;
@ -71,26 +69,43 @@ static bool check_host_origin(struct lws *wsi) {
return len > 0 && strcasecmp(buf, host_buf) == 0; return len > 0 && strcasecmp(buf, host_buf) == 0;
} }
static void process_read_cb(void *ctx, pty_buf_t *buf, bool eof) { static pty_ctx_t *pty_ctx_init(struct pss_tty *pss) {
struct pss_tty *pss = (struct pss_tty *)ctx; pty_ctx_t *ctx = xmalloc(sizeof(pty_ctx_t));
if (eof && !process_running(pss->process)) ctx->pss = pss;
pss->lws_close_status = pss->process->exit_code == 0 ? 1000 : 1006; ctx->ws_closed = false;
else return ctx;
pss->pty_buf = buf;
lws_callback_on_writable(pss->wsi);
} }
static void process_exit_cb(void *ctx, pty_process *process) { static void pty_ctx_free(pty_ctx_t *ctx) { free(ctx); }
struct pss_tty *pss = (struct pss_tty *)ctx;
pss->process = NULL; static void process_read_cb(pty_process *process, pty_buf_t *buf, bool eof) {
if (process->killed) { pty_ctx_t *ctx = (pty_ctx_t *)process->ctx;
lwsl_notice("process killed with signal %d, pid: %d\n", process->exit_signal, process->pid); if (ctx->ws_closed) {
} else { pty_buf_free(buf);
lwsl_notice("process exited with code %d, pid: %d\n", process->exit_code, process->pid); return;
pss->lws_close_status = process->exit_code == 0 ? 1000 : 1006;
lws_callback_on_writable(pss->wsi);
} }
if (eof && !process_running(process))
ctx->pss->lws_close_status = process->exit_code == 0 ? 1000 : 1006;
else
ctx->pss->pty_buf = buf;
lws_callback_on_writable(ctx->pss->wsi);
}
static void process_exit_cb(pty_process *process) {
pty_ctx_t *ctx = (pty_ctx_t *)process->ctx;
if (ctx->ws_closed) {
lwsl_notice("process killed with signal %d, pid: %d\n", process->exit_signal, process->pid);
goto done;
}
lwsl_notice("process exited with code %d, pid: %d\n", process->exit_code, process->pid);
ctx->pss->process = NULL;
ctx->pss->lws_close_status = process->exit_code == 0 ? 1000 : 1006;
lws_callback_on_writable(ctx->pss->wsi);
done:
pty_ctx_free(ctx);
} }
static char **build_args(struct pss_tty *pss) { static char **build_args(struct pss_tty *pss) {
@ -133,7 +148,7 @@ static char **build_env(struct pss_tty *pss) {
} }
static bool spawn_process(struct pss_tty *pss, uint16_t columns, uint16_t rows) { static bool spawn_process(struct pss_tty *pss, uint16_t columns, uint16_t rows) {
pty_process *process = process_init((void *)pss, server->loop, build_args(pss), build_env(pss)); pty_process *process = process_init((void *)pty_ctx_init(pss), server->loop, build_args(pss), build_env(pss));
if (server->cwd != NULL) process->cwd = strdup(server->cwd); if (server->cwd != NULL) process->cwd = strdup(server->cwd);
if (columns > 0) process->columns = columns; if (columns > 0) process->columns = columns;
if (rows > 0) process->rows = rows; if (rows > 0) process->rows = rows;
@ -348,17 +363,19 @@ int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user,
server->client_count--; server->client_count--;
lwsl_notice("WS closed from %s, clients: %d\n", pss->address, server->client_count); lwsl_notice("WS closed from %s, clients: %d\n", pss->address, server->client_count);
if (pss->buffer != NULL) { if (pss->buffer != NULL) free(pss->buffer);
free(pss->buffer); if (pss->pty_buf != NULL) pty_buf_free(pss->pty_buf);
}
for (int i = 0; i < pss->argc; i++) { for (int i = 0; i < pss->argc; i++) {
free(pss->args[i]); free(pss->args[i]);
} }
if (process_running(pss->process)) { if (pss->process != NULL) {
pty_pause(pss->process); ((pty_ctx_t *)pss->process->ctx)->ws_closed = true;
lwsl_notice("killing process, pid: %d\n", pss->process->pid); if (process_running(pss->process)) {
pty_kill(pss->process, server->sig_code); pty_pause(pss->process);
lwsl_notice("killing process, pid: %d\n", pss->process->pid);
pty_kill(pss->process, server->sig_code);
}
} }
if (server->once && server->client_count == 0) { if (server->once && server->client_count == 0) {

View file

@ -51,20 +51,21 @@ pty_buf_t *pty_buf_init(char *base, size_t len) {
} }
void pty_buf_free(pty_buf_t *buf) { void pty_buf_free(pty_buf_t *buf) {
free(buf->base); if (buf == NULL) return;
if (buf->base != NULL) free(buf->base);
free(buf); free(buf);
} }
static void read_cb(uv_stream_t *stream, ssize_t n, const uv_buf_t *buf) { static void read_cb(uv_stream_t *stream, ssize_t n, const uv_buf_t *buf) {
uv_read_stop(stream); uv_read_stop(stream);
pty_io_t *io = (pty_io_t *) stream->data; pty_process *process = (pty_process *) stream->data;
if (n <= 0) { if (n <= 0) {
if (n == UV_ENOBUFS || n == 0) return; if (n == UV_ENOBUFS || n == 0) return;
if (n != UV_EOF) printf("== uv_read failed with error %ld: %s\n", n, uv_strerror(n)); if (n != UV_EOF) printf("== uv_read failed with error %ld: %s\n", n, uv_strerror(n));
io->read_cb(io->ctx, NULL, true); process->read_cb(process, NULL, true);
goto done; goto done;
} }
io->read_cb(io->ctx, pty_buf_init(buf->base, (size_t) n), false); process->read_cb(process, pty_buf_init(buf->base, (size_t) n), false);
done: done:
free(buf->base); free(buf->base);
@ -76,24 +77,6 @@ static void write_cb(uv_write_t *req, int unused) {
free(req); free(req);
} }
static pty_io_t *pty_io_init(pty_process *process, pty_read_cb read_cb) {
pty_io_t *io = xmalloc(sizeof(pty_io_t));
io->in = xmalloc(sizeof(uv_pipe_t));
io->out = xmalloc(sizeof(uv_pipe_t));
uv_pipe_init(process->loop, io->in, 0);
uv_pipe_init(process->loop, io->out, 0);
io->paused = true;
io->read_cb = read_cb;
io->ctx = process->ctx;
return io;
}
static void pty_io_free(pty_io_t *io) {
uv_close((uv_handle_t *) io->in, close_cb);
uv_close((uv_handle_t *) io->out, close_cb);
free(io);
}
pty_process *process_init(void *ctx, uv_loop_t *loop, char *argv[], char *envp[]) { pty_process *process_init(void *ctx, uv_loop_t *loop, char *argv[], char *envp[]) {
pty_process *process = xmalloc(sizeof(pty_process)); pty_process *process = xmalloc(sizeof(pty_process));
memset(process, 0, sizeof(pty_process)); memset(process, 0, sizeof(pty_process));
@ -121,9 +104,11 @@ void process_free(pty_process *process) {
if (process->pty != NULL) pClosePseudoConsole(process->pty); if (process->pty != NULL) pClosePseudoConsole(process->pty);
if (process->handle != NULL) CloseHandle(process->handle); if (process->handle != NULL) CloseHandle(process->handle);
#else #else
close(process->pty);
uv_thread_join(&process->tid); uv_thread_join(&process->tid);
#endif #endif
if (process->io != NULL) pty_io_free(process->io); if (process->in != NULL) uv_close((uv_handle_t *) process->in, close_cb);
if (process->out != NULL) uv_close((uv_handle_t *) process->out, close_cb);
if (process->argv != NULL) free(process->argv); if (process->argv != NULL) free(process->argv);
if (process->cwd != NULL) free(process->cwd); if (process->cwd != NULL) free(process->cwd);
char **p = process->envp; char **p = process->envp;
@ -134,17 +119,15 @@ void process_free(pty_process *process) {
void pty_pause(pty_process *process) { void pty_pause(pty_process *process) {
if (process == NULL) return; if (process == NULL) return;
pty_io_t *io = process->io; if (process->paused) return;
if (io->paused) return; uv_read_stop((uv_stream_t *) process->out);
uv_read_stop((uv_stream_t *) io->out);
} }
void pty_resume(pty_process *process) { void pty_resume(pty_process *process) {
if (process == NULL) return; if (process == NULL) return;
pty_io_t *io = process->io; if (!process->paused) return;
if (!io->paused) return; process->out->data = process;
io->out->data = io; uv_read_start((uv_stream_t *) process->out, alloc_cb, read_cb);
uv_read_start((uv_stream_t *) io->out, alloc_cb, read_cb);
} }
int pty_write(pty_process *process, pty_buf_t *buf) { int pty_write(pty_process *process, pty_buf_t *buf) {
@ -152,11 +135,10 @@ int pty_write(pty_process *process, pty_buf_t *buf) {
pty_buf_free(buf); pty_buf_free(buf);
return UV_ESRCH; return UV_ESRCH;
} }
pty_io_t *io = process->io;
uv_buf_t b = uv_buf_init(buf->base, buf->len); uv_buf_t b = uv_buf_init(buf->base, buf->len);
uv_write_t *req = xmalloc(sizeof(uv_write_t)); uv_write_t *req = xmalloc(sizeof(uv_write_t));
req->data = buf; req->data = buf;
return uv_write(req, (uv_stream_t *) io->in, &b, 1, write_cb); return uv_write(req, (uv_stream_t *) process->in, &b, 1, write_cb);
} }
bool pty_resize(pty_process *process) { bool pty_resize(pty_process *process) {
@ -173,7 +155,6 @@ bool pty_resize(pty_process *process) {
bool pty_kill(pty_process *process, int sig) { bool pty_kill(pty_process *process, int sig) {
if (process == NULL) return false; if (process == NULL) return false;
process->killed = true;
#ifdef _WIN32 #ifdef _WIN32
return TerminateProcess(process->handle, 1) != 0; return TerminateProcess(process->handle, 1) != 0;
#else #else
@ -324,7 +305,7 @@ static void async_cb(uv_async_t *async) {
GetExitCodeProcess(process->handle, &exit_code); GetExitCodeProcess(process->handle, &exit_code);
process->exit_code = (int) exit_code; process->exit_code = (int) exit_code;
process->exit_signal = 1; process->exit_signal = 1;
process->exit_cb(process->ctx, process); process->exit_cb(process);
uv_close((uv_handle_t *) async, NULL); uv_close((uv_handle_t *) async, NULL);
process_free(process); process_free(process);
@ -341,12 +322,15 @@ int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb) {
SetConsoleCtrlHandler(NULL, FALSE); SetConsoleCtrlHandler(NULL, FALSE);
int status = 1; int status = 1;
pty_io_t *io = pty_io_init(process, read_cb); process->in = xmalloc(sizeof(uv_pipe_t));
process->out = xmalloc(sizeof(uv_pipe_t));
uv_pipe_init(process->loop, process->in, 0);
uv_pipe_init(process->loop, process->out, 0);
uv_connect_t *in_req = xmalloc(sizeof(uv_connect_t)); uv_connect_t *in_req = xmalloc(sizeof(uv_connect_t));
uv_connect_t *out_req = xmalloc(sizeof(uv_connect_t)); uv_connect_t *out_req = xmalloc(sizeof(uv_connect_t));
uv_pipe_connect(in_req, io->in, in_name, connect_cb); uv_pipe_connect(in_req, process->in, in_name, connect_cb);
uv_pipe_connect(out_req, io->out, out_name, connect_cb); uv_pipe_connect(out_req, process->out, out_name, connect_cb);
PROCESS_INFORMATION pi = {0}; PROCESS_INFORMATION pi = {0};
WCHAR *cmdline, *cwd; WCHAR *cmdline, *cwd;
@ -373,14 +357,14 @@ int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb) {
process->pid = pi.dwProcessId; process->pid = pi.dwProcessId;
process->handle = pi.hProcess; process->handle = pi.hProcess;
process->io = io; process->paused = true;
process->read_cb = read_cb;
process->exit_cb = exit_cb; process->exit_cb = exit_cb;
process->async.data = process; process->async.data = process;
uv_async_init(process->loop, &process->async, async_cb); uv_async_init(process->loop, &process->async, async_cb);
if (!RegisterWaitForSingleObject(&process->wait, pi.hProcess, conpty_exit, process, INFINITE, WT_EXECUTEONLYONCE)) { if (!RegisterWaitForSingleObject(&process->wait, pi.hProcess, conpty_exit, process, INFINITE, WT_EXECUTEONLYONCE)) {
print_error("RegisterWaitForSingleObject"); print_error("RegisterWaitForSingleObject");
pty_io_free(io);
goto cleanup; goto cleanup;
} }
@ -434,7 +418,7 @@ static void wait_cb(void *arg) {
static void async_cb(uv_async_t *async) { static void async_cb(uv_async_t *async) {
pty_process *process = (pty_process *) async->data; pty_process *process = (pty_process *) async->data;
process->exit_cb(process->ctx, process); process->exit_cb(process);
uv_close((uv_handle_t *) async, NULL); uv_close((uv_handle_t *) async, NULL);
process_free(process); process_free(process);
@ -479,16 +463,20 @@ int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb) {
goto error; goto error;
} }
pty_io_t *io = pty_io_init(process, read_cb); process->in = xmalloc(sizeof(uv_pipe_t));
if (!fd_duplicate(master, io->in) || !fd_duplicate(master, io->out)) { process->out = xmalloc(sizeof(uv_pipe_t));
uv_pipe_init(process->loop, process->in, 0);
uv_pipe_init(process->loop, process->out, 0);
if (!fd_duplicate(master, process->in) || !fd_duplicate(master, process->out)) {
status = -errno; status = -errno;
pty_io_free(io);
goto error; goto error;
} }
process->pty = master; process->pty = master;
process->pid = pid; process->pid = pid;
process->io = io; process->paused = true;
process->read_cb = read_cb;
process->exit_cb = exit_cb; process->exit_cb = exit_cb;
process->async.data = process; process->async.data = process;
uv_async_init(process->loop, &process->async, async_cb); uv_async_init(process->loop, &process->async, async_cb);

View file

@ -21,25 +21,14 @@ typedef struct {
size_t len; size_t len;
} pty_buf_t; } pty_buf_t;
typedef void (*pty_read_cb)(void *, pty_buf_t *, bool);
typedef struct {
uv_pipe_t *in;
uv_pipe_t *out;
bool paused;
pty_read_cb read_cb;
void *ctx;
} pty_io_t;
struct pty_process_; struct pty_process_;
typedef struct pty_process_ pty_process; typedef struct pty_process_ pty_process;
typedef void (*pty_exit_cb)(void *, pty_process *); typedef void (*pty_read_cb)(pty_process *, pty_buf_t *, bool);
typedef void (*pty_exit_cb)(pty_process *);
struct pty_process_ { struct pty_process_ {
int pid, exit_code, exit_signal; int pid, exit_code, exit_signal;
uint16_t columns, rows; uint16_t columns, rows;
bool killed;
#ifdef _WIN32 #ifdef _WIN32
STARTUPINFOEXW si; STARTUPINFOEXW si;
HPCON pty; HPCON pty;
@ -55,7 +44,11 @@ struct pty_process_ {
uv_loop_t *loop; uv_loop_t *loop;
uv_async_t async; uv_async_t async;
pty_io_t *io; uv_pipe_t *in;
uv_pipe_t *out;
bool paused;
pty_read_cb read_cb;
pty_exit_cb exit_cb; pty_exit_cb exit_cb;
void *ctx; void *ctx;
}; };

View file

@ -56,6 +56,11 @@ struct pss_tty {
int lws_close_status; int lws_close_status;
}; };
typedef struct {
struct pss_tty *pss;
bool ws_closed;
} pty_ctx_t ;
struct server { struct server {
int client_count; // client count int client_count; // client count
char *prefs_json; // client preferences char *prefs_json; // client preferences