diff options
-rw-r--r-- | .github/workflows/release.yml | 32 | ||||
-rw-r--r-- | CMakeLists.txt | 14 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | app.rc.in | 32 | ||||
-rw-r--r-- | src/protocol.c | 69 | ||||
-rw-r--r-- | src/pty.c | 76 | ||||
-rw-r--r-- | src/pty.h | 21 | ||||
-rw-r--r-- | src/server.h | 5 |
8 files changed, 163 insertions, 88 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 09ced6d..fa028aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,4 +47,34 @@ jobs: upload_url: ${{ needs.release.outputs.upload_url }} asset_path: build/ttyd asset_name: ttyd.${{ matrix.target }} - asset_content_type: application/octet-stream
\ No newline at end of file + 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index aa0d4e4..919c983 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,15 @@ cmake_minimum_required(VERSION 2.8) -project(ttyd C) -set(PROJECT_VERSION "1.7.0") +if(${CMAKE_VERSION} VERSION_LESS "3.12.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) if(GIT_FOUND) @@ -72,6 +80,8 @@ endif() if(WIN32) 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) list(APPEND LINK_LIBS util) endif() @@ -66,7 +66,7 @@ USAGE: ttyd [options] <command> [<arguments...>] VERSION: - 1.7.0 + 1.7.1 OPTIONS: -p, --port Port to listen (default: 7681, use `0` for random port) diff --git a/app.rc.in b/app.rc.in new file mode 100644 index 0000000..3b51859 --- /dev/null +++ b/app.rc.in @@ -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 diff --git a/src/protocol.c b/src/protocol.c index ca1e086..5341c3d 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -51,9 +51,7 @@ static bool check_host_origin(struct lws *wsi) { char buf[256]; memset(buf, 0, sizeof(buf)); int len = lws_hdr_copy(wsi, buf, (int)sizeof(buf), WSI_TOKEN_ORIGIN); - if (len <= 0) { - return false; - } + if (len <= 0) return false; const char *prot, *address, *path; int port; @@ -71,26 +69,43 @@ static bool check_host_origin(struct lws *wsi) { return len > 0 && strcasecmp(buf, host_buf) == 0; } -static void process_read_cb(void *ctx, pty_buf_t *buf, bool eof) { - struct pss_tty *pss = (struct pss_tty *)ctx; - if (eof && !process_running(pss->process)) - pss->lws_close_status = pss->process->exit_code == 0 ? 1000 : 1006; - else - pss->pty_buf = buf; +static pty_ctx_t *pty_ctx_init(struct pss_tty *pss) { + pty_ctx_t *ctx = xmalloc(sizeof(pty_ctx_t)); + ctx->pss = pss; + ctx->ws_closed = false; + return ctx; +} - lws_callback_on_writable(pss->wsi); +static void pty_ctx_free(pty_ctx_t *ctx) { free(ctx); } + +static void process_read_cb(pty_process *process, pty_buf_t *buf, bool eof) { + pty_ctx_t *ctx = (pty_ctx_t *)process->ctx; + if (ctx->ws_closed) { + pty_buf_free(buf); + return; + } + + 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(void *ctx, pty_process *process) { - struct pss_tty *pss = (struct pss_tty *)ctx; - pss->process = NULL; - if (process->killed) { +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); - } else { - lwsl_notice("process exited with code %d, pid: %d\n", process->exit_code, process->pid); - pss->lws_close_status = process->exit_code == 0 ? 1000 : 1006; - lws_callback_on_writable(pss->wsi); + 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) { @@ -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) { - 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 (columns > 0) process->columns = columns; 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--; lwsl_notice("WS closed from %s, clients: %d\n", pss->address, server->client_count); - if (pss->buffer != NULL) { - free(pss->buffer); - } + if (pss->buffer != NULL) free(pss->buffer); + if (pss->pty_buf != NULL) pty_buf_free(pss->pty_buf); for (int i = 0; i < pss->argc; i++) { free(pss->args[i]); } - if (process_running(pss->process)) { - pty_pause(pss->process); - lwsl_notice("killing process, pid: %d\n", pss->process->pid); - pty_kill(pss->process, server->sig_code); + if (pss->process != NULL) { + ((pty_ctx_t *)pss->process->ctx)->ws_closed = true; + if (process_running(pss->process)) { + 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) { @@ -51,20 +51,21 @@ pty_buf_t *pty_buf_init(char *base, size_t len) { } void pty_buf_free(pty_buf_t *buf) { - free(buf->base); + if (buf == NULL) return; + if (buf->base != NULL) free(buf->base); free(buf); } static void read_cb(uv_stream_t *stream, ssize_t n, const uv_buf_t *buf) { 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 == UV_ENOBUFS || n == 0) return; 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; } - 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: free(buf->base); @@ -76,24 +77,6 @@ static void write_cb(uv_write_t *req, int unused) { 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 = xmalloc(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->handle != NULL) CloseHandle(process->handle); #else + close(process->pty); uv_thread_join(&process->tid); #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->cwd != NULL) free(process->cwd); char **p = process->envp; @@ -134,17 +119,15 @@ void process_free(pty_process *process) { void pty_pause(pty_process *process) { if (process == NULL) return; - pty_io_t *io = process->io; - if (io->paused) return; - uv_read_stop((uv_stream_t *) io->out); + if (process->paused) return; + uv_read_stop((uv_stream_t *) process->out); } void pty_resume(pty_process *process) { if (process == NULL) return; - pty_io_t *io = process->io; - if (!io->paused) return; - io->out->data = io; - uv_read_start((uv_stream_t *) io->out, alloc_cb, read_cb); + if (!process->paused) return; + process->out->data = process; + uv_read_start((uv_stream_t *) process->out, alloc_cb, read_cb); } 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); return UV_ESRCH; } - pty_io_t *io = process->io; uv_buf_t b = uv_buf_init(buf->base, buf->len); uv_write_t *req = xmalloc(sizeof(uv_write_t)); 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) { @@ -173,7 +155,6 @@ bool pty_resize(pty_process *process) { bool pty_kill(pty_process *process, int sig) { if (process == NULL) return false; - process->killed = true; #ifdef _WIN32 return TerminateProcess(process->handle, 1) != 0; #else @@ -324,7 +305,7 @@ static void async_cb(uv_async_t *async) { GetExitCodeProcess(process->handle, &exit_code); process->exit_code = (int) exit_code; process->exit_signal = 1; - process->exit_cb(process->ctx, process); + process->exit_cb(process); uv_close((uv_handle_t *) async, NULL); 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); 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 *out_req = xmalloc(sizeof(uv_connect_t)); - uv_pipe_connect(in_req, io->in, in_name, connect_cb); - uv_pipe_connect(out_req, io->out, out_name, connect_cb); + uv_pipe_connect(in_req, process->in, in_name, connect_cb); + uv_pipe_connect(out_req, process->out, out_name, connect_cb); PROCESS_INFORMATION pi = {0}; 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->handle = pi.hProcess; - process->io = io; + process->paused = true; + process->read_cb = read_cb; process->exit_cb = exit_cb; process->async.data = process; uv_async_init(process->loop, &process->async, async_cb); if (!RegisterWaitForSingleObject(&process->wait, pi.hProcess, conpty_exit, process, INFINITE, WT_EXECUTEONLYONCE)) { print_error("RegisterWaitForSingleObject"); - pty_io_free(io); goto cleanup; } @@ -434,7 +418,7 @@ static void wait_cb(void *arg) { static void async_cb(uv_async_t *async) { pty_process *process = (pty_process *) async->data; - process->exit_cb(process->ctx, process); + process->exit_cb(process); uv_close((uv_handle_t *) async, NULL); 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; } - pty_io_t *io = pty_io_init(process, read_cb); - if (!fd_duplicate(master, io->in) || !fd_duplicate(master, io->out)) { + 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); + + if (!fd_duplicate(master, process->in) || !fd_duplicate(master, process->out)) { status = -errno; - pty_io_free(io); goto error; } process->pty = master; process->pid = pid; - process->io = io; + process->paused = true; + process->read_cb = read_cb; process->exit_cb = exit_cb; process->async.data = process; uv_async_init(process->loop, &process->async, async_cb); @@ -21,25 +21,14 @@ typedef struct { size_t len; } 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_; 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_ { int pid, exit_code, exit_signal; uint16_t columns, rows; - bool killed; #ifdef _WIN32 STARTUPINFOEXW si; HPCON pty; @@ -55,7 +44,11 @@ struct pty_process_ { uv_loop_t *loop; 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; void *ctx; }; diff --git a/src/server.h b/src/server.h index b122d1c..794965a 100644 --- a/src/server.h +++ b/src/server.h @@ -56,6 +56,11 @@ struct pss_tty { int lws_close_status; }; +typedef struct { + struct pss_tty *pss; + bool ws_closed; +} pty_ctx_t ; + struct server { int client_count; // client count char *prefs_json; // client preferences |