import { Logger , ImperativeError } from "@zowe/imperative" ; import { ClientChannel , Client } from "ssh2" ; import { SshSession } from "./SshSession" ; import { ZosUssMessages } from "./constants/ZosUss.messages" ; let authPos = 0 ; const authsAllowed = [ "none" ] ; let hasAuthFailed = false ; export const startCmdFlag = "@@START OF COMMAND@@" ; export class Shell { public static executeSsh ( session , command , stdoutHandler ) { const promise = new Promise < any > ( ( resolve , reject ) => { if ( session . ISshSession . privateKey != null && session . ISshSession . privateKey !== "undefined" ) { authsAllowed . push ( "publickey" ) ; } if ( session . ISshSession . password != null && session . ISshSession . password !== "undefined" ) { authsAllowed . push ( "password" ) ; } const conn = new Client ( ) ; conn . on ( "ready" , ( ) => { conn . shell ( ( err , stream ) => { if ( err ) { throw err ; } let dataBuffer = "" ; let dataToPrint = "" ; let isUserCommand = false ; let rc ; stream . on ( "exit" , ( exitcode ) => { Logger . getAppLogger ( ) . debug ( "Return Code: " + exitcode ) ; if ( dataBuffer . trim ( ) . length > 1 ) { stdoutHandler ( dataBuffer . slice ( 0 , dataBuffer . lastIndexOf ( "$" ) ) ) ; } rc = exitcode ; } ) ; stream . on ( "close" , ( ) => { Logger . getAppLogger ( ) . debug ( "SSH connection closed" ) ; stdoutHandler ( "\n" ) ; conn . end ( ) ; resolve ( rc ) ; } ) ; stream . on ( "data" , ( data ) => { Logger . getAppLogger ( ) . debug ( "\n[Received data begin]" + data + "[Received data end]\n" ) ; dataBuffer += data ; if ( dataBuffer . includes ( "\r" ) ) { dataToPrint = dataBuffer . slice ( 0 , dataBuffer . lastIndexOf ( "\r" ) ) ; dataBuffer = dataBuffer . slice ( dataBuffer . lastIndexOf ( "\r" ) ) ; if ( dataToPrint . match ( new RegExp ( `\n${ startCmdFlag }` ) ) || dataToPrint . match ( new RegExp ( "\\$ " + startCmdFlag ) ) ) { dataToPrint = dataToPrint . slice ( dataToPrint . indexOf ( startCmdFlag ) + startCmdFlag . length ) ; isUserCommand = true ; } if ( isUserCommand && dataToPrint . match ( /\\$ exit/ ) ) { dataToPrint = dataToPrint . slice ( 0 , dataToPrint . indexOf ( "$ exit" ) ) ; stdoutHandler ( dataToPrint ) ; dataToPrint = "" ; isUserCommand = false ; } else if ( isUserCommand ) { stdoutHandler ( dataToPrint ) ; dataToPrint = "" ; } } } ) ; stream . write ( `export PS1='$ '\necho ${ startCmdFlag }\n${ command }\n` + `exit $?\nexit $?\nexit $?\nexit $?\nexit $?\nexit $?\nexit $?\nexit $?\n` ) ; stream . end ( ) ; } ) ; } ) ; conn . connect ( { host : session . ISshSession . hostname , port : session . ISshSession . port , username : session . ISshSession . user , password : session . ISshSession . password , privateKey : ( session . ISshSession . privateKey != null && session . ISshSession . privateKey !== "undefined" ) ? require ( "fs" ) . readFileSync ( session . ISshSession . privateKey ) : "" , passphrase : session . ISshSession . keyPassphrase , authHandler : Shell . authenticationHandler , readyTimeout : ( session . ISshSession . handshakeTimeout != null && session . ISshSession . handshakeTimeout !== undefined ) ? session . ISshSession . handshakeTimeout : 0 } ) ; conn . on ( "error" , ( err ) => { if ( err . message . includes ( ZosUssMessages . allAuthMethodsFailed . message ) ) { hasAuthFailed = true ; reject ( new ImperativeError ( { msg : ZosUssMessages . allAuthMethodsFailed . message } ) ) ; } else if ( ! hasAuthFailed && err . message . includes ( ZosUssMessages . handshakeTimeout . message ) ) { reject ( new ImperativeError ( { msg : ZosUssMessages . handshakeTimeout . message } ) ) ; } else if ( err . message . includes ( "ECONNREFUSED" ) ) { reject ( new ImperativeError ( { msg : ZosUssMessages . connectionRefused . message + ":\n" + err . message } ) ) ; } else { reject ( new ImperativeError ( { msg : ZosUssMessages . unexpected . message + ":\n" + err . message } ) ) ; } } ) ; } ) ; return promise ; } public static async executeSshCwd ( session , command , cwd , stdoutHandler ) { const cwdCommand = `cd ${ cwd } && ${ command }` ; return this . executeSsh ( session , cwdCommand , stdoutHandler ) ; } returns { Logger } private static get log ( ) : Logger { return Logger . getAppLogger ( ) ; } private static authenticationHandler ( methodsLeft , partialSuccess , callback ) { partialSuccess = true ; if ( authPos === authsAllowed . length ) { return false ; } return authsAllowed [ authPos ++ ] ; } }