自动重连(实用版): pssh ssh -o ReconnectInterval=N 断线(keepalive 发送失败判定)由外层循环自动重连并重跑命令/重开shell(pssh_cmd_ssh 返回-2, pssh main 重试); 依赖 ServerAliveInterval 心跳探测; STATUS/AGENT/CHANGELOG 更新
这个提交包含在:
@@ -107,4 +107,9 @@ session ticket key 支持持久化到文件:新增 `paze_tls_config_set_session_
|
|||||||
- `pssh ssh -A`:开 `auth-agent@openssh.com` 通道,事件循环桥接本地 agent socket 与通道(`ssh_agent_fd`)。
|
- `pssh ssh -A`:开 `auth-agent@openssh.com` 通道,事件循环桥接本地 agent socket 与通道(`ssh_agent_fd`)。
|
||||||
- 限制:PazeSSH 仅 POSIX 可用;Windows agent 为命名管道(无 select fd)运行时明确禁用并提示;无本地 agent 时报 `cannot connect local agent`。
|
- 限制:PazeSSH 仅 POSIX 可用;Windows agent 为命名管道(无 select fd)运行时明确禁用并提示;无本地 agent 时报 `cannot connect local agent`。
|
||||||
|
|
||||||
|
### 11. 自动重连(实用版)(✅ 已完成 2026-08-14)
|
||||||
|
|
||||||
|
- `pssh ssh -o ReconnectInterval=N`:断线(keepalive 发送失败判定)由外层循环自动重连并重新执行命令/重开 shell(pssh_cmd_ssh 返回 -2)。
|
||||||
|
- 说明:依赖 ServerAliveInterval 心跳探测(建议两者搭配);恢复的是"重连+重跑",不恢复进行中 PTY 状态。
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
@@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
## [开发主线] — 2026-08-14
|
## [开发主线] — 2026-08-14
|
||||||
|
|
||||||
|
### 新增 —— 自动重连(实用版)
|
||||||
|
|
||||||
|
- `pssh ssh -o ReconnectInterval=N`:断线(keepalive 心跳发送失败判定)时由外层循环自动重连并**重新执行命令/重开 shell**(`pssh_cmd_ssh` 返回 -2 触发)。
|
||||||
|
- 说明:依赖 `ServerAliveInterval` 心跳探测(建议配 `-o ServerAliveInterval=N -o ReconnectInterval=N`);恢复的是"重连+重跑",不能恢复进行中的 PTY 状态。
|
||||||
|
|
||||||
### 新增 —— agent 转发(-A)
|
### 新增 —— agent 转发(-A)
|
||||||
|
|
||||||
- `pssh ssh -A`:打开 `auth-agent@openssh.com` 通道,事件循环把本地 agent socket 与通道双向桥接(`ssh_agent_fd` 暴露 POSIX fd)。
|
- `pssh ssh -A`:打开 `auth-agent@openssh.com` 通道,事件循环把本地 agent socket 与通道双向桥接(`ssh_agent_fd` 暴露 POSIX fd)。
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
- 通道:session(exec/shell 真实进程执行)、direct-tcpip(出站桥接)
|
- 通道:session(exec/shell 真实进程执行)、direct-tcpip(出站桥接)
|
||||||
- **SFTP 子系统 v3**:OPEN/READ/WRITE/OPENDIR/READDIR/STAT/REALPATH 等,与 OpenSSH sftp / scp -s 互操作(Git OpenSSH 实测通过)
|
- **SFTP 子系统 v3**:OPEN/READ/WRITE/OPENDIR/READDIR/STAT/REALPATH 等,与 OpenSSH sftp / scp -s 互操作(Git OpenSSH 实测通过)
|
||||||
- 客户端命令:
|
- 客户端命令:
|
||||||
- `pssh ssh` — 远程 shell / 执行命令,`-L`/`-R`/`-D` 端口转发,`-J` 代理跳板(单跳)、`-W host:port` stdio 转发、`-N` 仅转发、`-A` agent 转发(POSIX;Windows agent 为命名管道不支持);config 读取 `-F` / `~/.ssh/config`(OpenSSH 兼容) / `~/.pssh/config.conf`;`-o ServerAliveInterval=N` 空闲心跳保活(keepalive@openssh.com)
|
- `pssh ssh` — 远程 shell / 执行命令,`-L`/`-R`/`-D` 端口转发,`-J` 代理跳板(单跳)、`-W host:port` stdio 转发、`-N` 仅转发、`-A` agent 转发(POSIX;Windows agent 为命名管道不支持);config 读取 `-F` / `~/.ssh/config`(OpenSSH 兼容) / `~/.pssh/config.conf`;`-o ServerAliveInterval=N` 空闲心跳保活(keepalive@openssh.com)、`-o ReconnectInterval=N` 断线自动重连(重新执行命令/重开 shell)
|
||||||
|
|
||||||
- `pssh scp` — SFTP 优先(OpenSSH 9.x 默认),回退 legacy SCP;`-r` 递归、`-p` 保留时间戳
|
- `pssh scp` — SFTP 优先(OpenSSH 9.x 默认),回退 legacy SCP;`-r` 递归、`-p` 保留时间戳
|
||||||
- `pssh keygen` / `keyscan` / `copy-id` — 密钥工具
|
- `pssh keygen` / `keyscan` / `copy-id` — 密钥工具
|
||||||
|
|||||||
+19
-1
@@ -18,9 +18,14 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#define PSSH_SLEEP_MS(ms) Sleep(ms)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#define PSSH_SLEEP_MS(ms) usleep((ms) * 1000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int pssh_cmd_ssh(int argc, char **argv);
|
int pssh_cmd_ssh(int argc, char **argv);
|
||||||
|
int pssh_ssh_reconnect_interval(void); /* apps/ssh/main.c */
|
||||||
int pssh_cmd_keygen(int argc, char **argv);
|
int pssh_cmd_keygen(int argc, char **argv);
|
||||||
int pssh_cmd_keyscan(int argc, char **argv);
|
int pssh_cmd_keyscan(int argc, char **argv);
|
||||||
int pssh_cmd_copyid(int argc, char **argv);
|
int pssh_cmd_copyid(int argc, char **argv);
|
||||||
@@ -95,7 +100,20 @@ int main(int argc, char **argv) {
|
|||||||
usage();
|
usage();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (strcmp(cmd, "ssh") == 0) return pssh_cmd_ssh(argc - 1, argv + 1);
|
if (strcmp(cmd, "ssh") == 0) {
|
||||||
|
/* 断线自动重连(实用版):每 ReconnectInterval 秒重新调用 pssh_cmd_ssh
|
||||||
|
* (重连并重新执行命令/重开 shell)。-2 表示断线请求重连。 */
|
||||||
|
for (;;) {
|
||||||
|
int r = pssh_cmd_ssh(argc - 1, argv + 1);
|
||||||
|
if (r == -2 && pssh_ssh_reconnect_interval() > 0) {
|
||||||
|
fprintf(stderr, "pssh: connection lost, reconnecting in %d s...\n",
|
||||||
|
pssh_ssh_reconnect_interval());
|
||||||
|
PSSH_SLEEP_MS(pssh_ssh_reconnect_interval() * 1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (strcmp(cmd, "keygen") == 0) return pssh_cmd_keygen(argc - 1, argv + 1);
|
if (strcmp(cmd, "keygen") == 0) return pssh_cmd_keygen(argc - 1, argv + 1);
|
||||||
if (strcmp(cmd, "keyscan") == 0) return pssh_cmd_keyscan(argc - 1, argv + 1);
|
if (strcmp(cmd, "keyscan") == 0) return pssh_cmd_keyscan(argc - 1, argv + 1);
|
||||||
if (strcmp(cmd, "copy-id") == 0 || strcmp(cmd, "copyid") == 0)
|
if (strcmp(cmd, "copy-id") == 0 || strcmp(cmd, "copyid") == 0)
|
||||||
|
|||||||
+12
-1
@@ -55,6 +55,12 @@ typedef struct {
|
|||||||
/* -v / -c 选项(跨 connect_target 传递) */
|
/* -v / -c 选项(跨 connect_target 传递) */
|
||||||
static int g_verbose = 0;
|
static int g_verbose = 0;
|
||||||
static const char *g_cipher = NULL;
|
static const char *g_cipher = NULL;
|
||||||
|
/* -o ReconnectInterval=N:断线自动重连间隔秒(0=禁用)。断线时 pssh_cmd_ssh 返回 -2,
|
||||||
|
* 由 pssh main 循环重连。 */
|
||||||
|
static int g_reconnect = 0;
|
||||||
|
|
||||||
|
/* 供外层(pssh main)查询自动重连间隔秒 */
|
||||||
|
int pssh_ssh_reconnect_interval(void) { return g_reconnect; }
|
||||||
|
|
||||||
/* 选项缺少参数时的用法提示;缺参返回 -1 */
|
/* 选项缺少参数时的用法提示;缺参返回 -1 */
|
||||||
static int opt_missing(int argc, int i, const char *opt, const char *hint) {
|
static int opt_missing(int argc, int i, const char *opt, const char *hint) {
|
||||||
@@ -343,6 +349,7 @@ int pssh_cmd_ssh(int argc, char **argv) {
|
|||||||
int no_command = 0;
|
int no_command = 0;
|
||||||
int agent_fwd = 0; /* -A:agent 转发(POSIX;Windows agent 为命名管道不支持) */
|
int agent_fwd = 0; /* -A:agent 转发(POSIX;Windows agent 为命名管道不支持) */
|
||||||
char stdio_host[256];
|
char stdio_host[256];
|
||||||
|
int reconnect_flag = 0; /* 断线标记(供自动重连) */
|
||||||
const char *user_cfg = NULL; /* -F <file>:显式指定 config 文件 */
|
const char *user_cfg = NULL; /* -F <file>:显式指定 config 文件 */
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
@@ -445,6 +452,9 @@ int pssh_cmd_ssh(int argc, char **argv) {
|
|||||||
else if (strncmp(kv, "ServerAliveInterval=", 20) == 0) {
|
else if (strncmp(kv, "ServerAliveInterval=", 20) == 0) {
|
||||||
server_alive = atoi(kv + 20);
|
server_alive = atoi(kv + 20);
|
||||||
}
|
}
|
||||||
|
else if (strncmp(kv, "ReconnectInterval=", 18) == 0) {
|
||||||
|
g_reconnect = atoi(kv + 18); /* 断线自动重连间隔秒 */
|
||||||
|
}
|
||||||
} else if (a[0] == '-') {
|
} else if (a[0] == '-') {
|
||||||
continue;
|
continue;
|
||||||
} else if (!host) {
|
} else if (!host) {
|
||||||
@@ -625,7 +635,7 @@ int pssh_cmd_ssh(int argc, char **argv) {
|
|||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now - last_alive >= server_alive) {
|
if (now - last_alive >= server_alive) {
|
||||||
last_alive = now;
|
last_alive = now;
|
||||||
if (ssh_keepalive_send(s) < 0) { fprintf(stderr, "pssh: keepalive send failed\n"); break; }
|
if (ssh_keepalive_send(s) < 0) { fprintf(stderr, "pssh: keepalive send failed\n"); reconnect_flag = 1; break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
@@ -786,5 +796,6 @@ done:
|
|||||||
ssh_session_free(jump);
|
ssh_session_free(jump);
|
||||||
}
|
}
|
||||||
ssh_session_free(s);
|
ssh_session_free(s);
|
||||||
|
if (reconnect_flag && g_reconnect > 0) return -2; /* 通知上层自动重连 */
|
||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户