|
29 | 29 |
|
30 | 30 | package cc.arduino.packages.ssh;
|
31 | 31 |
|
| 32 | +import java.io.BufferedReader; |
32 | 33 | import java.io.IOException;
|
33 | 34 | import java.io.InputStream;
|
| 35 | +import java.io.InputStreamReader; |
34 | 36 |
|
| 37 | +import org.eclipse.core.runtime.IStatus; |
| 38 | +import org.eclipse.core.runtime.Status; |
35 | 39 | import org.eclipse.ui.console.MessageConsoleStream;
|
36 | 40 |
|
37 | 41 | import com.jcraft.jsch.Channel;
|
38 | 42 | import com.jcraft.jsch.ChannelExec;
|
39 | 43 | import com.jcraft.jsch.JSchException;
|
40 | 44 | import com.jcraft.jsch.Session;
|
41 | 45 |
|
42 |
| -@SuppressWarnings({ "nls","unused" }) |
| 46 | +import io.sloeber.core.Messages; |
| 47 | +import io.sloeber.core.common.Common; |
| 48 | +import io.sloeber.core.common.Const; |
| 49 | + |
| 50 | +@SuppressWarnings({ "nls", "unused" }) |
43 | 51 | public class SSH {
|
44 | 52 |
|
45 |
| - final Session session; |
46 |
| - |
47 |
| - public SSH(Session session) { |
48 |
| - this.session = session; |
49 |
| - } |
50 |
| - |
51 |
| - public boolean execSyncCommand(String command) throws JSchException, IOException { |
52 |
| - return execSyncCommand(command, null, null); |
53 |
| - } |
54 |
| - |
55 |
| - @SuppressWarnings("resource") |
56 |
| - public boolean execSyncCommand(String command, MessageConsoleStream stdoutConsumer, |
57 |
| - MessageConsoleStream stderrConsumer) throws JSchException, IOException { |
58 |
| - InputStream stdout = null; |
59 |
| - InputStream stderr = null; |
60 |
| - Channel channel = null; |
61 |
| - try { |
62 |
| - channel = session.openChannel("exec"); |
63 |
| - ((ChannelExec) channel).setCommand(command); |
64 |
| - |
65 |
| - channel.setInputStream(null); |
66 |
| - |
67 |
| - stdout = channel.getInputStream(); |
68 |
| - stderr = ((ChannelExec) channel).getErrStream(); |
69 |
| - |
70 |
| - channel.connect(); |
71 |
| - |
72 |
| - int exitCode = consumeOutputSyncAndReturnExitCode(channel); |
73 |
| - |
74 |
| - return exitCode == 0; |
75 |
| - |
76 |
| - } finally { |
77 |
| - try { |
78 |
| - if (stdout != null) { |
79 |
| - stdout.close(); |
80 |
| - } |
81 |
| - if (stderr != null) { |
82 |
| - stderr.close(); |
83 |
| - } |
84 |
| - } catch (IOException e) { |
85 |
| - e.printStackTrace(); |
86 |
| - } |
87 |
| - if (channel != null) { |
88 |
| - channel.disconnect(); |
89 |
| - } |
90 |
| - } |
91 |
| - } |
92 |
| - |
93 |
| - private static int consumeOutputSyncAndReturnExitCode(Channel channel) { |
94 |
| - while (true) { |
95 |
| - |
96 |
| - if (channel.isClosed()) { |
97 |
| - return channel.getExitStatus(); |
98 |
| - } |
99 |
| - try { |
100 |
| - Thread.sleep(100); |
101 |
| - } catch (Exception ee) { |
102 |
| - // noop |
103 |
| - } |
104 |
| - } |
105 |
| - } |
106 |
| - |
107 |
| - // @SuppressWarnings("static-method") |
108 |
| - // private void consumeStream(byte[] buffer, InputStream in, PrintStream |
109 |
| - // out) throws IOException { |
110 |
| - // while (in.available() > 0) { |
111 |
| - // int length = in.read(buffer, 0, buffer.length); |
112 |
| - // if (length < 0) { |
113 |
| - // break; |
114 |
| - // } |
115 |
| - // if (out != null) { |
116 |
| - // out.print(new String(buffer, 0, length)); |
117 |
| - // } |
118 |
| - // } |
119 |
| - // } |
| 53 | + final Session session; |
| 54 | + |
| 55 | + public SSH(Session session) { |
| 56 | + this.session = session; |
| 57 | + } |
| 58 | + |
| 59 | + public boolean execSyncCommand(String command) throws JSchException, IOException { |
| 60 | + return execSyncCommand(command, null, null); |
| 61 | + } |
| 62 | + |
| 63 | + public boolean execSyncCommand(String command, MessageConsoleStream stdoutStream, |
| 64 | + MessageConsoleStream stderrConsumer) throws JSchException, IOException { |
| 65 | + ChannelExec channel = (ChannelExec) session.openChannel("exec"); |
| 66 | + |
| 67 | + try (InputStream stdout = channel.getInputStream(); InputStream stderr = channel.getErrStream();) { |
| 68 | + |
| 69 | + channel.setCommand(command); |
| 70 | + |
| 71 | + channel.setInputStream(null); |
| 72 | + |
| 73 | + // for one reason or another I need to swap error and output stream here |
| 74 | + Thread stdoutRunner = new Thread(new LogStreamRunner(stderr, stdoutStream)); |
| 75 | + Thread stderrRunner = new Thread(new LogStreamRunner(stdout, stderrConsumer)); |
| 76 | + stdoutRunner.start(); |
| 77 | + stderrRunner.start(); |
| 78 | + |
| 79 | + channel.connect(); |
| 80 | + |
| 81 | + int exitCode = consumeOutputSyncAndReturnExitCode(channel); |
| 82 | + |
| 83 | + return exitCode == 0; |
| 84 | + |
| 85 | + } finally { |
| 86 | + if (channel != null) { |
| 87 | + channel.disconnect(); |
| 88 | + } |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + private static int consumeOutputSyncAndReturnExitCode(Channel channel) { |
| 93 | + while (true) { |
| 94 | + |
| 95 | + if (channel.isClosed()) { |
| 96 | + return channel.getExitStatus(); |
| 97 | + } |
| 98 | + try { |
| 99 | + Thread.sleep(100); |
| 100 | + } catch (Exception ee) { |
| 101 | + // noop |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * A runnable class that will read a Stream until EOF, storing each line in a |
| 108 | + * List and also calling a listener for each line. |
| 109 | + */ |
| 110 | + private class LogStreamRunner implements Runnable { |
| 111 | + |
| 112 | + private final BufferedReader fReader; |
| 113 | + private MessageConsoleStream fConsoleOutput = null; |
| 114 | + |
| 115 | + /** |
| 116 | + * Construct a Streamrunner that will read the given InputStream and log all |
| 117 | + * lines in the given List. |
| 118 | + * <p> |
| 119 | + * If a valid <code>OutputStream</code> is set, everything read by this |
| 120 | + * <code>LogStreamRunner</code> is also written to it. |
| 121 | + * |
| 122 | + * @param instream |
| 123 | + * <code>InputStream</code> to read |
| 124 | + * @param log |
| 125 | + * <code>List<String></code> where all lines of the instream |
| 126 | + * are stored |
| 127 | + * @param consolestream |
| 128 | + * <code>OutputStream</code> for secondary console output, or |
| 129 | + * <code>null</code> for no console output. |
| 130 | + */ |
| 131 | + public LogStreamRunner(InputStream instream, MessageConsoleStream consolestream) { |
| 132 | + this.fReader = new BufferedReader(new InputStreamReader(instream)); |
| 133 | + this.fConsoleOutput = consolestream; |
| 134 | + } |
| 135 | + |
| 136 | + /* |
| 137 | + * (non-Javadoc) |
| 138 | + * |
| 139 | + * @see java.lang.Runnable#run() |
| 140 | + */ |
| 141 | + @Override |
| 142 | + public void run() { |
| 143 | + try { |
| 144 | + for (;;) { |
| 145 | + // Processes a new process output line. |
| 146 | + // If a Listener has been registered, call it |
| 147 | + int read = this.fReader.read(); |
| 148 | + if (read != -1) { |
| 149 | + String readChar = new String() + (char) read; |
| 150 | + |
| 151 | + // And print to the console (if active) |
| 152 | + if (this.fConsoleOutput != null) { |
| 153 | + this.fConsoleOutput.print(readChar); |
| 154 | + } |
| 155 | + } else { |
| 156 | + break; |
| 157 | + } |
| 158 | + } |
| 159 | + } catch (IOException e) { |
| 160 | + // This is unlikely to happen, but log it nevertheless |
| 161 | + IStatus status = new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, Messages.command_io, e); |
| 162 | + Common.log(status); |
| 163 | + } finally { |
| 164 | + try { |
| 165 | + this.fReader.close(); |
| 166 | + } catch (IOException e) { |
| 167 | + // can't do anything |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + } |
120 | 172 |
|
121 | 173 | }
|
0 commit comments