Java通过jsch远程操作Linux

package com.ssh.example;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.StringWriter;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.net.SocketOptions;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.ChannelDirectTCPIP;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;
import com.jcraft.jsch.SocketFactory;

/**
 * 
 * @author 黄坤平
 *
 */
public class SshHelper implements SocketImplFactory, com.jcraft.jsch.Logger {

	private static final Logger LOG = LoggerFactory.getLogger(SshHelper.class
			.getName());

	private static final int DEFAULT_SSH_PORT = 22;

	private static final int EOF = -1;

	private static final String DEFAULT_CHARSET = "utf-8";

	private Session session;

	public static SshHelper create(String host, String user, String passwd)
			throws JSchException, IOException {
		return new SshHelper(host, user, passwd);
	}

	private SshHelper(String host, String user, String passwd)
			throws JSchException, IOException {

		JSch jsch = new JSch();
		JSch.setLogger(this);

		session = jsch.getSession(user, host, DEFAULT_SSH_PORT);
		session.setConfig("StrictHostKeyChecking", "no");
		session.setPassword(passwd);
		session.setDaemonThread(true);
		session.setSocketFactory(new SshSocketFactory());
		session.connect();
		if (!session.isConnected()) {
			throw new IOException("Session not connected");
		}
	}

	public void close() {
		if (session != null && session.isConnected()) {
			session.disconnect();
		}
	}

	@Override
	public boolean isEnabled(int level) {
		switch (level) {
		case com.jcraft.jsch.Logger.DEBUG:
			return LOG.isDebugEnabled();
		case com.jcraft.jsch.Logger.INFO:
			return LOG.isInfoEnabled();
		case com.jcraft.jsch.Logger.WARN:
			return LOG.isWarnEnabled();
		case com.jcraft.jsch.Logger.ERROR:
			return LOG.isErrorEnabled();
		case com.jcraft.jsch.Logger.FATAL:
			return LOG.isTraceEnabled();
		default:
			return false;
		}
	}

	@Override
	public void log(int level, String message) {
		switch (level) {
		case com.jcraft.jsch.Logger.DEBUG:
			LOG.debug(message);
			break;
		case com.jcraft.jsch.Logger.INFO:
			LOG.info(message);
			break;
		case com.jcraft.jsch.Logger.WARN:
			LOG.warn(message);
			break;
		case com.jcraft.jsch.Logger.ERROR:
			LOG.error(message);
			break;
		case com.jcraft.jsch.Logger.FATAL:
			LOG.trace(message);
			break;
		}
	}

	@Override
	public SocketImpl createSocketImpl() {
		return new SshSocketImpl(session);
	}

	static class SshProcess extends Process {

		private ChannelExec channel;
		private InputStream is;
		private InputStream es;
		private OutputStream os;

		SshProcess(ChannelExec channel) throws IOException {
			this.channel = channel;
			is = channel.getInputStream();
			es = channel.getErrStream();
			os = channel.getOutputStream();
		}

		@Override
		public void destroy() {
			channel.disconnect();
		}

		@Override
		public int exitValue() {
			return channel.getExitStatus();
		}

		@Override
		public InputStream getErrorStream() {
			return es;
		}

		@Override
		public InputStream getInputStream() {
			return is;
		}

		@Override
		public OutputStream getOutputStream() {
			return os;
		}

		@Override
		public int waitFor() throws InterruptedException {
			while (channel.isConnected()) {
				Thread.sleep(1000);
			}
			return channel.getExitStatus();
		}

	}

	public Process ssh(String cmd) throws JSchException, IOException {
		ChannelExec channel = (ChannelExec) session.openChannel("exec");
		channel.setCommand(cmd);
		channel.setPty(true);
		channel.connect();
		return new SshProcess(channel);
	}

