安装脚本

This commit is contained in:
snltty
2025-09-17 00:19:09 +08:00
parent 1caf652c88
commit 274623374f
7 changed files with 549 additions and 217 deletions

View File

@@ -107,11 +107,13 @@ namespace linker.messenger.flow
private void OnlineTask()
{
UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork);
udpClient.Client.WindowsUdpBug();
TimerHelper.SetIntervalLong(() =>
{
try
{
Report();
Report(udpClient);
}
catch (Exception ex)
{
@@ -122,7 +124,7 @@ namespace linker.messenger.flow
}
}, 5000);
}
private void Report()
private void Report(UdpClient udpClient)
{
List<FlowReportNetInfo> nets = signCaching.Get().Where(c => c.Args.ContainsKey("tunnelNet")).Select(c => c.Args["tunnelNet"].DeJson<SignInArgsNetInfo>()).GroupBy(c => c.City).Select(c => new FlowReportNetInfo
{
@@ -139,16 +141,11 @@ namespace linker.messenger.flow
total.ToBytes(buffer.Slice(5));
netBytes.CopyTo(buffer.Slice(9));
using UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork);
udpClient.Client.WindowsUdpBug();
string domain = "linker.snltty.com";
#if DEBUG
domain = "127.0.0.1";
#endif
udpClient.Send(buffer.Slice(0, 9 + netBytes.Length), domain, 1802);
udpClient.Dispose();
}
}

View File

@@ -201,7 +201,7 @@ namespace linker.plugins.sforward.proxy
serverUdp.WindowsUdpBug();
using IMemoryOwner<byte> buffer = MemoryPool<byte>.Shared.Rent(65535);
//回复服务器
flagBytes.AsMemory().CopyTo(buffer.Memory);
id.ToBytes(buffer.Memory.Slice(flagBytes.Length));
await serverUdp.SendToAsync(buffer.Memory, server).ConfigureAwait(false);

View File

@@ -7,6 +7,7 @@ using linker.messenger.relay.client;
using linker.messenger.signin;
using linker.messenger.pcp;
using linker.messenger.tuntap.cidr;
using System.Net.Sockets;
namespace linker.messenger.tuntap
{
@@ -120,11 +121,12 @@ namespace linker.messenger.tuntap
}, ip);
return;
}
ushort ackLength = 0;
if (connection.PacketBuffer.Length > 0 && fakeAckTransfer.Read(packet.IPPacket, connection.PacketBuffer, connection.SendBufferFree, out ackLength)) return;
await connection.SendAsync(packet.Buffer, packet.Offset, packet.Length).ConfigureAwait(false);
if (ackLength > 0) Callback.Receive(connection, connection.PacketBuffer.AsMemory(0, ackLength));
}
/// <summary>

View File

