cmd
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
BasedOnStyle: Google
|
||||
Language: Cpp
|
||||
ColumnLimit: 120
|
||||
IndentWidth: 2
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
1
ttyd/.gitattributes
vendored
1
ttyd/.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
src/html.h linguist-generated
|
2
ttyd/.github/FUNDING.yml
vendored
2
ttyd/.github/FUNDING.yml
vendored
@@ -1,2 +0,0 @@
|
||||
github: tsl0922
|
||||
patreon: tsl0922
|
31
ttyd/.github/ISSUE_TEMPLATE/bug_report.md
vendored
31
ttyd/.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment:**
|
||||
- OS: [e.g. macOS 10.15.2]
|
||||
- Browser: [e.g. Chrome 79.0.3945.130]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
ttyd/.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
ttyd/.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
10
ttyd/.github/ISSUE_TEMPLATE/support-request.md
vendored
10
ttyd/.github/ISSUE_TEMPLATE/support-request.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Support Request
|
||||
about: Support request or question
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Describe your problem or question here.
|
7
ttyd/.github/dependabot.yml
vendored
7
ttyd/.github/dependabot.yml
vendored
@@ -1,7 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/html"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
39
ttyd/.github/workflows/backend.yml
vendored
39
ttyd/.github/workflows/backend.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: backend
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/backend.yml"
|
||||
- "CMakeLists.txt"
|
||||
- "src/*"
|
||||
- "scripts/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/backend.yml"
|
||||
- "CMakeLists.txt"
|
||||
- "src/*"
|
||||
- "scripts/*"
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
cross:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [i686, x86_64, arm, armhf, aarch64, mips, mipsel, mips64, mips64el, s390x, win32]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y autoconf automake build-essential cmake curl file libtool
|
||||
- name: Cross build (${{ matrix.target }})
|
||||
env:
|
||||
BUILD_TARGET: ${{ matrix.target }}
|
||||
run: ./scripts/cross-build.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ttyd.${{ matrix.target }}
|
||||
path: build/ttyd*
|
||||
|
71
ttyd/.github/workflows/docker.yml
vendored
71
ttyd/.github/workflows/docker.yml
vendored
@@ -1,71 +0,0 @@
|
||||
name: docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
tags: ["*"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y autoconf automake build-essential cmake curl file libtool
|
||||
- name: Cross build multi-arch binary
|
||||
run: |
|
||||
mkdir dist
|
||||
for arch in amd64 armv7 arm64 s390x; do
|
||||
env BUILD_TARGET=$arch ./scripts/cross-build.sh
|
||||
[ "$arch" = "armv7" ] && arch="arm"
|
||||
mkdir -p dist/$arch && cp build/ttyd dist/$arch/ttyd
|
||||
done
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Determine docker tags
|
||||
id: docker_tag
|
||||
run: |
|
||||
case $GITHUB_REF in
|
||||
refs/tags/*)
|
||||
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||
echo "DOCKER_TAG=tsl0922/ttyd:${TAG_NAME}" >> $GITHUB_ENV
|
||||
echo "ALPINE_TAG=tsl0922/ttyd:${TAG_NAME}-alpine" >> $GITHUB_ENV
|
||||
;;
|
||||
*)
|
||||
echo "DOCKER_TAG=tsl0922/ttyd:latest" >> $GITHUB_ENV
|
||||
echo "ALPINE_TAG=tsl0922/ttyd:alpine" >> $GITHUB_ENV
|
||||
esac
|
||||
- name: build/push docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.DOCKER_TAG }}
|
||||
ghcr.io/${{ env.DOCKER_TAG }}
|
||||
- name: build/push docker image (alpine)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.alpine
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.ALPINE_TAG }}
|
||||
ghcr.io/${{ env.ALPINE_TAG }}
|
28
ttyd/.github/workflows/frontend.yml
vendored
28
ttyd/.github/workflows/frontend.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/frontend.yml"
|
||||
- "html/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/frontend.yml"
|
||||
- "html/*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Run yarn install, check and build
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare yarn@stable --activate
|
||||
yarn install
|
||||
yarn run check
|
||||
yarn run build
|
||||
working-directory: html
|
37
ttyd/.github/workflows/release.yml
vendored
37
ttyd/.github/workflows/release.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["*"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/backend.yml
|
||||
publish:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check version bump
|
||||
run: |
|
||||
TAG=$(git describe --tags --match "[0-9]*.[0-9]*.[0-9]*" --abbrev=8)
|
||||
VERSION=$(grep project CMakeLists.txt| awk '{print $3}')
|
||||
if [ "$TAG" != "$VERSION" ]; then
|
||||
echo "=== Version in CMakeLists.txt and git tag does not match!"
|
||||
echo "=== Git Tag: $TAG, Version: $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: |
|
||||
mkdir build
|
||||
for file in ttyd.*/*; do
|
||||
target=$(echo $file | awk -F/ '{print $1}')
|
||||
[[ $file == *.exe ]] && target="$target.exe"
|
||||
mv $file build/$target
|
||||
done
|
||||
pushd build; sha256sum ttyd.* > SHA256SUMS; popd
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: build/*
|
||||
allowUpdates: true
|
||||
draft: true
|
54
ttyd/.gitignore
vendored
54
ttyd/.gitignore
vendored
@@ -1,54 +0,0 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
|
||||
# Cmake files
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CTestTestfile.cmake
|
||||
build
|
||||
|
||||
# Clion files
|
||||
.idea/
|
||||
|
||||
# VSCode files
|
||||
.vscode/
|
||||
|
||||
# Project files
|
||||
!init.d
|
@@ -1,569 +0,0 @@
|
||||
# This is the CMakeCache file.
|
||||
# For build in directory: /home/init/桌面/ttyd
|
||||
# It was generated by CMake: /usr/bin/cmake
|
||||
# You can edit this file to change values found and used by cmake.
|
||||
# If you do not want to change any of the values, simply exit the editor.
|
||||
# If you do want to change a value, simply edit, save, and exit the editor.
|
||||
# The syntax for the file is as follows:
|
||||
# KEY:TYPE=VALUE
|
||||
# KEY is the name of a variable in the cache.
|
||||
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
|
||||
# VALUE is the current value for the KEY.
|
||||
|
||||
########################
|
||||
# EXTERNAL cache entries
|
||||
########################
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_AR:FILEPATH=/usr/bin/ar
|
||||
|
||||
//Choose the type of build, options are: None Debug Release RelWithDebInfo
|
||||
// MinSizeRel ...
|
||||
CMAKE_BUILD_TYPE:STRING=
|
||||
|
||||
//Enable/Disable color output during build.
|
||||
CMAKE_COLOR_MAKEFILE:BOOL=ON
|
||||
|
||||
//C compiler
|
||||
CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc
|
||||
|
||||
//A wrapper around 'ar' adding the appropriate '--plugin' option
|
||||
// for the GCC compiler
|
||||
CMAKE_C_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-13
|
||||
|
||||
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
|
||||
// for the GCC compiler
|
||||
CMAKE_C_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-13
|
||||
|
||||
//Flags used by the C compiler during all build types.
|
||||
CMAKE_C_FLAGS:STRING=
|
||||
|
||||
//Flags used by the C compiler during DEBUG builds.
|
||||
CMAKE_C_FLAGS_DEBUG:STRING=-g
|
||||
|
||||
//Flags used by the C compiler during MINSIZEREL builds.
|
||||
CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
|
||||
|
||||
//Flags used by the C compiler during RELEASE builds.
|
||||
CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
|
||||
|
||||
//Flags used by the C compiler during RELWITHDEBINFO builds.
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
|
||||
|
||||
//Flags used by the linker during all build types.
|
||||
CMAKE_EXE_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during DEBUG builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during MINSIZEREL builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during RELEASE builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during RELWITHDEBINFO builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Enable/Disable output of compile commands during generation.
|
||||
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
|
||||
|
||||
//Value Computed by CMake.
|
||||
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/home/init/桌面/ttyd/CMakeFiles/pkgRedirects
|
||||
|
||||
//User executables (bin)
|
||||
CMAKE_INSTALL_BINDIR:PATH=bin
|
||||
|
||||
//Read-only architecture-independent data (DATAROOTDIR)
|
||||
CMAKE_INSTALL_DATADIR:PATH=
|
||||
|
||||
//Read-only architecture-independent data root (share)
|
||||
CMAKE_INSTALL_DATAROOTDIR:PATH=share
|
||||
|
||||
//Documentation root (DATAROOTDIR/doc/PROJECT_NAME)
|
||||
CMAKE_INSTALL_DOCDIR:PATH=
|
||||
|
||||
//C header files (include)
|
||||
CMAKE_INSTALL_INCLUDEDIR:PATH=include
|
||||
|
||||
//Info documentation (DATAROOTDIR/info)
|
||||
CMAKE_INSTALL_INFODIR:PATH=
|
||||
|
||||
//Object code libraries (lib)
|
||||
CMAKE_INSTALL_LIBDIR:PATH=lib
|
||||
|
||||
//Program executables (libexec)
|
||||
CMAKE_INSTALL_LIBEXECDIR:PATH=libexec
|
||||
|
||||
//Locale-dependent data (DATAROOTDIR/locale)
|
||||
CMAKE_INSTALL_LOCALEDIR:PATH=
|
||||
|
||||
//Modifiable single-machine data (var)
|
||||
CMAKE_INSTALL_LOCALSTATEDIR:PATH=var
|
||||
|
||||
//Man documentation (DATAROOTDIR/man)
|
||||
CMAKE_INSTALL_MANDIR:PATH=
|
||||
|
||||
//C header files for non-gcc (/usr/include)
|
||||
CMAKE_INSTALL_OLDINCLUDEDIR:PATH=/usr/include
|
||||
|
||||
//Install path prefix, prepended onto install directories.
|
||||
CMAKE_INSTALL_PREFIX:PATH=/usr/local
|
||||
|
||||
//Run-time variable data (LOCALSTATEDIR/run)
|
||||
CMAKE_INSTALL_RUNSTATEDIR:PATH=
|
||||
|
||||
//System admin executables (sbin)
|
||||
CMAKE_INSTALL_SBINDIR:PATH=sbin
|
||||
|
||||
//Modifiable architecture-independent data (com)
|
||||
CMAKE_INSTALL_SHAREDSTATEDIR:PATH=com
|
||||
|
||||
//Read-only single-machine data (etc)
|
||||
CMAKE_INSTALL_SYSCONFDIR:PATH=etc
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_LINKER:FILEPATH=/usr/bin/ld
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/gmake
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// all build types.
|
||||
CMAKE_MODULE_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// DEBUG builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// MINSIZEREL builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// RELEASE builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// RELWITHDEBINFO builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_NM:FILEPATH=/usr/bin/nm
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_DESCRIPTION:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_NAME:STATIC=ttyd
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION:STATIC=1.7.7
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_MAJOR:STATIC=1
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_MINOR:STATIC=7
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_PATCH:STATIC=7
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_TWEAK:STATIC=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_READELF:FILEPATH=/usr/bin/readelf
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during all build types.
|
||||
CMAKE_SHARED_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during DEBUG builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during MINSIZEREL builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during RELEASE builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during RELWITHDEBINFO builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//If set, runtime paths are not added when installing shared libraries,
|
||||
// but are added when building.
|
||||
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
|
||||
|
||||
//If set, runtime paths are not added when using shared libraries.
|
||||
CMAKE_SKIP_RPATH:BOOL=NO
|
||||
|
||||
//Flags used by the linker during the creation of static libraries
|
||||
// during all build types.
|
||||
CMAKE_STATIC_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of static libraries
|
||||
// during DEBUG builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of static libraries
|
||||
// during MINSIZEREL builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of static libraries
|
||||
// during RELEASE builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of static libraries
|
||||
// during RELWITHDEBINFO builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_STRIP:FILEPATH=/usr/bin/strip
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_TAPI:FILEPATH=CMAKE_TAPI-NOTFOUND
|
||||
|
||||
//If this value is on, makefiles will be generated without the
|
||||
// .SILENT directive, and all commands will be echoed to the console
|
||||
// during the make. This is useful for debugging only. With Visual
|
||||
// Studio IDE projects all commands are done without /nologo.
|
||||
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
|
||||
|
||||
//Git command line client
|
||||
GIT_EXECUTABLE:FILEPATH=/usr/bin/git
|
||||
|
||||
//Path to a file.
|
||||
JSON-C_INCLUDE_DIR:PATH=/usr/include/json-c
|
||||
|
||||
//Path to a library.
|
||||
JSON-C_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libjson-c.so
|
||||
|
||||
//Path to a library.
|
||||
LIBUTIL:FILEPATH=/usr/lib/x86_64-linux-gnu/libutil.a
|
||||
|
||||
//Path to a file.
|
||||
LIBUV_INCLUDE_DIR:PATH=/usr/include
|
||||
|
||||
//Path to a library.
|
||||
LIBUV_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libuv.so
|
||||
|
||||
//The directory containing a CMake configuration file for Libwebsockets.
|
||||
Libwebsockets_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/libwebsockets
|
||||
|
||||
//Path to a library.
|
||||
OPENSSL_CRYPTO_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libcrypto.so
|
||||
|
||||
//Path to a file.
|
||||
OPENSSL_INCLUDE_DIR:PATH=/usr/include
|
||||
|
||||
//Path to a library.
|
||||
OPENSSL_SSL_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libssl.so
|
||||
|
||||
//Arguments to supply to pkg-config
|
||||
PKG_CONFIG_ARGN:STRING=
|
||||
|
||||
//pkg-config executable
|
||||
PKG_CONFIG_EXECUTABLE:FILEPATH=/usr/bin/pkg-config
|
||||
|
||||
//Path to a file.
|
||||
ZLIB_INCLUDE_DIR:PATH=/usr/include
|
||||
|
||||
//Path to a library.
|
||||
ZLIB_LIBRARY_DEBUG:FILEPATH=ZLIB_LIBRARY_DEBUG-NOTFOUND
|
||||
|
||||
//Path to a library.
|
||||
ZLIB_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libz.so
|
||||
|
||||
//Path to a library.
|
||||
pkgcfg_lib__OPENSSL_crypto:FILEPATH=/usr/lib/x86_64-linux-gnu/libcrypto.so
|
||||
|
||||
//Path to a library.
|
||||
pkgcfg_lib__OPENSSL_ssl:FILEPATH=/usr/lib/x86_64-linux-gnu/libssl.so
|
||||
|
||||
//Value Computed by CMake
|
||||
ttyd_BINARY_DIR:STATIC=/home/init/桌面/ttyd
|
||||
|
||||
//Value Computed by CMake
|
||||
ttyd_IS_TOP_LEVEL:STATIC=ON
|
||||
|
||||
//Value Computed by CMake
|
||||
ttyd_SOURCE_DIR:STATIC=/home/init/桌面/ttyd
|
||||
|
||||
|
||||
########################
|
||||
# INTERNAL cache entries
|
||||
########################
|
||||
|
||||
//ADVANCED property for variable: CMAKE_ADDR2LINE
|
||||
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_AR
|
||||
CMAKE_AR-ADVANCED:INTERNAL=1
|
||||
//This is the directory where this CMakeCache.txt was created
|
||||
CMAKE_CACHEFILE_DIR:INTERNAL=/home/init/桌面/ttyd
|
||||
//Major version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
|
||||
//Minor version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MINOR_VERSION:INTERNAL=28
|
||||
//Patch version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_PATCH_VERSION:INTERNAL=3
|
||||
//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
|
||||
CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
|
||||
//Path to CMake executable.
|
||||
CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
|
||||
//Path to cpack program executable.
|
||||
CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
|
||||
//Path to ctest program executable.
|
||||
CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
|
||||
//ADVANCED property for variable: CMAKE_C_COMPILER
|
||||
CMAKE_C_COMPILER-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_COMPILER_AR
|
||||
CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
|
||||
CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_FLAGS
|
||||
CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
|
||||
CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_DLLTOOL
|
||||
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
|
||||
//Executable file format
|
||||
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
|
||||
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
|
||||
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
|
||||
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
|
||||
//Name of external makefile project generator.
|
||||
CMAKE_EXTRA_GENERATOR:INTERNAL=
|
||||
//Name of generator.
|
||||
CMAKE_GENERATOR:INTERNAL=Unix Makefiles
|
||||
//Generator instance identifier.
|
||||
CMAKE_GENERATOR_INSTANCE:INTERNAL=
|
||||
//Name of generator platform.
|
||||
CMAKE_GENERATOR_PLATFORM:INTERNAL=
|
||||
//Name of generator toolset.
|
||||
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
||||
//Source directory with the top level CMakeLists.txt file for this
|
||||
// project
|
||||
CMAKE_HOME_DIRECTORY:INTERNAL=/home/init/桌面/ttyd
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_BINDIR
|
||||
CMAKE_INSTALL_BINDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_DATADIR
|
||||
CMAKE_INSTALL_DATADIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_DATAROOTDIR
|
||||
CMAKE_INSTALL_DATAROOTDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_DOCDIR
|
||||
CMAKE_INSTALL_DOCDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_INCLUDEDIR
|
||||
CMAKE_INSTALL_INCLUDEDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_INFODIR
|
||||
CMAKE_INSTALL_INFODIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_LIBDIR
|
||||
CMAKE_INSTALL_LIBDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_LIBEXECDIR
|
||||
CMAKE_INSTALL_LIBEXECDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_LOCALEDIR
|
||||
CMAKE_INSTALL_LOCALEDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_LOCALSTATEDIR
|
||||
CMAKE_INSTALL_LOCALSTATEDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_MANDIR
|
||||
CMAKE_INSTALL_MANDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_OLDINCLUDEDIR
|
||||
CMAKE_INSTALL_OLDINCLUDEDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_RUNSTATEDIR
|
||||
CMAKE_INSTALL_RUNSTATEDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_SBINDIR
|
||||
CMAKE_INSTALL_SBINDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_SHAREDSTATEDIR
|
||||
CMAKE_INSTALL_SHAREDSTATEDIR-ADVANCED:INTERNAL=1
|
||||
//Install .so files without execute permission.
|
||||
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_INSTALL_SYSCONFDIR
|
||||
CMAKE_INSTALL_SYSCONFDIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_LINKER
|
||||
CMAKE_LINKER-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
|
||||
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
|
||||
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
|
||||
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_NM
|
||||
CMAKE_NM-ADVANCED:INTERNAL=1
|
||||
//number of local generators
|
||||
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_OBJCOPY
|
||||
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_OBJDUMP
|
||||
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
|
||||
//Platform information initialized
|
||||
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_RANLIB
|
||||
CMAKE_RANLIB-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_READELF
|
||||
CMAKE_READELF-ADVANCED:INTERNAL=1
|
||||
//Path to CMake installation.
|
||||
CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.28
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
|
||||
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
|
||||
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
|
||||
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SKIP_RPATH
|
||||
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
|
||||
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
|
||||
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STRIP
|
||||
CMAKE_STRIP-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_TAPI
|
||||
CMAKE_TAPI-ADVANCED:INTERNAL=1
|
||||
//uname command
|
||||
CMAKE_UNAME:INTERNAL=/usr/bin/uname
|
||||
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
|
||||
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
|
||||
//Details about finding Git
|
||||
FIND_PACKAGE_MESSAGE_DETAILS_Git:INTERNAL=[/usr/bin/git][v2.43.0()]
|
||||
//Details about finding JSON-C
|
||||
FIND_PACKAGE_MESSAGE_DETAILS_JSON-C:INTERNAL=[/usr/lib/x86_64-linux-gnu/libjson-c.so][/usr/include/json-c][v()]
|
||||
//Details about finding LIBUV
|
||||
FIND_PACKAGE_MESSAGE_DETAILS_LIBUV:INTERNAL=[/usr/lib/x86_64-linux-gnu/libuv.so][/usr/include][v()]
|
||||
//Details about finding OpenSSL
|
||||
FIND_PACKAGE_MESSAGE_DETAILS_OpenSSL:INTERNAL=[/usr/lib/x86_64-linux-gnu/libcrypto.so][/usr/include][c ][v3.0.13()]
|
||||
//Details about finding ZLIB
|
||||
FIND_PACKAGE_MESSAGE_DETAILS_ZLIB:INTERNAL=[/usr/lib/x86_64-linux-gnu/libz.so][/usr/include][c ][v1.3()]
|
||||
//ADVANCED property for variable: GIT_EXECUTABLE
|
||||
GIT_EXECUTABLE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: JSON-C_INCLUDE_DIR
|
||||
JSON-C_INCLUDE_DIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: JSON-C_LIBRARY
|
||||
JSON-C_LIBRARY-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: LIBUV_INCLUDE_DIR
|
||||
LIBUV_INCLUDE_DIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: LIBUV_LIBRARY
|
||||
LIBUV_LIBRARY-ADVANCED:INTERNAL=1
|
||||
//Have symbol LWS_WITH_MBEDTLS
|
||||
LWS_MBEDTLS_ENABLED:INTERNAL=
|
||||
//Have symbol LWS_OPENSSL_SUPPORT
|
||||
LWS_OPENSSL_ENABLED:INTERNAL=1
|
||||
//Have symbol LWS_WITH_LIBUV
|
||||
LWS_WITH_LIBUV:INTERNAL=1
|
||||
//ADVANCED property for variable: OPENSSL_CRYPTO_LIBRARY
|
||||
OPENSSL_CRYPTO_LIBRARY-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: OPENSSL_INCLUDE_DIR
|
||||
OPENSSL_INCLUDE_DIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: OPENSSL_SSL_LIBRARY
|
||||
OPENSSL_SSL_LIBRARY-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: PKG_CONFIG_ARGN
|
||||
PKG_CONFIG_ARGN-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: PKG_CONFIG_EXECUTABLE
|
||||
PKG_CONFIG_EXECUTABLE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: ZLIB_INCLUDE_DIR
|
||||
ZLIB_INCLUDE_DIR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: ZLIB_LIBRARY_DEBUG
|
||||
ZLIB_LIBRARY_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: ZLIB_LIBRARY_RELEASE
|
||||
ZLIB_LIBRARY_RELEASE-ADVANCED:INTERNAL=1
|
||||
//linker supports push/pop state
|
||||
_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED:INTERNAL=TRUE
|
||||
//CMAKE_INSTALL_PREFIX during last run
|
||||
_GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX:INTERNAL=/usr/local
|
||||
_OPENSSL_CFLAGS:INTERNAL=-I/usr/include
|
||||
_OPENSSL_CFLAGS_I:INTERNAL=
|
||||
_OPENSSL_CFLAGS_OTHER:INTERNAL=
|
||||
_OPENSSL_FOUND:INTERNAL=1
|
||||
_OPENSSL_INCLUDEDIR:INTERNAL=/usr/include
|
||||
_OPENSSL_INCLUDE_DIRS:INTERNAL=/usr/include
|
||||
_OPENSSL_LDFLAGS:INTERNAL=-L/usr/lib/x86_64-linux-gnu;-lssl;-lcrypto
|
||||
_OPENSSL_LDFLAGS_OTHER:INTERNAL=
|
||||
_OPENSSL_LIBDIR:INTERNAL=/usr/lib/x86_64-linux-gnu
|
||||
_OPENSSL_LIBRARIES:INTERNAL=ssl;crypto
|
||||
_OPENSSL_LIBRARY_DIRS:INTERNAL=/usr/lib/x86_64-linux-gnu
|
||||
_OPENSSL_LIBS:INTERNAL=
|
||||
_OPENSSL_LIBS_L:INTERNAL=
|
||||
_OPENSSL_LIBS_OTHER:INTERNAL=
|
||||
_OPENSSL_LIBS_PATHS:INTERNAL=
|
||||
_OPENSSL_MODULE_NAME:INTERNAL=openssl
|
||||
_OPENSSL_PREFIX:INTERNAL=/usr
|
||||
_OPENSSL_STATIC_CFLAGS:INTERNAL=-I/usr/include
|
||||
_OPENSSL_STATIC_CFLAGS_I:INTERNAL=
|
||||
_OPENSSL_STATIC_CFLAGS_OTHER:INTERNAL=
|
||||
_OPENSSL_STATIC_INCLUDE_DIRS:INTERNAL=/usr/include
|
||||
_OPENSSL_STATIC_LDFLAGS:INTERNAL=-L/usr/lib/x86_64-linux-gnu;-lssl;-L/usr/lib/x86_64-linux-gnu;-ldl;-pthread;-lcrypto;-ldl;-pthread
|
||||
_OPENSSL_STATIC_LDFLAGS_OTHER:INTERNAL=-pthread;-pthread
|
||||
_OPENSSL_STATIC_LIBDIR:INTERNAL=
|
||||
_OPENSSL_STATIC_LIBRARIES:INTERNAL=ssl;dl;crypto;dl
|
||||
_OPENSSL_STATIC_LIBRARY_DIRS:INTERNAL=/usr/lib/x86_64-linux-gnu;/usr/lib/x86_64-linux-gnu
|
||||
_OPENSSL_STATIC_LIBS:INTERNAL=
|
||||
_OPENSSL_STATIC_LIBS_L:INTERNAL=
|
||||
_OPENSSL_STATIC_LIBS_OTHER:INTERNAL=
|
||||
_OPENSSL_STATIC_LIBS_PATHS:INTERNAL=
|
||||
_OPENSSL_VERSION:INTERNAL=3.0.13
|
||||
_OPENSSL_openssl_INCLUDEDIR:INTERNAL=
|
||||
_OPENSSL_openssl_LIBDIR:INTERNAL=
|
||||
_OPENSSL_openssl_PREFIX:INTERNAL=
|
||||
_OPENSSL_openssl_VERSION:INTERNAL=
|
||||
__pkg_config_arguments__OPENSSL:INTERNAL=QUIET;openssl
|
||||
__pkg_config_checked__OPENSSL:INTERNAL=1
|
||||
//ADVANCED property for variable: pkgcfg_lib__OPENSSL_crypto
|
||||
pkgcfg_lib__OPENSSL_crypto-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: pkgcfg_lib__OPENSSL_ssl
|
||||
pkgcfg_lib__OPENSSL_ssl-ADVANCED:INTERNAL=1
|
||||
prefix_result:INTERNAL=/usr/lib/x86_64-linux-gnu
|
||||
|
@@ -1,16 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# Relative path conversion top directories.
|
||||
set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/init/桌面/ttyd")
|
||||
set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/init/桌面/ttyd")
|
||||
|
||||
# Force unix paths in dependencies.
|
||||
set(CMAKE_FORCE_UNIX_PATHS 1)
|
||||
|
||||
|
||||
# The C and CXX include file regular expressions for this directory.
|
||||
set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$")
|
||||
set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$")
|
||||
set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})
|
||||
set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN})
|
@@ -1,62 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# The generator used is:
|
||||
set(CMAKE_DEPENDS_GENERATOR "Unix Makefiles")
|
||||
|
||||
# The top level Makefile was generated from the following files:
|
||||
set(CMAKE_MAKEFILE_DEPENDS
|
||||
"CMakeCache.txt"
|
||||
"CMakeFiles/3.28.3/CMakeCCompiler.cmake"
|
||||
"CMakeFiles/3.28.3/CMakeSystem.cmake"
|
||||
"CMakeLists.txt"
|
||||
"cmake/GetGitVersion.cmake"
|
||||
"/usr/lib/x86_64-linux-gnu/cmake/libwebsockets/LibwebsocketsTargets-none.cmake"
|
||||
"/usr/lib/x86_64-linux-gnu/cmake/libwebsockets/LibwebsocketsTargets.cmake"
|
||||
"/usr/lib/x86_64-linux-gnu/cmake/libwebsockets/LwsCheckRequirements.cmake"
|
||||
"/usr/lib/x86_64-linux-gnu/cmake/libwebsockets/libwebsockets-config-version.cmake"
|
||||
"/usr/lib/x86_64-linux-gnu/cmake/libwebsockets/libwebsockets-config.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeCInformation.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeCommonLanguageInclude.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeGenericSystem.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeInitializeConfigs.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeLanguageInformation.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeSystemSpecificInformation.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CMakeSystemSpecificInitialize.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CheckCSourceCompiles.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CheckIncludeFile.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/CheckSymbolExists.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Compiler/CMakeCommonCompilerMacros.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Compiler/GNU-C.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Compiler/GNU.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindGit.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindOpenSSL.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindPackageHandleStandardArgs.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindPackageMessage.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindPkgConfig.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/FindZLIB.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/GNUInstallDirs.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Internal/CheckSourceCompiles.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Platform/Linux-GNU-C.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Platform/Linux-GNU.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Platform/Linux-Initialize.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Platform/Linux.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/Platform/UnixPaths.cmake"
|
||||
"/usr/share/cmake-3.28/Modules/SelectLibraryConfigurations.cmake"
|
||||
)
|
||||
|
||||
# The corresponding makefile is:
|
||||
set(CMAKE_MAKEFILE_OUTPUTS
|
||||
"Makefile"
|
||||
"CMakeFiles/cmake.check_cache"
|
||||
)
|
||||
|
||||
# Byproducts of CMake generate step:
|
||||
set(CMAKE_MAKEFILE_PRODUCTS
|
||||
"CMakeFiles/CMakeDirectoryInformation.cmake"
|
||||
)
|
||||
|
||||
# Dependency information for all targets:
|
||||
set(CMAKE_DEPEND_INFO_FILES
|
||||
"CMakeFiles/ttyd.dir/DependInfo.cmake"
|
||||
)
|
@@ -1,112 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# Default target executed when no arguments are given to make.
|
||||
default_target: all
|
||||
.PHONY : default_target
|
||||
|
||||
#=============================================================================
|
||||
# Special targets provided by cmake.
|
||||
|
||||
# Disable implicit rules so canonical targets will work.
|
||||
.SUFFIXES:
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : %,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : SCCS/s.%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : s.%
|
||||
|
||||
.SUFFIXES: .hpux_make_needs_suffix_list
|
||||
|
||||
# Command-line flag to silence nested $(MAKE).
|
||||
$(VERBOSE)MAKESILENT = -s
|
||||
|
||||
#Suppress display of executed commands.
|
||||
$(VERBOSE).SILENT:
|
||||
|
||||
# A target that is always out of date.
|
||||
cmake_force:
|
||||
.PHONY : cmake_force
|
||||
|
||||
#=============================================================================
|
||||
# Set environment variables for the build.
|
||||
|
||||
# The shell in which to execute make rules.
|
||||
SHELL = /bin/sh
|
||||
|
||||
# The CMake executable.
|
||||
CMAKE_COMMAND = /usr/bin/cmake
|
||||
|
||||
# The command to remove a file.
|
||||
RM = /usr/bin/cmake -E rm -f
|
||||
|
||||
# Escaping for special characters.
|
||||
EQUALS = =
|
||||
|
||||
# The top-level source directory on which CMake was run.
|
||||
CMAKE_SOURCE_DIR = /home/init/桌面/ttyd
|
||||
|
||||
# The top-level build directory on which CMake was run.
|
||||
CMAKE_BINARY_DIR = /home/init/桌面/ttyd
|
||||
|
||||
#=============================================================================
|
||||
# Directory level rules for the build root directory
|
||||
|
||||
# The main recursive "all" target.
|
||||
all: CMakeFiles/ttyd.dir/all
|
||||
.PHONY : all
|
||||
|
||||
# The main recursive "preinstall" target.
|
||||
preinstall:
|
||||
.PHONY : preinstall
|
||||
|
||||
# The main recursive "clean" target.
|
||||
clean: CMakeFiles/ttyd.dir/clean
|
||||
.PHONY : clean
|
||||
|
||||
#=============================================================================
|
||||
# Target rules for target CMakeFiles/ttyd.dir
|
||||
|
||||
# All Build rule for target.
|
||||
CMakeFiles/ttyd.dir/all:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/depend
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/build
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=1,2,3,4,5,6 "Built target ttyd"
|
||||
.PHONY : CMakeFiles/ttyd.dir/all
|
||||
|
||||
# Build rule for subdir invocation for target.
|
||||
CMakeFiles/ttyd.dir/rule: cmake_check_build_system
|
||||
$(CMAKE_COMMAND) -E cmake_progress_start /home/init/桌面/ttyd/CMakeFiles 6
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 CMakeFiles/ttyd.dir/all
|
||||
$(CMAKE_COMMAND) -E cmake_progress_start /home/init/桌面/ttyd/CMakeFiles 0
|
||||
.PHONY : CMakeFiles/ttyd.dir/rule
|
||||
|
||||
# Convenience name for target.
|
||||
ttyd: CMakeFiles/ttyd.dir/rule
|
||||
.PHONY : ttyd
|
||||
|
||||
# clean rule for target.
|
||||
CMakeFiles/ttyd.dir/clean:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/clean
|
||||
.PHONY : CMakeFiles/ttyd.dir/clean
|
||||
|
||||
#=============================================================================
|
||||
# Special targets to cleanup operation of make.
|
||||
|
||||
# Special rule to run CMake to check the build system integrity.
|
||||
# No rule that depends on this can have commands that come from listfiles
|
||||
# because they might be regenerated.
|
||||
cmake_check_build_system:
|
||||
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
|
||||
.PHONY : cmake_check_build_system
|
||||
|
@@ -1,7 +0,0 @@
|
||||
/home/init/桌面/ttyd/CMakeFiles/ttyd.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/edit_cache.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/rebuild_cache.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/list_install_components.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/install.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/install/local.dir
|
||||
/home/init/桌面/ttyd/CMakeFiles/install/strip.dir
|
@@ -1 +0,0 @@
|
||||
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
@@ -1 +0,0 @@
|
||||
6
|
@@ -1,27 +0,0 @@
|
||||
|
||||
# Consider dependencies only in project.
|
||||
set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF)
|
||||
|
||||
# The set of languages for which implicit dependencies are needed:
|
||||
set(CMAKE_DEPENDS_LANGUAGES
|
||||
)
|
||||
|
||||
# The set of dependency files which are needed:
|
||||
set(CMAKE_DEPENDS_DEPENDENCY_FILES
|
||||
"/home/init/桌面/ttyd/src/http.c" "CMakeFiles/ttyd.dir/src/http.c.o" "gcc" "CMakeFiles/ttyd.dir/src/http.c.o.d"
|
||||
"/home/init/桌面/ttyd/src/protocol.c" "CMakeFiles/ttyd.dir/src/protocol.c.o" "gcc" "CMakeFiles/ttyd.dir/src/protocol.c.o.d"
|
||||
"/home/init/桌面/ttyd/src/pty.c" "CMakeFiles/ttyd.dir/src/pty.c.o" "gcc" "CMakeFiles/ttyd.dir/src/pty.c.o.d"
|
||||
"/home/init/桌面/ttyd/src/server.c" "CMakeFiles/ttyd.dir/src/server.c.o" "gcc" "CMakeFiles/ttyd.dir/src/server.c.o.d"
|
||||
"/home/init/桌面/ttyd/src/utils.c" "CMakeFiles/ttyd.dir/src/utils.c.o" "gcc" "CMakeFiles/ttyd.dir/src/utils.c.o.d"
|
||||
)
|
||||
|
||||
# Targets to which this target links which contain Fortran sources.
|
||||
set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES
|
||||
)
|
||||
|
||||
# Targets to which this target links which contain Fortran sources.
|
||||
set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES
|
||||
)
|
||||
|
||||
# Fortran module output directory.
|
||||
set(CMAKE_Fortran_TARGET_MODULE_DIR "")
|
@@ -1,183 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# Delete rule output on recipe failure.
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
#=============================================================================
|
||||
# Special targets provided by cmake.
|
||||
|
||||
# Disable implicit rules so canonical targets will work.
|
||||
.SUFFIXES:
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : %,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : SCCS/s.%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : s.%
|
||||
|
||||
.SUFFIXES: .hpux_make_needs_suffix_list
|
||||
|
||||
# Command-line flag to silence nested $(MAKE).
|
||||
$(VERBOSE)MAKESILENT = -s
|
||||
|
||||
#Suppress display of executed commands.
|
||||
$(VERBOSE).SILENT:
|
||||
|
||||
# A target that is always out of date.
|
||||
cmake_force:
|
||||
.PHONY : cmake_force
|
||||
|
||||
#=============================================================================
|
||||
# Set environment variables for the build.
|
||||
|
||||
# The shell in which to execute make rules.
|
||||
SHELL = /bin/sh
|
||||
|
||||
# The CMake executable.
|
||||
CMAKE_COMMAND = /usr/bin/cmake
|
||||
|
||||
# The command to remove a file.
|
||||
RM = /usr/bin/cmake -E rm -f
|
||||
|
||||
# Escaping for special characters.
|
||||
EQUALS = =
|
||||
|
||||
# The top-level source directory on which CMake was run.
|
||||
CMAKE_SOURCE_DIR = /home/init/桌面/ttyd
|
||||
|
||||
# The top-level build directory on which CMake was run.
|
||||
CMAKE_BINARY_DIR = /home/init/桌面/ttyd
|
||||
|
||||
# Include any dependencies generated for this target.
|
||||
include CMakeFiles/ttyd.dir/depend.make
|
||||
# Include any dependencies generated by the compiler for this target.
|
||||
include CMakeFiles/ttyd.dir/compiler_depend.make
|
||||
|
||||
# Include the progress variables for this target.
|
||||
include CMakeFiles/ttyd.dir/progress.make
|
||||
|
||||
# Include the compile flags for this target's objects.
|
||||
include CMakeFiles/ttyd.dir/flags.make
|
||||
|
||||
CMakeFiles/ttyd.dir/src/utils.c.o: CMakeFiles/ttyd.dir/flags.make
|
||||
CMakeFiles/ttyd.dir/src/utils.c.o: src/utils.c
|
||||
CMakeFiles/ttyd.dir/src/utils.c.o: CMakeFiles/ttyd.dir/compiler_depend.ts
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building C object CMakeFiles/ttyd.dir/src/utils.c.o"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/ttyd.dir/src/utils.c.o -MF CMakeFiles/ttyd.dir/src/utils.c.o.d -o CMakeFiles/ttyd.dir/src/utils.c.o -c /home/init/桌面/ttyd/src/utils.c
|
||||
|
||||
CMakeFiles/ttyd.dir/src/utils.c.i: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/ttyd.dir/src/utils.c.i"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/init/桌面/ttyd/src/utils.c > CMakeFiles/ttyd.dir/src/utils.c.i
|
||||
|
||||
CMakeFiles/ttyd.dir/src/utils.c.s: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/ttyd.dir/src/utils.c.s"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/init/桌面/ttyd/src/utils.c -o CMakeFiles/ttyd.dir/src/utils.c.s
|
||||
|
||||
CMakeFiles/ttyd.dir/src/pty.c.o: CMakeFiles/ttyd.dir/flags.make
|
||||
CMakeFiles/ttyd.dir/src/pty.c.o: src/pty.c
|
||||
CMakeFiles/ttyd.dir/src/pty.c.o: CMakeFiles/ttyd.dir/compiler_depend.ts
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building C object CMakeFiles/ttyd.dir/src/pty.c.o"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/ttyd.dir/src/pty.c.o -MF CMakeFiles/ttyd.dir/src/pty.c.o.d -o CMakeFiles/ttyd.dir/src/pty.c.o -c /home/init/桌面/ttyd/src/pty.c
|
||||
|
||||
CMakeFiles/ttyd.dir/src/pty.c.i: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/ttyd.dir/src/pty.c.i"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/init/桌面/ttyd/src/pty.c > CMakeFiles/ttyd.dir/src/pty.c.i
|
||||
|
||||
CMakeFiles/ttyd.dir/src/pty.c.s: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/ttyd.dir/src/pty.c.s"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/init/桌面/ttyd/src/pty.c -o CMakeFiles/ttyd.dir/src/pty.c.s
|
||||
|
||||
CMakeFiles/ttyd.dir/src/protocol.c.o: CMakeFiles/ttyd.dir/flags.make
|
||||
CMakeFiles/ttyd.dir/src/protocol.c.o: src/protocol.c
|
||||
CMakeFiles/ttyd.dir/src/protocol.c.o: CMakeFiles/ttyd.dir/compiler_depend.ts
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building C object CMakeFiles/ttyd.dir/src/protocol.c.o"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/ttyd.dir/src/protocol.c.o -MF CMakeFiles/ttyd.dir/src/protocol.c.o.d -o CMakeFiles/ttyd.dir/src/protocol.c.o -c /home/init/桌面/ttyd/src/protocol.c
|
||||
|
||||
CMakeFiles/ttyd.dir/src/protocol.c.i: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/ttyd.dir/src/protocol.c.i"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/init/桌面/ttyd/src/protocol.c > CMakeFiles/ttyd.dir/src/protocol.c.i
|
||||
|
||||
CMakeFiles/ttyd.dir/src/protocol.c.s: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/ttyd.dir/src/protocol.c.s"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/init/桌面/ttyd/src/protocol.c -o CMakeFiles/ttyd.dir/src/protocol.c.s
|
||||
|
||||
CMakeFiles/ttyd.dir/src/http.c.o: CMakeFiles/ttyd.dir/flags.make
|
||||
CMakeFiles/ttyd.dir/src/http.c.o: src/http.c
|
||||
CMakeFiles/ttyd.dir/src/http.c.o: CMakeFiles/ttyd.dir/compiler_depend.ts
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Building C object CMakeFiles/ttyd.dir/src/http.c.o"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/ttyd.dir/src/http.c.o -MF CMakeFiles/ttyd.dir/src/http.c.o.d -o CMakeFiles/ttyd.dir/src/http.c.o -c /home/init/桌面/ttyd/src/http.c
|
||||
|
||||
CMakeFiles/ttyd.dir/src/http.c.i: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/ttyd.dir/src/http.c.i"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/init/桌面/ttyd/src/http.c > CMakeFiles/ttyd.dir/src/http.c.i
|
||||
|
||||
CMakeFiles/ttyd.dir/src/http.c.s: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/ttyd.dir/src/http.c.s"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/init/桌面/ttyd/src/http.c -o CMakeFiles/ttyd.dir/src/http.c.s
|
||||
|
||||
CMakeFiles/ttyd.dir/src/server.c.o: CMakeFiles/ttyd.dir/flags.make
|
||||
CMakeFiles/ttyd.dir/src/server.c.o: src/server.c
|
||||
CMakeFiles/ttyd.dir/src/server.c.o: CMakeFiles/ttyd.dir/compiler_depend.ts
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_5) "Building C object CMakeFiles/ttyd.dir/src/server.c.o"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/ttyd.dir/src/server.c.o -MF CMakeFiles/ttyd.dir/src/server.c.o.d -o CMakeFiles/ttyd.dir/src/server.c.o -c /home/init/桌面/ttyd/src/server.c
|
||||
|
||||
CMakeFiles/ttyd.dir/src/server.c.i: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/ttyd.dir/src/server.c.i"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/init/桌面/ttyd/src/server.c > CMakeFiles/ttyd.dir/src/server.c.i
|
||||
|
||||
CMakeFiles/ttyd.dir/src/server.c.s: cmake_force
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/ttyd.dir/src/server.c.s"
|
||||
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/init/桌面/ttyd/src/server.c -o CMakeFiles/ttyd.dir/src/server.c.s
|
||||
|
||||
# Object files for target ttyd
|
||||
ttyd_OBJECTS = \
|
||||
"CMakeFiles/ttyd.dir/src/utils.c.o" \
|
||||
"CMakeFiles/ttyd.dir/src/pty.c.o" \
|
||||
"CMakeFiles/ttyd.dir/src/protocol.c.o" \
|
||||
"CMakeFiles/ttyd.dir/src/http.c.o" \
|
||||
"CMakeFiles/ttyd.dir/src/server.c.o"
|
||||
|
||||
# External object files for target ttyd
|
||||
ttyd_EXTERNAL_OBJECTS =
|
||||
|
||||
ttyd: CMakeFiles/ttyd.dir/src/utils.c.o
|
||||
ttyd: CMakeFiles/ttyd.dir/src/pty.c.o
|
||||
ttyd: CMakeFiles/ttyd.dir/src/protocol.c.o
|
||||
ttyd: CMakeFiles/ttyd.dir/src/http.c.o
|
||||
ttyd: CMakeFiles/ttyd.dir/src/server.c.o
|
||||
ttyd: CMakeFiles/ttyd.dir/build.make
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libz.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libwebsockets.a
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libwebsockets.so.19
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libjson-c.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libuv.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libssl.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libcrypto.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libcap.so
|
||||
ttyd: /usr/lib/x86_64-linux-gnu/libz.so
|
||||
ttyd: CMakeFiles/ttyd.dir/link.txt
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/init/桌面/ttyd/CMakeFiles --progress-num=$(CMAKE_PROGRESS_6) "Linking C executable ttyd"
|
||||
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/ttyd.dir/link.txt --verbose=$(VERBOSE)
|
||||
|
||||
# Rule to build all files generated by this target.
|
||||
CMakeFiles/ttyd.dir/build: ttyd
|
||||
.PHONY : CMakeFiles/ttyd.dir/build
|
||||
|
||||
CMakeFiles/ttyd.dir/clean:
|
||||
$(CMAKE_COMMAND) -P CMakeFiles/ttyd.dir/cmake_clean.cmake
|
||||
.PHONY : CMakeFiles/ttyd.dir/clean
|
||||
|
||||
CMakeFiles/ttyd.dir/depend:
|
||||
cd /home/init/桌面/ttyd && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/init/桌面/ttyd /home/init/桌面/ttyd /home/init/桌面/ttyd /home/init/桌面/ttyd /home/init/桌面/ttyd/CMakeFiles/ttyd.dir/DependInfo.cmake "--color=$(COLOR)"
|
||||
.PHONY : CMakeFiles/ttyd.dir/depend
|
||||
|
@@ -1,19 +0,0 @@
|
||||
file(REMOVE_RECURSE
|
||||
"CMakeFiles/ttyd.dir/src/http.c.o"
|
||||
"CMakeFiles/ttyd.dir/src/http.c.o.d"
|
||||
"CMakeFiles/ttyd.dir/src/protocol.c.o"
|
||||
"CMakeFiles/ttyd.dir/src/protocol.c.o.d"
|
||||
"CMakeFiles/ttyd.dir/src/pty.c.o"
|
||||
"CMakeFiles/ttyd.dir/src/pty.c.o.d"
|
||||
"CMakeFiles/ttyd.dir/src/server.c.o"
|
||||
"CMakeFiles/ttyd.dir/src/server.c.o.d"
|
||||
"CMakeFiles/ttyd.dir/src/utils.c.o"
|
||||
"CMakeFiles/ttyd.dir/src/utils.c.o.d"
|
||||
"ttyd"
|
||||
"ttyd.pdb"
|
||||
)
|
||||
|
||||
# Per-language clean rules from dependency scanning.
|
||||
foreach(lang C)
|
||||
include(CMakeFiles/ttyd.dir/cmake_clean_${lang}.cmake OPTIONAL)
|
||||
endforeach()
|
@@ -1,2 +0,0 @@
|
||||
# Empty dependencies file for ttyd.
|
||||
# This may be replaced when dependencies are built.
|
@@ -1,10 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# compile C with /usr/bin/cc
|
||||
C_DEFINES = -DTTYD_VERSION=\"1.7.7-05422dc\"
|
||||
|
||||
C_INCLUDES = -I/usr/include/json-c
|
||||
|
||||
C_FLAGS = -D_GNU_SOURCE -std=gnu99
|
||||
|
@@ -1 +0,0 @@
|
||||
/usr/bin/cc -D_GNU_SOURCE CMakeFiles/ttyd.dir/src/utils.c.o CMakeFiles/ttyd.dir/src/pty.c.o CMakeFiles/ttyd.dir/src/protocol.c.o CMakeFiles/ttyd.dir/src/http.c.o CMakeFiles/ttyd.dir/src/server.c.o -o ttyd /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libwebsockets.a /usr/lib/x86_64-linux-gnu/libwebsockets.so.19 /usr/lib/x86_64-linux-gnu/libjson-c.so /usr/lib/x86_64-linux-gnu/libuv.so /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib/x86_64-linux-gnu/libcrypto.so -lutil /usr/lib/x86_64-linux-gnu/libcap.so -ldl -lpthread /usr/lib/x86_64-linux-gnu/libz.so
|
@@ -1,7 +0,0 @@
|
||||
CMAKE_PROGRESS_1 = 1
|
||||
CMAKE_PROGRESS_2 = 2
|
||||
CMAKE_PROGRESS_3 = 3
|
||||
CMAKE_PROGRESS_4 = 4
|
||||
CMAKE_PROGRESS_5 = 5
|
||||
CMAKE_PROGRESS_6 = 6
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,94 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.12.0)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
project(ttyd VERSION 1.7.7 LANGUAGES C)
|
||||
|
||||
set(TTYD_VERSION "${PROJECT_VERSION}")
|
||||
|
||||
include(GetGitVersion)
|
||||
get_git_version(GIT_VERSION SEM_VER)
|
||||
get_git_head(GIT_COMMIT)
|
||||
if("${SEM_VER}" VERSION_GREATER "${TTYD_VERSION}")
|
||||
set(TTYD_VERSION "${SEM_VER}")
|
||||
endif()
|
||||
if(NOT "${GIT_COMMIT}" STREQUAL "")
|
||||
set(TTYD_VERSION "${TTYD_VERSION}-${GIT_COMMIT}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
|
||||
if(CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
endif()
|
||||
|
||||
set(SOURCE_FILES src/utils.c src/pty.c src/protocol.c src/http.c src/server.c)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_path(LIBUV_INCLUDE_DIR NAMES uv.h)
|
||||
find_library(LIBUV_LIBRARY NAMES uv libuv)
|
||||
find_package_handle_standard_args(LIBUV REQUIRED_VARS LIBUV_LIBRARY LIBUV_INCLUDE_DIR)
|
||||
mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)
|
||||
if(LIBUV_FOUND)
|
||||
SET(LIBUV_INCLUDE_DIRS "${LIBUV_INCLUDE_DIR}")
|
||||
SET(LIBUV_LIBRARIES "${LIBUV_LIBRARY}")
|
||||
endif()
|
||||
|
||||
find_path(JSON-C_INCLUDE_DIR NAMES json.h PATH_SUFFIXES json-c)
|
||||
find_library(JSON-C_LIBRARY NAMES json-c)
|
||||
find_package_handle_standard_args(JSON-C REQUIRED_VARS JSON-C_LIBRARY JSON-C_INCLUDE_DIR)
|
||||
mark_as_advanced(JSON-C_INCLUDE_DIR JSON-C_LIBRARY)
|
||||
if(JSON-C_FOUND)
|
||||
SET(JSON-C_INCLUDE_DIRS "${JSON-C_INCLUDE_DIR}")
|
||||
SET(JSON-C_LIBRARIES "${JSON-C_LIBRARY}")
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Libwebsockets 3.2.0 REQUIRED)
|
||||
|
||||
set(INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${LIBWEBSOCKETS_INCLUDE_DIRS} ${JSON-C_INCLUDE_DIRS} ${LIBUV_INCLUDE_DIRS})
|
||||
set(LINK_LIBS ${ZLIB_LIBRARIES} ${LIBWEBSOCKETS_LIBRARIES} ${JSON-C_LIBRARIES} ${LIBUV_LIBRARIES})
|
||||
|
||||
set (CMAKE_REQUIRED_INCLUDES ${INCLUDE_DIRS})
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(LWS_WITH_LIBUV "lws_config.h" LWS_WITH_LIBUV)
|
||||
check_symbol_exists(LWS_OPENSSL_SUPPORT "lws_config.h" LWS_OPENSSL_ENABLED)
|
||||
check_symbol_exists(LWS_WITH_MBEDTLS "lws_config.h" LWS_MBEDTLS_ENABLED)
|
||||
if(NOT LWS_WITH_LIBUV)
|
||||
message(FATAL_ERROR "libwebsockets was not build with libuv support (-DLWS_WITH_LIBUV=ON)")
|
||||
endif()
|
||||
if(LWS_OPENSSL_ENABLED AND NOT LWS_MBEDTLS_ENABLED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
list(APPEND INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
|
||||
list(APPEND LINK_LIBS ${OPENSSL_LIBRARIES})
|
||||
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)
|
||||
else()
|
||||
find_library(LIBUTIL NAMES util)
|
||||
if(LIBUTIL)
|
||||
list(APPEND LINK_LIBS util)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS})
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC
|
||||
TTYD_VERSION="${TTYD_VERSION}"
|
||||
$<$<PLATFORM_ID:Windows>:_WIN32_WINNT=0xa00 WINVER=0xa00>
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT prog)
|
||||
install(FILES man/ttyd.1 DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT doc)
|
@@ -1,15 +0,0 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
# Dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tini && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Application
|
||||
COPY ./dist/${TARGETARCH}/ttyd /usr/bin/ttyd
|
||||
|
||||
EXPOSE 7681
|
||||
WORKDIR /root
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["ttyd", "-W", "bash"]
|
@@ -1,15 +0,0 @@
|
||||
FROM alpine
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
# Dependencies
|
||||
RUN apk add --no-cache bash tini
|
||||
|
||||
# Application
|
||||
COPY ./dist/${TARGETARCH}/ttyd /usr/bin/ttyd
|
||||
|
||||
EXPOSE 7681
|
||||
WORKDIR /root
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["ttyd", "-W", "bash"]
|
21
ttyd/LICENSE
21
ttyd/LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2025 Shuanglei Tao <tsl0922@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
338
ttyd/Makefile
338
ttyd/Makefile
@@ -1,338 +0,0 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
|
||||
|
||||
# Default target executed when no arguments are given to make.
|
||||
default_target: all
|
||||
.PHONY : default_target
|
||||
|
||||
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
|
||||
.NOTPARALLEL:
|
||||
|
||||
#=============================================================================
|
||||
# Special targets provided by cmake.
|
||||
|
||||
# Disable implicit rules so canonical targets will work.
|
||||
.SUFFIXES:
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : %,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : RCS/%,v
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : SCCS/s.%
|
||||
|
||||
# Disable VCS-based implicit rules.
|
||||
% : s.%
|
||||
|
||||
.SUFFIXES: .hpux_make_needs_suffix_list
|
||||
|
||||
# Command-line flag to silence nested $(MAKE).
|
||||
$(VERBOSE)MAKESILENT = -s
|
||||
|
||||
#Suppress display of executed commands.
|
||||
$(VERBOSE).SILENT:
|
||||
|
||||
# A target that is always out of date.
|
||||
cmake_force:
|
||||
.PHONY : cmake_force
|
||||
|
||||
#=============================================================================
|
||||
# Set environment variables for the build.
|
||||
|
||||
# The shell in which to execute make rules.
|
||||
SHELL = /bin/sh
|
||||
|
||||
# The CMake executable.
|
||||
CMAKE_COMMAND = /usr/bin/cmake
|
||||
|
||||
# The command to remove a file.
|
||||
RM = /usr/bin/cmake -E rm -f
|
||||
|
||||
# Escaping for special characters.
|
||||
EQUALS = =
|
||||
|
||||
# The top-level source directory on which CMake was run.
|
||||
CMAKE_SOURCE_DIR = /home/init/桌面/ttyd
|
||||
|
||||
# The top-level build directory on which CMake was run.
|
||||
CMAKE_BINARY_DIR = /home/init/桌面/ttyd
|
||||
|
||||
#=============================================================================
|
||||
# Targets provided globally by CMake.
|
||||
|
||||
# Special rule for the target edit_cache
|
||||
edit_cache:
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "No interactive CMake dialog available..."
|
||||
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
|
||||
.PHONY : edit_cache
|
||||
|
||||
# Special rule for the target edit_cache
|
||||
edit_cache/fast: edit_cache
|
||||
.PHONY : edit_cache/fast
|
||||
|
||||
# Special rule for the target rebuild_cache
|
||||
rebuild_cache:
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake to regenerate build system..."
|
||||
/usr/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
|
||||
.PHONY : rebuild_cache
|
||||
|
||||
# Special rule for the target rebuild_cache
|
||||
rebuild_cache/fast: rebuild_cache
|
||||
.PHONY : rebuild_cache/fast
|
||||
|
||||
# Special rule for the target list_install_components
|
||||
list_install_components:
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Available install components are: \"doc\" \"prog\""
|
||||
.PHONY : list_install_components
|
||||
|
||||
# Special rule for the target list_install_components
|
||||
list_install_components/fast: list_install_components
|
||||
.PHONY : list_install_components/fast
|
||||
|
||||
# Special rule for the target install
|
||||
install: preinstall
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Install the project..."
|
||||
/usr/bin/cmake -P cmake_install.cmake
|
||||
.PHONY : install
|
||||
|
||||
# Special rule for the target install
|
||||
install/fast: preinstall/fast
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Install the project..."
|
||||
/usr/bin/cmake -P cmake_install.cmake
|
||||
.PHONY : install/fast
|
||||
|
||||
# Special rule for the target install/local
|
||||
install/local: preinstall
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing only the local directory..."
|
||||
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
|
||||
.PHONY : install/local
|
||||
|
||||
# Special rule for the target install/local
|
||||
install/local/fast: preinstall/fast
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing only the local directory..."
|
||||
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
|
||||
.PHONY : install/local/fast
|
||||
|
||||
# Special rule for the target install/strip
|
||||
install/strip: preinstall
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing the project stripped..."
|
||||
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
|
||||
.PHONY : install/strip
|
||||
|
||||
# Special rule for the target install/strip
|
||||
install/strip/fast: preinstall/fast
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing the project stripped..."
|
||||
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
|
||||
.PHONY : install/strip/fast
|
||||
|
||||
# The main all target
|
||||
all: cmake_check_build_system
|
||||
$(CMAKE_COMMAND) -E cmake_progress_start /home/init/桌面/ttyd/CMakeFiles /home/init/桌面/ttyd//CMakeFiles/progress.marks
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 all
|
||||
$(CMAKE_COMMAND) -E cmake_progress_start /home/init/桌面/ttyd/CMakeFiles 0
|
||||
.PHONY : all
|
||||
|
||||
# The main clean target
|
||||
clean:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 clean
|
||||
.PHONY : clean
|
||||
|
||||
# The main clean target
|
||||
clean/fast: clean
|
||||
.PHONY : clean/fast
|
||||
|
||||
# Prepare targets for installation.
|
||||
preinstall: all
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
|
||||
.PHONY : preinstall
|
||||
|
||||
# Prepare targets for installation.
|
||||
preinstall/fast:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
|
||||
.PHONY : preinstall/fast
|
||||
|
||||
# clear depends
|
||||
depend:
|
||||
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
|
||||
.PHONY : depend
|
||||
|
||||
#=============================================================================
|
||||
# Target rules for targets named ttyd
|
||||
|
||||
# Build rule for target.
|
||||
ttyd: cmake_check_build_system
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 ttyd
|
||||
.PHONY : ttyd
|
||||
|
||||
# fast build rule for target.
|
||||
ttyd/fast:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/build
|
||||
.PHONY : ttyd/fast
|
||||
|
||||
src/http.o: src/http.c.o
|
||||
.PHONY : src/http.o
|
||||
|
||||
# target to build an object file
|
||||
src/http.c.o:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/http.c.o
|
||||
.PHONY : src/http.c.o
|
||||
|
||||
src/http.i: src/http.c.i
|
||||
.PHONY : src/http.i
|
||||
|
||||
# target to preprocess a source file
|
||||
src/http.c.i:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/http.c.i
|
||||
.PHONY : src/http.c.i
|
||||
|
||||
src/http.s: src/http.c.s
|
||||
.PHONY : src/http.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
src/http.c.s:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/http.c.s
|
||||
.PHONY : src/http.c.s
|
||||
|
||||
src/protocol.o: src/protocol.c.o
|
||||
.PHONY : src/protocol.o
|
||||
|
||||
# target to build an object file
|
||||
src/protocol.c.o:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/protocol.c.o
|
||||
.PHONY : src/protocol.c.o
|
||||
|
||||
src/protocol.i: src/protocol.c.i
|
||||
.PHONY : src/protocol.i
|
||||
|
||||
# target to preprocess a source file
|
||||
src/protocol.c.i:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/protocol.c.i
|
||||
.PHONY : src/protocol.c.i
|
||||
|
||||
src/protocol.s: src/protocol.c.s
|
||||
.PHONY : src/protocol.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
src/protocol.c.s:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/protocol.c.s
|
||||
.PHONY : src/protocol.c.s
|
||||
|
||||
src/pty.o: src/pty.c.o
|
||||
.PHONY : src/pty.o
|
||||
|
||||
# target to build an object file
|
||||
src/pty.c.o:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/pty.c.o
|
||||
.PHONY : src/pty.c.o
|
||||
|
||||
src/pty.i: src/pty.c.i
|
||||
.PHONY : src/pty.i
|
||||
|
||||
# target to preprocess a source file
|
||||
src/pty.c.i:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/pty.c.i
|
||||
.PHONY : src/pty.c.i
|
||||
|
||||
src/pty.s: src/pty.c.s
|
||||
.PHONY : src/pty.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
src/pty.c.s:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/pty.c.s
|
||||
.PHONY : src/pty.c.s
|
||||
|
||||
src/server.o: src/server.c.o
|
||||
.PHONY : src/server.o
|
||||
|
||||
# target to build an object file
|
||||
src/server.c.o:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/server.c.o
|
||||
.PHONY : src/server.c.o
|
||||
|
||||
src/server.i: src/server.c.i
|
||||
.PHONY : src/server.i
|
||||
|
||||
# target to preprocess a source file
|
||||
src/server.c.i:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/server.c.i
|
||||
.PHONY : src/server.c.i
|
||||
|
||||
src/server.s: src/server.c.s
|
||||
.PHONY : src/server.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
src/server.c.s:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/server.c.s
|
||||
.PHONY : src/server.c.s
|
||||
|
||||
src/utils.o: src/utils.c.o
|
||||
.PHONY : src/utils.o
|
||||
|
||||
# target to build an object file
|
||||
src/utils.c.o:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/utils.c.o
|
||||
.PHONY : src/utils.c.o
|
||||
|
||||
src/utils.i: src/utils.c.i
|
||||
.PHONY : src/utils.i
|
||||
|
||||
# target to preprocess a source file
|
||||
src/utils.c.i:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/utils.c.i
|
||||
.PHONY : src/utils.c.i
|
||||
|
||||
src/utils.s: src/utils.c.s
|
||||
.PHONY : src/utils.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
src/utils.c.s:
|
||||
$(MAKE) $(MAKESILENT) -f CMakeFiles/ttyd.dir/build.make CMakeFiles/ttyd.dir/src/utils.c.s
|
||||
.PHONY : src/utils.c.s
|
||||
|
||||
# Help Target
|
||||
help:
|
||||
@echo "The following are some of the valid targets for this Makefile:"
|
||||
@echo "... all (the default if no target is provided)"
|
||||
@echo "... clean"
|
||||
@echo "... depend"
|
||||
@echo "... edit_cache"
|
||||
@echo "... install"
|
||||
@echo "... install/local"
|
||||
@echo "... install/strip"
|
||||
@echo "... list_install_components"
|
||||
@echo "... rebuild_cache"
|
||||
@echo "... ttyd"
|
||||
@echo "... src/http.o"
|
||||
@echo "... src/http.i"
|
||||
@echo "... src/http.s"
|
||||
@echo "... src/protocol.o"
|
||||
@echo "... src/protocol.i"
|
||||
@echo "... src/protocol.s"
|
||||
@echo "... src/pty.o"
|
||||
@echo "... src/pty.i"
|
||||
@echo "... src/pty.s"
|
||||
@echo "... src/server.o"
|
||||
@echo "... src/server.i"
|
||||
@echo "... src/server.s"
|
||||
@echo "... src/utils.o"
|
||||
@echo "... src/utils.i"
|
||||
@echo "... src/utils.s"
|
||||
.PHONY : help
|
||||
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# Special targets to cleanup operation of make.
|
||||
|
||||
# Special rule to run CMake to check the build system integrity.
|
||||
# No rule that depends on this can have commands that come from listfiles
|
||||
# because they might be regenerated.
|
||||
cmake_check_build_system:
|
||||
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
|
||||
.PHONY : cmake_check_build_system
|
||||
|
108
ttyd/README.md
108
ttyd/README.md
@@ -1,108 +0,0 @@
|
||||

|
||||

|
||||
[](https://github.com/tsl0922/ttyd/releases)
|
||||
[](https://hub.docker.com/r/tsl0922/ttyd)
|
||||
[](https://repology.org/project/ttyd/versions)
|
||||

|
||||
|
||||
# ttyd - Share your terminal over the web
|
||||
|
||||
ttyd is a simple command-line tool for sharing terminal over the web.
|
||||
|
||||

|
||||
|
||||
# Features
|
||||
|
||||
- Built on top of [libuv](https://libuv.org) and [WebGL2](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) for speed
|
||||
- Fully-featured terminal with [CJK](https://en.wikipedia.org/wiki/CJK_characters) and IME support
|
||||
- [ZMODEM](https://en.wikipedia.org/wiki/ZMODEM) ([lrzsz](https://ohse.de/uwe/software/lrzsz.html)) / [trzsz](https://trzsz.github.io) file transfer support
|
||||
- [Sixel](https://en.wikipedia.org/wiki/Sixel) image output support ([img2sixel](https://saitoha.github.io/libsixel) / [lsix](https://github.com/hackerb9/lsix))
|
||||
- SSL support based on [OpenSSL](https://www.openssl.org) / [Mbed TLS](https://github.com/Mbed-TLS/mbedtls)
|
||||
- Run any custom command with options
|
||||
- Basic authentication support and many other custom options
|
||||
- Cross platform: macOS, Linux, FreeBSD/OpenBSD, [OpenWrt](https://openwrt.org), Windows
|
||||
|
||||
> ❤ Special thanks to [JetBrains](https://www.jetbrains.com/?from=ttyd) for sponsoring the opensource license to this project.
|
||||
|
||||
# Installation
|
||||
|
||||
## Install on macOS
|
||||
|
||||
- Install with [Homebrew](http://brew.sh): `brew install ttyd`
|
||||
- Install with [MacPorts](https://www.macports.org): `sudo port install ttyd`
|
||||
|
||||
## Install on Linux
|
||||
|
||||
- Binary version (recommended): download from the [releases](https://github.com/tsl0922/ttyd/releases) page
|
||||
- Install with [Homebrew](https://docs.brew.sh/Homebrew-on-Linux) : `brew install ttyd`
|
||||
- Install the snap: `sudo snap install ttyd --classic`
|
||||
- Build from source (debian/ubuntu):
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential cmake git libjson-c-dev libwebsockets-dev
|
||||
git clone https://github.com/tsl0922/ttyd.git
|
||||
cd ttyd && mkdir build && cd build
|
||||
cmake ..
|
||||
make && sudo make install
|
||||
```
|
||||
You may also need to compile/install [libwebsockets](https://libwebsockets.org) from source if the `libwebsockets-dev` package is outdated.
|
||||
- Install on OpenWrt: `opkg install ttyd`
|
||||
- Install on Gentoo: clone the [repo](https://bitbucket.org/mgpagano/ttyd/src/master) and follow the directions [here](https://wiki.gentoo.org/wiki/Custom_repository#Creating_a_local_repository).
|
||||
|
||||
## Install on Windows
|
||||
|
||||
- Binary version (recommended): download from the [releases](https://github.com/tsl0922/ttyd/releases) page
|
||||
- Install with [WinGet](https://github.com/microsoft/winget-cli): `winget install tsl0922.ttyd`
|
||||
- Install with [Scoop](https://scoop.sh/#/apps?q=ttyd&s=2&d=1&o=true): `scoop install ttyd`
|
||||
- [Compile on Windows](https://github.com/tsl0922/ttyd/wiki/Compile-on-Windows)
|
||||
|
||||
# Usage
|
||||
|
||||
## Command-line Options
|
||||
|
||||
```
|
||||
USAGE:
|
||||
ttyd [options] <command> [<arguments...>]
|
||||
|
||||
OPTIONS:
|
||||
-p, --port Port to listen (default: 7681, use `0` for random port)
|
||||
-i, --interface Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)
|
||||
-U, --socket-owner User owner of the UNIX domain socket file, when enabled (eg: user:group)
|
||||
-c, --credential Credential for basic authentication (format: username:password)
|
||||
-H, --auth-header HTTP Header name for auth proxy, this will configure ttyd to let a HTTP reverse proxy handle authentication
|
||||
-u, --uid User id to run with
|
||||
-g, --gid Group id to run with
|
||||
-s, --signal Signal to send to the command when exit it (default: 1, SIGHUP)
|
||||
-w, --cwd Working directory to be set for the child program
|
||||
-a, --url-arg Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)
|
||||
-W, --writable Allow clients to write to the TTY (readonly by default)
|
||||
-t, --client-option Send option to client (format: key=value), repeat to add more options
|
||||
-T, --terminal-type Terminal type to report, default: xterm-256color
|
||||
-O, --check-origin Do not allow websocket connection from different origin
|
||||
-m, --max-clients Maximum clients to support (default: 0, no limit)
|
||||
-o, --once Accept only one client and exit on disconnection
|
||||
-q, --exit-no-conn Exit on all clients disconnection
|
||||
-B, --browser Open terminal with the default system browser
|
||||
-I, --index Custom index.html path
|
||||
-b, --base-path Expected base path for requests coming from a reverse proxy (eg: /mounted/here, max length: 128)
|
||||
-P, --ping-interval Websocket ping interval(sec) (default: 5)
|
||||
-6, --ipv6 Enable IPv6 support
|
||||
-S, --ssl Enable SSL
|
||||
-C, --ssl-cert SSL certificate file path
|
||||
-K, --ssl-key SSL key file path
|
||||
-A, --ssl-ca SSL CA file path for client certificate verification
|
||||
-d, --debug Set log level (default: 7)
|
||||
-v, --version Print the version and exit
|
||||
-h, --help Print this text and exit
|
||||
```
|
||||
|
||||
Read the example usage on the [wiki](https://github.com/tsl0922/ttyd/wiki/Example-Usage).
|
||||
|
||||
## Browser Support
|
||||
|
||||
Modern browsers, See [Browser Support](https://github.com/xtermjs/xterm.js#browser-support).
|
||||
|
||||
## Alternatives
|
||||
|
||||
* [Wetty](https://github.com/krishnasrinivas/wetty): [Node](https://nodejs.org) based web terminal (SSH/login)
|
||||
* [GoTTY](https://github.com/yudai/gotty): [Go](https://golang.org) based web terminal
|
@@ -1,32 +0,0 @@
|
||||
#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", "ttyd\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-2025 Shuanglei Tao\0"
|
||||
VALUE "Comment", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
@@ -1,47 +0,0 @@
|
||||
find_package(Git)
|
||||
|
||||
function(get_git_version var1 var2)
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --match "[0-9]*.[0-9]*.[0-9]*" --abbrev=8
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE status
|
||||
OUTPUT_VARIABLE GIT_VERSION
|
||||
)
|
||||
if (${status})
|
||||
set(GIT_VERSION "0.0.0")
|
||||
else()
|
||||
string(STRIP ${GIT_VERSION} GIT_VERSION)
|
||||
string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION})
|
||||
endif()
|
||||
else()
|
||||
set(GIT_VERSION "0.0.0")
|
||||
endif()
|
||||
|
||||
string(REGEX MATCH "^[0-9]+.[0-9]+.[0-9]+" SEM_VER "${GIT_VERSION}")
|
||||
|
||||
message("-- Git Tag: ${GIT_VERSION}, Sem Ver: ${SEM_VER}")
|
||||
|
||||
set(${var1} ${GIT_VERSION} PARENT_SCOPE)
|
||||
set(${var2} ${SEM_VER} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head var1)
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} --git-dir ${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse --short HEAD
|
||||
RESULT_VARIABLE status
|
||||
OUTPUT_VARIABLE GIT_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(${status})
|
||||
set(GIT_COMMIT "unknown")
|
||||
endif()
|
||||
|
||||
message("-- Git Commit: ${GIT_COMMIT}")
|
||||
|
||||
set(${var1} ${GIT_COMMIT} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
@@ -1,78 +0,0 @@
|
||||
# Install script for directory: /home/init/桌面/ttyd
|
||||
|
||||
# Set the install prefix
|
||||
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||
endif()
|
||||
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# Set the install configuration name.
|
||||
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
||||
if(BUILD_TYPE)
|
||||
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "")
|
||||
endif()
|
||||
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||
endif()
|
||||
|
||||
# Set the component getting installed.
|
||||
if(NOT CMAKE_INSTALL_COMPONENT)
|
||||
if(COMPONENT)
|
||||
message(STATUS "Install component: \"${COMPONENT}\"")
|
||||
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_COMPONENT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install shared libraries without execute permission?
|
||||
if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
|
||||
set(CMAKE_INSTALL_SO_NO_EXE "1")
|
||||
endif()
|
||||
|
||||
# Is this installation the result of a crosscompile?
|
||||
if(NOT DEFINED CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_CROSSCOMPILING "FALSE")
|
||||
endif()
|
||||
|
||||
# Set default install directory permissions.
|
||||
if(NOT DEFINED CMAKE_OBJDUMP)
|
||||
set(CMAKE_OBJDUMP "/usr/bin/objdump")
|
||||
endif()
|
||||
|
||||
if(CMAKE_INSTALL_COMPONENT STREQUAL "prog" OR NOT CMAKE_INSTALL_COMPONENT)
|
||||
if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd" AND
|
||||
NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd")
|
||||
file(RPATH_CHECK
|
||||
FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd"
|
||||
RPATH "")
|
||||
endif()
|
||||
file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/home/init/桌面/ttyd/ttyd")
|
||||
if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd" AND
|
||||
NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd")
|
||||
if(CMAKE_INSTALL_DO_STRIP)
|
||||
execute_process(COMMAND "/usr/bin/strip" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/ttyd")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_INSTALL_COMPONENT STREQUAL "prog" OR NOT CMAKE_INSTALL_COMPONENT)
|
||||
include("/home/init/桌面/ttyd/CMakeFiles/ttyd.dir/install-cxx-module-bmi-noconfig.cmake" OPTIONAL)
|
||||
endif()
|
||||
|
||||
if(CMAKE_INSTALL_COMPONENT STREQUAL "doc" OR NOT CMAKE_INSTALL_COMPONENT)
|
||||
file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1" TYPE FILE FILES "/home/init/桌面/ttyd/man/ttyd.1")
|
||||
endif()
|
||||
|
||||
if(CMAKE_INSTALL_COMPONENT)
|
||||
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
|
||||
else()
|
||||
set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
|
||||
"${CMAKE_INSTALL_MANIFEST_FILES}")
|
||||
file(WRITE "/home/init/桌面/ttyd/${CMAKE_INSTALL_MANIFEST}"
|
||||
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
@@ -1,14 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.json, *.scss}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
@@ -1 +0,0 @@
|
||||
dist/
|
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/gts/",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"parserOptions": {
|
||||
"jsxPragma": "h"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-duplicate-enum-values": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["gulpfile.js", "webpack.config.js"],
|
||||
"rules": {
|
||||
"node/no-unpublished-require": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
9
ttyd/html/.gitignore
vendored
9
ttyd/html/.gitignore
vendored
@@ -1,9 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
*.log
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
...require('gts/.prettierrc.json'),
|
||||
"bracketSpacing": true,
|
||||
"tabWidth": 4,
|
||||
"printWidth": 120,
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
diff --git a/src/zsession.js b/src/zsession.js
|
||||
index 5f0b8f9d8afa6fba0acd6dd0477afa186f7aad9a..c7ea98e0f08c97d63d321f784a5dd8bf66888743 100644
|
||||
--- a/src/zsession.js
|
||||
+++ b/src/zsession.js
|
||||
@@ -548,20 +548,17 @@ Zmodem.Session.Receive = class ZmodemReceiveSession extends Zmodem.Session {
|
||||
if (this._got_ZFIN) {
|
||||
if (this._input_buffer.length < 2) return;
|
||||
|
||||
- //if it’s OO, then set this._bytes_after_OO
|
||||
- if (Zmodem.ZMLIB.find_subarray(this._input_buffer, OVER_AND_OUT) === 0) {
|
||||
+ if (Zmodem.ZMLIB.find_subarray(this._input_buffer, OVER_AND_OUT) !== 0) {
|
||||
+ console.warn( "PROTOCOL: Only thing after ZFIN should be “OO” (79,79), not: " + this._input_buffer.join() );
|
||||
+ }
|
||||
|
||||
- //This doubles as an indication that the session has ended.
|
||||
- //We need to set this right away so that handlers like
|
||||
- //"session_end" will have access to it.
|
||||
- this._bytes_after_OO = _trim_OO(this._bytes_being_consumed.slice(0));
|
||||
- this._on_session_end();
|
||||
+ //This doubles as an indication that the session has ended.
|
||||
+ //We need to set this right away so that handlers like
|
||||
+ //"session_end" will have access to it.
|
||||
+ this._bytes_after_OO = _trim_OO(this._bytes_being_consumed.slice(0));
|
||||
+ this._on_session_end();
|
||||
|
||||
- return;
|
||||
- }
|
||||
- else {
|
||||
- throw( "PROTOCOL: Only thing after ZFIN should be “OO” (79,79), not: " + this._input_buffer.join() );
|
||||
- }
|
||||
+ return;
|
||||
}
|
||||
|
||||
var parsed;
|
@@ -1 +0,0 @@
|
||||
nodeLinker: node-modules
|
@@ -1,14 +0,0 @@
|
||||
## Prerequisites
|
||||
|
||||
> **NOTE:** yarn v2 is required.
|
||||
|
||||
Install [Yarn](https://yarnpkg.com/getting-started/install), and run: `yarn install`.
|
||||
|
||||
## Development
|
||||
|
||||
1. Start ttyd: `ttyd bash`
|
||||
2. Start the dev server: `yarn run start`
|
||||
|
||||
## Publish
|
||||
|
||||
Run `yarn run build`, this will compile the inlined html to `../src/html.h`.
|
@@ -1,68 +0,0 @@
|
||||
const { src, dest, task, series } = require('gulp');
|
||||
const clean = require('gulp-clean');
|
||||
const gzip = require('gulp-gzip');
|
||||
const inlineSource = require('gulp-inline-source');
|
||||
const rename = require('gulp-rename');
|
||||
const through2 = require('through2');
|
||||
|
||||
const genHeader = (size, buf, len) => {
|
||||
let idx = 0;
|
||||
let data = 'unsigned char index_html[] = {\n ';
|
||||
|
||||
for (const value of buf) {
|
||||
idx++;
|
||||
|
||||
const current = value < 0 ? value + 256 : value;
|
||||
|
||||
data += '0x';
|
||||
data += (current >>> 4).toString(16);
|
||||
data += (current & 0xf).toString(16);
|
||||
|
||||
if (idx === len) {
|
||||
data += '\n';
|
||||
} else {
|
||||
data += idx % 12 === 0 ? ',\n ' : ', ';
|
||||
}
|
||||
}
|
||||
|
||||
data += '};\n';
|
||||
data += `unsigned int index_html_len = ${len};\n`;
|
||||
data += `unsigned int index_html_size = ${size};\n`;
|
||||
return data;
|
||||
};
|
||||
let fileSize = 0;
|
||||
|
||||
task('clean', () => {
|
||||
return src('dist', { read: false, allowEmpty: true }).pipe(clean());
|
||||
});
|
||||
|
||||
task('inline', () => {
|
||||
const options = {
|
||||
compress: false,
|
||||
};
|
||||
|
||||
return src('dist/index.html').pipe(inlineSource(options)).pipe(rename('inline.html')).pipe(dest('dist/'));
|
||||
});
|
||||
|
||||
task(
|
||||
'default',
|
||||
series('inline', () => {
|
||||
return src('dist/inline.html')
|
||||
.pipe(
|
||||
through2.obj((file, enc, cb) => {
|
||||
fileSize = file.contents.length;
|
||||
return cb(null, file);
|
||||
})
|
||||
)
|
||||
.pipe(gzip())
|
||||
.pipe(
|
||||
through2.obj((file, enc, cb) => {
|
||||
const buf = file.contents;
|
||||
file.contents = Buffer.from(genHeader(fileSize, buf, buf.length));
|
||||
return cb(null, file);
|
||||
})
|
||||
)
|
||||
.pipe(rename('html.h'))
|
||||
.pipe(dest('../src/'));
|
||||
})
|
||||
);
|
15575
ttyd/html/package-lock.json
generated
15575
ttyd/html/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "ttyd",
|
||||
"version": "1.0.0",
|
||||
"description": "Share your terminal over the web",
|
||||
"repository": {
|
||||
"url": "git@github.com:tsl0922/ttyd.git",
|
||||
"type": "git"
|
||||
},
|
||||
"author": "Shuanglei Tao <tsl0922@gmail.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prestart": "gulp clean",
|
||||
"start": "NODE_ENV=development && webpack serve",
|
||||
"build": "NODE_ENV=production webpack && gulp",
|
||||
"inline": "NODE_ENV=production webpack && gulp inline",
|
||||
"check": "gts check",
|
||||
"fix": "gts fix"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^6.10.0",
|
||||
"css-minimizer-webpack-plugin": "^6.0.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"gts": "^5.2.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-inline-source": "^4.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"mini-css-extract-plugin": "^2.8.1",
|
||||
"sass": "^1.71.1",
|
||||
"sass-loader": "^14.1.1",
|
||||
"scssfmt": "^1.0.7",
|
||||
"style-loader": "^3.3.4",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"through2": "^4.0.2",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.3.3",
|
||||
"util": "^0.12.5",
|
||||
"webpack": "^5.90.3",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.2",
|
||||
"webpack-merge": "^5.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-clipboard": "^0.1.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-image": "^0.8.0",
|
||||
"@xterm/addon-unicode11": "^0.8.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/addon-webgl": "^0.18.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"decko": "^1.2.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"preact": "^10.19.6",
|
||||
"raw-loader": "^4.0.2",
|
||||
"trzsz": "^1.1.5",
|
||||
"whatwg-fetch": "^3.6.20",
|
||||
"zmodem.js": "^0.1.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"zmodem.js@^0.1.10": "patch:zmodem.js@npm%3A0.1.10#./.yarn/patches/zmodem.js-npm-0.1.10-e5537fa2ed.patch"
|
||||
},
|
||||
"packageManager": "yarn@3.6.3"
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { Terminal } from './terminal';
|
||||
|
||||
import type { ITerminalOptions, ITheme } from '@xterm/xterm';
|
||||
import type { ClientOptions, FlowControl } from './terminal/xterm';
|
||||
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const path = window.location.pathname.replace(/[/]+$/, '');
|
||||
const wsUrl = [protocol, '//', window.location.host, path, '/ws', window.location.search].join('');
|
||||
const tokenUrl = [window.location.protocol, '//', window.location.host, path, '/token'].join('');
|
||||
const clientOptions = {
|
||||
rendererType: 'webgl',
|
||||
disableLeaveAlert: false,
|
||||
disableResizeOverlay: false,
|
||||
enableZmodem: false,
|
||||
enableTrzsz: false,
|
||||
enableSixel: false,
|
||||
closeOnDisconnect: false,
|
||||
isWindows: false,
|
||||
unicodeVersion: '11',
|
||||
} as ClientOptions;
|
||||
const termOptions = {
|
||||
fontSize: 13,
|
||||
fontFamily: 'Consolas,Liberation Mono,Menlo,Courier,monospace',
|
||||
theme: {
|
||||
foreground: '#d2d2d2',
|
||||
background: '#2b2b2b',
|
||||
cursor: '#adadad',
|
||||
black: '#000000',
|
||||
red: '#d81e00',
|
||||
green: '#5ea702',
|
||||
yellow: '#cfae00',
|
||||
blue: '#427ab3',
|
||||
magenta: '#89658e',
|
||||
cyan: '#00a7aa',
|
||||
white: '#dbded8',
|
||||
brightBlack: '#686a66',
|
||||
brightRed: '#f54235',
|
||||
brightGreen: '#99e343',
|
||||
brightYellow: '#fdeb61',
|
||||
brightBlue: '#84b0d8',
|
||||
brightMagenta: '#bc94b7',
|
||||
brightCyan: '#37e6e8',
|
||||
brightWhite: '#f1f1f0',
|
||||
} as ITheme,
|
||||
allowProposedApi: true,
|
||||
} as ITerminalOptions;
|
||||
const flowControl = {
|
||||
limit: 100000,
|
||||
highWater: 10,
|
||||
lowWater: 4,
|
||||
} as FlowControl;
|
||||
|
||||
export class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Terminal
|
||||
id="terminal-container"
|
||||
wsUrl={wsUrl}
|
||||
tokenUrl={tokenUrl}
|
||||
clientOptions={clientOptions}
|
||||
termOptions={termOptions}
|
||||
flowControl={flowControl}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import { h, Component, ComponentChildren } from 'preact';
|
||||
|
||||
import './modal.scss';
|
||||
|
||||
interface Props {
|
||||
show: boolean;
|
||||
children: ComponentChildren;
|
||||
}
|
||||
|
||||
export class Modal extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render({ show, children }: Props) {
|
||||
return (
|
||||
show && (
|
||||
<div className="modal">
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<div className="box">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
.modal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.modal-background {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background-color: #4a4a4acc;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: 0 20px;
|
||||
max-height: calc(100vh - 160px);
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.box {
|
||||
background-color: #fff;
|
||||
color: #4a4a4a;
|
||||
display: block;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
header {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
height: .01em;
|
||||
left: 0;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: .01em;
|
||||
}
|
||||
|
||||
.file-cta {
|
||||
cursor: pointer;
|
||||
background-color: #f5f5f5;
|
||||
color: #6200ee;
|
||||
outline: none;
|
||||
align-items: center;
|
||||
box-shadow: none;
|
||||
display: inline-flex;
|
||||
height: 2.25em;
|
||||
justify-content: flex-start;
|
||||
line-height: 1.5;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
border-color: #dbdbdb;
|
||||
border-radius: 3px;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
padding: calc(.375em - 1px) 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (min-width: 769px) {
|
||||
.modal-content {
|
||||
margin: 0 auto;
|
||||
max-height: calc(100vh - 40px);
|
||||
width: 640px;
|
||||
}
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
import { bind } from 'decko';
|
||||
import { Component, h } from 'preact';
|
||||
import { Xterm, XtermOptions } from './xterm';
|
||||
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import { Modal } from '../modal';
|
||||
|
||||
interface Props extends XtermOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
modal: boolean;
|
||||
}
|
||||
|
||||
export class Terminal extends Component<Props, State> {
|
||||
private container: HTMLElement;
|
||||
private xterm: Xterm;
|
||||
|
||||
constructor(props: Props) {
|
||||
super();
|
||||
this.xterm = new Xterm(props, this.showModal);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.xterm.refreshToken();
|
||||
this.xterm.open(this.container);
|
||||
this.xterm.connect();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.xterm.dispose();
|
||||
}
|
||||
|
||||
render({ id }: Props, { modal }: State) {
|
||||
return (
|
||||
<div id={id} ref={c => (this.container = c as HTMLElement)}>
|
||||
<Modal show={modal}>
|
||||
<label class="file-label">
|
||||
<input onChange={this.sendFile} class="file-input" type="file" multiple />
|
||||
<span class="file-cta">Choose files…</span>
|
||||
</label>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
showModal() {
|
||||
this.setState({ modal: true });
|
||||
}
|
||||
|
||||
@bind
|
||||
sendFile(event: Event) {
|
||||
this.setState({ modal: false });
|
||||
const files = (event.target as HTMLInputElement).files;
|
||||
if (files) this.xterm.sendFile(files);
|
||||
}
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
// ported from hterm.Terminal.prototype.showOverlay
|
||||
// https://chromium.googlesource.com/apps/libapps/+/master/hterm/js/hterm_terminal.js
|
||||
import { bind } from 'decko';
|
||||
import { ITerminalAddon, Terminal } from '@xterm/xterm';
|
||||
|
||||
export class OverlayAddon implements ITerminalAddon {
|
||||
private terminal: Terminal;
|
||||
private overlayNode: HTMLElement;
|
||||
private overlayTimeout?: number;
|
||||
|
||||
constructor() {
|
||||
this.overlayNode = document.createElement('div');
|
||||
this.overlayNode.style.cssText = `border-radius: 15px;
|
||||
font-size: xx-large;
|
||||
opacity: 0.75;
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
position: absolute;
|
||||
-webkit-user-select: none;
|
||||
-webkit-transition: opacity 180ms ease-in;
|
||||
-moz-user-select: none;
|
||||
-moz-transition: opacity 180ms ease-in;`;
|
||||
|
||||
this.overlayNode.addEventListener(
|
||||
'mousedown',
|
||||
e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
activate(terminal: Terminal): void {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
dispose(): void {}
|
||||
|
||||
@bind
|
||||
showOverlay(msg: string, timeout?: number): void {
|
||||
const { terminal, overlayNode } = this;
|
||||
if (!terminal.element) return;
|
||||
|
||||
overlayNode.style.color = '#101010';
|
||||
overlayNode.style.backgroundColor = '#f0f0f0';
|
||||
overlayNode.textContent = msg;
|
||||
overlayNode.style.opacity = '0.75';
|
||||
|
||||
if (!overlayNode.parentNode) {
|
||||
terminal.element.appendChild(overlayNode);
|
||||
}
|
||||
|
||||
const divSize = terminal.element.getBoundingClientRect();
|
||||
const overlaySize = overlayNode.getBoundingClientRect();
|
||||
|
||||
overlayNode.style.top = (divSize.height - overlaySize.height) / 2 + 'px';
|
||||
overlayNode.style.left = (divSize.width - overlaySize.width) / 2 + 'px';
|
||||
|
||||
if (this.overlayTimeout) clearTimeout(this.overlayTimeout);
|
||||
if (!timeout) return;
|
||||
|
||||
this.overlayTimeout = window.setTimeout(() => {
|
||||
overlayNode.style.opacity = '0';
|
||||
this.overlayTimeout = window.setTimeout(() => {
|
||||
if (overlayNode.parentNode) {
|
||||
overlayNode.parentNode.removeChild(overlayNode);
|
||||
}
|
||||
this.overlayTimeout = undefined;
|
||||
overlayNode.style.opacity = '0.75';
|
||||
}, 200);
|
||||
}, timeout || 1500);
|
||||
}
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
import { bind } from 'decko';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm';
|
||||
import * as Zmodem from 'zmodem.js/src/zmodem_browser';
|
||||
import { TrzszFilter } from 'trzsz';
|
||||
|
||||
export interface ZmodeOptions {
|
||||
zmodem: boolean;
|
||||
trzsz: boolean;
|
||||
windows: boolean;
|
||||
trzszDragInitTimeout: number;
|
||||
onSend: () => void;
|
||||
sender: (data: string | Uint8Array) => void;
|
||||
writer: (data: string | Uint8Array) => void;
|
||||
}
|
||||
|
||||
export class ZmodemAddon implements ITerminalAddon {
|
||||
private disposables: IDisposable[] = [];
|
||||
private terminal: Terminal;
|
||||
private sentry: Zmodem.Sentry;
|
||||
private session: Zmodem.Session;
|
||||
private denier: () => void;
|
||||
private trzszFilter: TrzszFilter;
|
||||
|
||||
constructor(private options: ZmodeOptions) {}
|
||||
|
||||
activate(terminal: Terminal) {
|
||||
this.terminal = terminal;
|
||||
if (this.options.zmodem) this.zmodemInit();
|
||||
if (this.options.trzsz) this.trzszInit();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (const d of this.disposables) {
|
||||
d.dispose();
|
||||
}
|
||||
this.disposables.length = 0;
|
||||
}
|
||||
|
||||
consume(data: ArrayBuffer) {
|
||||
try {
|
||||
if (this.options.trzsz) {
|
||||
this.trzszFilter.processServerOutput(data);
|
||||
} else {
|
||||
this.sentry.consume(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ttyd] zmodem consume: ', e);
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private reset() {
|
||||
this.terminal.options.disableStdin = false;
|
||||
this.terminal.focus();
|
||||
}
|
||||
|
||||
private addDisposableListener(target: EventTarget, type: string, listener: EventListener) {
|
||||
target.addEventListener(type, listener);
|
||||
this.disposables.push({ dispose: () => target.removeEventListener(type, listener) });
|
||||
}
|
||||
|
||||
@bind
|
||||
private trzszInit() {
|
||||
const { terminal } = this;
|
||||
const { sender, writer, zmodem } = this.options;
|
||||
this.trzszFilter = new TrzszFilter({
|
||||
writeToTerminal: data => {
|
||||
if (!this.trzszFilter.isTransferringFiles() && zmodem) {
|
||||
this.sentry.consume(data);
|
||||
} else {
|
||||
writer(typeof data === 'string' ? data : new Uint8Array(data as ArrayBuffer));
|
||||
}
|
||||
},
|
||||
sendToServer: data => sender(data),
|
||||
terminalColumns: terminal.cols,
|
||||
isWindowsShell: this.options.windows,
|
||||
dragInitTimeout: this.options.trzszDragInitTimeout,
|
||||
});
|
||||
const element = terminal.element as EventTarget;
|
||||
this.addDisposableListener(element, 'dragover', event => event.preventDefault());
|
||||
this.addDisposableListener(element, 'drop', event => {
|
||||
event.preventDefault();
|
||||
this.trzszFilter
|
||||
.uploadFiles((event as DragEvent).dataTransfer?.items as DataTransferItemList)
|
||||
.then(() => console.log('[ttyd] upload success'))
|
||||
.catch(err => console.log('[ttyd] upload failed: ' + err));
|
||||
});
|
||||
this.disposables.push(terminal.onResize(size => this.trzszFilter.setTerminalColumns(size.cols)));
|
||||
}
|
||||
|
||||
@bind
|
||||
private zmodemInit() {
|
||||
const { sender, writer } = this.options;
|
||||
const { terminal, reset, zmodemDetect } = this;
|
||||
this.session = null;
|
||||
this.sentry = new Zmodem.Sentry({
|
||||
to_terminal: octets => writer(new Uint8Array(octets)),
|
||||
sender: octets => sender(new Uint8Array(octets)),
|
||||
on_retract: () => reset(),
|
||||
on_detect: detection => zmodemDetect(detection),
|
||||
});
|
||||
this.disposables.push(
|
||||
terminal.onKey(e => {
|
||||
const event = e.domEvent;
|
||||
if (event.ctrlKey && event.key === 'c') {
|
||||
if (this.denier) this.denier();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
private zmodemDetect(detection: Zmodem.Detection): void {
|
||||
const { terminal, receiveFile } = this;
|
||||
terminal.options.disableStdin = true;
|
||||
|
||||
this.denier = () => detection.deny();
|
||||
this.session = detection.confirm();
|
||||
this.session.on('session_end', () => this.reset());
|
||||
|
||||
if (this.session.type === 'send') {
|
||||
this.options.onSend();
|
||||
} else {
|
||||
receiveFile();
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
public sendFile(files: FileList) {
|
||||
const { session, writeProgress } = this;
|
||||
Zmodem.Browser.send_files(session, files, {
|
||||
on_progress: (_, offer) => writeProgress(offer),
|
||||
})
|
||||
.then(() => session.close())
|
||||
.catch(() => this.reset());
|
||||
}
|
||||
|
||||
@bind
|
||||
private receiveFile() {
|
||||
const { session, writeProgress } = this;
|
||||
|
||||
session.on('offer', offer => {
|
||||
offer.on('input', () => writeProgress(offer));
|
||||
offer
|
||||
.accept()
|
||||
.then(payloads => {
|
||||
const blob = new Blob(payloads, { type: 'application/octet-stream' });
|
||||
saveAs(blob, offer.get_details().name);
|
||||
})
|
||||
.catch(() => this.reset());
|
||||
});
|
||||
|
||||
session.start();
|
||||
}
|
||||
|
||||
@bind
|
||||
private writeProgress(offer: Zmodem.Offer) {
|
||||
const { bytesHuman } = this;
|
||||
const file = offer.get_details();
|
||||
const name = file.name;
|
||||
const size = file.size;
|
||||
const offset = offer.get_offset();
|
||||
const percent = ((100 * offset) / size).toFixed(2);
|
||||
|
||||
this.options.writer(`${name} ${percent}% ${bytesHuman(offset, 2)}/${bytesHuman(size, 2)}\r`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private bytesHuman(bytes: any, precision: number): string {
|
||||
if (!/^([-+])?|(\.\d+)(\d+(\.\d+)?|(\d+\.)|Infinity)$/.test(bytes)) {
|
||||
return '-';
|
||||
}
|
||||
if (bytes === 0) return '0';
|
||||
if (typeof precision === 'undefined') precision = 1;
|
||||
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||
const num = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
const value = (bytes / Math.pow(1024, Math.floor(num))).toFixed(precision);
|
||||
return `${value} ${units[num]}`;
|
||||
}
|
||||
}
|
@@ -1 +0,0 @@
|
||||
ls\n
|
@@ -1,539 +0,0 @@
|
||||
import { bind } from 'decko';
|
||||
import type { IDisposable, ITerminalOptions } from '@xterm/xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { CanvasAddon } from '@xterm/addon-canvas';
|
||||
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
||||
import { WebglAddon } from '@xterm/addon-webgl';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { ImageAddon } from '@xterm/addon-image';
|
||||
import { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
import { OverlayAddon } from './addons/overlay';
|
||||
import { ZmodemAddon } from './addons/zmodem';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
|
||||
interface TtydTerminal extends Terminal {
|
||||
fit(): void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
term: TtydTerminal;
|
||||
}
|
||||
}
|
||||
|
||||
enum Command {
|
||||
// server side
|
||||
OUTPUT = '0',
|
||||
SET_WINDOW_TITLE = '1',
|
||||
SET_PREFERENCES = '2',
|
||||
|
||||
// client side
|
||||
INPUT = '0',
|
||||
RESIZE_TERMINAL = '1',
|
||||
PAUSE = '2',
|
||||
RESUME = '3',
|
||||
}
|
||||
type Preferences = ITerminalOptions & ClientOptions;
|
||||
|
||||
export type RendererType = 'dom' | 'canvas' | 'webgl';
|
||||
|
||||
export interface ClientOptions {
|
||||
rendererType: RendererType;
|
||||
disableLeaveAlert: boolean;
|
||||
disableResizeOverlay: boolean;
|
||||
enableZmodem: boolean;
|
||||
enableTrzsz: boolean;
|
||||
enableSixel: boolean;
|
||||
titleFixed?: string;
|
||||
isWindows: boolean;
|
||||
trzszDragInitTimeout: number;
|
||||
unicodeVersion: string;
|
||||
closeOnDisconnect: boolean;
|
||||
}
|
||||
|
||||
export interface FlowControl {
|
||||
limit: number;
|
||||
highWater: number;
|
||||
lowWater: number;
|
||||
}
|
||||
|
||||
export interface XtermOptions {
|
||||
wsUrl: string;
|
||||
tokenUrl: string;
|
||||
flowControl: FlowControl;
|
||||
clientOptions: ClientOptions;
|
||||
termOptions: ITerminalOptions;
|
||||
}
|
||||
|
||||
function toDisposable(f: () => void): IDisposable {
|
||||
return { dispose: f };
|
||||
}
|
||||
|
||||
function addEventListener(target: EventTarget, type: string, listener: EventListener): IDisposable {
|
||||
target.addEventListener(type, listener);
|
||||
return toDisposable(() => target.removeEventListener(type, listener));
|
||||
}
|
||||
|
||||
export class Xterm {
|
||||
private disposables: IDisposable[] = [];
|
||||
private textEncoder = new TextEncoder();
|
||||
private textDecoder = new TextDecoder();
|
||||
private written = 0;
|
||||
private pending = 0;
|
||||
|
||||
private terminal: Terminal;
|
||||
private fitAddon = new FitAddon();
|
||||
private overlayAddon = new OverlayAddon();
|
||||
private clipboardAddon = new ClipboardAddon();
|
||||
private webLinksAddon = new WebLinksAddon();
|
||||
private webglAddon?: WebglAddon;
|
||||
private canvasAddon?: CanvasAddon;
|
||||
private zmodemAddon?: ZmodemAddon;
|
||||
|
||||
private socket?: WebSocket;
|
||||
private token: string;
|
||||
private opened = false;
|
||||
private title?: string;
|
||||
private titleFixed?: string;
|
||||
private resizeOverlay = true;
|
||||
private reconnect = true;
|
||||
private doReconnect = true;
|
||||
private closeOnDisconnect = false;
|
||||
|
||||
private writeFunc = (data: ArrayBuffer) => this.writeData(new Uint8Array(data));
|
||||
|
||||
constructor(
|
||||
private options: XtermOptions,
|
||||
private sendCb: () => void
|
||||
) {}
|
||||
|
||||
dispose() {
|
||||
for (const d of this.disposables) {
|
||||
d.dispose();
|
||||
}
|
||||
this.disposables.length = 0;
|
||||
}
|
||||
|
||||
@bind
|
||||
private register<T extends IDisposable>(d: T): T {
|
||||
this.disposables.push(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
@bind
|
||||
public sendFile(files: FileList) {
|
||||
this.zmodemAddon?.sendFile(files);
|
||||
}
|
||||
|
||||
@bind
|
||||
public async refreshToken() {
|
||||
try {
|
||||
const resp = await fetch(this.options.tokenUrl);
|
||||
if (resp.ok) {
|
||||
const json = await resp.json();
|
||||
this.token = json.token;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[ttyd] fetch ${this.options.tokenUrl}: `, e);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private onWindowUnload(event: BeforeUnloadEvent) {
|
||||
event.preventDefault();
|
||||
if (this.socket?.readyState === WebSocket.OPEN) {
|
||||
const message = 'Close terminal? this will also terminate the command.';
|
||||
event.returnValue = message;
|
||||
return message;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@bind
|
||||
public open(parent: HTMLElement) {
|
||||
this.terminal = new Terminal(this.options.termOptions);
|
||||
const { terminal, fitAddon, overlayAddon, clipboardAddon, webLinksAddon } = this;
|
||||
window.term = terminal as TtydTerminal;
|
||||
window.term.fit = () => {
|
||||
this.fitAddon.fit();
|
||||
};
|
||||
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.loadAddon(overlayAddon);
|
||||
terminal.loadAddon(clipboardAddon);
|
||||
terminal.loadAddon(webLinksAddon);
|
||||
|
||||
terminal.open(parent);
|
||||
fitAddon.fit();
|
||||
}
|
||||
|
||||
@bind
|
||||
private initListeners() {
|
||||
const { terminal, fitAddon, overlayAddon, register, sendData } = this;
|
||||
register(
|
||||
terminal.onTitleChange(data => {
|
||||
if (data && data !== '' && !this.titleFixed) {
|
||||
document.title = data + ' | ' + this.title;
|
||||
}
|
||||
})
|
||||
);
|
||||
register(terminal.onData(data => sendData(data)));
|
||||
register(terminal.onBinary(data => sendData(Uint8Array.from(data, v => v.charCodeAt(0)))));
|
||||
register(
|
||||
terminal.onResize(({ cols, rows }) => {
|
||||
const msg = JSON.stringify({ columns: cols, rows: rows });
|
||||
this.socket?.send(this.textEncoder.encode(Command.RESIZE_TERMINAL + msg));
|
||||
if (this.resizeOverlay) overlayAddon.showOverlay(`${cols}x${rows}`, 300);
|
||||
})
|
||||
);
|
||||
register(
|
||||
terminal.onSelectionChange(() => {
|
||||
if (this.terminal.getSelection() === '') return;
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
this.overlayAddon?.showOverlay('\u2702', 200);
|
||||
})
|
||||
);
|
||||
register(addEventListener(window, 'resize', () => fitAddon.fit()));
|
||||
register(addEventListener(window, 'beforeunload', this.onWindowUnload));
|
||||
}
|
||||
|
||||
@bind
|
||||
public writeData(data: string | Uint8Array) {
|
||||
const { terminal, textEncoder } = this;
|
||||
const { limit, highWater, lowWater } = this.options.flowControl;
|
||||
|
||||
this.written += data.length;
|
||||
if (this.written > limit) {
|
||||
terminal.write(data, () => {
|
||||
this.pending = Math.max(this.pending - 1, 0);
|
||||
if (this.pending < lowWater) {
|
||||
this.socket?.send(textEncoder.encode(Command.RESUME));
|
||||
}
|
||||
});
|
||||
this.pending++;
|
||||
this.written = 0;
|
||||
if (this.pending > highWater) {
|
||||
this.socket?.send(textEncoder.encode(Command.PAUSE));
|
||||
}
|
||||
} else {
|
||||
terminal.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
public sendData(data: string | Uint8Array) {
|
||||
const { socket, textEncoder } = this;
|
||||
if (socket?.readyState !== WebSocket.OPEN) return;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
const payload = new Uint8Array(data.length * 3 + 1);
|
||||
payload[0] = Command.INPUT.charCodeAt(0);
|
||||
const stats = textEncoder.encodeInto(data, payload.subarray(1));
|
||||
socket.send(payload.subarray(0, (stats.written as number) + 1));
|
||||
} else {
|
||||
const payload = new Uint8Array(data.length + 1);
|
||||
payload[0] = Command.INPUT.charCodeAt(0);
|
||||
payload.set(data, 1);
|
||||
socket.send(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
public connect() {
|
||||
this.socket = new WebSocket(this.options.wsUrl, ['tty']);
|
||||
const { socket, register } = this;
|
||||
|
||||
socket.binaryType = 'arraybuffer';
|
||||
register(addEventListener(socket, 'open', this.onSocketOpen));
|
||||
register(addEventListener(socket, 'message', this.onSocketData as EventListener));
|
||||
register(addEventListener(socket, 'close', this.onSocketClose as EventListener));
|
||||
register(addEventListener(socket, 'error', () => (this.doReconnect = false)));
|
||||
}
|
||||
|
||||
@bind
|
||||
private onSocketOpen() {
|
||||
console.log('[ttyd] websocket connection opened');
|
||||
|
||||
const { textEncoder, terminal, overlayAddon } = this;
|
||||
const msg = JSON.stringify({ AuthToken: this.token, columns: terminal.cols, rows: terminal.rows });
|
||||
this.socket?.send(textEncoder.encode(msg));
|
||||
|
||||
if (this.opened) {
|
||||
terminal.reset();
|
||||
terminal.options.disableStdin = false;
|
||||
overlayAddon.showOverlay('Reconnected', 300);
|
||||
} else {
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
this.doReconnect = this.reconnect;
|
||||
this.initListeners();
|
||||
terminal.focus();
|
||||
console.log('000');
|
||||
}
|
||||
|
||||
@bind
|
||||
private onSocketClose(event: CloseEvent) {
|
||||
console.log(`[ttyd] websocket connection closed with code: ${event.code}`);
|
||||
|
||||
const { refreshToken, connect, doReconnect, overlayAddon } = this;
|
||||
overlayAddon.showOverlay('Connection Closed');
|
||||
this.dispose();
|
||||
|
||||
// 1000: CLOSE_NORMAL
|
||||
if (event.code !== 1000 && doReconnect) {
|
||||
overlayAddon.showOverlay('Reconnecting...');
|
||||
refreshToken().then(connect);
|
||||
} else if (this.closeOnDisconnect) {
|
||||
window.close();
|
||||
} else {
|
||||
const { terminal } = this;
|
||||
const keyDispose = terminal.onKey(e => {
|
||||
const event = e.domEvent;
|
||||
if (event.key === 'Enter') {
|
||||
keyDispose.dispose();
|
||||
overlayAddon.showOverlay('Reconnecting...');
|
||||
refreshToken().then(connect);
|
||||
}
|
||||
});
|
||||
overlayAddon.showOverlay('Press ⏎ to Reconnect');
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private parseOptsFromUrlQuery(query: string): Preferences {
|
||||
const { terminal } = this;
|
||||
const { clientOptions } = this.options;
|
||||
const prefs = {} as Preferences;
|
||||
const queryObj = Array.from(new URLSearchParams(query) as unknown as Iterable<[string, string]>);
|
||||
|
||||
for (const [k, queryVal] of queryObj) {
|
||||
let v = clientOptions[k];
|
||||
if (v === undefined) v = terminal.options[k];
|
||||
switch (typeof v) {
|
||||
case 'boolean':
|
||||
prefs[k] = queryVal === 'true' || queryVal === '1';
|
||||
break;
|
||||
case 'number':
|
||||
case 'bigint':
|
||||
prefs[k] = Number.parseInt(queryVal, 10);
|
||||
break;
|
||||
case 'string':
|
||||
prefs[k] = queryVal;
|
||||
break;
|
||||
case 'object':
|
||||
prefs[k] = JSON.parse(queryVal);
|
||||
break;
|
||||
default:
|
||||
console.warn(`[ttyd] maybe unknown option: ${k}=${queryVal}, treating as string`);
|
||||
prefs[k] = queryVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
@bind
|
||||
private onSocketData(event: MessageEvent) {
|
||||
const { textDecoder } = this;
|
||||
const rawData = event.data as ArrayBuffer;
|
||||
const cmd = String.fromCharCode(new Uint8Array(rawData)[0]);
|
||||
const data = rawData.slice(1);
|
||||
|
||||
switch (cmd) {
|
||||
case Command.OUTPUT:
|
||||
this.writeFunc(data);
|
||||
break;
|
||||
case Command.SET_WINDOW_TITLE:
|
||||
this.title = textDecoder.decode(data);
|
||||
document.title = this.title;
|
||||
break;
|
||||
case Command.SET_PREFERENCES:
|
||||
this.applyPreferences({
|
||||
...this.options.clientOptions,
|
||||
...JSON.parse(textDecoder.decode(data)),
|
||||
...this.parseOptsFromUrlQuery(window.location.search),
|
||||
} as Preferences);
|
||||
break;
|
||||
default:
|
||||
console.warn(`[ttyd] unknown command: ${cmd}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private applyPreferences(prefs: Preferences) {
|
||||
const { terminal, fitAddon, register } = this;
|
||||
if (prefs.enableZmodem || prefs.enableTrzsz) {
|
||||
this.zmodemAddon = new ZmodemAddon({
|
||||
zmodem: prefs.enableZmodem,
|
||||
trzsz: prefs.enableTrzsz,
|
||||
windows: prefs.isWindows,
|
||||
trzszDragInitTimeout: prefs.trzszDragInitTimeout,
|
||||
onSend: this.sendCb,
|
||||
sender: this.sendData,
|
||||
writer: this.writeData,
|
||||
});
|
||||
this.writeFunc = data => this.zmodemAddon?.consume(data);
|
||||
terminal.loadAddon(register(this.zmodemAddon));
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(prefs)) {
|
||||
switch (key) {
|
||||
case 'rendererType':
|
||||
this.setRendererType(value);
|
||||
break;
|
||||
case 'disableLeaveAlert':
|
||||
if (value) {
|
||||
window.removeEventListener('beforeunload', this.onWindowUnload);
|
||||
console.log('[ttyd] Leave site alert disabled');
|
||||
}
|
||||
break;
|
||||
case 'disableResizeOverlay':
|
||||
if (value) {
|
||||
console.log('[ttyd] Resize overlay disabled');
|
||||
this.resizeOverlay = false;
|
||||
}
|
||||
break;
|
||||
case 'disableReconnect':
|
||||
if (value) {
|
||||
console.log('[ttyd] Reconnect disabled');
|
||||
this.reconnect = false;
|
||||
this.doReconnect = false;
|
||||
}
|
||||
break;
|
||||
case 'enableZmodem':
|
||||
if (value) console.log('[ttyd] Zmodem enabled');
|
||||
break;
|
||||
case 'enableTrzsz':
|
||||
if (value) console.log('[ttyd] trzsz enabled');
|
||||
break;
|
||||
case 'trzszDragInitTimeout':
|
||||
if (value) console.log(`[ttyd] trzsz drag init timeout: ${value}`);
|
||||
break;
|
||||
case 'enableSixel':
|
||||
if (value) {
|
||||
terminal.loadAddon(register(new ImageAddon()));
|
||||
console.log('[ttyd] Sixel enabled');
|
||||
}
|
||||
break;
|
||||
case 'closeOnDisconnect':
|
||||
if (value) {
|
||||
console.log('[ttyd] close on disconnect enabled (Reconnect disabled)');
|
||||
this.closeOnDisconnect = true;
|
||||
this.reconnect = false;
|
||||
this.doReconnect = false;
|
||||
}
|
||||
break;
|
||||
case 'titleFixed':
|
||||
if (!value || value === '') return;
|
||||
console.log(`[ttyd] setting fixed title: ${value}`);
|
||||
this.titleFixed = value;
|
||||
document.title = value;
|
||||
break;
|
||||
case 'isWindows':
|
||||
if (value) console.log('[ttyd] is windows');
|
||||
break;
|
||||
case 'unicodeVersion':
|
||||
switch (value) {
|
||||
case 6:
|
||||
case '6':
|
||||
console.log('[ttyd] setting Unicode version: 6');
|
||||
break;
|
||||
case 11:
|
||||
case '11':
|
||||
default:
|
||||
console.log('[ttyd] setting Unicode version: 11');
|
||||
terminal.loadAddon(new Unicode11Addon());
|
||||
terminal.unicode.activeVersion = '11';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log(`[ttyd] option: ${key}=${JSON.stringify(value)}`);
|
||||
if (terminal.options[key] instanceof Object) {
|
||||
console.log(111);
|
||||
terminal.options[key] = Object.assign({}, terminal.options[key], value);
|
||||
} else {
|
||||
console.log(222);
|
||||
console.log(JSON.stringify(value));
|
||||
terminal.options[key] = value;
|
||||
this.sendData(JSON.stringify(value));
|
||||
}
|
||||
if (key.indexOf('font') === 0) fitAddon.fit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private setRendererType(value: RendererType) {
|
||||
const { terminal } = this;
|
||||
const disposeCanvasRenderer = () => {
|
||||
try {
|
||||
this.canvasAddon?.dispose();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
this.canvasAddon = undefined;
|
||||
};
|
||||
const disposeWebglRenderer = () => {
|
||||
try {
|
||||
this.webglAddon?.dispose();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
this.webglAddon = undefined;
|
||||
};
|
||||
const enableCanvasRenderer = () => {
|
||||
if (this.canvasAddon) return;
|
||||
this.canvasAddon = new CanvasAddon();
|
||||
disposeWebglRenderer();
|
||||
try {
|
||||
this.terminal.loadAddon(this.canvasAddon);
|
||||
console.log('[ttyd] canvas renderer loaded');
|
||||
} catch (e) {
|
||||
console.log('[ttyd] canvas renderer could not be loaded, falling back to dom renderer', e);
|
||||
disposeCanvasRenderer();
|
||||
}
|
||||
};
|
||||
const enableWebglRenderer = () => {
|
||||
if (this.webglAddon) return;
|
||||
this.webglAddon = new WebglAddon();
|
||||
disposeCanvasRenderer();
|
||||
try {
|
||||
this.webglAddon.onContextLoss(() => {
|
||||
this.webglAddon?.dispose();
|
||||
});
|
||||
terminal.loadAddon(this.webglAddon);
|
||||
console.log('[ttyd] WebGL renderer loaded');
|
||||
} catch (e) {
|
||||
console.log('[ttyd] WebGL renderer could not be loaded, falling back to canvas renderer', e);
|
||||
disposeWebglRenderer();
|
||||
enableCanvasRenderer();
|
||||
}
|
||||
};
|
||||
|
||||
switch (value) {
|
||||
case 'canvas':
|
||||
enableCanvasRenderer();
|
||||
break;
|
||||
case 'webgl':
|
||||
enableWebglRenderer();
|
||||
break;
|
||||
case 'dom':
|
||||
disposeWebglRenderer();
|
||||
disposeCanvasRenderer();
|
||||
console.log('[ttyd] dom renderer loaded');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,9 +0,0 @@
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
require('preact/debug');
|
||||
}
|
||||
import 'whatwg-fetch';
|
||||
import { h, render } from 'preact';
|
||||
import { App } from './components/app';
|
||||
import './style/index.scss';
|
||||
|
||||
render(<App />, document.body);
|
@@ -1,18 +0,0 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
.terminal {
|
||||
padding: 5px;
|
||||
height: calc(100% - 10px);
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link inline rel="icon" type="image/png" href="favicon.png">
|
||||
<% for (const css in htmlWebpackPlugin.files.css) { %>
|
||||
<link inline rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.files.css[css] %>">
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<% for (const js in htmlWebpackPlugin.files.js) { %>
|
||||
<script inline type="text/javascript" src="<%= htmlWebpackPlugin.files.js[js] %>"></script>
|
||||
<% } %>
|
||||
</body>
|
||||
</html>
|
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/gts/tsconfig-google.json",
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "h",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": false,
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"lib": ["es2019", "dom"],
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.ts",
|
||||
]
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
const path = require('path');
|
||||
const { merge } = require('webpack-merge');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const baseConfig = {
|
||||
context: path.resolve(__dirname, 'src'),
|
||||
entry: {
|
||||
app: './index.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: devMode ? '[name].js' : '[name].[contenthash].js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.s?[ac]ss$/,
|
||||
use: [devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new ESLintPlugin({
|
||||
context: path.resolve(__dirname, '.'),
|
||||
extensions: ['js', 'jsx', 'ts', 'tsx'],
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [{ from: './favicon.png', to: '.' }],
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: devMode ? '[name].css' : '[name].[contenthash].css',
|
||||
chunkFilename: devMode ? '[id].css' : '[id].[contenthash].css',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
},
|
||||
title: 'ttyd - Terminal',
|
||||
template: './template.html',
|
||||
}),
|
||||
],
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
};
|
||||
|
||||
const devConfig = {
|
||||
mode: 'development',
|
||||
devServer: {
|
||||
static: path.join(__dirname, 'dist'),
|
||||
compress: true,
|
||||
port: 9000,
|
||||
client: {
|
||||
overlay: {
|
||||
errors: true,
|
||||
warnings: false,
|
||||
},
|
||||
},
|
||||
proxy: [
|
||||
{
|
||||
context: ['/token', '/ws'],
|
||||
target: 'http://localhost:7681',
|
||||
ws: true,
|
||||
},
|
||||
],
|
||||
webSocketServer: {
|
||||
type: 'sockjs',
|
||||
options: {
|
||||
path: '/sockjs-node',
|
||||
},
|
||||
},
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
};
|
||||
|
||||
const prodConfig = {
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
|
||||
},
|
||||
devtool: 'source-map',
|
||||
};
|
||||
|
||||
module.exports = merge(baseConfig, devMode ? devConfig : prodConfig);
|
8099
ttyd/html/yarn.lock
8099
ttyd/html/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
/usr/local/bin/ttyd
|
||||
/usr/local/share/man/man1/ttyd.1
|
@@ -1,6 +0,0 @@
|
||||
# Building the man page
|
||||
|
||||
```bash
|
||||
go get github.com/cpuguy83/go-md2man
|
||||
go-md2man < ttyd.man.md > ttyd.1
|
||||
```
|
353
ttyd/man/ttyd.1
353
ttyd/man/ttyd.1
@@ -1,353 +0,0 @@
|
||||
.nh
|
||||
.TH ttyd 1 "September 2016" ttyd "User Manual"
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
ttyd - Share your terminal over the web
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBttyd\fP [options] <command> [<arguments...>]
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
ttyd is a command-line tool for sharing terminal over the web that runs in *nix and windows systems, with the following features:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Built on top of Libwebsockets with libuv for speed
|
||||
.IP \(bu 2
|
||||
Fully-featured terminal based on Xterm.js with CJK (Chinese, Japanese, Korean) and IME support
|
||||
.IP \(bu 2
|
||||
Graphical ZMODEM integration with lrzsz support
|
||||
.IP \(bu 2
|
||||
Sixel image output support
|
||||
.IP \(bu 2
|
||||
SSL support based on OpenSSL
|
||||
.IP \(bu 2
|
||||
Run any custom command with options
|
||||
.IP \(bu 2
|
||||
Basic authentication support and many other custom options
|
||||
.IP \(bu 2
|
||||
Cross platform: macOS, Linux, FreeBSD/OpenBSD, OpenWrt/LEDE, Windows
|
||||
|
||||
.RE
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
-p, --port
|
||||
Port to listen (default: 7681, use \fB\fC0\fR for random port)
|
||||
|
||||
.PP
|
||||
-i, --interface
|
||||
Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)
|
||||
|
||||
.PP
|
||||
-U, --socket-owner
|
||||
User owner of the UNIX domain socket file, when enabled (eg: user:group)
|
||||
|
||||
.PP
|
||||
-c, --credential USER[:PASSWORD]
|
||||
Credential for Basic Authentication (format: username:password)
|
||||
|
||||
.PP
|
||||
-H, --auth-header
|
||||
HTTP Header name for auth proxy, this will configure ttyd to let a HTTP reverse proxy handle authentication
|
||||
|
||||
.PP
|
||||
-u, --uid
|
||||
User id to run with
|
||||
|
||||
.PP
|
||||
-g, --gid
|
||||
Group id to run with
|
||||
|
||||
.PP
|
||||
-s, --signal
|
||||
Signal to send to the command when exit it (default: 1, SIGHUP)
|
||||
|
||||
.PP
|
||||
-w, --cwd
|
||||
Working directory to be set for the child program
|
||||
|
||||
.PP
|
||||
-a, --url-arg
|
||||
Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)
|
||||
|
||||
.PP
|
||||
-W, --writable
|
||||
Allow clients to write to the TTY (readonly by default)
|
||||
|
||||
.PP
|
||||
-t, --client-option
|
||||
Send option to client (format: key=value), repeat to add more options, see \fBCLIENT OPTIONS\fP for details
|
||||
|
||||
.PP
|
||||
-T, --terminal-type
|
||||
Terminal type to report, default: xterm-256color
|
||||
|
||||
.PP
|
||||
-O, --check-origin
|
||||
Do not allow websocket connection from different origin
|
||||
|
||||
.PP
|
||||
-m, --max-clients
|
||||
Maximum clients to support (default: 0, no limit)
|
||||
|
||||
.PP
|
||||
-o, --once
|
||||
Accept only one client and exit on disconnection
|
||||
|
||||
.PP
|
||||
-q, --exit-no-conn
|
||||
Exit on all clients disconnection
|
||||
|
||||
.PP
|
||||
-B, --browser
|
||||
Open terminal with the default system browser
|
||||
|
||||
.PP
|
||||
-I, --index
|
||||
Custom index.html path
|
||||
|
||||
.PP
|
||||
-b, --base-path
|
||||
Expected base path for requests coming from a reverse proxy (eg: /mounted/here, max length: 128)
|
||||
|
||||
.PP
|
||||
-P, --ping-interval
|
||||
Websocket ping interval(sec) (default: 5)
|
||||
|
||||
.PP
|
||||
-f, --srv-buf-size
|
||||
Maximum chunk of file (in bytes) that can be sent at once, a larger value may improve throughput (default: 4096)
|
||||
|
||||
.PP
|
||||
-6, --ipv6
|
||||
Enable IPv6 support
|
||||
|
||||
.PP
|
||||
-S, --ssl
|
||||
Enable SSL
|
||||
|
||||
.PP
|
||||
-C, --ssl-cert
|
||||
SSL certificate file path
|
||||
|
||||
.PP
|
||||
-K, --ssl-key
|
||||
SSL key file path
|
||||
|
||||
.PP
|
||||
-A, --ssl-ca
|
||||
SSL CA file path for client certificate verification
|
||||
|
||||
.PP
|
||||
-d, --debug
|
||||
Set log level (default: 7)
|
||||
|
||||
.PP
|
||||
-v, --version
|
||||
Print the version and exit
|
||||
|
||||
.PP
|
||||
-h, --help
|
||||
Print this text and exit
|
||||
|
||||
|
||||
.SH CLIENT OPTIONS
|
||||
.PP
|
||||
ttyd has a mechanism to pass server side command-line arguments to the browser page which is called \fBclient options\fP:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
-t, --client-option Send option to client (format: key=value), repeat to add more options
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH Basic usage
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB\fC-t rendererType=canvas\fR: use the \fB\fCcanvas\fR renderer for xterm.js (default: \fB\fCwebgl\fR)
|
||||
.IP \(bu 2
|
||||
\fB\fC-t disableLeaveAlert=true\fR: disable the leave page alert
|
||||
.IP \(bu 2
|
||||
\fB\fC-t disableResizeOverlay=true\fR: disable the terminal resize overlay
|
||||
.IP \(bu 2
|
||||
\fB\fC-t disableReconnect=true\fR: prevent the terminal from reconnecting on connection error/close
|
||||
.IP \(bu 2
|
||||
\fB\fC-t enableZmodem=true\fR: enable ZMODEM
|
||||
\[la]https://en.wikipedia.org/wiki/ZMODEM\[ra] / lrzsz
|
||||
\[la]https://ohse.de/uwe/software/lrzsz.html\[ra] file transfer support
|
||||
.IP \(bu 2
|
||||
\fB\fC-t enableTrzsz=true\fR: enable trzsz
|
||||
\[la]https://trzsz.github.io\[ra] file transfer support
|
||||
.IP \(bu 2
|
||||
\fB\fC-t enableSixel=true\fR: enable Sixel
|
||||
\[la]https://en.wikipedia.org/wiki/Sixel\[ra] image output support (Usage
|
||||
\[la]https://saitoha.github.io/libsixel/\[ra])
|
||||
.IP \(bu 2
|
||||
\fB\fC-t closeOnDisconnect=true\fR: close the terminal on disconnection, this will disable reconnect
|
||||
.IP \(bu 2
|
||||
\fB\fC-t titleFixed=hello\fR: set a fixed title for the browser window
|
||||
.IP \(bu 2
|
||||
\fB\fC-t fontSize=20\fR: change the font size of the terminal
|
||||
.IP \(bu 2
|
||||
\fB\fC-t unicodeVersion=11\fR: set xterm unicode support level (default: 11, use 6 to disable unicode addon)
|
||||
.IP \(bu 2
|
||||
\fB\fC-t trzszDragInitTimeout=3000\fR: set the timeout in milliseconds for initializing drag and drop files to upload. (default: 3000)
|
||||
|
||||
.RE
|
||||
|
||||
.SH Advanced usage
|
||||
.PP
|
||||
You can use the client option to change all the settings of xterm defined in ITerminalOptions
|
||||
\[la]https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/\[ra], examples:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB\fC-t cursorStyle=bar\fR: set cursor style to \fB\fCbar\fR
|
||||
.IP \(bu 2
|
||||
\fB\fC-t lineHeight=1.5\fR: set line-height to \fB\fC1.5\fR
|
||||
.IP \(bu 2
|
||||
\fB\fC-t 'theme={"background": "green"}'\fR: set background color to \fB\fCgreen\fR
|
||||
|
||||
.RE
|
||||
|
||||
.PP
|
||||
to try the example options above, run:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
ttyd -t cursorStyle=bar -t lineHeight=1.5 -t 'theme={"background": "green"}' bash
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
ttyd starts web server at port 7681 by default, you can use the -p option to change it, the command will be started with arguments as options. For example, run:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
ttyd -p 8080 bash -x
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.PP
|
||||
Then open http://localhost:8080 with a browser, you will get a bash shell with debug mode enabled. More examples:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
If you want to login with your system accounts on the web browser, run \fB\fCttyd login\fR\&.
|
||||
.IP \(bu 2
|
||||
You can even run a non-shell command like vim, try: \fB\fCttyd vim\fR, the web browser will show you a vim editor.
|
||||
.IP \(bu 2
|
||||
Sharing single process with multiple clients: \fB\fCttyd tmux new -A -s ttyd vim\fR, run \fB\fCtmux new -A -s ttyd\fR to connect to the tmux session from terminal.
|
||||
|
||||
.RE
|
||||
|
||||
|
||||
.SH SSL how-to
|
||||
.PP
|
||||
Generate SSL CA and self signed server/client certificates:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
# CA certificate (FQDN must be different from server/client)
|
||||
openssl genrsa -out ca.key 2048
|
||||
openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt
|
||||
|
||||
# server certificate (for multiple domains, change subjectAltName to: DNS:example.com,DNS:www.example.com)
|
||||
openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=localhost" -out server.csr
|
||||
openssl x509 -sha256 -req -extfile <(printf "subjectAltName=DNS:localhost") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
|
||||
|
||||
# client certificate (the p12/pem format may be useful for some clients)
|
||||
openssl req -newkey rsa:2048 -nodes -keyout client.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=client" -out client.csr
|
||||
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
|
||||
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
|
||||
openssl pkcs12 -in client.p12 -out client.pem -clcerts
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.PP
|
||||
Then start ttyd:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
ttyd --ssl --ssl-cert server.crt --ssl-key server.key --ssl-ca ca.crt bash
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.PP
|
||||
You may want to test the client certificate verification with \fIcurl\fP(1):
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
curl --insecure --cert client.p12[:password] -v https://localhost:7681
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.PP
|
||||
If you don't want to enable client certificate verification, remove the \fB\fC--ssl-ca\fR option.
|
||||
|
||||
|
||||
.SH Docker and ttyd
|
||||
.PP
|
||||
Docker containers are jailed environments which are more secure, this is useful for protecting the host system, you may use ttyd with docker like this:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Sharing single docker container with multiple clients: docker run -it --rm -p 7681:7681 tsl0922/ttyd.
|
||||
.IP \(bu 2
|
||||
Creating new docker container for each client: ttyd docker run -it --rm ubuntu.
|
||||
|
||||
.RE
|
||||
|
||||
|
||||
.SH Nginx reverse proxy
|
||||
.PP
|
||||
Sample config to proxy ttyd under the \fB\fC/ttyd\fR path:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
location ~ ^/ttyd(.*)$ {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://127.0.0.1:7681/$1;
|
||||
}
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
Shuanglei Tao <tsl0922@gmail.com> Visit https://github.com/tsl0922/ttyd to get more information and report bugs.
|
@@ -1,218 +0,0 @@
|
||||
ttyd 1 "September 2016" ttyd "User Manual"
|
||||
==================================================
|
||||
|
||||
# NAME
|
||||
ttyd - Share your terminal over the web
|
||||
|
||||
# SYNOPSIS
|
||||
**ttyd** [options] \<command\> [\<arguments...\>]
|
||||
|
||||
# DESCRIPTION
|
||||
ttyd is a command-line tool for sharing terminal over the web that runs in *nix and windows systems, with the following features:
|
||||
|
||||
- Built on top of Libwebsockets with libuv for speed
|
||||
- Fully-featured terminal based on Xterm.js with CJK (Chinese, Japanese, Korean) and IME support
|
||||
- Graphical ZMODEM integration with lrzsz support
|
||||
- Sixel image output support
|
||||
- SSL support based on OpenSSL
|
||||
- Run any custom command with options
|
||||
- Basic authentication support and many other custom options
|
||||
- Cross platform: macOS, Linux, FreeBSD/OpenBSD, OpenWrt/LEDE, Windows
|
||||
|
||||
# OPTIONS
|
||||
-p, --port <port>
|
||||
Port to listen (default: 7681, use `0` for random port)
|
||||
|
||||
-i, --interface <interface>
|
||||
Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)
|
||||
|
||||
-U, --socket-owner
|
||||
User owner of the UNIX domain socket file, when enabled (eg: user:group)
|
||||
|
||||
-c, --credential USER[:PASSWORD]
|
||||
Credential for Basic Authentication (format: username:password)
|
||||
|
||||
-H, --auth-header <name>
|
||||
HTTP Header name for auth proxy, this will configure ttyd to let a HTTP reverse proxy handle authentication
|
||||
|
||||
-u, --uid <uid>
|
||||
User id to run with
|
||||
|
||||
-g, --gid <gid>
|
||||
Group id to run with
|
||||
|
||||
-s, --signal <signal string>
|
||||
Signal to send to the command when exit it (default: 1, SIGHUP)
|
||||
|
||||
-w, --cwd <path>
|
||||
Working directory to be set for the child program
|
||||
|
||||
-a, --url-arg
|
||||
Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)
|
||||
|
||||
-W, --writable
|
||||
Allow clients to write to the TTY (readonly by default)
|
||||
|
||||
-t, --client-option <key=value>
|
||||
Send option to client (format: key=value), repeat to add more options, see **CLIENT OPTIONS** for details
|
||||
|
||||
-T, --terminal-type
|
||||
Terminal type to report, default: xterm-256color
|
||||
|
||||
-O, --check-origin
|
||||
Do not allow websocket connection from different origin
|
||||
|
||||
-m, --max-clients
|
||||
Maximum clients to support (default: 0, no limit)
|
||||
|
||||
-o, --once
|
||||
Accept only one client and exit on disconnection
|
||||
|
||||
-q, --exit-no-conn
|
||||
Exit on all clients disconnection
|
||||
|
||||
-B, --browser
|
||||
Open terminal with the default system browser
|
||||
|
||||
-I, --index <index file>
|
||||
Custom index.html path
|
||||
|
||||
-b, --base-path
|
||||
Expected base path for requests coming from a reverse proxy (eg: /mounted/here, max length: 128)
|
||||
|
||||
-P, --ping-interval
|
||||
Websocket ping interval(sec) (default: 5)
|
||||
|
||||
-f, --srv-buf-size
|
||||
Maximum chunk of file (in bytes) that can be sent at once, a larger value may improve throughput (default: 4096)
|
||||
|
||||
-6, --ipv6
|
||||
Enable IPv6 support
|
||||
|
||||
-S, --ssl
|
||||
Enable SSL
|
||||
|
||||
-C, --ssl-cert <cert path>
|
||||
SSL certificate file path
|
||||
|
||||
-K, --ssl-key <key path>
|
||||
SSL key file path
|
||||
|
||||
-A, --ssl-ca <ca path>
|
||||
SSL CA file path for client certificate verification
|
||||
|
||||
-d, --debug <level>
|
||||
Set log level (default: 7)
|
||||
|
||||
-v, --version
|
||||
Print the version and exit
|
||||
|
||||
-h, --help
|
||||
Print this text and exit
|
||||
|
||||
# CLIENT OPTIONS
|
||||
ttyd has a mechanism to pass server side command-line arguments to the browser page which is called **client options**:
|
||||
|
||||
```bash
|
||||
-t, --client-option Send option to client (format: key=value), repeat to add more options
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
- `-t rendererType=canvas`: use the `canvas` renderer for xterm.js (default: `webgl`)
|
||||
- `-t disableLeaveAlert=true`: disable the leave page alert
|
||||
- `-t disableResizeOverlay=true`: disable the terminal resize overlay
|
||||
- `-t disableReconnect=true`: prevent the terminal from reconnecting on connection error/close
|
||||
- `-t enableZmodem=true`: enable [ZMODEM](https://en.wikipedia.org/wiki/ZMODEM) / [lrzsz](https://ohse.de/uwe/software/lrzsz.html) file transfer support
|
||||
- `-t enableTrzsz=true`: enable [trzsz](https://trzsz.github.io) file transfer support
|
||||
- `-t enableSixel=true`: enable [Sixel](https://en.wikipedia.org/wiki/Sixel) image output support ([Usage](https://saitoha.github.io/libsixel/))
|
||||
- `-t closeOnDisconnect=true`: close the terminal on disconnection, this will disable reconnect
|
||||
- `-t titleFixed=hello`: set a fixed title for the browser window
|
||||
- `-t fontSize=20`: change the font size of the terminal
|
||||
- `-t unicodeVersion=11`: set xterm unicode support level (default: 11, use 6 to disable unicode addon)
|
||||
- `-t trzszDragInitTimeout=3000`: set the timeout in milliseconds for initializing drag and drop files to upload. (default: 3000)
|
||||
|
||||
## Advanced usage
|
||||
|
||||
You can use the client option to change all the settings of xterm defined in [ITerminalOptions](https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/), examples:
|
||||
|
||||
- `-t cursorStyle=bar`: set cursor style to `bar`
|
||||
- `-t lineHeight=1.5`: set line-height to `1.5`
|
||||
- `-t 'theme={"background": "green"}'`: set background color to `green`
|
||||
|
||||
to try the example options above, run:
|
||||
|
||||
```bash
|
||||
ttyd -t cursorStyle=bar -t lineHeight=1.5 -t 'theme={"background": "green"}' bash
|
||||
```
|
||||
|
||||
# EXAMPLES
|
||||
ttyd starts web server at port 7681 by default, you can use the -p option to change it, the command will be started with arguments as options. For example, run:
|
||||
|
||||
```
|
||||
ttyd -p 8080 bash -x
|
||||
```
|
||||
|
||||
Then open http://localhost:8080 with a browser, you will get a bash shell with debug mode enabled. More examples:
|
||||
|
||||
- If you want to login with your system accounts on the web browser, run `ttyd login`.
|
||||
- You can even run a non-shell command like vim, try: `ttyd vim`, the web browser will show you a vim editor.
|
||||
- Sharing single process with multiple clients: `ttyd tmux new -A -s ttyd vim`, run `tmux new -A -s ttyd` to connect to the tmux session from terminal.
|
||||
|
||||
# SSL how-to
|
||||
Generate SSL CA and self signed server/client certificates:
|
||||
|
||||
```
|
||||
# CA certificate (FQDN must be different from server/client)
|
||||
openssl genrsa -out ca.key 2048
|
||||
openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt
|
||||
|
||||
# server certificate (for multiple domains, change subjectAltName to: DNS:example.com,DNS:www.example.com)
|
||||
openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=localhost" -out server.csr
|
||||
openssl x509 -sha256 -req -extfile <(printf "subjectAltName=DNS:localhost") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
|
||||
|
||||
# client certificate (the p12/pem format may be useful for some clients)
|
||||
openssl req -newkey rsa:2048 -nodes -keyout client.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=client" -out client.csr
|
||||
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
|
||||
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
|
||||
openssl pkcs12 -in client.p12 -out client.pem -clcerts
|
||||
```
|
||||
|
||||
Then start ttyd:
|
||||
|
||||
```
|
||||
ttyd --ssl --ssl-cert server.crt --ssl-key server.key --ssl-ca ca.crt bash
|
||||
```
|
||||
|
||||
You may want to test the client certificate verification with *curl*(1):
|
||||
|
||||
```
|
||||
curl --insecure --cert client.p12[:password] -v https://localhost:7681
|
||||
```
|
||||
|
||||
If you don't want to enable client certificate verification, remove the `--ssl-ca` option.
|
||||
|
||||
# Docker and ttyd
|
||||
Docker containers are jailed environments which are more secure, this is useful for protecting the host system, you may use ttyd with docker like this:
|
||||
|
||||
- Sharing single docker container with multiple clients: docker run -it --rm -p 7681:7681 tsl0922/ttyd.
|
||||
- Creating new docker container for each client: ttyd docker run -it --rm ubuntu.
|
||||
|
||||
# Nginx reverse proxy
|
||||
|
||||
Sample config to proxy ttyd under the `/ttyd` path:
|
||||
|
||||
```nginx
|
||||
location ~ ^/ttyd(.*)$ {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://127.0.0.1:7681/$1;
|
||||
}
|
||||
```
|
||||
|
||||
# AUTHOR
|
||||
Shuanglei Tao \<tsl0922@gmail.com\> Visit https://github.com/tsl0922/ttyd to get more information and report bugs.
|
Binary file not shown.
Before Width: | Height: | Size: 125 KiB |
@@ -1,191 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Example:
|
||||
# env BUILD_TARGET=mips ./scripts/cross-build.sh
|
||||
#
|
||||
set -eo pipefail
|
||||
|
||||
CROSS_ROOT="${CROSS_ROOT:-/opt/cross}"
|
||||
STAGE_ROOT="${STAGE_ROOT:-/opt/stage}"
|
||||
BUILD_ROOT="${BUILD_ROOT:-/opt/build}"
|
||||
BUILD_TARGET="${BUILD_TARGET:-x86_64}"
|
||||
|
||||
ZLIB_VERSION="${ZLIB_VERSION:-1.3.1}"
|
||||
JSON_C_VERSION="${JSON_C_VERSION:-0.17}"
|
||||
MBEDTLS_VERSION="${MBEDTLS_VERSION:-2.28.5}"
|
||||
LIBUV_VERSION="${LIBUV_VERSION:-1.44.2}"
|
||||
LIBWEBSOCKETS_VERSION="${LIBWEBSOCKETS_VERSION:-4.3.3}"
|
||||
|
||||
build_zlib() {
|
||||
echo "=== Building zlib-${ZLIB_VERSION} (${TARGET})..."
|
||||
curl -fSsLo- "https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
|
||||
pushd "${BUILD_DIR}"/zlib-"${ZLIB_VERSION}"
|
||||
env CHOST="${TARGET}" ./configure --static --archs="-fPIC" --prefix="${STAGE_DIR}"
|
||||
make -j"$(nproc)" install
|
||||
popd
|
||||
}
|
||||
|
||||
build_json-c() {
|
||||
echo "=== Building json-c-${JSON_C_VERSION} (${TARGET})..."
|
||||
curl -fSsLo- "https://s3.amazonaws.com/json-c_releases/releases/json-c-${JSON_C_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
|
||||
pushd "${BUILD_DIR}/json-c-${JSON_C_VERSION}"
|
||||
rm -rf build && mkdir -p build && cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DDISABLE_THREAD_LOCAL_STORAGE=ON \
|
||||
..
|
||||
make -j"$(nproc)" install
|
||||
popd
|
||||
}
|
||||
|
||||
build_mbedtls() {
|
||||
echo "=== Building mbedtls-${MBEDTLS_VERSION} (${TARGET})..."
|
||||
curl -fSsLo- "https://github.com/ARMmbed/mbedtls/archive/v${MBEDTLS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
|
||||
pushd "${BUILD_DIR}/mbedtls-${MBEDTLS_VERSION}"
|
||||
rm -rf build && mkdir -p build && cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
|
||||
-DENABLE_TESTING=OFF \
|
||||
..
|
||||
make -j"$(nproc)" install
|
||||
popd
|
||||
}
|
||||
|
||||
build_libuv() {
|
||||
echo "=== Building libuv-${LIBUV_VERSION} (${TARGET})..."
|
||||
curl -fSsLo- "https://dist.libuv.org/dist/v${LIBUV_VERSION}/libuv-v${LIBUV_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
|
||||
pushd "${BUILD_DIR}/libuv-v${LIBUV_VERSION}"
|
||||
./autogen.sh
|
||||
env CFLAGS=-fPIC ./configure --disable-shared --enable-static --prefix="${STAGE_DIR}" --host="${TARGET}"
|
||||
make -j"$(nproc)" install
|
||||
popd
|
||||
}
|
||||
|
||||
install_cmake_cross_file() {
|
||||
cat << EOF > "${BUILD_DIR}/cross-${TARGET}.cmake"
|
||||
SET(CMAKE_SYSTEM_NAME $1)
|
||||
|
||||
set(CMAKE_C_COMPILER "${TARGET}-gcc")
|
||||
set(CMAKE_CXX_COMPILER "${TARGET}-g++")
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH "${STAGE_DIR}")
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
EOF
|
||||
}
|
||||
|
||||
build_libwebsockets() {
|
||||
echo "=== Building libwebsockets-${LIBWEBSOCKETS_VERSION} (${TARGET})..."
|
||||
curl -fSsLo- "https://github.com/warmcat/libwebsockets/archive/v${LIBWEBSOCKETS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
|
||||
pushd "${BUILD_DIR}/libwebsockets-${LIBWEBSOCKETS_VERSION}"
|
||||
sed -i 's/ websockets_shared//g' cmake/libwebsockets-config.cmake.in
|
||||
sed -i 's/ OR PC_OPENSSL_FOUND//g' lib/tls/CMakeLists.txt
|
||||
sed -i '/PC_OPENSSL/d' lib/tls/CMakeLists.txt
|
||||
rm -rf build && mkdir -p build && cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
|
||||
-DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-static" \
|
||||
-DLWS_WITHOUT_TESTAPPS=ON \
|
||||
-DLWS_WITH_MBEDTLS=ON \
|
||||
-DLWS_WITH_LIBUV=ON \
|
||||
-DLWS_STATIC_PIC=ON \
|
||||
-DLWS_WITH_SHARED=OFF \
|
||||
-DLWS_UNIX_SOCK=ON \
|
||||
-DLWS_IPV6=ON \
|
||||
-DLWS_ROLE_RAW_FILE=OFF \
|
||||
-DLWS_WITH_HTTP2=ON \
|
||||
-DLWS_WITH_HTTP_BASIC_AUTH=OFF \
|
||||
-DLWS_WITH_UDP=OFF \
|
||||
-DLWS_WITHOUT_CLIENT=ON \
|
||||
-DLWS_WITHOUT_EXTENSIONS=OFF \
|
||||
-DLWS_WITH_LEJP=OFF \
|
||||
-DLWS_WITH_LEJP_CONF=OFF \
|
||||
-DLWS_WITH_LWSAC=OFF \
|
||||
-DLWS_WITH_SEQUENCER=OFF \
|
||||
..
|
||||
make -j"$(nproc)" install
|
||||
popd
|
||||
}
|
||||
|
||||
build_ttyd() {
|
||||
echo "=== Building ttyd (${TARGET})..."
|
||||
rm -rf build && mkdir -p build && cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
|
||||
-DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
|
||||
-DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
|
||||
-DCMAKE_C_FLAGS="-Os -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -flto" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-static -no-pie -Wl,-s -Wl,-Bsymbolic -Wl,--gc-sections" \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
..
|
||||
make install
|
||||
}
|
||||
|
||||
build() {
|
||||
TARGET="$1"
|
||||
ALIAS="$2"
|
||||
STAGE_DIR="${STAGE_ROOT}/${TARGET}"
|
||||
BUILD_DIR="${BUILD_ROOT}/${TARGET}"
|
||||
MUSL_CC_URL="https://github.com/tsl0922/musl-toolchains/releases/download/2021-11-23"
|
||||
COMPONENTS="1"
|
||||
SYSTEM="Linux"
|
||||
|
||||
if [ "$ALIAS" = "win32" ]; then
|
||||
COMPONENTS=2
|
||||
SYSTEM="Windows"
|
||||
fi
|
||||
|
||||
echo "=== Installing toolchain ${ALIAS} (${TARGET})..."
|
||||
|
||||
mkdir -p "${CROSS_ROOT}" && export PATH="${PATH}:${CROSS_ROOT}/bin"
|
||||
curl -fSsLo- "${MUSL_CC_URL}/${TARGET}-cross.tgz" | tar xz -C "${CROSS_ROOT}" --strip-components=${COMPONENTS}
|
||||
|
||||
echo "=== Building target ${ALIAS} (${TARGET})..."
|
||||
|
||||
rm -rf "${STAGE_DIR}" "${BUILD_DIR}"
|
||||
mkdir -p "${STAGE_DIR}" "${BUILD_DIR}"
|
||||
export PKG_CONFIG_PATH="${STAGE_DIR}/lib/pkgconfig"
|
||||
|
||||
install_cmake_cross_file ${SYSTEM}
|
||||
|
||||
build_zlib
|
||||
build_json-c
|
||||
build_libuv
|
||||
build_mbedtls
|
||||
build_libwebsockets
|
||||
build_ttyd
|
||||
}
|
||||
|
||||
case ${BUILD_TARGET} in
|
||||
amd64) BUILD_TARGET="x86_64" ;;
|
||||
arm64) BUILD_TARGET="aarch64" ;;
|
||||
armv7) BUILD_TARGET="armv7l" ;;
|
||||
esac
|
||||
|
||||
case ${BUILD_TARGET} in
|
||||
i686|x86_64|aarch64|mips|mipsel|mips64|mips64el|s390x)
|
||||
build "${BUILD_TARGET}-linux-musl" "${BUILD_TARGET}"
|
||||
;;
|
||||
arm)
|
||||
build "${BUILD_TARGET}-linux-musleabi" "${BUILD_TARGET}"
|
||||
;;
|
||||
armhf)
|
||||
build arm-linux-musleabihf "${BUILD_TARGET}"
|
||||
;;
|
||||
armv7l)
|
||||
build armv7l-linux-musleabihf "${BUILD_TARGET}"
|
||||
;;
|
||||
win32)
|
||||
build x86_64-w64-mingw32 "${BUILD_TARGET}"
|
||||
;;
|
||||
*)
|
||||
echo "unknown cross target: ${BUILD_TARGET}" && exit 1
|
||||
esac
|
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
build_libwebsockets() {
|
||||
svn co https://github.com/msys2/MINGW-packages/trunk/mingw-w64-libwebsockets
|
||||
sed -i 's/openssl/mbedtls/' mingw-w64-libwebsockets/PKGBUILD
|
||||
sed -i '/-DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX}/a \ -DLWS_WITH_MBEDTLS=ON \\' mingw-w64-libwebsockets/PKGBUILD
|
||||
sed -i '/-DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX}/a \ -DLWS_WITH_LIBUV=ON \\' mingw-w64-libwebsockets/PKGBUILD
|
||||
pushd mingw-w64-libwebsockets
|
||||
makepkg-mingw --cleanbuild --syncdeps --force --noconfirm
|
||||
pacman -U *.pkg.tar.zst --noconfirm
|
||||
popd
|
||||
}
|
||||
|
||||
build_libwebsockets
|
||||
|
||||
# workaround for the lib name change
|
||||
cp ${MINGW_PREFIX}/lib/libuv_a.a ${MINGW_PREFIX}/lib/libuv.a
|
||||
|
||||
rm -rf build && mkdir -p build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
|
||||
-DCMAKE_C_FLAGS="-Os -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -flto" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-static -no-pie -Wl,-s -Wl,-Bsymbolic -Wl,--gc-sections" \
|
||||
..
|
||||
cmake --build .
|
@@ -1,49 +0,0 @@
|
||||
name: ttyd
|
||||
adopt-info: ttyd
|
||||
summary: Share your terminal over the web
|
||||
description: |
|
||||
ttyd is a simple command-line tool for sharing terminal over the web
|
||||
|
||||
grade: stable
|
||||
confinement: classic
|
||||
base: core20
|
||||
compression: lzo
|
||||
|
||||
license: MIT
|
||||
|
||||
assumes:
|
||||
- command-chain
|
||||
|
||||
apps:
|
||||
ttyd:
|
||||
command: usr/bin/ttyd
|
||||
command-chain:
|
||||
- bin/homeishome-launch
|
||||
|
||||
parts:
|
||||
ttyd:
|
||||
source: https://github.com/tsl0922/ttyd
|
||||
source-type: git
|
||||
plugin: cmake
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
build-environment:
|
||||
- LDFLAGS: "-pthread"
|
||||
|
||||
override-pull: |
|
||||
snapcraftctl pull
|
||||
snapcraftctl set-version "$(git describe --tags | sed 's/^v//' | cut -d "-" -f1)"
|
||||
|
||||
build-packages:
|
||||
- build-essential
|
||||
- libjson-c-dev
|
||||
- libwebsockets-dev
|
||||
|
||||
stage-packages:
|
||||
- libjson-c4
|
||||
- libwebsockets15
|
||||
|
||||
homeishome-launch:
|
||||
plugin: nil
|
||||
stage-snaps:
|
||||
- homeishome-launch
|
16301
ttyd/src/html.h
16301
ttyd/src/html.h
File diff suppressed because it is too large
Load Diff
240
ttyd/src/http.c
240
ttyd/src/http.c
@@ -1,240 +0,0 @@
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "html.h"
|
||||
#include "server.h"
|
||||
#include "utils.h"
|
||||
|
||||
enum { AUTH_OK, AUTH_FAIL, AUTH_ERROR };
|
||||
|
||||
static char *html_cache = NULL;
|
||||
static size_t html_cache_len = 0;
|
||||
|
||||
static int send_unauthorized(struct lws *wsi, unsigned int code, enum lws_token_indexes header) {
|
||||
unsigned char buffer[1024 + LWS_PRE], *p, *end;
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
if (lws_add_http_header_status(wsi, code, &p, end) ||
|
||||
lws_add_http_header_by_token(wsi, header, (unsigned char *)"Basic realm=\"ttyd\"", 18, &p, end) ||
|
||||
lws_add_http_header_content_length(wsi, 0, &p, end) || lws_finalize_http_header(wsi, &p, end) ||
|
||||
lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return AUTH_FAIL;
|
||||
|
||||
return lws_http_transaction_completed(wsi) ? AUTH_FAIL : AUTH_ERROR;
|
||||
}
|
||||
|
||||
static int check_auth(struct lws *wsi, struct pss_http *pss) {
|
||||
if (server->auth_header != NULL) {
|
||||
if (lws_hdr_custom_length(wsi, server->auth_header, strlen(server->auth_header)) > 0) return AUTH_OK;
|
||||
return send_unauthorized(wsi, HTTP_STATUS_PROXY_AUTH_REQUIRED, WSI_TOKEN_HTTP_PROXY_AUTHENTICATE);
|
||||
}
|
||||
|
||||
if(server->credential != NULL) {
|
||||
char buf[256];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
if (len >= 7 && strstr(buf, "Basic ")) {
|
||||
if (!strcmp(buf + 6, server->credential)) return AUTH_OK;
|
||||
}
|
||||
return send_unauthorized(wsi, HTTP_STATUS_UNAUTHORIZED, WSI_TOKEN_HTTP_WWW_AUTHENTICATE);
|
||||
}
|
||||
|
||||
return AUTH_OK;
|
||||
}
|
||||
|
||||
static bool accept_gzip(struct lws *wsi) {
|
||||
char buf[256];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
return len > 0 && strstr(buf, "gzip") != NULL;
|
||||
}
|
||||
|
||||
static bool uncompress_html(char **output, size_t *output_len) {
|
||||
if (html_cache == NULL || html_cache_len == 0) {
|
||||
z_stream stream;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
if (inflateInit2(&stream, 16 + 15) != Z_OK) return false;
|
||||
|
||||
html_cache_len = index_html_size;
|
||||
html_cache = xmalloc(html_cache_len);
|
||||
|
||||
stream.avail_in = index_html_len;
|
||||
stream.avail_out = html_cache_len;
|
||||
stream.next_in = (void *)index_html;
|
||||
stream.next_out = (void *)html_cache;
|
||||
|
||||
int ret = inflate(&stream, Z_SYNC_FLUSH);
|
||||
inflateEnd(&stream);
|
||||
if (ret != Z_STREAM_END) {
|
||||
free(html_cache);
|
||||
html_cache = NULL;
|
||||
html_cache_len = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*output = html_cache;
|
||||
*output_len = html_cache_len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pss_buffer_free(struct pss_http *pss) {
|
||||
if (pss->buffer != (char *)index_html && pss->buffer != html_cache) free(pss->buffer);
|
||||
}
|
||||
|
||||
static void access_log(struct lws *wsi, const char *path) {
|
||||
char rip[50];
|
||||
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), rip, sizeof(rip));
|
||||
lwsl_notice("HTTP %s - %s\n", path, rip);
|
||||
}
|
||||
|
||||
int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
||||
struct pss_http *pss = (struct pss_http *)user;
|
||||
unsigned char buffer[4096 + LWS_PRE], *p, *end;
|
||||
char buf[256];
|
||||
bool done = false;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_HTTP:
|
||||
access_log(wsi, (const char *)in);
|
||||
snprintf(pss->path, sizeof(pss->path), "%s", (const char *)in);
|
||||
switch (check_auth(wsi, pss)) {
|
||||
case AUTH_OK:
|
||||
break;
|
||||
case AUTH_FAIL:
|
||||
return 0;
|
||||
case AUTH_ERROR:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
if (strcmp(pss->path, endpoints.token) == 0) {
|
||||
const char *credential = server->credential != NULL ? server->credential : "";
|
||||
size_t n = sprintf(buf, "{\"token\": \"%s\"}", credential);
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end) ||
|
||||
lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *)"application/json;charset=utf-8", 30, &p, end) ||
|
||||
lws_add_http_header_content_length(wsi, (unsigned long)n, &p, end) ||
|
||||
lws_finalize_http_header(wsi, &p, end) ||
|
||||
lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
|
||||
pss->buffer = pss->ptr = strdup(buf);
|
||||
pss->len = n;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
// redirects `/base-path` to `/base-path/`
|
||||
if (strcmp(pss->path, endpoints.parent) == 0) {
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_FOUND, &p, end) ||
|
||||
lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, (unsigned char *)endpoints.index,
|
||||
(int)strlen(endpoints.index), &p, end) ||
|
||||
lws_add_http_header_content_length(wsi, 0, &p, end) || lws_finalize_http_header(wsi, &p, end) ||
|
||||
lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
if (strcmp(pss->path, endpoints.index) != 0) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
const char *content_type = "text/html";
|
||||
if (server->index != NULL) {
|
||||
int n = lws_serve_http_file(wsi, server->index, content_type, NULL, 0);
|
||||
if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) return 1;
|
||||
} else {
|
||||
char *output = (char *)index_html;
|
||||
size_t output_len = index_html_len;
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end) ||
|
||||
lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (const unsigned char *)content_type, 9, &p,
|
||||
end))
|
||||
return 1;
|
||||
#ifdef LWS_WITH_HTTP_STREAM_COMPRESSION
|
||||
if (!uncompress_html(&output, &output_len)) return 1;
|
||||
#else
|
||||
if (accept_gzip(wsi)) {
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING, (unsigned char *)"gzip", 4, &p, end))
|
||||
return 1;
|
||||
} else {
|
||||
if (!uncompress_html(&output, &output_len)) return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lws_add_http_header_content_length(wsi, (unsigned long)output_len, &p, end) ||
|
||||
lws_finalize_http_header(wsi, &p, end) ||
|
||||
lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
|
||||
pss->buffer = pss->ptr = output;
|
||||
pss->len = output_len;
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
||||
if (!pss->buffer || pss->len == 0) {
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
do {
|
||||
int n = sizeof(buffer) - LWS_PRE;
|
||||
int m = lws_get_peer_write_allowance(wsi);
|
||||
if (m == 0) {
|
||||
lws_callback_on_writable(wsi);
|
||||
return 0;
|
||||
} else if (m != -1 && m < n) {
|
||||
n = m;
|
||||
}
|
||||
if (pss->ptr + n > pss->buffer + pss->len) {
|
||||
n = (int)(pss->len - (pss->ptr - pss->buffer));
|
||||
done = true;
|
||||
}
|
||||
memcpy(buffer + LWS_PRE, pss->ptr, n);
|
||||
pss->ptr += n;
|
||||
if (lws_write_http(wsi, buffer + LWS_PRE, (size_t)n) < n) {
|
||||
pss_buffer_free(pss);
|
||||
return -1;
|
||||
}
|
||||
} while (!lws_send_pipe_choked(wsi) && !done);
|
||||
|
||||
if (!done && pss->ptr < pss->buffer + pss->len) {
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
pss_buffer_free(pss);
|
||||
goto try_to_reuse;
|
||||
|
||||
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
||||
goto try_to_reuse;
|
||||
#if (defined(LWS_OPENSSL_SUPPORT) || defined(LWS_WITH_TLS)) && !defined(LWS_WITH_MBEDTLS)
|
||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||
if (!len || (SSL_get_verify_result((SSL *)in) != X509_V_OK)) {
|
||||
int err = X509_STORE_CTX_get_error((X509_STORE_CTX *)user);
|
||||
int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX *)user);
|
||||
const char *msg = X509_verify_cert_error_string(err);
|
||||
lwsl_err("client certificate verification error: %s (%d), depth: %d\n", msg, err, depth);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
|
||||
try_to_reuse:
|
||||
if (lws_http_transaction_completed(wsi)) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
@@ -1,395 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <json.h>
|
||||
#include <libwebsockets.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pty.h"
|
||||
#include "server.h"
|
||||
#include "utils.h"
|
||||
|
||||
// initial message list
|
||||
static char initial_cmds[] = {SET_WINDOW_TITLE, SET_PREFERENCES};
|
||||
|
||||
static int send_initial_message(struct lws *wsi, int index) {
|
||||
unsigned char message[LWS_PRE + 1 + 4096];
|
||||
unsigned char *p = &message[LWS_PRE];
|
||||
char buffer[128];
|
||||
int n = 0;
|
||||
|
||||
char cmd = initial_cmds[index];
|
||||
switch (cmd) {
|
||||
case SET_WINDOW_TITLE:
|
||||
gethostname(buffer, sizeof(buffer) - 1);
|
||||
n = sprintf((char *)p, "%c%s (%s)", cmd, server->command, buffer);
|
||||
break;
|
||||
case SET_PREFERENCES:
|
||||
n = sprintf((char *)p, "%c%s", cmd, server->prefs_json);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY);
|
||||
}
|
||||
|
||||
static json_object *parse_window_size(const char *buf, size_t len, uint16_t *cols, uint16_t *rows) {
|
||||
json_tokener *tok = json_tokener_new();
|
||||
json_object *obj = json_tokener_parse_ex(tok, buf, len);
|
||||
struct json_object *o = NULL;
|
||||
|
||||
if (json_object_object_get_ex(obj, "columns", &o)) *cols = (uint16_t)json_object_get_int(o);
|
||||
if (json_object_object_get_ex(obj, "rows", &o)) *rows = (uint16_t)json_object_get_int(o);
|
||||
|
||||
json_tokener_free(tok);
|
||||
return obj;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const char *prot, *address, *path;
|
||||
int port;
|
||||
if (lws_parse_uri(buf, &prot, &address, &port, &path)) return false;
|
||||
if (port == 80 || port == 443) {
|
||||
sprintf(buf, "%s", address);
|
||||
} else {
|
||||
sprintf(buf, "%s:%d", address, port);
|
||||
}
|
||||
|
||||
char host_buf[256];
|
||||
memset(host_buf, 0, sizeof(host_buf));
|
||||
len = lws_hdr_copy(wsi, host_buf, (int)sizeof(host_buf), WSI_TOKEN_HOST);
|
||||
|
||||
return len > 0 && strcasecmp(buf, host_buf) == 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(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) {
|
||||
int i, n = 0;
|
||||
char **argv = xmalloc((server->argc + pss->argc + 1) * sizeof(char *));
|
||||
|
||||
for (i = 0; i < server->argc; i++) {
|
||||
argv[n++] = server->argv[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < pss->argc; i++) {
|
||||
argv[n++] = pss->args[i];
|
||||
}
|
||||
|
||||
argv[n] = NULL;
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
static char **build_env(struct pss_tty *pss) {
|
||||
int i = 0, n = 2;
|
||||
char **envp = xmalloc(n * sizeof(char *));
|
||||
|
||||
// TERM
|
||||
envp[i] = xmalloc(36);
|
||||
snprintf(envp[i], 36, "TERM=%s", server->terminal_type);
|
||||
i++;
|
||||
|
||||
// TTYD_USER
|
||||
if (strlen(pss->user) > 0) {
|
||||
envp = xrealloc(envp, (++n) * sizeof(char *));
|
||||
envp[i] = xmalloc(40);
|
||||
snprintf(envp[i], 40, "TTYD_USER=%s", pss->user);
|
||||
i++;
|
||||
}
|
||||
|
||||
envp[i] = NULL;
|
||||
|
||||
return envp;
|
||||
}
|
||||
|
||||
static bool spawn_process(struct pss_tty *pss, uint16_t columns, uint16_t rows) {
|
||||
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;
|
||||
if (pty_spawn(process, process_read_cb, process_exit_cb) != 0) {
|
||||
lwsl_err("pty_spawn: %d (%s)\n", errno, strerror(errno));
|
||||
process_free(process);
|
||||
return false;
|
||||
}
|
||||
lwsl_notice("started process, pid: %d\n", process->pid);
|
||||
pss->process = process;
|
||||
lws_callback_on_writable(pss->wsi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void wsi_output(struct lws *wsi, pty_buf_t *buf) {
|
||||
if (buf == NULL) return;
|
||||
char *message = xmalloc(LWS_PRE + 1 + buf->len);
|
||||
char *ptr = message + LWS_PRE;
|
||||
|
||||
*ptr = OUTPUT;
|
||||
memcpy(ptr + 1, buf->base, buf->len);
|
||||
size_t n = buf->len + 1;
|
||||
|
||||
if (lws_write(wsi, (unsigned char *)ptr, n, LWS_WRITE_BINARY) < n) {
|
||||
lwsl_err("write OUTPUT to WS\n");
|
||||
}
|
||||
|
||||
free(message);
|
||||
}
|
||||
|
||||
static bool check_auth(struct lws *wsi, struct pss_tty *pss) {
|
||||
if (server->auth_header != NULL) {
|
||||
return lws_hdr_custom_copy(wsi, pss->user, sizeof(pss->user), server->auth_header, strlen(server->auth_header)) > 0;
|
||||
}
|
||||
|
||||
if (server->credential != NULL) {
|
||||
char buf[256];
|
||||
size_t n = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
return n >= 7 && strstr(buf, "Basic ") && !strcmp(buf + 6, server->credential);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
||||
struct pss_tty *pss = (struct pss_tty *)user;
|
||||
char buf[256];
|
||||
size_t n = 0;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
||||
if (server->once && server->client_count > 0) {
|
||||
lwsl_warn("refuse to serve WS client due to the --once option.\n");
|
||||
return 1;
|
||||
}
|
||||
if (server->max_clients > 0 && server->client_count == server->max_clients) {
|
||||
lwsl_warn("refuse to serve WS client due to the --max-clients option.\n");
|
||||
return 1;
|
||||
}
|
||||
if (!check_auth(wsi, pss)) return 1;
|
||||
|
||||
n = lws_hdr_copy(wsi, pss->path, sizeof(pss->path), WSI_TOKEN_GET_URI);
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (n <= 0) n = lws_hdr_copy(wsi, pss->path, sizeof(pss->path), WSI_TOKEN_HTTP_COLON_PATH);
|
||||
#endif
|
||||
if (strncmp(pss->path, endpoints.ws, n) != 0) {
|
||||
lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", pss->path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (server->check_origin && !check_host_origin(wsi)) {
|
||||
lwsl_warn(
|
||||
"refuse to serve WS client from different origin due to the "
|
||||
"--check-origin option.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->initialized = false;
|
||||
pss->authenticated = false;
|
||||
pss->wsi = wsi;
|
||||
pss->lws_close_status = LWS_CLOSE_STATUS_NOSTATUS;
|
||||
|
||||
if (server->url_arg) {
|
||||
while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) {
|
||||
if (strncmp(buf, "arg=", 4) == 0) {
|
||||
pss->args = xrealloc(pss->args, (pss->argc + 1) * sizeof(char *));
|
||||
pss->args[pss->argc] = strdup(&buf[4]);
|
||||
pss->argc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server->client_count++;
|
||||
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), pss->address, sizeof(pss->address));
|
||||
lwsl_notice("WS %s - %s, clients: %d\n", pss->path, pss->address, server->client_count);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
if (!pss->initialized) {
|
||||
if (pss->initial_cmd_index == sizeof(initial_cmds)) {
|
||||
pss->initialized = true;
|
||||
pty_resume(pss->process);
|
||||
break;
|
||||
}
|
||||
if (send_initial_message(wsi, pss->initial_cmd_index) < 0) {
|
||||
lwsl_err("failed to send initial message, index: %d\n", pss->initial_cmd_index);
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
pss->initial_cmd_index++;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pss->lws_close_status > LWS_CLOSE_STATUS_NOSTATUS) {
|
||||
lws_close_reason(wsi, pss->lws_close_status, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pss->pty_buf != NULL) {
|
||||
wsi_output(wsi, pss->pty_buf);
|
||||
pty_buf_free(pss->pty_buf);
|
||||
pss->pty_buf = NULL;
|
||||
pty_resume(pss->process);
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
if (pss->buffer == NULL) {
|
||||
pss->buffer = xmalloc(len);
|
||||
pss->len = len;
|
||||
memcpy(pss->buffer, in, len);
|
||||
} else {
|
||||
pss->buffer = xrealloc(pss->buffer, pss->len + len);
|
||||
memcpy(pss->buffer + pss->len, in, len);
|
||||
pss->len += len;
|
||||
}
|
||||
|
||||
const char command = pss->buffer[0];
|
||||
|
||||
// check auth
|
||||
if (server->credential != NULL && !pss->authenticated && command != JSON_DATA) {
|
||||
lwsl_warn("WS client not authenticated\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if there are more fragmented messages
|
||||
if (lws_remaining_packet_payload(wsi) > 0 || !lws_is_final_fragment(wsi)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case INPUT:
|
||||
if (!server->writable) break;
|
||||
int err = pty_write(pss->process, pty_buf_init(pss->buffer + 1, pss->len - 1));
|
||||
if (err) {
|
||||
lwsl_err("uv_write: %s (%s)\n", uv_err_name(err), uv_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case RESIZE_TERMINAL:
|
||||
if (pss->process == NULL) break;
|
||||
json_object_put(
|
||||
parse_window_size(pss->buffer + 1, pss->len - 1, &pss->process->columns, &pss->process->rows));
|
||||
pty_resize(pss->process);
|
||||
break;
|
||||
case PAUSE:
|
||||
pty_pause(pss->process);
|
||||
break;
|
||||
case RESUME:
|
||||
pty_resume(pss->process);
|
||||
break;
|
||||
case JSON_DATA:
|
||||
if (pss->process != NULL) break;
|
||||
uint16_t columns = 0;
|
||||
uint16_t rows = 0;
|
||||
json_object *obj = parse_window_size(pss->buffer, pss->len, &columns, &rows);
|
||||
if (server->credential != NULL) {
|
||||
struct json_object *o = NULL;
|
||||
if (json_object_object_get_ex(obj, "AuthToken", &o)) {
|
||||
const char *token = json_object_get_string(o);
|
||||
if (token != NULL && !strcmp(token, server->credential))
|
||||
pss->authenticated = true;
|
||||
else
|
||||
lwsl_warn("WS authentication failed with token: %s\n", token);
|
||||
}
|
||||
if (!pss->authenticated) {
|
||||
json_object_put(obj);
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
json_object_put(obj);
|
||||
if (!spawn_process(pss, columns, rows)) return 1;
|
||||
break;
|
||||
default:
|
||||
lwsl_warn("ignored unknown message type: %c\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pss->buffer != NULL) {
|
||||
free(pss->buffer);
|
||||
pss->buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
if (pss->wsi == NULL) break;
|
||||
|
||||
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->pty_buf != NULL) pty_buf_free(pss->pty_buf);
|
||||
for (int i = 0; i < pss->argc; i++) {
|
||||
free(pss->args[i]);
|
||||
}
|
||||
|
||||
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->exit_no_conn) && server->client_count == 0) {
|
||||
lwsl_notice("exiting due to the --once/--exit-no-conn option.\n");
|
||||
force_exit = true;
|
||||
lws_cancel_service(context);
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
485
ttyd/src/pty.c
485
ttyd/src/pty.c
@@ -1,485 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__APPLE__)
|
||||
#include <util.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <libutil.h>
|
||||
#else
|
||||
#include <pty.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "pty.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
HRESULT (WINAPI *pCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON *);
|
||||
HRESULT (WINAPI *pResizePseudoConsole)(HPCON, COORD);
|
||||
void (WINAPI *pClosePseudoConsole)(HPCON);
|
||||
#endif
|
||||
|
||||
static void alloc_cb(uv_handle_t *unused, size_t suggested_size, uv_buf_t *buf) {
|
||||
buf->base = xmalloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
static void close_cb(uv_handle_t *handle) { free(handle); }
|
||||
|
||||
static void async_free_cb(uv_handle_t *handle) {
|
||||
free((uv_async_t *) handle -> data);
|
||||
}
|
||||
|
||||
pty_buf_t *pty_buf_init(char *base, size_t len) {
|
||||
pty_buf_t *buf = xmalloc(sizeof(pty_buf_t));
|
||||
buf->base = xmalloc(len);
|
||||
memcpy(buf->base, base, len);
|
||||
buf->len = len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void pty_buf_free(pty_buf_t *buf) {
|
||||
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_process *process = (pty_process *) stream->data;
|
||||
if (n <= 0) {
|
||||
if (n == UV_ENOBUFS || n == 0) return;
|
||||
process->read_cb(process, NULL, true);
|
||||
goto done;
|
||||
}
|
||||
process->read_cb(process, pty_buf_init(buf->base, (size_t) n), false);
|
||||
|
||||
done:
|
||||
free(buf->base);
|
||||
}
|
||||
|
||||
static void write_cb(uv_write_t *req, int unused) {
|
||||
pty_buf_t *buf = (pty_buf_t *) req->data;
|
||||
pty_buf_free(buf);
|
||||
free(req);
|
||||
}
|
||||
|
||||
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));
|
||||
process->ctx = ctx;
|
||||
process->loop = loop;
|
||||
process->argv = argv;
|
||||
process->envp = envp;
|
||||
process->columns = 80;
|
||||
process->rows = 24;
|
||||
process->exit_code = -1;
|
||||
return process;
|
||||
}
|
||||
|
||||
bool process_running(pty_process *process) {
|
||||
return process != NULL && process->pid > 0 && uv_kill(process->pid, 0) == 0;
|
||||
}
|
||||
|
||||
void process_free(pty_process *process) {
|
||||
if (process == NULL) return;
|
||||
#ifdef _WIN32
|
||||
if (process->si.lpAttributeList != NULL) {
|
||||
DeleteProcThreadAttributeList(process->si.lpAttributeList);
|
||||
free(process->si.lpAttributeList);
|
||||
}
|
||||
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->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;
|
||||
for (; *p; p++) free(*p);
|
||||
free(process->envp);
|
||||
}
|
||||
|
||||
void pty_pause(pty_process *process) {
|
||||
if (process == NULL) return;
|
||||
if (process->paused) return;
|
||||
uv_read_stop((uv_stream_t *) process->out);
|
||||
}
|
||||
|
||||
void pty_resume(pty_process *process) {
|
||||
if (process == NULL) return;
|
||||
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) {
|
||||
if (process == NULL) {
|
||||
pty_buf_free(buf);
|
||||
return UV_ESRCH;
|
||||
}
|
||||
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 *) process->in, &b, 1, write_cb);
|
||||
}
|
||||
|
||||
bool pty_resize(pty_process *process) {
|
||||
if (process == NULL) return false;
|
||||
if (process->columns <= 0 || process->rows <= 0) return false;
|
||||
#ifdef _WIN32
|
||||
COORD size = {(int16_t) process->columns, (int16_t) process->rows};
|
||||
return pResizePseudoConsole(process->pty, size) == S_OK;
|
||||
#else
|
||||
struct winsize size = {process->rows, process->columns, 0, 0};
|
||||
return ioctl(process->pty, TIOCSWINSZ, &size) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool pty_kill(pty_process *process, int sig) {
|
||||
if (process == NULL) return false;
|
||||
#ifdef _WIN32
|
||||
return TerminateProcess(process->handle, 1) != 0;
|
||||
#else
|
||||
return uv_kill(-process->pid, sig) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool conpty_init() {
|
||||
uv_lib_t kernel;
|
||||
if (uv_dlopen("kernel32.dll", &kernel)) {
|
||||
uv_dlclose(&kernel);
|
||||
return false;
|
||||
}
|
||||
static struct {
|
||||
char *name;
|
||||
FARPROC *ptr;
|
||||
} conpty_entry[] = {{"CreatePseudoConsole", (FARPROC *) &pCreatePseudoConsole},
|
||||
{"ResizePseudoConsole", (FARPROC *) &pResizePseudoConsole},
|
||||
{"ClosePseudoConsole", (FARPROC *) &pClosePseudoConsole},
|
||||
{NULL, NULL}};
|
||||
for (int i = 0; conpty_entry[i].name != NULL && conpty_entry[i].ptr != NULL; i++) {
|
||||
if (uv_dlsym(&kernel, conpty_entry[i].name, (void **) conpty_entry[i].ptr)) {
|
||||
uv_dlclose(&kernel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static WCHAR *to_utf16(char *str) {
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
if (len <= 0) return NULL;
|
||||
WCHAR *wstr = xmalloc((len + 1) * sizeof(WCHAR));
|
||||
if (len != MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, len)) {
|
||||
free(wstr);
|
||||
return NULL;
|
||||
}
|
||||
wstr[len] = L'\0';
|
||||
return wstr;
|
||||
}
|
||||
|
||||
// convert argv to cmdline for CreateProcessW
|
||||
static WCHAR *join_args(char **argv) {
|
||||
char args[256] = {0};
|
||||
char **ptr = argv;
|
||||
for (; *ptr; ptr++) {
|
||||
char *quoted = (char *) quote_arg(*ptr);
|
||||
size_t arg_len = strlen(args) + 1;
|
||||
size_t quoted_len = strlen(quoted);
|
||||
if (arg_len == 1) memset(args, 0, 2);
|
||||
if (arg_len != 1) strcat(args, " ");
|
||||
strncat(args, quoted, quoted_len);
|
||||
if (quoted != *ptr) free(quoted);
|
||||
}
|
||||
if (args[255] != '\0') args[255] = '\0'; // truncate
|
||||
return to_utf16(args);
|
||||
}
|
||||
|
||||
static bool conpty_setup(HPCON *hnd, COORD size, STARTUPINFOEXW *si_ex, char **in_name, char **out_name) {
|
||||
static int count = 0;
|
||||
char buf[256];
|
||||
HPCON pty = INVALID_HANDLE_VALUE;
|
||||
SECURITY_ATTRIBUTES sa = {0};
|
||||
HANDLE in_pipe = INVALID_HANDLE_VALUE;
|
||||
HANDLE out_pipe = INVALID_HANDLE_VALUE;
|
||||
const DWORD open_mode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
|
||||
const DWORD pipe_mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
|
||||
DWORD pid = GetCurrentProcessId();
|
||||
bool ret = false;
|
||||
|
||||
sa.nLength = sizeof(sa);
|
||||
snprintf(buf, sizeof(buf), "\\\\.\\pipe\\ttyd-term-in-%d-%d", pid, count);
|
||||
*in_name = strdup(buf);
|
||||
snprintf(buf, sizeof(buf), "\\\\.\\pipe\\ttyd-term-out-%d-%d", pid, count);
|
||||
*out_name = strdup(buf);
|
||||
in_pipe = CreateNamedPipeA(*in_name, open_mode, pipe_mode, 1, 0, 0, 30000, &sa);
|
||||
out_pipe = CreateNamedPipeA(*out_name, open_mode, pipe_mode, 1, 0, 0, 30000, &sa);
|
||||
if (in_pipe == INVALID_HANDLE_VALUE || out_pipe == INVALID_HANDLE_VALUE) {
|
||||
print_error("CreateNamedPipeA");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
HRESULT hr = pCreatePseudoConsole(size, in_pipe, out_pipe, 0, &pty);
|
||||
if (FAILED(hr)) {
|
||||
print_error("CreatePseudoConsole");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
si_ex->StartupInfo.cb = sizeof(STARTUPINFOEXW);
|
||||
si_ex->StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
si_ex->StartupInfo.hStdError = NULL;
|
||||
si_ex->StartupInfo.hStdInput = NULL;
|
||||
si_ex->StartupInfo.hStdOutput = NULL;
|
||||
size_t bytes_required;
|
||||
InitializeProcThreadAttributeList(NULL, 1, 0, &bytes_required);
|
||||
si_ex->lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) xmalloc(bytes_required);
|
||||
if (!InitializeProcThreadAttributeList(si_ex->lpAttributeList, 1, 0, &bytes_required)) {
|
||||
print_error("InitializeProcThreadAttributeList");
|
||||
goto failed;
|
||||
}
|
||||
if (!UpdateProcThreadAttribute(si_ex->lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, pty, sizeof(HPCON),
|
||||
NULL, NULL)) {
|
||||
print_error("UpdateProcThreadAttribute");
|
||||
goto failed;
|
||||
}
|
||||
count++;
|
||||
*hnd = pty;
|
||||
ret = true;
|
||||
goto done;
|
||||
|
||||
failed:
|
||||
ret = false;
|
||||
free(*in_name);
|
||||
*in_name = NULL;
|
||||
free(*out_name);
|
||||
*out_name = NULL;
|
||||
done:
|
||||
if (in_pipe != INVALID_HANDLE_VALUE) CloseHandle(in_pipe);
|
||||
if (out_pipe != INVALID_HANDLE_VALUE) CloseHandle(out_pipe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void connect_cb(uv_connect_t *req, int status) { free(req); }
|
||||
|
||||
static void CALLBACK conpty_exit(void *context, BOOLEAN unused) {
|
||||
pty_process *process = (pty_process *) context;
|
||||
uv_async_send(&process->async);
|
||||
}
|
||||
|
||||
static void async_cb(uv_async_t *async) {
|
||||
pty_process *process = (pty_process *) async->data;
|
||||
UnregisterWait(process->wait);
|
||||
|
||||
DWORD exit_code;
|
||||
GetExitCodeProcess(process->handle, &exit_code);
|
||||
process->exit_code = (int) exit_code;
|
||||
process->exit_signal = 1;
|
||||
process->exit_cb(process);
|
||||
|
||||
uv_close((uv_handle_t *) async, async_free_cb);
|
||||
process_free(process);
|
||||
}
|
||||
|
||||
int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb) {
|
||||
char *in_name = NULL;
|
||||
char *out_name = NULL;
|
||||
DWORD flags = EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT;
|
||||
COORD size = {(int16_t) process->columns, (int16_t) process->rows};
|
||||
|
||||
if (!conpty_setup(&process->pty, size, &process->si, &in_name, &out_name)) return 1;
|
||||
|
||||
SetConsoleCtrlHandler(NULL, FALSE);
|
||||
|
||||
int status = 1;
|
||||
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, process->in, in_name, connect_cb);
|
||||
uv_pipe_connect(out_req, process->out, out_name, connect_cb);
|
||||
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
WCHAR *cmdline, *cwd;
|
||||
cmdline = join_args(process->argv);
|
||||
if (cmdline == NULL) goto cleanup;
|
||||
if (process->envp != NULL) {
|
||||
char **p = process->envp;
|
||||
for (; *p; p++) {
|
||||
WCHAR *env = to_utf16(*p);
|
||||
if (env == NULL) goto cleanup;
|
||||
_wputenv(env);
|
||||
free(env);
|
||||
}
|
||||
}
|
||||
if (process->cwd != NULL) {
|
||||
cwd = to_utf16(process->cwd);
|
||||
if (cwd == NULL) goto cleanup;
|
||||
}
|
||||
|
||||
if (!CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, flags, NULL, cwd, &process->si.StartupInfo, &pi)) {
|
||||
print_error("CreateProcessW");
|
||||
DWORD exitCode = 0;
|
||||
if (GetExitCodeProcess(pi.hProcess, &exitCode)) printf("== exit code: %d\n", exitCode);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
process->pid = pi.dwProcessId;
|
||||
process->handle = pi.hProcess;
|
||||
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");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
|
||||
cleanup:
|
||||
if (in_name != NULL) free(in_name);
|
||||
if (out_name != NULL) free(out_name);
|
||||
if (cmdline != NULL) free(cmdline);
|
||||
if (cwd != NULL) free(cwd);
|
||||
return status;
|
||||
}
|
||||
#else
|
||||
static bool fd_set_cloexec(const int fd) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags < 0) return false;
|
||||
return (flags & FD_CLOEXEC) == 0 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1;
|
||||
}
|
||||
|
||||
static bool fd_duplicate(int fd, uv_pipe_t *pipe) {
|
||||
int fd_dup = dup(fd);
|
||||
if (fd_dup < 0) return false;
|
||||
|
||||
if (!fd_set_cloexec(fd_dup)) return false;
|
||||
|
||||
int status = uv_pipe_open(pipe, fd_dup);
|
||||
if (status) close(fd_dup);
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
static void wait_cb(void *arg) {
|
||||
pty_process *process = (pty_process *) arg;
|
||||
|
||||
pid_t pid;
|
||||
int stat;
|
||||
do
|
||||
pid = waitpid(process->pid, &stat, 0);
|
||||
while (pid != process->pid && errno == EINTR);
|
||||
|
||||
if (WIFEXITED(stat)) {
|
||||
process->exit_code = WEXITSTATUS(stat);
|
||||
}
|
||||
if (WIFSIGNALED(stat)) {
|
||||
int sig = WTERMSIG(stat);
|
||||
process->exit_code = 128 + sig;
|
||||
process->exit_signal = sig;
|
||||
}
|
||||
|
||||
uv_async_send(&process->async);
|
||||
}
|
||||
|
||||
static void async_cb(uv_async_t *async) {
|
||||
pty_process *process = (pty_process *) async->data;
|
||||
process->exit_cb(process);
|
||||
|
||||
uv_close((uv_handle_t *) async, async_free_cb);
|
||||
process_free(process);
|
||||
}
|
||||
|
||||
int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb) {
|
||||
int status = 0;
|
||||
|
||||
uv_disable_stdio_inheritance();
|
||||
|
||||
int master, pid;
|
||||
struct winsize size = {process->rows, process->columns, 0, 0};
|
||||
pid = forkpty(&master, NULL, NULL, &size);
|
||||
if (pid < 0) {
|
||||
status = -errno;
|
||||
return status;
|
||||
} else if (pid == 0) {
|
||||
setsid();
|
||||
if (process->cwd != NULL) chdir(process->cwd);
|
||||
if (process->envp != NULL) {
|
||||
char **p = process->envp;
|
||||
for (; *p; p++) putenv(*p);
|
||||
}
|
||||
int ret = execvp(process->argv[0], process->argv);
|
||||
if (ret < 0) {
|
||||
perror("execvp failed\n");
|
||||
_exit(-errno);
|
||||
}
|
||||
}
|
||||
|
||||
int flags = fcntl(master, F_GETFL);
|
||||
if (flags == -1) {
|
||||
status = -errno;
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(master, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
status = -errno;
|
||||
goto error;
|
||||
}
|
||||
if (!fd_set_cloexec(master)) {
|
||||
status = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
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;
|
||||
goto error;
|
||||
}
|
||||
|
||||
process->pty = master;
|
||||
process->pid = pid;
|
||||
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);
|
||||
uv_thread_create(&process->tid, wait_cb, process);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
close(master);
|
||||
uv_kill(pid, SIGKILL);
|
||||
waitpid(pid, NULL, 0);
|
||||
return status;
|
||||
}
|
||||
#endif
|
@@ -1,68 +0,0 @@
|
||||
#ifndef TTYD_PTY_H
|
||||
#define TTYD_PTY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <uv.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef HPCON
|
||||
#define HPCON VOID *
|
||||
#endif
|
||||
#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
|
||||
#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
|
||||
#endif
|
||||
|
||||
bool conpty_init();
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char *base;
|
||||
size_t len;
|
||||
} pty_buf_t;
|
||||
|
||||
struct pty_process_;
|
||||
typedef struct pty_process_ 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;
|
||||
#ifdef _WIN32
|
||||
STARTUPINFOEXW si;
|
||||
HPCON pty;
|
||||
HANDLE handle;
|
||||
HANDLE wait;
|
||||
#else
|
||||
pid_t pty;
|
||||
uv_thread_t tid;
|
||||
#endif
|
||||
char **argv;
|
||||
char **envp;
|
||||
char *cwd;
|
||||
|
||||
uv_loop_t *loop;
|
||||
uv_async_t async;
|
||||
uv_pipe_t *in;
|
||||
uv_pipe_t *out;
|
||||
bool paused;
|
||||
|
||||
pty_read_cb read_cb;
|
||||
pty_exit_cb exit_cb;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
pty_buf_t *pty_buf_init(char *base, size_t len);
|
||||
void pty_buf_free(pty_buf_t *buf);
|
||||
pty_process *process_init(void *ctx, uv_loop_t *loop, char *argv[], char *envp[]);
|
||||
bool process_running(pty_process *process);
|
||||
void process_free(pty_process *process);
|
||||
int pty_spawn(pty_process *process, pty_read_cb read_cb, pty_exit_cb exit_cb);
|
||||
void pty_pause(pty_process *process);
|
||||
void pty_resume(pty_process *process);
|
||||
int pty_write(pty_process *process, pty_buf_t *buf);
|
||||
bool pty_resize(pty_process *process);
|
||||
bool pty_kill(pty_process *process, int sig);
|
||||
|
||||
#endif // TTYD_PTY_H
|
@@ -1,634 +0,0 @@
|
||||
#include "server.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <json.h>
|
||||
#include <libwebsockets.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef TTYD_VERSION
|
||||
#define TTYD_VERSION "unknown"
|
||||
#endif
|
||||
|
||||
volatile bool force_exit = false;
|
||||
struct lws_context *context;
|
||||
struct server *server;
|
||||
struct endpoints endpoints = {"/ws", "/", "/token", ""};
|
||||
|
||||
extern int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||
extern int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||
|
||||
// websocket protocols
|
||||
static const struct lws_protocols protocols[] = {{"http-only", callback_http, sizeof(struct pss_http), 0},
|
||||
{"tty", callback_tty, sizeof(struct pss_tty), 0},
|
||||
{NULL, NULL, 0, 0}};
|
||||
|
||||
#ifndef LWS_WITHOUT_EXTENSIONS
|
||||
// websocket extensions
|
||||
static const struct lws_extension extensions[] = {
|
||||
{"permessage-deflate", lws_extension_callback_pm_deflate, "permessage-deflate"},
|
||||
{"deflate-frame", lws_extension_callback_pm_deflate, "deflate_frame"},
|
||||
{NULL, NULL, NULL}};
|
||||
#endif
|
||||
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 4000000
|
||||
static const uint32_t backoff_ms[] = {1000, 2000, 3000, 4000, 5000};
|
||||
static lws_retry_bo_t retry = {
|
||||
.retry_ms_table = backoff_ms,
|
||||
.retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms),
|
||||
.conceal_count = LWS_ARRAY_SIZE(backoff_ms),
|
||||
.secs_since_valid_ping = 5,
|
||||
.secs_since_valid_hangup = 10,
|
||||
.jitter_percent = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
// command line options
|
||||
static const struct option options[] = {{"port", required_argument, NULL, 'p'},
|
||||
{"interface", required_argument, NULL, 'i'},
|
||||
{"socket-owner", required_argument, NULL, 'U'},
|
||||
{"credential", required_argument, NULL, 'c'},
|
||||
{"auth-header", required_argument, NULL, 'H'},
|
||||
{"uid", required_argument, NULL, 'u'},
|
||||
{"gid", required_argument, NULL, 'g'},
|
||||
{"signal", required_argument, NULL, 's'},
|
||||
{"cwd", required_argument, NULL, 'w'},
|
||||
{"index", required_argument, NULL, 'I'},
|
||||
{"base-path", required_argument, NULL, 'b'},
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 4000000
|
||||
{"ping-interval", required_argument, NULL, 'P'},
|
||||
#endif
|
||||
{"srv-buf-size", required_argument, NULL, 'f'},
|
||||
{"ipv6", no_argument, NULL, '6'},
|
||||
{"ssl", no_argument, NULL, 'S'},
|
||||
{"ssl-cert", required_argument, NULL, 'C'},
|
||||
{"ssl-key", required_argument, NULL, 'K'},
|
||||
{"ssl-ca", required_argument, NULL, 'A'},
|
||||
{"url-arg", no_argument, NULL, 'a'},
|
||||
{"writable", no_argument, NULL, 'W'},
|
||||
{"terminal-type", required_argument, NULL, 'T'},
|
||||
{"client-option", required_argument, NULL, 't'},
|
||||
{"check-origin", no_argument, NULL, 'O'},
|
||||
{"max-clients", required_argument, NULL, 'm'},
|
||||
{"once", no_argument, NULL, 'o'},
|
||||
{"exit-no-conn", no_argument, NULL, 'q'},
|
||||
{"browser", no_argument, NULL, 'B'},
|
||||
{"debug", required_argument, NULL, 'd'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{NULL, 0, 0, 0}};
|
||||
static const char *opt_string = "p:i:U:c:H:u:g:s:w:I:b:P:f:6aSC:K:A:Wt:T:Om:oqBd:vh";
|
||||
|
||||
static void print_help() {
|
||||
// clang-format off
|
||||
fprintf(stderr, "ttyd is a tool for sharing terminal over the web\n\n"
|
||||
"USAGE:\n"
|
||||
" ttyd [options] <command> [<arguments...>]\n\n"
|
||||
"VERSION:\n"
|
||||
" %s\n\n"
|
||||
"OPTIONS:\n"
|
||||
" -p, --port Port to listen (default: 7681, use `0` for random port)\n"
|
||||
" -i, --interface Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock)\n"
|
||||
" -U, --socket-owner User owner of the UNIX domain socket file, when enabled (eg: user:group)\n"
|
||||
" -c, --credential Credential for basic authentication (format: username:password)\n"
|
||||
" -H, --auth-header HTTP Header name for auth proxy, this will configure ttyd to let a HTTP reverse proxy handle authentication\n"
|
||||
" -u, --uid User id to run with\n"
|
||||
" -g, --gid Group id to run with\n"
|
||||
" -s, --signal Signal to send to the command when exit it (default: 1, SIGHUP)\n"
|
||||
" -w, --cwd Working directory to be set for the child program\n"
|
||||
" -a, --url-arg Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar)\n"
|
||||
" -W, --writable Allow clients to write to the TTY (readonly by default)\n"
|
||||
" -t, --client-option Send option to client (format: key=value), repeat to add more options\n"
|
||||
" -T, --terminal-type Terminal type to report, default: xterm-256color\n"
|
||||
" -O, --check-origin Do not allow websocket connection from different origin\n"
|
||||
" -m, --max-clients Maximum clients to support (default: 0, no limit)\n"
|
||||
" -o, --once Accept only one client and exit on disconnection\n"
|
||||
" -q, --exit-no-conn Exit on all clients disconnection\n"
|
||||
" -B, --browser Open terminal with the default system browser\n"
|
||||
" -I, --index Custom index.html path\n"
|
||||
" -b, --base-path Expected base path for requests coming from a reverse proxy (eg: /mounted/here, max length: 128)\n"
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 4000000
|
||||
" -P, --ping-interval Websocket ping interval(sec) (default: 5)\n"
|
||||
#endif
|
||||
" -f, --srv-buf-size Maximum chunk of file (in bytes) that can be sent at once, a larger value may improve throughput (default: 4096)\n"
|
||||
#ifdef LWS_WITH_IPV6
|
||||
" -6, --ipv6 Enable IPv6 support\n"
|
||||
#endif
|
||||
#if defined(LWS_OPENSSL_SUPPORT) || defined(LWS_WITH_TLS)
|
||||
" -S, --ssl Enable SSL\n"
|
||||
" -C, --ssl-cert SSL certificate file path\n"
|
||||
" -K, --ssl-key SSL key file path\n"
|
||||
" -A, --ssl-ca SSL CA file path for client certificate verification\n"
|
||||
#endif
|
||||
" -d, --debug Set log level (default: 7)\n"
|
||||
" -v, --version Print the version and exit\n"
|
||||
" -h, --help Print this text and exit\n\n"
|
||||
"Visit https://github.com/tsl0922/ttyd to get more information and report bugs.\n",
|
||||
TTYD_VERSION
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static void print_config() {
|
||||
lwsl_notice("tty configuration:\n");
|
||||
if (server->credential != NULL) lwsl_notice(" credential: %s\n", server->credential);
|
||||
lwsl_notice(" start command: %s\n", server->command);
|
||||
lwsl_notice(" close signal: %s (%d)\n", server->sig_name, server->sig_code);
|
||||
lwsl_notice(" terminal type: %s\n", server->terminal_type);
|
||||
if (endpoints.parent[0]) {
|
||||
lwsl_notice("endpoints:\n");
|
||||
lwsl_notice(" base-path: %s\n", endpoints.parent);
|
||||
lwsl_notice(" index : %s\n", endpoints.index);
|
||||
lwsl_notice(" token : %s\n", endpoints.token);
|
||||
lwsl_notice(" websocket: %s\n", endpoints.ws);
|
||||
}
|
||||
if (server->auth_header != NULL) lwsl_notice(" auth header: %s\n", server->auth_header);
|
||||
if (server->check_origin) lwsl_notice(" check origin: true\n");
|
||||
if (server->url_arg) lwsl_notice(" allow url arg: true\n");
|
||||
if (server->max_clients > 0) lwsl_notice(" max clients: %d\n", server->max_clients);
|
||||
if (server->once) lwsl_notice(" once: true\n");
|
||||
if (server->exit_no_conn) lwsl_notice(" exit_no_conn: true\n");
|
||||
if (server->index != NULL) lwsl_notice(" custom index.html: %s\n", server->index);
|
||||
if (server->cwd != NULL) lwsl_notice(" working directory: %s\n", server->cwd);
|
||||
if (!server->writable) lwsl_warn("The --writable option is not set, will start in readonly mode\n");
|
||||
}
|
||||
|
||||
static struct server *server_new(int argc, char **argv, int start) {
|
||||
struct server *ts;
|
||||
size_t cmd_len = 0;
|
||||
|
||||
ts = xmalloc(sizeof(struct server));
|
||||
|
||||
memset(ts, 0, sizeof(struct server));
|
||||
ts->client_count = 0;
|
||||
ts->sig_code = SIGHUP;
|
||||
sprintf(ts->terminal_type, "%s", "xterm-256color");
|
||||
get_sig_name(ts->sig_code, ts->sig_name, sizeof(ts->sig_name));
|
||||
if (start == argc) return ts;
|
||||
|
||||
int cmd_argc = argc - start;
|
||||
char **cmd_argv = &argv[start];
|
||||
ts->argv = xmalloc(sizeof(char *) * (cmd_argc + 1));
|
||||
for (int i = 0; i < cmd_argc; i++) {
|
||||
ts->argv[i] = strdup(cmd_argv[i]);
|
||||
cmd_len += strlen(ts->argv[i]);
|
||||
if (i != cmd_argc - 1) {
|
||||
cmd_len++; // for space
|
||||
}
|
||||
}
|
||||
ts->argv[cmd_argc] = NULL;
|
||||
ts->argc = cmd_argc;
|
||||
|
||||
ts->command = xmalloc(cmd_len + 1);
|
||||
char *ptr = ts->command;
|
||||
for (int i = 0; i < cmd_argc; i++) {
|
||||
size_t len = strlen(ts->argv[i]);
|
||||
ptr = memcpy(ptr, ts->argv[i], len + 1) + len;
|
||||
if (i != cmd_argc - 1) {
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
*ptr = '\0'; // null terminator
|
||||
|
||||
ts->loop = xmalloc(sizeof *ts->loop);
|
||||
uv_loop_init(ts->loop);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
static void server_free(struct server *ts) {
|
||||
if (ts == NULL) return;
|
||||
if (ts->credential != NULL) free(ts->credential);
|
||||
if (ts->auth_header != NULL) free(ts->auth_header);
|
||||
if (ts->index != NULL) free(ts->index);
|
||||
if (ts->cwd != NULL) free(ts->cwd);
|
||||
free(ts->command);
|
||||
free(ts->prefs_json);
|
||||
|
||||
char **p = ts->argv;
|
||||
for (; *p; p++) free(*p);
|
||||
free(ts->argv);
|
||||
|
||||
if (strlen(ts->socket_path) > 0) {
|
||||
struct stat st;
|
||||
if (!stat(ts->socket_path, &st)) {
|
||||
unlink(ts->socket_path);
|
||||
}
|
||||
}
|
||||
|
||||
uv_loop_close(ts->loop);
|
||||
|
||||
free(ts->loop);
|
||||
free(ts);
|
||||
}
|
||||
|
||||
static void signal_cb(uv_signal_t *watcher, int signum) {
|
||||
char sig_name[20];
|
||||
|
||||
switch (watcher->signum) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
get_sig_name(watcher->signum, sig_name, sizeof(sig_name));
|
||||
lwsl_notice("received signal: %s (%d), exiting...\n", sig_name, watcher->signum);
|
||||
break;
|
||||
default:
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (force_exit) exit(EXIT_FAILURE);
|
||||
force_exit = true;
|
||||
|
||||
lws_cancel_service(context);
|
||||
uv_stop(server->loop);
|
||||
|
||||
lwsl_notice("send ^C to force exit.\n");
|
||||
}
|
||||
|
||||
static int parse_int(char *name, char *str) {
|
||||
char *endptr;
|
||||
errno = 0;
|
||||
long val = strtol(str, &endptr, 0);
|
||||
if (errno != 0 || endptr == str) {
|
||||
fprintf(stderr, "ttyd: invalid value for %s: %s\n", name, str);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return (int)val;
|
||||
}
|
||||
|
||||
static int calc_command_start(int argc, char **argv) {
|
||||
// make a copy of argc and argv
|
||||
int argc_copy = argc;
|
||||
char **argv_copy = xmalloc(sizeof(char *) * argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv_copy[i] = strdup(argv[i]);
|
||||
}
|
||||
|
||||
// do not print error message for invalid option
|
||||
opterr = 0;
|
||||
while (getopt_long(argc_copy, argv_copy, opt_string, options, NULL) != -1)
|
||||
;
|
||||
|
||||
int start = argc;
|
||||
if (optind < argc) {
|
||||
char *command = argv_copy[optind];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], command) == 0) {
|
||||
start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free argv copy
|
||||
for (int i = 0; i < argc; i++) {
|
||||
free(argv_copy[i]);
|
||||
}
|
||||
free(argv_copy);
|
||||
|
||||
// reset for next use
|
||||
opterr = 1;
|
||||
optind = 0;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc == 1) {
|
||||
print_help();
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!conpty_init()) {
|
||||
fprintf(stderr, "ERROR: ConPTY init failed! Make sure you are on Windows 10 1809 or later.");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int start = calc_command_start(argc, argv);
|
||||
server = server_new(argc, argv, start);
|
||||
|
||||
struct lws_context_creation_info info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.port = 7681;
|
||||
info.iface = NULL;
|
||||
info.protocols = protocols;
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.max_http_header_pool = 16;
|
||||
info.options = LWS_SERVER_OPTION_LIBUV | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_DISABLE_IPV6;
|
||||
#ifndef LWS_WITHOUT_EXTENSIONS
|
||||
info.extensions = extensions;
|
||||
#endif
|
||||
info.max_http_header_data = 65535;
|
||||
|
||||
int debug_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
char iface[128] = "";
|
||||
char socket_owner[128] = "";
|
||||
bool browser = false;
|
||||
bool ssl = false;
|
||||
char cert_path[1024] = "";
|
||||
char key_path[1024] = "";
|
||||
char ca_path[1024] = "";
|
||||
|
||||
struct json_object *client_prefs = json_object_new_object();
|
||||
|
||||
#ifdef _WIN32
|
||||
json_object_object_add(client_prefs, "isWindows", json_object_new_boolean(true));
|
||||
#endif
|
||||
|
||||
// parse command line options
|
||||
int c;
|
||||
while ((c = getopt_long(start, argv, opt_string, options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
return 0;
|
||||
case 'v':
|
||||
printf("ttyd version %s\n", TTYD_VERSION);
|
||||
return 0;
|
||||
case 'd':
|
||||
debug_level = parse_int("debug", optarg);
|
||||
break;
|
||||
case 'a':
|
||||
server->url_arg = true;
|
||||
break;
|
||||
case 'W':
|
||||
server->writable = true;
|
||||
break;
|
||||
case 'O':
|
||||
server->check_origin = true;
|
||||
break;
|
||||
case 'm':
|
||||
server->max_clients = parse_int("max-clients", optarg);
|
||||
break;
|
||||
case 'o':
|
||||
server->once = true;
|
||||
break;
|
||||
case 'q':
|
||||
server->exit_no_conn = true;
|
||||
break;
|
||||
case 'B':
|
||||
browser = true;
|
||||
break;
|
||||
case 'p':
|
||||
info.port = parse_int("port", optarg);
|
||||
if (info.port < 0) {
|
||||
fprintf(stderr, "ttyd: invalid port: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
strncpy(iface, optarg, sizeof(iface) - 1);
|
||||
iface[sizeof(iface) - 1] = '\0';
|
||||
break;
|
||||
case 'U':
|
||||
strncpy(socket_owner, optarg, sizeof(socket_owner) - 1);
|
||||
socket_owner[sizeof(socket_owner) - 1] = '\0';
|
||||
break;
|
||||
case 'c':
|
||||
if (strchr(optarg, ':') == NULL) {
|
||||
fprintf(stderr, "ttyd: invalid credential, format: username:password\n");
|
||||
return -1;
|
||||
}
|
||||
char b64_text[256];
|
||||
lws_b64_encode_string(optarg, strlen(optarg), b64_text, sizeof(b64_text));
|
||||
server->credential = strdup(b64_text);
|
||||
break;
|
||||
case 'H':
|
||||
server->auth_header = strdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
info.uid = parse_int("uid", optarg);
|
||||
break;
|
||||
case 'g':
|
||||
info.gid = parse_int("gid", optarg);
|
||||
break;
|
||||
case 's': {
|
||||
int sig = get_sig(optarg);
|
||||
if (sig > 0) {
|
||||
server->sig_code = sig;
|
||||
get_sig_name(sig, server->sig_name, sizeof(server->sig_name));
|
||||
} else {
|
||||
fprintf(stderr, "ttyd: invalid signal: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
} break;
|
||||
case 'w':
|
||||
server->cwd = strdup(optarg);
|
||||
break;
|
||||
case 'I':
|
||||
if (!strncmp(optarg, "~/", 2)) {
|
||||
const char *home = getenv("HOME");
|
||||
server->index = malloc(strlen(home) + strlen(optarg) - 1);
|
||||
sprintf(server->index, "%s%s", home, optarg + 1);
|
||||
} else {
|
||||
server->index = strdup(optarg);
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(server->index, &st) == -1) {
|
||||
fprintf(stderr, "Can not stat index.html: %s, error: %s\n", server->index, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "Invalid index.html path: %s, is it a dir?\n", server->index);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'b': {
|
||||
char path[128];
|
||||
strncpy(path, optarg, 128);
|
||||
size_t len = strlen(path);
|
||||
while (len && path[len - 1] == '/') path[--len] = 0; // trim trailing /
|
||||
if (!len) break;
|
||||
#define sc(f) \
|
||||
strncpy(path + len, endpoints.f, 128 - len); \
|
||||
endpoints.f = strdup(path);
|
||||
sc(ws) sc(index) sc(token) sc(parent)
|
||||
#undef sc
|
||||
} break;
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 4000000
|
||||
case 'P': {
|
||||
int interval = parse_int("ping-interval", optarg);
|
||||
if (interval < 0) {
|
||||
fprintf(stderr, "ttyd: invalid ping interval: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
retry.secs_since_valid_ping = interval;
|
||||
retry.secs_since_valid_hangup = interval + 7;
|
||||
} break;
|
||||
#endif
|
||||
case 'f': {
|
||||
int serv_buf_size = parse_int("srv-buf-size", optarg);
|
||||
if (serv_buf_size < 0) {
|
||||
fprintf(stderr, "ttyd: invalid srv-buf-size: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
info.pt_serv_buf_size = serv_buf_size;
|
||||
} break;
|
||||
case '6':
|
||||
info.options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
|
||||
break;
|
||||
#if defined(LWS_OPENSSL_SUPPORT) || defined(LWS_WITH_TLS)
|
||||
case 'S':
|
||||
ssl = true;
|
||||
break;
|
||||
case 'C':
|
||||
strncpy(cert_path, optarg, sizeof(cert_path) - 1);
|
||||
cert_path[sizeof(cert_path) - 1] = '\0';
|
||||
break;
|
||||
case 'K':
|
||||
strncpy(key_path, optarg, sizeof(key_path) - 1);
|
||||
key_path[sizeof(key_path) - 1] = '\0';
|
||||
break;
|
||||
case 'A':
|
||||
strncpy(ca_path, optarg, sizeof(ca_path) - 1);
|
||||
ca_path[sizeof(ca_path) - 1] = '\0';
|
||||
break;
|
||||
#endif
|
||||
case 'T':
|
||||
strncpy(server->terminal_type, optarg, sizeof(server->terminal_type) - 1);
|
||||
server->terminal_type[sizeof(server->terminal_type) - 1] = '\0';
|
||||
break;
|
||||
case '?':
|
||||
break;
|
||||
case 't':
|
||||
optind--;
|
||||
for (; optind < start && *argv[optind] != '-'; optind++) {
|
||||
char *option = optarg;
|
||||
char *key = strsep(&option, "=");
|
||||
if (key == NULL) {
|
||||
fprintf(stderr, "ttyd: invalid client option: %s, format: key=value\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
char *value = strsep(&option, "=");
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "ttyd: invalid client option: %s, format: key=value\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
struct json_object *obj = json_tokener_parse(value);
|
||||
json_object_object_add(client_prefs, key, obj != NULL ? obj : json_object_new_string(value));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
server->prefs_json = strdup(json_object_to_json_string(client_prefs));
|
||||
json_object_put(client_prefs);
|
||||
|
||||
if (server->command == NULL || strlen(server->command) == 0) {
|
||||
fprintf(stderr, "ttyd: missing start command\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_set_log_level(debug_level, NULL);
|
||||
|
||||
char server_hdr[128] = "";
|
||||
sprintf(server_hdr, "ttyd/%s (libwebsockets/%s)", TTYD_VERSION, LWS_LIBRARY_VERSION);
|
||||
info.server_string = server_hdr;
|
||||
|
||||
#if LWS_LIBRARY_VERSION_NUMBER < 4000000
|
||||
info.ws_ping_pong_interval = 5;
|
||||
#else
|
||||
info.retry_and_idle_policy = &retry;
|
||||
#endif
|
||||
|
||||
if (strlen(iface) > 0) {
|
||||
info.iface = iface;
|
||||
if (endswith(info.iface, ".sock") || endswith(info.iface, ".socket")) {
|
||||
#if defined(LWS_USE_UNIX_SOCK) || defined(LWS_WITH_UNIX_SOCK)
|
||||
info.options |= LWS_SERVER_OPTION_UNIX_SOCK;
|
||||
info.port = 0; // warmcat/libwebsockets#1985
|
||||
strncpy(server->socket_path, info.iface, sizeof(server->socket_path) - 1);
|
||||
if (strlen(socket_owner) > 0) {
|
||||
info.unix_socket_perms = socket_owner;
|
||||
}
|
||||
#else
|
||||
fprintf(stderr, "libwebsockets is not compiled with UNIX domain socket support");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) || defined(LWS_WITH_TLS)
|
||||
if (ssl) {
|
||||
info.ssl_cert_filepath = cert_path;
|
||||
info.ssl_private_key_filepath = key_path;
|
||||
#ifndef LWS_WITH_MBEDTLS
|
||||
info.ssl_options_set = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
|
||||
#endif
|
||||
if (strlen(ca_path) > 0) {
|
||||
info.ssl_ca_filepath = ca_path;
|
||||
info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
|
||||
}
|
||||
info.options |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT | LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsl_notice("ttyd %s (libwebsockets %s)\n", TTYD_VERSION, LWS_LIBRARY_VERSION);
|
||||
print_config();
|
||||
|
||||
// lws custom header requires lower case name, and terminating :
|
||||
if (server->auth_header != NULL) {
|
||||
size_t auth_header_len = strlen(server->auth_header);
|
||||
server->auth_header = xrealloc(server->auth_header, auth_header_len + 2);
|
||||
strcat(server->auth_header + auth_header_len, ":");
|
||||
lowercase(server->auth_header);
|
||||
}
|
||||
|
||||
void *foreign_loops[1];
|
||||
foreign_loops[0] = server->loop;
|
||||
info.foreign_loops = foreign_loops;
|
||||
info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
lwsl_err("libwebsockets context creation failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct lws_vhost *vhost = lws_create_vhost(context, &info);
|
||||
if (vhost == NULL) {
|
||||
lwsl_err("libwebsockets vhost creation failed\n");
|
||||
return 1;
|
||||
}
|
||||
int port = lws_get_vhost_listen_port(vhost);
|
||||
lwsl_notice(" Listening on port: %d\n", port);
|
||||
|
||||
if (browser) {
|
||||
char url[30];
|
||||
sprintf(url, "%s://localhost:%d", ssl ? "https" : "http", port);
|
||||
open_uri(url);
|
||||
}
|
||||
|
||||
#define sig_count 2
|
||||
int sig_nums[] = {SIGINT, SIGTERM};
|
||||
uv_signal_t signals[sig_count];
|
||||
for (int i = 0; i < sig_count; i++) {
|
||||
uv_signal_init(server->loop, &signals[i]);
|
||||
uv_signal_start(&signals[i], signal_cb, sig_nums[i]);
|
||||
}
|
||||
|
||||
lws_service(context, 0);
|
||||
|
||||
for (int i = 0; i < sig_count; i++) {
|
||||
uv_signal_stop(&signals[i]);
|
||||
}
|
||||
#undef sig_count
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
// cleanup
|
||||
server_free(server);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
#include <libwebsockets.h>
|
||||
#include <stdbool.h>
|
||||
#include <uv.h>
|
||||
|
||||
#include "pty.h"
|
||||
|
||||
// client message
|
||||
#define INPUT '0'
|
||||
#define RESIZE_TERMINAL '1'
|
||||
#define PAUSE '2'
|
||||
#define RESUME '3'
|
||||
#define JSON_DATA '{'
|
||||
|
||||
// server message
|
||||
#define OUTPUT '0'
|
||||
#define SET_WINDOW_TITLE '1'
|
||||
#define SET_PREFERENCES '2'
|
||||
|
||||
// url paths
|
||||
struct endpoints {
|
||||
char *ws;
|
||||
char *index;
|
||||
char *token;
|
||||
char *parent;
|
||||
};
|
||||
|
||||
extern volatile bool force_exit;
|
||||
extern struct lws_context *context;
|
||||
extern struct server *server;
|
||||
extern struct endpoints endpoints;
|
||||
|
||||
struct pss_http {
|
||||
char path[128];
|
||||
char *buffer;
|
||||
char *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct pss_tty {
|
||||
bool initialized;
|
||||
int initial_cmd_index;
|
||||
bool authenticated;
|
||||
char user[30];
|
||||
char address[50];
|
||||
char path[128];
|
||||
char **args;
|
||||
int argc;
|
||||
|
||||
struct lws *wsi;
|
||||
char *buffer;
|
||||
size_t len;
|
||||
|
||||
pty_process *process;
|
||||
pty_buf_t *pty_buf;
|
||||
|
||||
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
|
||||
char *credential; // encoded basic auth credential
|
||||
char *auth_header; // header name used for auth proxy
|
||||
char *index; // custom index.html
|
||||
char *command; // full command line
|
||||
char **argv; // command with arguments
|
||||
int argc; // command + arguments count
|
||||
char *cwd; // working directory
|
||||
int sig_code; // close signal
|
||||
char sig_name[20]; // human readable signal string
|
||||
bool url_arg; // allow client to send cli arguments in URL
|
||||
bool writable; // whether clients to write to the TTY
|
||||
bool check_origin; // whether allow websocket connection from different origin
|
||||
int max_clients; // maximum clients to support
|
||||
bool once; // whether accept only one client and exit on disconnection
|
||||
bool exit_no_conn; // whether exit on all clients disconnection
|
||||
char socket_path[255]; // UNIX domain socket path
|
||||
char terminal_type[30]; // terminal type to report
|
||||
|
||||
uv_loop_t *loop; // the libuv event loop
|
||||
};
|
163
ttyd/src/utils.c
163
ttyd/src/utils.c
@@ -1,163 +0,0 @@
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
const char *sys_signame[NSIG] = {
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "UNUSED", "FPE", "KILL", "USR1",
|
||||
"SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN",
|
||||
"TTOU", "URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "IO", "PWR", "SYS", NULL};
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#undef NSIG
|
||||
#define NSIG 33
|
||||
const char *sys_signame[NSIG] = {
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", "FPE", "KILL", "BUS",
|
||||
"SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN",
|
||||
"TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "PWR", "USR1", "USR2", NULL};
|
||||
#endif
|
||||
|
||||
void *xmalloc(size_t size) {
|
||||
if (size == 0) return NULL;
|
||||
void *p = malloc(size);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xrealloc(void *p, size_t size) {
|
||||
if ((size == 0) && (p == NULL)) return NULL;
|
||||
p = realloc(p, size);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
char *uppercase(char *s) {
|
||||
while(*s) {
|
||||
*s = (char)toupper((int)*s);
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char *lowercase(char *s) {
|
||||
while(*s) {
|
||||
*s = (char)tolower((int)*s);
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool endswith(const char *str, const char *suffix) {
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
return str_len > suffix_len && !strcmp(str + (str_len - suffix_len), suffix);
|
||||
}
|
||||
|
||||
int get_sig_name(int sig, char *buf, size_t len) {
|
||||
int n = snprintf(buf, len, "SIG%s", sig < NSIG ? sys_signame[sig] : "unknown");
|
||||
uppercase(buf);
|
||||
return n;
|
||||
}
|
||||
|
||||
int get_sig(const char *sig_name) {
|
||||
for (int sig = 1; sig < NSIG; sig++) {
|
||||
const char *name = sys_signame[sig];
|
||||
if (name != NULL && (strcasecmp(name, sig_name) == 0 || strcasecmp(name, sig_name + 3) == 0))
|
||||
return sig;
|
||||
}
|
||||
return atoi(sig_name);
|
||||
}
|
||||
|
||||
int open_uri(char *uri) {
|
||||
#ifdef __APPLE__
|
||||
char command[256];
|
||||
sprintf(command, "open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
#elif defined(_WIN32) || defined(__CYGWIN__)
|
||||
return ShellExecute(0, 0, uri, 0, 0, SW_SHOW) > (HINSTANCE)32 ? 0 : 1;
|
||||
#else
|
||||
// check if X server is running
|
||||
if (system("xset -q > /dev/null 2>&1")) return 1;
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
char *strsep(char **sp, char *sep) {
|
||||
char *p, *s;
|
||||
if (sp == NULL || *sp == NULL || **sp == '\0') return (NULL);
|
||||
s = *sp;
|
||||
p = s + strcspn(s, sep);
|
||||
if (*p != '\0') *p++ = '\0';
|
||||
*sp = p;
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *quote_arg(const char *arg) {
|
||||
int len = 0, n = 0;
|
||||
int force_quotes = 0;
|
||||
char *q, *d;
|
||||
const char *p = arg;
|
||||
if (!*p) force_quotes = 1;
|
||||
while (*p) {
|
||||
if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
|
||||
force_quotes = 1;
|
||||
else if (*p == '"')
|
||||
n++;
|
||||
else if (*p == '\\') {
|
||||
int count = 0;
|
||||
while (*p == '\\') {
|
||||
count++;
|
||||
p++;
|
||||
len++;
|
||||
}
|
||||
if (*p == '"' || !*p) n += count * 2 + 1;
|
||||
continue;
|
||||
}
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
if (!force_quotes && n == 0) return arg;
|
||||
|
||||
d = q = xmalloc(len + n + 3);
|
||||
*d++ = '"';
|
||||
while (*arg) {
|
||||
if (*arg == '"')
|
||||
*d++ = '\\';
|
||||
else if (*arg == '\\') {
|
||||
int count = 0;
|
||||
while (*arg == '\\') {
|
||||
count++;
|
||||
*d++ = *arg++;
|
||||
}
|
||||
if (*arg == '"' || !*arg) {
|
||||
while (count-- > 0) *d++ = '\\';
|
||||
if (!*arg) break;
|
||||
*d++ = '\\';
|
||||
}
|
||||
}
|
||||
*d++ = *arg++;
|
||||
}
|
||||
*d++ = '"';
|
||||
*d++ = '\0';
|
||||
return q;
|
||||
}
|
||||
|
||||
void print_error(char *func) {
|
||||
LPVOID buffer;
|
||||
DWORD dw = GetLastError();
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buffer, 0, NULL);
|
||||
wprintf(L"== %s failed with error %d: %s", func, dw, buffer);
|
||||
LocalFree(buffer);
|
||||
}
|
||||
#endif
|
@@ -1,39 +0,0 @@
|
||||
#ifndef TTYD_UTIL_H
|
||||
#define TTYD_UTIL_H
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const typeof(((type *)0)->member) *__mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
|
||||
// malloc with NULL check
|
||||
void *xmalloc(size_t size);
|
||||
|
||||
// realloc with NULL check
|
||||
void *xrealloc(void *p, size_t size);
|
||||
|
||||
// Convert a string to upper case
|
||||
char *uppercase(char *s);
|
||||
|
||||
// Convert a string to lower case
|
||||
char *lowercase(char *s);
|
||||
|
||||
// Check whether str ends with suffix
|
||||
bool endswith(const char *str, const char *suffix);
|
||||
|
||||
// Get human readable signal string
|
||||
int get_sig_name(int sig, char *buf, size_t len);
|
||||
|
||||
// Get signal code from string like SIGHUP
|
||||
int get_sig(const char *sig_name);
|
||||
|
||||
// Open uri with the default application of system
|
||||
int open_uri(char *uri);
|
||||
|
||||
#ifdef _WIN32
|
||||
char *strsep(char **sp, char *sep);
|
||||
const char *quote_arg(const char *arg);
|
||||
void print_error(char *func);
|
||||
#endif
|
||||
#endif // TTYD_UTIL_H
|
Reference in New Issue
Block a user