	public Pair<Boolean, String> exec(String cmd) throws JSchException,
			IOException, InterruptedException {
		Process p = ssh(cmd);
		p.getOutputStream().close();
		InputStreamReader input = new InputStreamReader(p.getInputStream(),
				DEFAULT_CHARSET);
		StringWriter output = new StringWriter();
		int exitCode = 1;
		try {
			exitCode = p.waitFor();
			int n = -1;
			char[] buffer = new char[1024];
			while (EOF != (n = input.read(buffer))) {
				output.write(buffer, 0, n);
			}
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return new Pair<Boolean, String>(exitCode == 0, output.toString());
	}

	private void mkdir(String path, ChannelSftp channel) throws JSchException,
			SftpException {
		String curDir = "";
		if (!path.startsWith("/")) {
			curDir = channel.pwd();
		}
		String[] pathnames = path.split("/");
		for (String pathname : pathnames) {
			if (pathname.equals("")) {
				continue;
			}
			curDir = curDir + "/" + pathname;
			SftpATTRS stat = null;
			try {
				stat = channel.lstat(curDir);
			} catch (SftpException e) {
				// ignore
			}
			if (stat == null) {
				channel.mkdir(curDir);
			} else if (!stat.isDir()) {
				throw new RuntimeException("Couldn't mkdir " + path
						+ ", because " + curDir + " is not a dir.");
			}
		}
	}
	
	public boolean isDir(String path) throws JSchException {
		ChannelSftp channel = createChannelSftp();
		try {
			SftpATTRS stat = channel.lstat(path);
			return stat.isDir(); 
		} catch (SftpException e) {
			throw new RuntimeException("The path is not exist. " + path);
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private void _ls(String path, ChannelSftp channel, List<FileEntry> fileEntries) throws JSchException {
		try {
			if (!isDir(path)) {
				int index = path.lastIndexOf("/");
				fileEntries.add(new FileEntry(path.substring(index + 1), path.substring(0, index)));
				return;
			}
			Vector<LsEntry> entries = channel.ls(path);
			for (LsEntry entry : entries) {
				String filename = entry.getFilename();
				if (filename.equals(".") || filename.equals("..")) {
					continue;
				}
				if (entry.getAttrs().isDir()) {
					fileEntries.add(new FileEntry(path));
					_ls(path + "/" +filename, channel, fileEntries);
				} else {
					fileEntries.add(new FileEntry(path, filename));
				}
			}
		} catch (SftpException e) {
			// ignore
		}
	}
	
	public List<FileEntry> ls(String path) throws JSchException {
		ChannelSftp channel = createChannelSftp();
		List<FileEntry> fileEntries = new ArrayList<FileEntry>();
		try {
			if (path.endsWith("/")) {
				path = path.substring(0, path.length() - 1);
			}
			_ls(path, channel, fileEntries);
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
		return fileEntries;
	}
	
	public static class FileEntry {
		public String name;
		public String path;
		public boolean isDir;
		public FileEntry(String path, String name) {
			this.name = name;
			this.path = path;
			this.isDir = false;
		}
		public FileEntry(String path) {
			this.path = path;
			this.isDir = true;
		}
		@Override
		public String toString() {
			return "FileEntry [name=" + name + ", path=" + path + ", isDir="
					+ isDir + "]";
		}
	}
	
	public void chgrp(int gid, String path) throws JSchException {
		ChannelSftp channel = createChannelSftp();
		try {
			channel.chgrp(gid, path);
		} catch (SftpException e) {
			throw new RuntimeException("Couldn't chgrp the path. " + gid + ", "+ path);
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}
	
	public void chmod(int permis, String path) throws JSchException {
		ChannelSftp channel = createChannelSftp();
		try {
			channel.chmod(permis, path);
		} catch (SftpException e) {
			throw new RuntimeException("Couldn't chmod the path. " + permis + ", "+ path);
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}

	public boolean exist(String path) throws JSchException {
		ChannelSftp channel = createChannelSftp();
		try {
			channel.lstat(path);
		} catch (SftpException e) {
			return false;
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
		return true;
	}
	
	public void mkdir(String path) throws JSchException, SftpException {
		ChannelSftp channel = createChannelSftp();
		try {
			mkdir(path, channel);
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}


	public void upload(String src, String dst) throws JSchException,
			SftpException {
		upload(src, dst, null);
	}

	public void upload(String src, String dst, SftpProgressMonitor monitor)
			throws JSchException, SftpException {
		ChannelSftp channel = createChannelSftp();
		try {
			mkdir(dst, channel);
			if (monitor == null) {
				channel.put(src, dst);
			} else {
				channel.put(src, dst, monitor);
			}
		} finally {
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}
	
	public void download(final String src, final String dst) throws Exception {
		download(src, null, new Callback<InputStream>() {

			@Override
			public void invoke(InputStream input) throws Exception {
				String filename = src.substring(src.lastIndexOf("/") + 1);
				FileOutputStream output = null;
				try {
					File f = new File(dst);
					if (!f.exists()) {
						f.mkdirs();
					}
					output = new FileOutputStream(new File(f, filename));
					byte[] buffer = new byte[1024];
					int n = -1;
					while (EOF != (n = input.read(buffer))) {
						output.write(buffer, 0, n);
					}
				} finally {
					if (output != null) {
						output.close();
					}
				}
				
			}
		});
	}
	
	public void download(String src, Callback<InputStream> callback) throws Exception {
		download(src, null, callback);
	}

	public void download(String src, SftpProgressMonitor monitor,
			Callback<InputStream> callback) throws Exception {
		ChannelSftp channel = createChannelSftp();
		InputStream input = null;
		try {
			if (monitor == null) {
				input = channel.get(src);
			} else {
				input = channel.get(src, monitor);
			}
			if (callback != null) {
				callback.invoke(input);
			}
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
			}
		}
	}

	private ChannelSftp createChannelSftp() throws JSchException {
		ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
		channel.connect();
		return channel;
	}

	class SshSocketFactory implements SocketFactory {

		public Socket createSocket(String host, int port) throws IOException,
				UnknownHostException {
			String socksHost = System.getProperty("socksProxyHost");
			Socket s;
			InetSocketAddress addr = new InetSocketAddress(host, port);
			if (socksHost != null) {
				Proxy proxy = new Proxy(Type.SOCKS, new InetSocketAddress(
						socksHost, 1080));
				s = new Socket(proxy);
				s.connect(addr);
			} else {
				SocketChannel sc = SocketChannel.open(addr);
				s = sc.socket();
			}
			s.setTcpNoDelay(true);
			return s;
		}

		public InputStream getInputStream(Socket socket) throws IOException {
			return new ChannelInputStream(socket.getChannel());
		}

		public OutputStream getOutputStream(Socket socket) throws IOException {
			return new ChannelOutputStream(socket.getChannel());
		}
	}

	class ChannelOutputStream extends OutputStream {
		private SocketChannel sc;

		public ChannelOutputStream(SocketChannel sc) {
			this.sc = sc;
		}

		@Override
		public void write(int b) throws IOException {
			byte bs[] = new byte[1];
			bs[0] = (byte) b;
			write(bs);
		}

		@Override
		public void write(byte b[], int off, int len) throws IOException {
			sc.write(ByteBuffer.wrap(b, off, len));
		}

	}

	class ChannelInputStream extends InputStream {
		private SocketChannel sc;

		public ChannelInputStream(SocketChannel sc) {
			this.sc = sc;
		}

		@Override
		public int read() throws IOException {
			byte b[] = new byte[1];
			if (read(b) != 1) {
				return -1;
			}
			return b[0] & 0xff;
		}

		@Override
		public int read(byte b[], int off, int len) throws IOException {
			return sc.read(ByteBuffer.wrap(b, off, len));
		}
	}

	class SshSocketImpl extends SocketImpl {

		private final Logger logger = LoggerFactory
				.getLogger(SshSocketImpl.class);

		private Session session;
		private ChannelDirectTCPIP channel;
		private InputStream is;
		private OutputStream os;

		SshSocketImpl(Session session) {
			this.session = session;
		}

		@Override
		protected void accept(SocketImpl s) throws IOException {
			throw new IOException("SshSocketImpl does not implement accept");
		}

		@Override
		protected int available() throws IOException {
			if (is == null) {
				throw new ConnectException("Not connected");
			}
			return is.available();
		}

		@Override
		protected void bind(InetAddress host, int port) throws IOException {
			if ((host != null && !host.isAnyLocalAddress()) || port != 0) {
				throw new IOException("SshSocketImpl does not implement bind");
			}
		}

		@Override
		protected void close() throws IOException {
			if (channel != null) {
				is = null;
				os = null;
			}
		}

		@Override
		protected void connect(String host, int port) throws IOException {
			connect(InetAddress.getByName(host), port);
		}

		@Override
		protected void connect(InetAddress address, int port)
				throws IOException {
			connect(new InetSocketAddress(address, port), 300000);
		}

		@Override
		protected void connect(SocketAddress address, int timeout)
				throws IOException {
			try {
				if (!session.isConnected()) {
					session.connect();
				}
				channel = (ChannelDirectTCPIP) session
						.openChannel("direct-tcpip");
				channel.setHost(((InetSocketAddress) address).getHostName());
				channel.setPort(((InetSocketAddress) address).getPort());
				channel.setOrgPort(22);
				is = new PipedInputStream();
				os = new PipedOutputStream();
				channel.setInputStream(new PipedInputStream(
						(PipedOutputStream) os));
				channel.setOutputStream(new PipedOutputStream(
						(PipedInputStream) is));
				channel.connect();
				if (!channel.isConnected()) {
					logger.error("Not connected");
				}
				if (channel.isEOF()) {
					logger.error("EOF");
				}
			} catch (JSchException e) {
				logger.error("Connect exception.", e);
				IOException newE = new IOException(e.getMessage());
				newE.setStackTrace(e.getStackTrace());
				throw newE;
			}
		}

		@Override
		protected void create(boolean stream) throws IOException {
			if (stream == false) {
				throw new IOException("Cannot handle UDP streams");
			}
		}

		@Override
		protected InputStream getInputStream() throws IOException {
			return is;
		}

		@Override
		protected OutputStream getOutputStream() throws IOException {
			return os;
		}

		@Override
		protected void listen(int backlog) throws IOException {
			throw new IOException("SshSocketImpl does not implement listen");
		}

		@Override
		protected void sendUrgentData(int data) throws IOException {
			throw new IOException(
					"SshSocketImpl does not implement sendUrgentData");
		}

		public Object getOption(int optID) throws SocketException {
			if (optID == SocketOptions.SO_SNDBUF)
				return Integer.valueOf(1024);
			else
				throw new SocketException(
						"SshSocketImpl does not implement getOption for "
								+ optID);
		}

		public void setOption(int optID, Object value) throws SocketException {
		}

	}

}

interface Callback<V> {
	void invoke(V v) throws Exception;
}

class Pair<A, B> {
	public A _1;
	public B _2;

	public Pair(A _1, B _2) {
		this._1 = _1;
		this._2 = _2;
	}

}

 

转载于:https://my.oschina.net/u/3209870/blog/837922

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值