package com.flyingzl.ssh;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.oro.text.regex.MalformedPatternException;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import expect4j.Closure;
import expect4j.Expect4j;
import expect4j.ExpectState;
import expect4j.matches.EofMatch;
import expect4j.matches.Match;
import expect4j.matches.RegExpMatch;
import expect4j.matches.TimeoutMatch;
publicclass Shell {
privatestatic Logger log = Logger.getLogger(Shell.class);
private Session session;
private ChannelShell channel;
privatestatic Expect4j expect =null;
privatestaticfinallong defaultTimeOut =1000;
private StringBuffer buffer=new StringBuffer();
publicstaticfinalint COMMAND_EXECUTION_SUCCESS_OPCODE =-2;
publicstaticfinal String BACKSLASH_R ="\r";
publicstaticfinal String BACKSLASH_N ="\n";
publicstaticfinal String COLON_CHAR =":";
publicstatic String ENTER_CHARACTER = BACKSLASH_R;
publicstaticfinalint SSH_PORT =22;
//正則匹配,用於處理伺服器返回的結果
publicstatic String[] linuxPromptRegEx =new String[] { "~]#", "~#", "#",
":~#", "/$", ">" };
publicstatic String[] errorMsg=new String[]{"could not acquire the config lock "};
//ssh伺服器的ip地址
private String ip;
//ssh伺服器的登入連接埠
privateint port;
//ssh伺服器的登入使用者名稱
private String user;
//ssh伺服器的登入密碼
private String password;
public Shell(String ip,int port,String user,String password) {
this.ip=ip;
this.port=port;
this.user=user;
this.password=password;
expect = getExpect();
}
/**
* 關閉SSH遠端連線
*/
publicvoid disconnect(){
if(channel!=null){
channel.disconnect();
}
if(session!=null){
session.disconnect();
}
}
/**
* 擷取伺服器返回的資訊
* @return 服務端的執行結果
*/
public String getResponse(){
return buffer.toString();
}
//獲得Expect4j對象,該對用可以往SSH發送命令請求
private Expect4j getExpect() {
try {
log.debug(String.format("Start logging to %s@%s:%s",user,ip,port));
JSch jsch =new JSch();
session = jsch.getSession(user, ip, port);
session.setPassword(password);
Hashtable<String, String> config =new Hashtable<String, String>();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
localUserInfo ui =new localUserInfo();
session.setUserInfo(ui);
session.connect();
channel = (ChannelShell) session.openChannel("shell");
Expect4j expect =new Expect4j(channel.getInputStream(), channel
.getOutputStream());
channel.connect();
log.debug(String.format("Logging to %s@%s:%s successfully!",user,ip,port));
return expect;
} catch (Exception ex) {
log.error("Connect to "+ip+":"+port+"failed,please check your username and password!");
ex.printStackTrace();
}
returnnull;
}
/**
* 執行配置命令
* @param commands 要執行的命令,為字元數組
* @return 執行是否成功
*/
publicboolean executeCommands(String[] commands) {
//如果expect返回為0,說明登入沒有成功
if(expect==null){
returnfalse;
}
log.debug("----------Running commands are listed as follows:----------");
for(String command:commands){
log.debug(command);
}
log.debug("----------End----------");
Closure closure =new Closure() {
publicvoid run(ExpectState expectState) throws Exception {
buffer.append(expectState.getBuffer());// buffer is string
// buffer for appending
// output of executed
// command
expectState.exp_continue();
}
};
List<Match> lstPattern =new ArrayList<Match>();
String[] regEx = linuxPromptRegEx;
if (regEx !=null&& regEx.length >0) {
synchronized (regEx) {
for (String regexElement : regEx) {// list of regx like, :>, />
// etc. it is possible
// command prompts of your
// remote machine
try {
RegExpMatch mat =new RegExpMatch(regexElement, closure);
lstPattern.add(mat);
} catch (MalformedPatternException e) {
returnfalse;
} catch (Exception e) {
returnfalse;
}
}
lstPattern.add(new EofMatch(new Closure() { // should cause
// entire page to be
// collected
publicvoid run(ExpectState state) {
}
}));
lstPattern.add(new TimeoutMatch(defaultTimeOut, new Closure() {
publicvoid run(ExpectState state) {
}
}));
}
}
try {
boolean isSuccess =true;
for (String strCmd : commands){
isSuccess = isSuccess(lstPattern, strCmd);
}
//防止最後一個命令執行不了
isSuccess =!checkResult(expect.expect(lstPattern));
//找不到錯誤資訊標示成功
String response=buffer.toString().toLowerCase();
for(String msg:errorMsg){
if(response.indexOf(msg)>-1){
returnfalse;
}
}
return isSuccess;
} catch (Exception ex) {
ex.printStackTrace();
returnfalse;
}
}
//檢查執行是否成功
privateboolean isSuccess(List<Match> objPattern, String strCommandPattern) {
try {
boolean isFailed = checkResult(expect.expect(objPattern));
if (!isFailed) {
expect.send(strCommandPattern);
expect.send("\r");
returntrue;
}
returnfalse;
} catch (MalformedPatternException ex) {
returnfalse;
} catch (Exception ex) {
returnfalse;
}
}
//檢查執行返回的狀態
privateboolean checkResult(int intRetVal) {
if (intRetVal == COMMAND_EXECUTION_SUCCESS_OPCODE) {
returntrue;
}
returnfalse;
}
//登入SSH時的控制資訊
//設定不提示輸入密碼、不顯示登入資訊等
publicstaticclass localUserInfo implements UserInfo {
String passwd;
public String getPassword() {
return passwd;
}
publicboolean promptYesNo(String str) {
returntrue;
}
public String getPassphrase() {
returnnull;
}
publicboolean promptPassphrase(String message) {
returntrue;
}
publicboolean promptPassword(String message) {
returntrue;
}
publicvoid showMessage(String message) {
}
}
}