ssh-client-wrapper 2.11.0 → 2.13.0

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/index.js CHANGED
@@ -1,22 +1,24 @@
1
- "use strict";
2
- const fs = require("fs");
3
- const path = require("path");
4
- const debug = require("debug")("sshClientWrapper:interface");
5
- const debugVerbose = require("debug")("sshClientWrapper:verbose:interface");
6
- const { sshExec, canConnect, disconnect, ls } = require("./sshExec.js");
7
- const { send, recv } = require("./rsyncExec.js");
8
- const { isArrayOfString, sanityCheck } = require("./util.js");
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import Debug from "debug";
4
+ import * as sshExecModule from "./sshExec.js";
5
+ import * as rsyncExecModule from "./rsyncExec.js";
6
+ import { isArrayOfString, sanityCheck } from "./util.js";
7
+
8
+ const debug = Debug("sshClientWrapper:debug:interface");
9
+ const debugVerbose = Debug("sshClientWrapper:verbose:interface");
9
10
 
10
11
  /**
11
12
  * Integer means integer number but it is defined as Object for now
12
13
  * workaround for eslint-plugin-jsdoc's no-undefined-types rule bug.
13
- * @typedef {Object} Integer
14
+ * @typedef {object} Integer
15
+ * @property
14
16
  */
15
17
 
16
18
  /**
17
19
  * host info object which contain all settings for 1 remote host
18
20
  * it will be (shallow) copied in constructor.
19
- * @param {Object} hostInfo - option object
21
+ * @param {object} hostInfo - option object
20
22
  * @param {string} hostInfo.host - destination
21
23
  * @param {string} hostInfo.user - login name
22
24
  * @param {number} hostInfo.port - port number
@@ -24,32 +26,33 @@ const { isArrayOfString, sanityCheck } = require("./util.js");
24
26
  * @param {string | Function} hostInfo.passphrase - passphrase or function which return passphrase of private key
25
27
  * @param {string} hostInfo.keyFile - private key filename
26
28
  * @param {boolean} hostInfo.noStrictHostKeyChecking - bypass host key checking if true
27
- * @param {Integer} hostInfo.ControlPersist=180 - how long keep master connection after last client connection has been closed (sec).
28
- * @param {Integer} hostInfo.ConnectTimeout=60 - timeout used when connectiong to ths SSH server
29
- * @param {Integer} hostInfo.maxRetry=3 - max number of retry
30
- * @param {Integer} hostInfo.retryDuration=1000 - duration time (msec) between each retry
29
+ * @param {Integer} hostInfo.ControlPersist - how long keep master connection after last client connection has been closed (sec).
30
+ * @param {Integer} hostInfo.ConnectTimeout - timeout used when connectiong to ths SSH server
31
+ * @param {Integer} hostInfo.maxRetry - max number of retry
32
+ * @param {Integer} hostInfo.retryDuration - duration time (msec) between each retry
31
33
  * @param {string} hostInfo.rcfile - rcfile path which will be sourced before executing actual command
32
34
  * @param {string} hostInfo.prependCmd - command string which will be executed before executing actual command
33
35
  * @param {string[]} hostInfo.sshOpt - additional options for ssh
34
36
  */
35
37
 
