ssh-client-wrapper 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/fork.js +7 -3
- package/lib/sshExec.js +46 -10
- package/package.json +2 -2
package/lib/fork.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const debug = require("debug")("sshClientWrapper:fork");
|
|
3
3
|
const debugVerbose = require("debug")("sshClientWrapper:fork_verbose");
|
|
4
|
+
const debugMasterVerbose = require("debug")("sshClientWrapper:fork_masterPty_verbose");
|
|
4
5
|
const debugSendPty = require("debug")("sshClientWrapper:fork_sendpty");
|
|
5
6
|
const { spawn } = require("node-pty");
|
|
6
7
|
|
|
@@ -53,7 +54,7 @@ function sshLoginCallback(output, ptyProcess, pw, ph) {
|
|
|
53
54
|
function getMasterPty(hostInfo) {
|
|
54
55
|
debug("fork pty for master connection");
|
|
55
56
|
const ptyProcess = spawn(nodePtyOpt.shell, [], nodePtyOpt);
|
|
56
|
-
ptyProcess.onData(buff2String.bind(null,
|
|
57
|
+
ptyProcess.onData(buff2String.bind(null, debugMasterVerbose));
|
|
57
58
|
ptyProcess.onData((data)=>{
|
|
58
59
|
const output = data.toString();
|
|
59
60
|
sshLoginCallback(output, ptyProcess, hostInfo.password, hostInfo.passphrase);
|
|
@@ -62,9 +63,10 @@ function getMasterPty(hostInfo) {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
async function fork(hostInfo, cmd, args, outputCallback) {
|
|
65
|
-
|
|
66
|
+
let ptyProcess = null;
|
|
67
|
+
const p = new Promise((resolve, reject)=>{
|
|
66
68
|
debug(`cmd=${cmd}: args=${args}`);
|
|
67
|
-
|
|
69
|
+
ptyProcess = spawn(cmd, args, nodePtyOpt);
|
|
68
70
|
ptyProcess.onData(buff2String.bind(null, debugVerbose));
|
|
69
71
|
ptyProcess.onData((data)=>{
|
|
70
72
|
const output = data.toString();
|
|
@@ -97,6 +99,8 @@ async function fork(hostInfo, cmd, args, outputCallback) {
|
|
|
97
99
|
return reject(err);
|
|
98
100
|
});
|
|
99
101
|
});
|
|
102
|
+
p.pty = ptyProcess;
|
|
103
|
+
return p;
|
|
100
104
|
}
|
|
101
105
|
module.exports = {
|
|
102
106
|
fork,
|
package/lib/sshExec.js
CHANGED
|
@@ -64,6 +64,9 @@ async function canConnect(hostInfo, timeout = 60) {
|
|
|
64
64
|
* @returns {boolean} - true if master session exists
|
|
65
65
|
*/
|
|
66
66
|
async function existsMaster(hostInfo) {
|
|
67
|
+
const ac = new AbortController();
|
|
68
|
+
const signal = ac.signal;
|
|
69
|
+
|
|
67
70
|
const args = getSshOption(hostInfo);
|
|
68
71
|
args.push("-Ocheck");
|
|
69
72
|
|
|
@@ -72,16 +75,39 @@ async function existsMaster(hostInfo) {
|
|
|
72
75
|
output += e;
|
|
73
76
|
};
|
|
74
77
|
|
|
78
|
+
let rt = true; //if timeout occurred during ssh -Ocheck, it will turn to false
|
|
79
|
+
let p;
|
|
80
|
+
const timeout = hostInfo.ConnectTimeout || 60;
|
|
81
|
+
setTimeoutPromise(timeout * 1000, "neverUsedValue", { signal })
|
|
82
|
+
.then(()=>{
|
|
83
|
+
debug(`can not finish master session check within ${timeout} sec`);
|
|
84
|
+
|
|
85
|
+
if (p.pty) {
|
|
86
|
+
p.pty.kill();
|
|
87
|
+
}
|
|
88
|
+
rt = false;
|
|
89
|
+
})
|
|
90
|
+
.catch((err)=>{
|
|
91
|
+
if (err.name === "AbortError") {
|
|
92
|
+
debug("timeout was aborted because master session check has done");
|
|
93
|
+
} else {
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
75
98
|
try {
|
|
76
99
|
debug(`check master session: ${sshCmd} ${args.join(" ")}`);
|
|
77
|
-
|
|
100
|
+
p = fork(hostInfo, sshCmd, args, cb);
|
|
101
|
+
await p;
|
|
102
|
+
ac.abort();
|
|
78
103
|
} catch (err) {
|
|
79
104
|
if (err.rt !== 255) {
|
|
80
105
|
debug("check master session failed", err);
|
|
81
106
|
throw err;
|
|
82
107
|
}
|
|
83
108
|
}
|
|
84
|
-
|
|
109
|
+
|
|
110
|
+
return rt && !/Control socket connect.*: No such file or directory/.test(output);
|
|
85
111
|
}
|
|
86
112
|
|
|
87
113
|
/**
|
|
@@ -94,24 +120,33 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
94
120
|
return;
|
|
95
121
|
}
|
|
96
122
|
debug(`create master ssh session to ${hostInfo.host}`);
|
|
97
|
-
|
|
98
|
-
hostInfo.masterPty
|
|
123
|
+
|
|
124
|
+
if (!hostInfo.masterPty) {
|
|
125
|
+
debug(`create master pty for ${hostInfo.host}`);
|
|
126
|
+
hostInfo.masterPty = getMasterPty(hostInfo);
|
|
127
|
+
} else {
|
|
128
|
+
debug(`master pty for ${hostInfo.host} exists`);
|
|
129
|
+
}
|
|
99
130
|
|
|
100
131
|
const ac = new AbortController();
|
|
101
132
|
const signal = ac.signal;
|
|
102
133
|
setTimeoutPromise(timeout * 1000, "neverUsedValue", { signal })
|
|
103
134
|
.then(()=>{
|
|
135
|
+
debug(`can not connect within ${timeout} sec`);
|
|
136
|
+
hostInfo.masterPty.write("\n");
|
|
137
|
+
hostInfo.masterPty.write("exit\n");
|
|
138
|
+
|
|
104
139
|
const err = new Error(`can not connect within ${timeout} sec`);
|
|
105
140
|
err.host = hostInfo.host;
|
|
106
141
|
err.user = hostInfo.user;
|
|
107
142
|
err.port = hostInfo.port;
|
|
108
|
-
pty.write("\n");
|
|
109
|
-
pty.write("exit\n");
|
|
110
143
|
throw err;
|
|
111
144
|
})
|
|
112
145
|
.catch((err)=>{
|
|
113
146
|
if (err.name === "AbortError") {
|
|
114
147
|
debug("timeout was aborted because connection check has done");
|
|
148
|
+
} else {
|
|
149
|
+
throw err;
|
|
115
150
|
}
|
|
116
151
|
});
|
|
117
152
|
|
|
@@ -119,8 +154,9 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
119
154
|
const re = new RegExp(`^${words}`);
|
|
120
155
|
const p = new Promise((resolve, reject)=>{
|
|
121
156
|
let done = false;
|
|
122
|
-
|
|
157
|
+
hostInfo.masterPty.onData((data)=>{
|
|
123
158
|
if (done) {
|
|
159
|
+
//safeguard
|
|
124
160
|
return;
|
|
125
161
|
}
|
|
126
162
|
const output = data.toString();
|
|
@@ -128,7 +164,7 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
128
164
|
|
|
129
165
|
if (re.test(output)) {
|
|
130
166
|
debug("test done. call exit on remotehost");
|
|
131
|
-
|
|
167
|
+
hostInfo.masterPty.write("exit\n");
|
|
132
168
|
done = true;
|
|
133
169
|
resolve();
|
|
134
170
|
} else if (/Permission denied \(/.test(output)) {
|
|
@@ -141,7 +177,7 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
141
177
|
reject(new Error("Operation timed out"));
|
|
142
178
|
}
|
|
143
179
|
});
|
|
144
|
-
|
|
180
|
+
hostInfo.masterPty.onExit((e)=>{
|
|
145
181
|
debug("master pty exit", e);
|
|
146
182
|
resolve(e);
|
|
147
183
|
});
|
|
@@ -151,7 +187,7 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
151
187
|
args.push(`echo ${words}`);
|
|
152
188
|
const cmd = `${sshCmd} ${args.join(" ")}\n`;
|
|
153
189
|
debugVerbose(cmd);
|
|
154
|
-
|
|
190
|
+
hostInfo.masterPty.write(cmd);
|
|
155
191
|
await p;
|
|
156
192
|
ac.abort();
|
|
157
193
|
debug(`connected to ${hostInfo.host}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh-client-wrapper",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "wrapper library for open ssh client and rsync",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ssh",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"debug": "^4.3.4",
|
|
38
|
-
"glob": "^10.3.
|
|
38
|
+
"glob": "^10.3.3",
|
|
39
39
|
"node-pty": "^0.10.1",
|
|
40
40
|
"nyc": "^15.1.0",
|
|
41
41
|
"promise-retry": "^2.0.1"
|