@@ -1,5 +1,4 @@
using linker.libs;
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
@@ -19,7 +18,7 @@ namespace linker.tun
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <param name="fakeBuffer">一个能容纳ACK包的缓冲区如果需要伪造ACK则写入到这里</param>
/// <param name="bufferFree">窗口大小</param>
/// <param name="bufferFree">缓冲区可用字节数会根据这个来计算ack的窗口大小</param>
/// <param name="fakeLength">ack包长度</param>
/// <returns>是否丢包</returns>
public bool Read(ReadOnlyMemory<byte> packet, ReadOnlyMemory<byte> fakeBuffer, long bufferFree, out ushort fakeLength)
@@ -77,6 +76,7 @@ namespace linker.tun
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
/*
//更新序列号
if (originPacket.TcpFlagAck && dic.TryGetValue(key, out FackAckState state))
{
state.Cq = originPacket.Cq;
@@ -283,28 +283,41 @@ namespace linker.tun
return totalLength;
}
/// <summary>
/// 从TCP的SYN包或SYN+ACK包中获取窗口缩放比例
/// </summary>
/// <param name="ipPtr">一个完整TCP/IP包</param>
/// <returns></returns>
public int FindOptionWindowScale(byte* ipPtr)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
int index = 20, end = (*(tcpPtr + 12) >> 4) * 4;
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
if (kind == 0) break;
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
byte shiftCount = *(tcpPtr + index + 2);
if (shiftCount > 14) break;
return 1 << shiftCount;
return shiftCount > 14 ? 1 : 1 << shiftCount;
}
index += length;
}

View File

@@ -46,17 +46,17 @@ namespace linker.tunnel.connection
public bool Receive { get; init; }
public Socket uUdpClient;
public Socket udpClient;
[JsonIgnore]
public Socket UdpClient
{
get
{
return uUdpClient;
return udpClient;
}
init
{
uUdpClient = value;
udpClient = value;
}
}
[JsonIgnore]
@@ -331,7 +331,7 @@ namespace linker.tunnel.connection
public void Dispose()
{
if (uUdpClient == null) return;
if (udpClient == null) return;
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error($"tunnel connection {this.GetHashCode()} writer offline {ToString()}");
@@ -343,7 +343,7 @@ namespace linker.tunnel.connection
LastTicks.Clear();
if (Receive == true)
UdpClient?.SafeClose();
uUdpClient = null;
udpClient = null;
cancellationTokenSource?.Cancel();
callback?.Closed(this, userToken);

View File

@@ -1,21 +1,262 @@
#!/bin/bash
LINKER_DOWNLOAD_URL="https://static.snltty.com/downloads/linker"
LINKER_DOWNLOAD_VERSION=""
LINKER_FILE_NAME="linker-linux-"
# Linker 安装管理脚本
set -e
LINKER_INSTALL_PATH=$1
if [ -z "$LINKER_INSTALL_PATH" ]; then
LINKER_INSTALL_PATH="/usr/local/bin"
fi
echo -e "安装位置:${LINKER_INSTALL_PATH}"
# 配置变量
BASE_URL="https://static.snltty.com/downloads/linker"
VERSION_URL="${BASE_URL}/version.txt"
TEMP_DIR="/tmp/linker-install"
INSTALL_DIR="/usr/local/bin/linker"
SERVICE_NAME="linker"
DOCKER_IMAGE="snltty/linker-musl"
DOCKER_CONTAINER="linker"
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
plain='\033[0m'
export PATH=$PATH:/usr/local/bin
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 输出彩色信息
info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
}
debug() {
echo -e "${BLUE}[DEBUG]${NC} $1"
}
# 检查命令是否存在
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# 检测系统架构
detect_arch() {
local arch
arch=$(uname -m)
case "$arch" in
x86_64|amd64)
echo "x64"
;;
aarch64|arm64)
echo "arm64"
;;
armv7l|armv8l|arm)
echo "arm"
;;
*)
error "不支持的架构: $arch"
exit 1
;;
esac
}
# 检测libc类型
detect_libc() {
if ldd --version 2>&1 | grep -q musl; then
echo "musl"
elif [ -f /etc/alpine-release ]; then
echo "musl"
else
echo "gnu"
fi
}
# 获取最新版本号
get_latest_version() {
if command_exists curl; then
curl -sSL --connect-timeout 10 "$VERSION_URL" | head -n 1 | tr -d '\r'
elif command_exists wget; then
wget -qO- --timeout=10 "$VERSION_URL" | head -n 1 | tr -d '\r'
else
error "需要 curl 或 wget 来获取版本信息"
exit 1
fi
}
# 安装Docker
install_docker() {
info "开始安装Docker..."
if command_exists docker; then
info "Docker 已经安装"
return 0
fi
# 使用官方Docker安装脚本
if command_exists curl; then
curl -fsSL https://get.docker.com -o get-docker.sh
elif command_exists wget; then
wget -q https://get.docker.com -O get-docker.sh
else
error "需要 curl 或 wget 来下载Docker安装脚本"
exit 1
fi
# 安装Docker
chmod +x get-docker.sh
sh get-docker.sh
# 启动Docker服务
if command_exists systemctl; then
systemctl start docker
systemctl enable docker
elif command_exists service; then
service docker start
fi
# 验证Docker安装
if docker --version; then
info "Docker 安装成功"
rm -f get-docker.sh
return 0
else
error "Docker 安装失败"
rm -f get-docker.sh
return 1
fi
}
# 检查Docker容器是否存在
docker_container_exists() {
docker ps -a --filter "name=^/${DOCKER_CONTAINER}$" --format '{{.Names}}' | grep -q "^${DOCKER_CONTAINER}$"
}
# 设置Docker容器
setup_docker() {
info "配置Docker容器..."
# 安装Docker如果不存在
if ! command_exists docker; then
warn "Docker 未安装,开始自动安装..."
if ! install_docker; then
error "Docker 安装失败无法继续Docker安装"
exit 1
fi
fi
# 检查容器是否已存在
if docker_container_exists; then
error "Docker容器 ${DOCKER_CONTAINER} 已存在,请先卸载或删除现有容器"
exit 1
fi
info "拉取Docker镜像: ${DOCKER_IMAGE}"
if ! docker pull "${DOCKER_IMAGE}"; then
error "拉取Docker镜像失败"
exit 1
fi
# 创建并运行容器
info "创建并启动Docker容器: ${DOCKER_CONTAINER}"
local docker_run_cmd="docker run -it -d \
--name ${DOCKER_CONTAINER} \
-v $INSTALL_DIR/configs:/app/configs \
-v $INSTALL_DIR/logs:/app/logs \
---device /dev/net/tun \
--restart=always \
--privileged=true \
--network host \
${DOCKER_IMAGE}"
debug "执行命令: ${docker_run_cmd}"
if eval "${docker_run_cmd}"; then
info "Docker容器创建成功"
# 等待容器启动
sleep 3
# 检查容器状态
if docker ps --filter "name=^/${DOCKER_CONTAINER}$" --format '{{.Status}}' | grep -q "Up"; then
info "Docker容器运行正常"
else
warn "容器已创建但可能未正常运行,请检查日志: docker logs ${DOCKER_CONTAINER}"
fi
else
error "Docker容器创建失败"
exit 1
fi
}
# 下载并安装Linker
download_install() {
local version=$1
local arch=$2
local libc=$3
# 确定文件名
local filename="${SERVICE_NAME}-linux-${arch}.zip"
local pathname="${SERVICE_NAME}-linux-${arch}"
os_alpine="0"
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1'
if [ "$os_alpine" == 1 ]; then
filename="${SERVICE_NAME}-linux-musl-${arch}.zip"
pathname="${SERVICE_NAME}-linux-musl-${arch}"
fi
local download_url="${BASE_URL}/${version}/${filename}"
info "下载链接: $download_url"
# 创建临时目录
rm -rf "$TEMP_DIR"
mkdir -p "$TEMP_DIR"
# 下载文件
if command_exists curl; then
if ! curl -sSL --connect-timeout 30 -o "${TEMP_DIR}/${filename}" "$download_url"; then
error "下载失败: $download_url"
exit 1
fi
elif command_exists wget; then
if ! wget -q --timeout=30 -O "${TEMP_DIR}/${filename}" "$download_url"; then
error "下载失败: $download_url"
exit 1
fi
else
error "需要 curl 或 wget 来下载文件"
exit 1
fi
# 检查下载是否成功
if [ ! -f "${TEMP_DIR}/${filename}" ]; then
error "下载失败,文件不存在"
exit 1
fi
# 解压文件
if command_exists unzip; then
if ! unzip -qo "${TEMP_DIR}/${filename}" -d "$TEMP_DIR"; then
error "解压失败"
exit 1
fi
else
error "需要 unzip 来解压文件"
exit 1
fi
# 安装到系统目录
mkdir -p "$INSTALL_DIR"
cp -r "${TEMP_DIR}/${pathname}/." "$INSTALL_DIR/"
rm -rf "$TEMP_DIR"
info "Linker 已安装到 ${INSTALL_DIR}/"
}
install_base() {
(command -v git >/dev/null 2>&1 && command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) ||
@@ -28,204 +269,283 @@ install_soft() {
(command -v apt-get >/dev/null 2>&1 && apt-get update >/dev/null 2>&1 && apt-get install $* iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
(command -v apk >/dev/null 2>&1 && apk update >/dev/null 2>&1 && apk add --no-cache net-tools iproute2 numactl-dev iputils iptables dmidecode -f >/dev/null 2>&1)
}
install_systemd() {
# 安装系统依赖
install_dependencies() {
info "安装系统依赖..."
os_arch=""
os_alpine="0"
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1'
[ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1 && echo "不支持此系统:未找到 systemctl 命令" && exit 1
# check root
[[ $EUID -ne 0 ]] && echo -e "${yellow}===================================================\n${red}错误: 必须使用root用户运行此脚本${plain}" && exit 1
## os_arch
if [[ $(uname -m | grep 'x86_64') != "" ]]; then
os_arch="x64"
elif [[ $(uname -m | grep 'aarch64\|armv8b\|armv8l') != "" ]]; then
os_arch="arm64"
elif [[ $(uname -m | grep 'arm') != "" ]]; then
os_arch="arm"
fi
if [ -z "$os_arch" ]; then
echo -e "${yellow}===================================================\n${red} 仅支持arm arm64 amd64 ${plain}" && exit 1
fi
LINKER_FILE_NAME="$LINKER_FILE_NAME$os_arch"
if [ "$os_alpine" == 1 ]; then
LINKER_FILE_NAME="$LINKER_FILE_NAME-musl"
fi
if [ -e "/etc/systemd/system/linker.service" ]; then
echo -e "${yellow}===================================================\n${plain}docker已存在linker服务请自选择${yellow}\n1. 继续安装\n2. 卸载\n3. 退出${plain}"
while true; do
read -e -r -p "请输入选择 [1-2]" option
case "${option}" in
1)
break
;;
2)
echo -e "${yellow}===================================================${plain}\n正在移除服务"
systemctl disable linker >/dev/null 2>&1
systemctl stop linker >/dev/null 2>&1
rm -rf /etc/systemd/system/linker.service
echo -e "${green}已移除服务${plain}"
exit 1
;;
3)
exit 1
;;
*)
echo "${red}请输入正确的数字 [1-3]${plain}"
;;
esac
done
systemctl disable linker >/dev/null 2>&1
systemctl stop linker >/dev/null 2>&1
rm -rf /etc/systemd/system/linker.service >/dev/null 2>&1
fi
echo -e "${yellow}===================================================${plain}\n正在安装依赖..."
install_base
install_soft
echo -e "${green}已安装依赖${plain}"
echo -e "${yellow}===================================================${plain}\n正在获取版本..."
LINKER_DOWNLOAD_VERSION=$(curl -m 10 -s $LINKER_DOWNLOAD_URL/version.txt | head -n 1 | tr -d '[:space:]')
if [ "${LINKER_DOWNLOAD_VERSION:0:1}" != "v" ]; then
echo -e "${red}获取版本号失败${plain}" && exit 1
fi
echo -e "${green}版本号:$LINKER_DOWNLOAD_VERSION${plain}"
echo -e "${yellow}===================================================${plain}\n正在下载程序..."
wget -t 2 -T 60 -O ${LINKER_INSTALL_PATH}/${LINKER_FILE_NAME}-${LINKER_DOWNLOAD_VERSION}.zip ${LINKER_DOWNLOAD_URL}/${LINKER_DOWNLOAD_VERSION}/${LINKER_FILE_NAME}.zip >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}下载程序失败,请检查本机能否连接 ${LINKER_DOWNLOAD_URL}/${LINKER_DOWNLOAD_VERSION}/${LINKER_FILE_NAME}.zip${plain}" && exit 1
fi
echo -e "${green}下载程序完成${plain}"
echo -e "${yellow}===================================================${plain}\n正在解压..."
unzip -qo -O UTF-8 "${LINKER_INSTALL_PATH}/${LINKER_FILE_NAME}-${LINKER_DOWNLOAD_VERSION}.zip" -d $LINKER_INSTALL_PATH
chmod 777 -R ${LINKER_INSTALL_PATH}/$LINKER_FILE_NAME
echo -e "${green}解压完成${plain}"
rm -rf ${LINKER_INSTALL_PATH}/${LINKER_FILE_NAME}-${LINKER_DOWNLOAD_VERSION}.zip
echo -e "${yellow}===================================================${plain}\n正在下载服务文件..."
wget -t 2 -T 60 -O ${LINKER_INSTALL_PATH}/${LINKER_FILE_NAME}/linker.service ${LINKER_DOWNLOAD_URL}/linker.service >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}下载服务文件失败,请检查本机能否连接 ${LINKER_DOWNLOAD_URL}/linker.service${plain}" && exit 1
fi
echo -e "${green}下载服务文件完成${plain}"
cp -f ${LINKER_INSTALL_PATH}/${LINKER_FILE_NAME}/linker.service /etc/systemd/system/linker.service
sed -i "s|{dir}|$LINKER_INSTALL_PATH/$LINKER_FILE_NAME|g" /etc/systemd/system/linker.service
systemctl daemon-reload >/dev/null 2>&1
systemctl enable linker >/dev/null 2>&1
systemctl restart linker >/dev/null 2>&1
echo -e "${yellow}===================================================\n1、已安装linker"
echo -e "2、如果你希望运行为客户端请使用浏览器打开0.0.0.0:1804进行初始化或者systemctl stop linker然后从别处导出配置将configs文件夹覆盖替换"
echo -e "3、如果你希望运行为服务端可以systemctl stop linker修改configs/server.json配置然后systemctl start linker再次运行"
echo -e "4、如果服务启动失败可以尝试手动运行./linkerk看报错信息缺什么就装什么${plain}"
}
install_docker() {
LINKER_FILE_NAME="linker"
os_alpine="0"
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1'
command -v docker >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${yellow}===================================================${plain}\n正在安装 Docker"
if [ "$os_alpine" != 1 ]; then
bash <(curl -sL https://get.docker.com -o get-docker.sh) >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}下载脚本失败,请检查本机能否连接 https://get.docker.com${plain}"
return 0
fi
systemctl enable docker.service >/dev/null 2>&1
systemctl start docker.service >/dev/null 2>&1
else
apk add docker docker-compose >/dev/null 2>&1
rc-update add docker
rc-service docker start
fi
echo -e "${green}Docker 安装成功${plain}"
fi
LINKER_IMAGES=$(docker ps | grep -w "linker")
if [ -n "$LINKER_IMAGES" ]; then
echo -e "${yellow}===================================================${plain}\ndocker已存在linker容器请自选择${yellow}\n1. 继续安装\n2. 卸载\n3. 退出${plain}"
while true; do
read -e -r -p "请输入选择 [1-3]" option
case "${option}" in
1)
break
;;
2)
echo -e "${yellow}===================================================${plain}\n正在移除容器"
docker stop linker >/dev/null 2>&1
docker rm linker >/dev/null 2>&1
echo -e "${green}已移除容器${plain}"
exit 1
;;
3)
exit 1
;;
*)
echo "${red}请输入正确的数字 [1-2]${plain}"
;;
esac
done
docker stop linker >/dev/null 2>&1
docker rm linker >/dev/null 2>&1
fi
echo -e "${yellow}===================================================${plain}\n开始拉取镜像并运行容器如果镜像拉取失败你可能需要更换镜像源"
docker run -it -d --name linker \
-v $LINKER_INSTALL_PATH/linker/configs:/app/configs \
-v $LINKER_INSTALL_PATH/linker/logs:/app/logs \
--device /dev/net/tun \
--restart=always \
--privileged=true \
--network host \
snltty/linker-musl
NEZHA_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "snltty/linker")
if [ -n "$NEZHA_IMAGES" ]; then
echo -e "${green}docker容器启动成功${plain}" && exit 1
if command_exists apt-get; then
apt-get update >/dev/null 2>&1
apt-get install -y curl wget unzip iproute2 net-tools iptables bash traceroute >/dev/null 2>&1
elif command_exists yum; then
yum install -y curl wget unzip iproute net-tools iptables bash traceroute >/dev/null 2>&1
elif command_exists dnf; then
dnf install -y curl wget unzip iproute net-tools iptables bash traceroute >/dev/null 2>&1
elif command_exists apk; then
apk add curl wget unzip iproute2 net-tools iptables bash traceroute >/dev/null 2>&1
else
echo -e "${red}docker容器启动失败${plain}" && exit 1
warn "无法确定包管理器,跳过依赖安装"
warn "请确保已安装: curl/wget, unzip, ip, ifconfig, iptables, bash, traceroute"
fi
}
select_version() {
if [[ -z $LINKER_IS_DOCKER ]]; then
echo -e "${yellow}===================================================\n${plain}请自行选择您的安装方式:${yellow}\n1. Docker\n2. 独立安装${plain}"
while true; do
read -e -r -p "请输入选择 [1-2]" option
case "${option}" in
# 配置systemctl服务
setup_systemctl() {
info "配置systemctl服务..."
local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
cat > "$service_file" << EOF
[Unit]
Description=${SERVICE_NAME}
After=network.target
[Service]
WorkingDirectory=${INSTALL_DIR}
ExecStartPre=/bin/chmod +x ${INSTALL_DIR}/${SERVICE_NAME}
ExecStart=${INSTALL_DIR}/${SERVICE_NAME}
ExecStop=/bin/kill $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl start "$SERVICE_NAME"
info "systemctl服务已配置并启动"
}
# 配置supervisor
setup_supervisor() {
info "配置supervisor..."
if ! command_exists supervisorctl; then
error "Supervisor未安装请先安装supervisor"
exit 1
fi
local supervisor_conf="/etc/supervisor/conf.d/${SERVICE_NAME}.conf"
cat > "$supervisor_conf" << EOF
[program:${SERVICE_NAME}]
command=${INSTALL_DIR}/${SERVICE_NAME}
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/${SERVICE_NAME}.log
EOF
supervisorctl update
supervisorctl start "$SERVICE_NAME"
info "supervisor配置已完成"
}
# 安装Linker
install_linker() {
local install_type=$1
info "开始安装${SERVICE_NAME}..."
# 安装依赖如果不是Docker方式
if [ "$install_type" != "3" ]; then
install_dependencies
fi
# 根据安装类型进行配置
case "$install_type" in
1|2|4)
# 获取版本和系统信息
info "获取最新版本..."
local version
version=$(get_latest_version)
info "最新版本: $version"
local arch
arch=$(detect_arch)
info "系统架构: $arch"
local libc
libc=$(detect_libc)
info "Libc类型: $libc"
# 下载并安装
download_install "$version" "$arch" "$libc"
# 配置服务
case "$install_type" in
1)
LINKER_IS_DOCKER=1
break
setup_systemctl
;;
2)
LINKER_IS_DOCKER=0
break
setup_supervisor
;;
*)
echo "${red}请输入正确的数字 [1-2]${plain}"
4)
info "直接安装完成,请手动运行: ${INSTALL_DIR}/${SERVICE_NAME}"
;;
esac
done
;;
3)
setup_docker
;;
*)
error "无效的安装类型"
exit 1
;;
esac
info "安装完成!"
}
# 卸载Docker容器
uninstall_docker() {
info "卸载Docker容器..."
if command_exists docker; then
if docker_container_exists; then
info "停止并删除Docker容器: ${DOCKER_CONTAINER}"
docker stop "$DOCKER_CONTAINER" 2>/dev/null || true
docker rm "$DOCKER_CONTAINER" 2>/dev/null || true
info "Docker容器已移除"
else
info "Docker容器不存在无需移除"
fi
# 可选:删除镜像
read -rp "是否删除Docker镜像 ${DOCKER_IMAGE}(y/N): " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
docker rmi "$DOCKER_IMAGE" 2>/dev/null || true
info "Docker镜像已移除"
fi
else
info "Docker 未安装跳过Docker相关卸载"
fi
}
install_base
select_version
# 卸载Linker
uninstall_linker() {
info "开始卸载${SERVICE_NAME}..."
# 停止服务
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
systemctl stop "$SERVICE_NAME"
systemctl disable "$SERVICE_NAME"
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
systemctl daemon-reload
info "已停止并移除systemctl服务"
fi
# 移除supervisor配置
if [ -f "/etc/supervisor/conf.d/${SERVICE_NAME}.conf" ]; then
supervisorctl stop "$SERVICE_NAME" 2>/dev/null || true
rm -f "/etc/supervisor/conf.d/${SERVICE_NAME}.conf"
supervisorctl update 2>/dev/null || true
info "已移除supervisor配置"
fi
# 移除安装文件
if [ -f "${INSTALL_DIR}/${SERVICE_NAME}/${SERVICE_NAME}" ]; then
rm -f "${INSTALL_DIR}/${SERVICE_NAME}"
info "已移除 ${INSTALL_DIR}/${SERVICE_NAME}"
fi
# 清理临时文件
rm -rf "$TEMP_DIR"
# 如果是Docker安装方式卸载Docker容器
read -rp "是否卸载Docker容器(y/N): " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
uninstall_docker
fi
info "卸载完成!"
}
# 显示菜单
show_menu() {
echo "========================================"
echo " ${SERVICE_NAME} 安装管理脚本 "
echo "========================================"
echo "请选择操作:"
echo " 1) 安装 ${SERVICE_NAME}"
echo " 2) 卸载 ${SERVICE_NAME}"
echo " 3) 退出"
echo "========================================"
read -rp "请输入选择 [1-3]: " choice
case $choice in
1)
echo "请选择安装方式:"
echo " 1) 使用 systemctl 管理"
echo " 2) 使用 supervisor 管理"
echo " 3) Docker 容器安装"
echo " 4) 直接安装(手动运行)"
echo "========================================"
read -rp "请输入选择 [1-4]: " install_type
case $install_type in
1|2|3|4)
install_linker "$install_type"
;;
*)
error "无效选择"
exit 1
;;
esac
;;
2)
uninstall_linker
;;
3)
info "退出脚本"
exit 0
;;
*)
error "无效选择"
exit 1
;;
esac
}
if [[ $LINKER_IS_DOCKER == 1 ]]; then
install_docker
elif [[ $LINKER_IS_DOCKER == 0 ]]; then
install_systemd
# 检查root权限
if [ "$(id -u)" -ne 0 ]; then
error "此脚本需要root权限运行"
exit 1
fi
# 主程序
if [ $# -eq 0 ]; then
show_menu
else
case $1 in
install)
if [ $# -gt 1 ]; then
case $2 in
systemctl) install_linker 1 ;;
supervisor) install_linker 2 ;;
docker) install_linker 3 ;;
direct) install_linker 4 ;;
*)
error "无效的安装类型"
echo "用法: $0 install [systemctl|supervisor|docker|direct]"
exit 1
;;
esac
else
echo "用法: $0 install [systemctl|supervisor|docker|direct]"
exit 1
fi
;;
uninstall)
uninstall_linker
;;
*)
echo "用法: $0 [install [systemctl|supervisor|docker|direct] | uninstall]"
exit 1
;;
esac
fi

View File

@@ -1,5 +1,5 @@
v1.9.1
2025-09-16 15:37:57
2025-09-17 00:19:08
1. 一些累计更新
2. 服务器转发多节点
3. 虚拟网卡下伪造ACK为TCP-in-TCP隧道提速