36
- const logAndReject = (message) => {
38
+ const logAndReject = (message)=>{
37
39
  debug(message);
38
40
  return Promise.reject(new Error(message));
39
41
  };
40
42
 
41
43
  /**
42
44
  * Facade class.
43
- * @constructor
45
+ * @class
44
46
  */
45
47
  class SshClientWrapper {
46
- constructor (hostInfo) {
48
+ constructor(hostInfo, deps = { ...sshExecModule, ...rsyncExecModule }) {
47
49
  debug("constructor called for", hostInfo.host);
48
50
  debugVerbose("hostInfo=", hostInfo);
49
51
  this.hostInfo = { ...hostInfo };
50
52
  sanityCheck(this.hostInfo);
51
53
  this.hostInfo.masterPty = null;
52
54
  this.hostInfo.rsyncVersion = null;
55
+ this.deps = deps;
53
56
 
54
57
  if (process.env.HOME) {
55
58
  try {
@@ -73,13 +76,46 @@ class SshClientWrapper {
73
76
  *
74
77
  * note rcfile and prependCmd override hostInfo.rcfile and hostInfo.prependCmd respectively
75
78
  */
76
- async exec (cmd, timeout = 0, outputCallback = null, rcfile = null, prependCmd = null) {
79
+ async exec(cmd, timeout = 0, outputCallback = null, rcfile = null, prependCmd = null) {
77
80
  debug("exec called", cmd);
78
81
 
79
82
  if (typeof cmd !== "string" || cmd === "") {
80
83
  return logAndReject("cmd must be string");
81
84
  }
82
- return sshExec(this.hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd);
85
+ return this.deps.sshExec(this.hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd);
86
+ }
87
+
88
+ /**
89
+ * execute command on remote host and get output text
90
+ * @param {string} cmd - Cmdline which will be executed.
91
+ * @param {Integer} timeout - timeout in secconds
92
+ * @param {string} rcfile - rcfile path which will be sourced before executing actual command
93
+ * @param {string} prependCmd - command string which will be executed before executing actual command
94
+ * @returns {object} - output test array (output) and return value of cmd (rt)
95
+ */
96
+ async execAndGetOutput(cmd, timeout = 0, rcfile = null, prependCmd = null) {
97
+ debug("execAndGetOutput called", cmd);
98
+ const outputArray = [];
99
+ const cb = (data)=>{
100
+ outputArray.push(data);
101
+ };
102
+ let rt = null;
103
+ try {
104
+ rt = await this.deps.sshExec(this.hostInfo, cmd, timeout, cb, rcfile, prependCmd);
105
+ } catch (e) {
106
+ //non-zero retrun should be allowed
107
+ if (typeof e.rt === "undefined" || e.rt < 0) {
108
+ throw e;
109
+ }
110
+ }
111
+ const output = outputArray.length === 0
112
+ ? []
113
+ : outputArray
114
+ .join("\n")
115
+ .replace(/\n+/g, "\n")
116
+ .replace(/\n$/, "")
117
+ .split("\n");
118
+ return { output, rt };
83
119
  }
84
120
 
85
121
  /**
@@ -89,9 +125,22 @@ class SshClientWrapper {
89
125
  * @param {Integer} timeout - timeout in secconds
90
126
  * @returns {string[]} - output from ls
91
127
  */
92
- async ls (target, lsOpt = [], timeout = 0) {
128
+ async ls(target, lsOpt = [], timeout = 0) {
93
129
  debug("ls called", target, lsOpt);
94
- return ls(this.hostInfo, target, lsOpt, timeout);
130
+ const { output, rt } = await this.execAndGetOutput(`ls ${lsOpt.join(" ")} ${target}`, timeout);
131
+ return rt === 0 ? output : rt;
132
+ }
133
+
134
+ /**
135
+ * execute series of comands like expect(3)
136
+ * @param {string} cmd - Cmdline which will be executed.
137
+ * @param {Array} expects - array of expect and send string pairs
138
+ * @param {Integer} timeout - timeout in secconds
139
+ * @returns {Integer} - Return code of cmd.
140
+ */
141
+ async expect(cmd, expects, timeout = 180) {
142
+ debug("expect called", expects);
143
+ return this.deps.expect(this.hostInfo, cmd, expects, timeout);
95
144
  }
96
145
 
97
146
  /**
@@ -102,13 +151,13 @@ class SshClientWrapper {
102
151
  * @param {Integer} timeout - timeout in secconds
103
152
  * @returns {Promise} - Resolved with undefined when file transfer is done.
104
153
  */
105
- async send (src, dst, opt = [], timeout = 0) {
154
+ async send(src, dst, opt = [], timeout = 0) {
106
155
  debug("send called", src, dst, opt);
107
156
 
108
157
  if (!isArrayOfString(src)) {
109
158
  return logAndReject("src must be array of string");
110
159
  }
111
- if (!src.some((e) => {
160
+ if (!src.some((e)=>{
112
161
  return e !== "";
113
162
  })) {
114
163
  return logAndReject("src must contain non-empty string");
@@ -122,7 +171,7 @@ class SshClientWrapper {
122
171
  if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
123
172
  return logAndReject("opt must be array of string");
124
173
  }
125
- return send(this.hostInfo, src, dst, opt, timeout);
174
+ return this.deps.send(this.hostInfo, src, dst, opt, timeout);
126
175
  }
127
176
 
128
177
  /**
@@ -133,13 +182,13 @@ class SshClientWrapper {
133
182
  * @param {Integer} timeout - timeout in secconds
134
183
  * @returns {Promise} - Resolved with undefined when file transfer is done.
135
184
  */
136
- async recv (src, dst, opt = [], timeout = 0) {
185
+ async recv(src, dst, opt = [], timeout = 0) {
137
186
  debug("recv called", src, dst, opt);
138
187
 
139
188
  if (!isArrayOfString(src)) {
140
189
  return logAndReject("src must be array of string");
141
190
  }
142
- if (!src.some((e) => {
191
+ if (!src.some((e)=>{
143
192
  return e !== "";
144
193
  })) {
145
194
  return logAndReject("src must contain non-empty string");
@@ -153,7 +202,7 @@ class SshClientWrapper {
153
202
  if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
154
203
  return logAndReject("opt must be array of string");
155
204
  }
156
- return recv(this.hostInfo, src, dst, opt, timeout);
205
+ return this.deps.recv(this.hostInfo, src, dst, opt, timeout);
157
206
  }
158
207
 
159
208
  /**
@@ -161,23 +210,23 @@ class SshClientWrapper {
161
210
  * @param {Integer} timeout - timeout in secconds
162
211
  * @returns {Promise} - Resolved with true on success, otherwise rejected with Error.
163
212
  */
164
- async canConnect (timeout = 60) {
213
+ async canConnect(timeout = 60) {
165
214
  debug("canConnect called with timeout=", timeout);
166
215
  debugVerbose("hostInfo=", this.hostInfo);
167
216
 
168
217
  if (!Number.isInteger(timeout) || timeout <= 0) {
169
218
  return logAndReject("timeout must be positive integer");
170
219
  }
171
- return canConnect(this.hostInfo, timeout);
220
+ return this.deps.canConnect(this.hostInfo, timeout);
172
221
  }
173
222
 
174
223
  /**
175
224
  * Disconnect master session
176
225
  */
177
- async disconnect () {
226
+ async disconnect() {
178
227
  debug(`disconnect from ${this.hostInfo.host} called`);
179
- disconnect(this.hostInfo);
228
+ this.deps.disconnect(this.hostInfo);
180
229
  }
181
230
  }
182
231
 
183
- module.exports = SshClientWrapper;
232
+ export default SshClientWrapper;
package/lib/rsyncExec.js CHANGED
@@ -1,18 +1,19 @@
1
- "use strict";
2
- const path = require("path");
3
- const fs = require("fs/promises");
4
- const debug = require("debug")("sshClientWrapper:debug:rsyncExec");
5
- const debugVerbose = require("debug")("sshClientWrapper:verbose:rsyncExec");
6
- const { glob, hasMagic } = require("glob");
1
+ import path from "path";
2
+ import fs from "fs/promises";
3
+ import Debug from "debug";
4
+ import { glob, hasMagic } from "glob";
7
5
 
8
- const { connect, retryWrapper, sshExec } = require("./sshExec.js");
9
- const { fork } = require("./fork.js");
6
+ import { connect, retryWrapper, sshExec } from "./sshExec.js";
7
+ import { fork } from "./fork.js";
10
8
 
11
- const { sshCmd, rsyncCmd, acceptableRsyncRetrunCodes, getSshOption } = require("./util.js");
9
+ import { sshCmd, rsyncCmd, acceptableRsyncRetrunCodes, getSshOption } from "./util.js";
12
10
 
13
- async function checkRsyncVersion (timeout) {
11
+ const debug = Debug("sshClientWrapper:debug:rsyncExec");
12
+ const debugVerbose = Debug("sshClientWrapper:verbose:rsyncExec");
13
+
14
+ export async function checkRsyncVersion(timeout) {
14
15
  const rt = {};
15
- await fork(null, rsyncCmd, ["--version"], timeout, (output) => {
16
+ await fork(null, rsyncCmd, ["--version"], timeout, (output)=>{
16
17
  const result = /^rsync *version *(\d+)\.(\d+)\.(\d+).*$/m.exec(output);
17
18
 
18
19
  if (result) {
@@ -26,7 +27,7 @@ async function checkRsyncVersion (timeout) {
26
27
 
27
28
  /**
28
29
  * Send file or directory and its child to server.
29
- * @param {Object} hostInfo - hostinfo object
30
+ * @param {object} hostInfo - hostinfo object
30
31
  * @param {[string]} argSrc - File or directory name which to be send.
31
32
  * @param {string} dst - Destination path.
32
33
  * @param {[string]} rsyncOpt - optional arguments for rsync
@@ -34,13 +35,13 @@ async function checkRsyncVersion (timeout) {
34
35
  *
35
36
  * this function execute rsync -avv --copy-unsafe-links src rsync://[user@]host[:port]/dst
36
37
  */
37
- async function send (hostInfo, argSrc, dst, rsyncOpt, timeout) {
38
+ export async function send(hostInfo, argSrc, dst, rsyncOpt, timeout) {
38
39
  await connect(hostInfo);
39
- const promises = await Promise.allSettled(argSrc.map((e) => {
40
+ const promises = await Promise.allSettled(argSrc.map((e)=>{
40
41
  return glob(e);
41
42
  }));
42
43
  const src = promises
43
- .map((e, i) => {
44
+ .map((e, i)=>{
44
45
  if (e.status !== "fulfilled") {
45
46
  return null;
46
47
  }
@@ -49,10 +50,10 @@ async function send (hostInfo, argSrc, dst, rsyncOpt, timeout) {
49
50
  }
50
51
  return e.value;
51
52
  })
52
- .filter((e) => {
53
+ .filter((e)=>{
53
54
  return e !== null;
54
55
  })
55
- .flatMap((e) => {
56
+ .flatMap((e)=>{
56
57
  return e;
57
58
  });
58
59
 
@@ -85,14 +86,14 @@ async function send (hostInfo, argSrc, dst, rsyncOpt, timeout) {
85
86
 
86
87
  /**
87
88
  * format src array to src string for recv
88
- * @param {Object} hostInfo - hostinfo object
89
+ * @param {object} hostInfo - hostinfo object
89
90
  * @returns {boolean} - rsync command version is higher than 3.2.4 or not
90
91
  */
91
- async function isNewRsync (hostInfo) {
92
- if (!hostInfo.rsyncVersion ||
93
- !Number.isInteger(hostInfo.rsyncVersion.major) ||
94
- !Number.isInteger(hostInfo.rsyncVersion.minor) ||
95
- !Number.isInteger(hostInfo.rsyncVersion.patch)) {
92
+ async function isNewRsync(hostInfo) {
93
+ if (!hostInfo.rsyncVersion
94
+ || !Number.isInteger(hostInfo.rsyncVersion.major)
95
+ || !Number.isInteger(hostInfo.rsyncVersion.minor)
96
+ || !Number.isInteger(hostInfo.rsyncVersion.patch)) {
96
97
  hostInfo.rsyncVersion = await checkRsyncVersion(3);
97
98
  }
98
99
  return hostInfo.rsyncVersion.major >= 3 && hostInfo.rsyncVersion.minor >= 2 && hostInfo.rsyncVersion.patch >= 4;
@@ -100,7 +101,7 @@ async function isNewRsync (hostInfo) {
100
101
 
101
102
  /**
102
103
  * Send file or directory and its child to server.
103
- * @param {Object} hostInfo - hostinfo object
104
+ * @param {object} hostInfo - hostinfo object
104
105
  * @param {[string]} argSrc - File or directory name which to be send.
105
106
  * @param {string} dst - Destination path.
106
107
  * @param {[string]} rsyncOpt - optional arguments for rsync
@@ -108,7 +109,7 @@ async function isNewRsync (hostInfo) {
108
109
  *
109
110
  * this function execute rsync -av --copy-unsafe-links rsync://[user@]host[:port]/src dst
110
111
  */
111
- async function recv (hostInfo, argSrc, dst, rsyncOpt, timeout) {
112
+ export async function recv(hostInfo, argSrc, dst, rsyncOpt, timeout) {
112
113
  await connect(hostInfo);
113
114
  const src = `${hostInfo.host}:${argSrc.join(" ")}`;
114
115
  const sshArgs = getSshOption(hostInfo, true);
@@ -118,7 +119,7 @@ async function recv (hostInfo, argSrc, dst, rsyncOpt, timeout) {
118
119
  args.unshift("--old-args");
119
120
  }
120
121
 
121
- // make destination directory
122
+ //make destination directory
122
123
  const dstDir = argSrc.len > 1 || hasMagic(argSrc[0]) ? dst : path.dirname(dst);
123
124
  await fs.mkdir(dstDir, { recursive: true });
124
125
 
@@ -126,8 +127,3 @@ async function recv (hostInfo, argSrc, dst, rsyncOpt, timeout) {
126
127
  debugVerbose(`recv: ${rsyncCmd} ${args.join(" ")}`);
127
128
  return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout, null, acceptableRsyncRetrunCodes), hostInfo);
128
129
  }
129
- module.exports = {
130
- checkRsyncVersion,
131
- send,
132
- recv
133
- };
package/lib/sshExec.js CHANGED
@@ -1,22 +1,28 @@
1
- "use strict";
2
- const crypto = require("crypto");
3
- const { setTimeout: setTimeoutPromise } = require("timers/promises");
4
- const debug = require("debug")("sshClientWrapper:debug:sshExec");
5
- const debugVerbose = require("debug")("sshClientWrapper:verbose:sshExec");
6
- const debugSendMasterPty = require("debug")("sshClientWrapperInsecure:sshExec_masterPty");
7
- const { sendPty, sshCmd, getSshOption, sanityCheck, watchDogTimer } = require("./util.js");
8
- const { fork, createMasterPty, sshLoginCallback } = require("./fork.js");
1
+ import crypto from "crypto";
2
+ import { setTimeout as setTimeoutPromise } from "timers/promises";
3
+ import Debug from "debug";
4
+ import { sendPty, sshCmd, getSshOption, sanityCheck, watchDogTimer } from "./util.js";
5
+ import { fork, createMasterPty, sshLoginCallback } from "./fork.js";
6
+
7
+ const debug = Debug("sshClientWrapper:debug:sshExec");
8
+ const debugVerbose = Debug("sshClientWrapper:verbose:sshExec");
9
+ const debugSendMasterPty = Debug("sshClientWrapper:insecure:sshExec:masterPty");
10
+
11
+ /**
12
+ * @typedef Integer {number} - integer number
13
+ */
9
14
 
10
15
  /**
11
16
  * Execute command on remote host.
12
- * @param {Object} hostInfo - hostinfo object
17
+ * @param {object} hostInfo - hostinfo object
13
18
  * @param {string} cmd - Cmdline which will be executed on remote host.
14
19
  * @param {number} timeout - timeout (in sec.)
20
+ * @param {Function} outputCallback - Callback routine for stdout and stderr.
15
21
  * @param {string} rcfile - rcfile path which will be sourced before executing actual command
16
22
  * @param {string} prependCmd - command string which will be executed before executing actual command
17
- * @param {Function} outputCallback - Callback routine for stdout and stderr.
23
+ * @returns {Integer} - return value of cmd
18
24
  */
19
- async function sshExec (hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd) {
25
+ export async function sshExec(hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd) {
20
26
  debug("sshExec called");
21
27
  await connect(hostInfo);
22
28
  debug("exec", cmd, "on remote server");
@@ -44,11 +50,42 @@ async function sshExec (hostInfo, cmd, timeout, outputCallback, rcfile, prependC
44
50
  }
45
51
  if (e.rt === 126) {
46
52
  debug(`${cmd} got Permission deny error but ignored`);
47
- return e.rt;
48
53
  }
49
54
  if (e.rt === 127) {
50
55
  debug(`${cmd} not found but ignored`);
51
- return e.rt;
56
+ }
57
+ return e.rt;
58
+ }
59
+ }
60
+
61
+ export async function expect(hostInfo, cmd, expects, timeout) {
62
+ debug("expect called");
63
+ await connect(hostInfo);
64
+ debugVerbose("exec", cmd, "on remote server with", expects);
65
+ const args = getSshOption(hostInfo, false);
66
+ const compiledExpects = expects.map((e)=>{
67
+ return {
68
+ reExpect: new RegExp(e.expect),
69
+ send: e.send,
70
+ keep: e.keep
71
+ };
72
+ });
73
+ const reExpect = hostInfo.prompt ? new RegExp(hostInfo.prompt) : /[$#>] /;
74
+ compiledExpects.unshift({ reExpect, send: cmd });
75
+ compiledExpects.push({ reExpect, send: "exit" });
76
+
77
+ try {
78
+ const rt = await fork(hostInfo, sshCmd, args, timeout, null, [], compiledExpects);
79
+ return rt;
80
+ } catch (e) {
81
+ if (typeof e.rt === "undefined") {
82
+ throw e;
83
+ }
84
+ if (e.rt === 126) {
85
+ debug(`${cmd} got Permission deny error but ignored`);
86
+ }
87
+ if (e.rt === 127) {
88
+ debug(`${cmd} not found but ignored`);
52
89
  }
53
90
  return e.rt;
54
91
  }
@@ -56,32 +93,32 @@ async function sshExec (hostInfo, cmd, timeout, outputCallback, rcfile, prependC
56
93
 
57
94
  /**
58
95
  * Check if you can connect to sepecified server
59
- * @param {Object} hostInfo - hostinfo object
96
+ * @param {object} hostInfo - hostinfo object
60
97
  * @param {Integer} timeout - timeout in secconds
61
98
  */
62
- async function canConnect (hostInfo, timeout = 60) {
99
+ export async function canConnect(hostInfo, timeout) {
63
100
  debug("canConnect called");
64
101
  sanityCheck(hostInfo);
65
102
 
66
103
  await connect(hostInfo, timeout);
67
104
  await disconnect(hostInfo);
68
- // if connection fail, Error is throwed.
69
- // so, if we reach here, connection must be succeeded
105
+ //if connection fail, Error is throwed.
106
+ //so, if we reach here, connection must be succeeded
70
107
  return true;
71
108
  }
72
109
 
73
110
  /**
74
111
  * check if master session exists or not
75
- * @param {Object} hostInfo - hostinfo object
112
+ * @param {object} hostInfo - hostinfo object
76
113
  * @returns {boolean} - true if master session exists
77
114
  */
78
- async function existsMaster (hostInfo, argTimeout) {
115
+ async function existsMaster(hostInfo, argTimeout) {
79
116
  debugVerbose("existsMaster called");
80
117
  const args = getSshOption(hostInfo);
81
118
  args.push("-Ocheck");
82
119
 
83
120
  let output = "";
84
- const cb = (e) => {
121
+ const cb = (e)=>{
85
122
  output += e;
86
123
  };
87
124
 
@@ -105,9 +142,9 @@ async function existsMaster (hostInfo, argTimeout) {
105
142
 
106
143
  /**
107
144
  * create master ssh connection
108
- * @param {Object} hostInfo - hostinfo object
145
+ * @param {object} hostInfo - hostinfo object
109
146
  */
110
- async function connect (hostInfo, timeout = 60) {
147
+ export async function connect(hostInfo, timeout = 60) {
111
148
  debug("connect called");
112
149
 
113
150
  if (await existsMaster(hostInfo, timeout)) {
@@ -123,8 +160,9 @@ async function connect (hostInfo, timeout = 60) {
123
160
  const re = new RegExp(`^${words}`);
124
161
 
125
162
  let ac = null;
126
- const p = new Promise((resolve, reject) => {
127
- ac = watchDogTimer(timeout, "make connection", () => {
163
+ let output = null;
164
+ const p = new Promise((resolve, reject)=>{
165
+ ac = watchDogTimer(timeout, "make connection", ()=>{
128
166
  sendPty(hostInfo.masterPty, "\n", debugSendMasterPty, 137);
129
167
  sendPty(hostInfo.masterPty, "exit\n", debugSendMasterPty, 138);
130
168
 
@@ -132,16 +170,17 @@ async function connect (hostInfo, timeout = 60) {
132
170
  err.host = hostInfo.host;
133
171
  err.user = hostInfo.user;
134
172
  err.port = hostInfo.port;
173
+ err.output = output;
135
174
  reject(err);
136
175
  });
137
176
 
138
177
  let done = false;
139
- hostInfo.masterPty.onData((data) => {
178
+ hostInfo.masterPty.onData((data)=>{
140
179
  if (done) {
141
- // safeguard
180
+ //safeguard
142
181
  return;
143
182
  }
144
- const output = data.toString();
183
+ output = data.toString();
145
184
  debugVerbose(output);
146
185
 
147
186
  if (re.test(output)) {
@@ -160,12 +199,12 @@ async function connect (hostInfo, timeout = 60) {
160
199
  reject(new Error("Control socket creation failed"));
161
200
  }
162
201
  });
163
- hostInfo.masterPty.onData((data) => {
202
+ hostInfo.masterPty.onData((data)=>{
164
203
  const output = data.toString();
165
204
  sshLoginCallback(output, hostInfo.masterPty, hostInfo.password, hostInfo.passphrase, debugSendMasterPty)
166
205
  .catch(reject);
167
206
  });
168
- hostInfo.masterPty.onExit((e) => {
207
+ hostInfo.masterPty.onExit((e)=>{
169
208
  debug("master pty exit", e);
170
209
  resolve(e);
171
210
  });
@@ -188,9 +227,9 @@ async function connect (hostInfo, timeout = 60) {
188
227
 
189
228
  /**
190
229
  * remove master session
191
- * @param {Object} hostInfo - hostinfo object
230
+ * @param {object} hostInfo - hostinfo object
192
231
  */
193
- async function disconnect (hostInfo) {
232
+ export async function disconnect(hostInfo) {
194
233
  debug("disconnect called");
195
234
 
196
235
  if (await existsMaster(hostInfo)) {
@@ -198,7 +237,7 @@ async function disconnect (hostInfo) {
198
237
  args.push("-Oexit");
199
238
 
200
239
  let output = "";
201
- const cb = (e) => {
240
+ const cb = (e)=>{
202
241
  output += e;
203
242
  };
204
243
  try {
@@ -225,50 +264,22 @@ async function disconnect (hostInfo) {
225
264
  }
226
265
  }
227
266
 
228
- /**
229
- * execute ls command and return output
230
- * @param {Object} hostInfo - hostinfo object
231
- * @param {string} target - file or directory path to watch
232
- * @param {string[]} lsOpt - optional arguments for ls
233
- * @param {number} timeout - timeout (in sec.)
234
- * @returns {string[]} - output from ls
235
- */
236
- async function ls (hostInfo, target, lsOpt, timeout) {
237
- debug("ls called");
238
- const outputArray = [];
239
- try {
240
- await sshExec(hostInfo, `ls ${lsOpt.join(" ")} ${target}`, timeout, (data) => {
241
- outputArray.push(data);
242
- });
243
- } catch (e) {
244
- // non-zero retrun should be allowed
245
- if (typeof e.rt === "undefined" || e.rt < 0) {
246
- throw e;
247
- }
248
- }
249
- return outputArray.length === 0
250
- ? []
251
- : outputArray
252
- .join("\n")
253
- .replace(/\n+/g, "\n")
254
- .replace(/\n$/, "")
255
- .split("\n");
256
- }
257
-
258
267
  /**
259
268
  * retry fork() if it throw retryable error
260
269
  * @param {Function} func - function to be wrapped
261
- * @param {Object} hostInfo - hostinfo object
270
+ * @param {object} hostInfo - hostinfo object
262
271
  * @param {Integer} argMaxRetry - max retry number
272
+ * @param {Integer} argDuration - time to wait before retring
273
+ * @returns {Integer} - return value of func
263
274
  */
264
- async function retryWrapper (func, hostInfo, argMaxRetry, argDuration) {
275
+ export async function retryWrapper(func, hostInfo, argMaxRetry, argDuration) {
265
276
  const maxRetry = argMaxRetry || hostInfo.maxRetry || 3;
266
277
  const duration = argDuration || hostInfo.retryDuration || 1000;
267
278
  if (maxRetry === 1) {
268
279
  return func();
269
280
  }
270
281
  let retryCount = 0;
271
- while (true) {
282
+ for (;;) {
272
283
  let rt;
273
284
  try {
274
285
  rt = await func();
@@ -291,12 +302,3 @@ async function retryWrapper (func, hostInfo, argMaxRetry, argDuration) {
291
302
  }
292
303
  }
293
304
  }
294
-
295
- module.exports = {
296
- sshExec,
297
- canConnect,
298
- connect,
299
- disconnect,
300
- ls,
301
- retryWrapper
302
- };