Skip to content

Commit 92ee034

Browse files
author
Federico Fissore
committed
Faster implementation of scp copy of www folder contents
1 parent 300ca07 commit 92ee034

File tree

3 files changed

+268
-217
lines changed

3 files changed

+268
-217
lines changed
Lines changed: 72 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package cc.arduino.packages.uploaders;
22

33
import cc.arduino.packages.Uploader;
4-
import com.jcraft.jsch.*;
4+
import cc.arduino.packages.uploaders.ssh.SCP;
5+
import cc.arduino.packages.uploaders.ssh.SSH;
6+
import com.jcraft.jsch.JSch;
7+
import com.jcraft.jsch.JSchException;
8+
import com.jcraft.jsch.Session;
59
import processing.app.Base;
610
import processing.app.Constants;
711
import processing.app.NetworkMonitor;
812
import processing.app.Preferences;
913
import processing.app.debug.RunnerException;
1014
import processing.app.debug.TargetPlatform;
11-
import processing.app.helpers.FileUtils;
1215
import processing.app.helpers.PreferencesMap;
1316

14-
import java.io.*;
17+
import java.io.File;
18+
import java.io.IOException;
1519
import java.util.regex.Matcher;
1620

1721
import static processing.app.I18n._;
@@ -44,6 +48,7 @@ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String
4448
}
4549

4650
Session session = null;
51+
SCP scp = null;
4752
try {
4853
JSch jSch = new JSch();
4954
session = jSch.getSession("root", ipAddress, 22);
@@ -52,24 +57,12 @@ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String
5257
session.setUserInfo(new NetworkMonitor.NoInteractionUserInfo());
5358
session.connect(30000);
5459

55-
SCP scp = new SCP(session);
60+
scp = new SCP(session);
5661
SSH ssh = new SSH(session);
5762

58-
File www = new File(sourcePath, "www");
59-
if (www.exists() && www.isDirectory() && www.canExecute() && canUploadWebFiles(ssh)) {
60-
File destination = new File("/www/sd/" + www.getParentFile().getName());
61-
ssh.execSyncCommand("mkdir -p " + FileUtils.getLinuxPathFrom(destination), System.out);
62-
copyWebFiles(scp, ssh, www, destination);
63-
}
64-
65-
String uploadedSketchFile = scp.scpHexToBoard(buildPath, className);
66-
67-
TargetPlatform targetPlatform = Base.getTargetPlatform();
68-
PreferencesMap prefs = Preferences.getMap();
69-
prefs.putAll(Base.getBoardPreferences());
70-
prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool")));
63+
scpFiles(scp, ssh, sourcePath, buildPath, className, session);
7164

72-
return new SSHAVRDude(session).runAVRDude(uploadedSketchFile, verbose ? prefs.get("upload.params.verbose") : prefs.get("upload.params.quiet"));
65+
return runAVRDude(ssh);
7366
} catch (JSchException e) {
7467
if ("Auth cancel".equals(e.getMessage())) {
7568
return false;
@@ -78,228 +71,90 @@ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String
7871
} catch (Exception e) {
7972
throw new RunnerException(e);
8073
} finally {
74+
if (scp != null) {
75+
try {
76+
scp.close();
77+
} catch (IOException e) {
78+
throw new RunnerException(e);
79+
}
80+
}
8181
if (session != null) {
8282
session.disconnect();
8383
}
8484
}
8585
}
8686

87-
private void copyWebFiles(SCP scp, SSH ssh, File from, File destination) throws IOException, JSchException {
88-
File[] files = from.listFiles();
89-
if (files == null) {
90-
throw new IOException("Cannot list files in " + from);
91-
}
92-
for (File file : files) {
93-
if (file.isDirectory() && file.canExecute()) {
94-
File newDestination = new File(destination, file.getName());
95-
ssh.execSyncCommand("mkdir -p " + FileUtils.getLinuxPathFrom(newDestination), System.out);
96-
copyWebFiles(scp, ssh, file, newDestination);
97-
} else {
98-
scp.scpFile(file, FileUtils.getLinuxPathFrom(new File(destination, file.getName())));
99-
}
100-
}
101-
}
87+
private boolean runAVRDude(SSH ssh) throws IOException, JSchException {
88+
TargetPlatform targetPlatform = Base.getTargetPlatform();
89+
PreferencesMap prefs = Preferences.getMap();
90+
prefs.putAll(Base.getBoardPreferences());
91+
prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool")));
10292

103-
private boolean canUploadWebFiles(SSH ssh) throws JSchException, IOException {
104-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
105-
PrintStream ps = new PrintStream(baos);
106-
ssh.execSyncCommand("if [ -L /www/sd ] && [ -d /www/sd ]; then echo 1; else echo 0; fi", ps);
107-
String output = new String(baos.toByteArray());
108-
return "1".equals(output.trim());
109-
}
93+
String additionalParams = verbose ? prefs.get("upload.params.verbose") : prefs.get("upload.params.quiet");
11094

111-
@Override
112-
public boolean burnBootloader() throws RunnerException {
113-
throw new RunnerException("Can't burn bootloader via SSH");
95+
boolean success = ssh.execSyncCommand("merge-sketch-with-bootloader /tmp/sketch.hex", System.out, System.err);
96+
ssh.execSyncCommand("kill-bridge");
97+
success = success && ssh.execSyncCommand("run-avrdude /tmp/sketch.hex '" + additionalParams + "'", System.out, System.err);
98+
return success;
11499
}
115100

116-
private static class SSH {
117-
118-
protected final Session session;
119-
120-
public SSH(Session session) {
121-
this.session = session;
122-
}
123-
124-
protected boolean execSyncCommand(String command, PrintStream stdoutConsumer) throws JSchException, IOException {
125-
return execSyncCommand(command, stdoutConsumer, null);
126-
}
127-
128-
protected boolean execSyncCommand(String command, PrintStream stdoutConsumer, PrintStream stderrConsumer) throws JSchException, IOException {
129-
InputStream stdout = null;
130-
InputStream stderr = null;
131-
Channel channel = null;
132-
try {
133-
channel = session.openChannel("exec");
134-
((ChannelExec) channel).setCommand(command);
135-
136-
channel.setInputStream(null);
137-
138-
stdout = channel.getInputStream();
139-
if (stderrConsumer != null) {
140-
stderr = ((ChannelExec) channel).getErrStream();
141-
}
142-
143-
channel.connect();
144-
145-
int exitCode = consumeOutputSyncAndReturnExitCode(channel, stdout, stdoutConsumer, stderr, stderrConsumer);
146-
147-
return stderrConsumer == null || exitCode == 0;
148-
149-
} finally {
150-
if (stdout != null) {
151-
stdout.close();
152-
}
153-
if (stderr != null) {
154-
stderr.close();
155-
}
156-
if (channel != null) {
157-
channel.disconnect();
158-
}
159-
}
160-
}
161-
162-
protected int consumeOutputSyncAndReturnExitCode(Channel channel, InputStream stdout, PrintStream stdoutConsumer, InputStream stderr, PrintStream stderrConsumer) throws IOException {
163-
byte[] tmp = new byte[102400];
164-
while (true) {
165-
consumeStream(tmp, stdout, stdoutConsumer);
166-
consumeStream(tmp, stderr, stderrConsumer);
167-
168-
if (channel.isClosed()) {
169-
return channel.getExitStatus();
170-
}
171-
try {
172-
Thread.sleep(100);
173-
} catch (Exception ee) {
174-
// noop
175-
}
176-
}
177-
}
178-
179-
private void consumeStream(byte[] buffer, InputStream in, PrintStream out) throws IOException {
180-
while (in != null && in.available() > 0) {
181-
int length = in.read(buffer, 0, buffer.length);
182-
if (length < 0) {
183-
break;
184-
}
185-
out.print(new String(buffer, 0, length));
101+
private void scpFiles(SCP scp, SSH ssh, File sourcePath, String buildPath, String className, Session session) throws JSchException, IOException {
102+
try {
103+
scp.open();
104+
scp.startFolder("tmp");
105+
scp.sendFile(new File(buildPath, className + ".hex"), "sketch.hex");
106+
scp.endFolder();
107+
108+
if (canUploadWWWFiles(sourcePath, ssh)) {
109+
scp.startFolder("www");
110+
scp.startFolder("sd");
111+
scp.startFolder(sourcePath.getName());
112+
recursiveSCP(new File(sourcePath, "www"), scp);
113+
scp.endFolder();
114+
scp.endFolder();
115+
scp.endFolder();
186116
}
117+
} finally {
118+
scp.close();
187119
}
188-
189-
}
190-
191-
private static class SSHAVRDude extends SSH {
192-
193-
public SSHAVRDude(Session session) {
194-
super(session);
195-
}
196-
197-
public boolean runAVRDude(String sketchFile, String additionalParams) throws IOException, JSchException {
198-
boolean success = execSyncCommand("merge-sketch-with-bootloader " + sketchFile, System.out, System.err);
199-
success = success && execSyncCommand("kill-bridge", System.out);
200-
success = success && execSyncCommand("run-avrdude " + sketchFile + " '" + additionalParams + "'", System.out, System.err);
201-
return success;
202-
}
203-
204120
}
205121

206-
private static class SCP extends SSH {
207-
208-
private static final String SKETCH_FILE = "/tmp/sketch.hex";
209-
210-
public SCP(Session session) {
211-
super(session);
122+
private boolean canUploadWWWFiles(File sourcePath, SSH ssh) throws IOException, JSchException {
123+
File www = new File(sourcePath, "www");
124+
if (!www.exists() || !www.isDirectory()) {
125+
return false;
212126
}
213-
214-
public void scpFile(File from, String absolutePathToDestination) throws JSchException, IOException {
215-
Channel channel = null;
216-
OutputStream out = null;
217-
InputStream in = null;
218-
try {
219-
channel = session.openChannel("exec");
220-
((ChannelExec) channel).setCommand("scp -t " + absolutePathToDestination);
221-
222-
out = channel.getOutputStream();
223-
in = channel.getInputStream();
224-
225-
channel.connect();
226-
227-
ensureAcknowledged(out, in);
228-
229-
sendFileSizeAndName(out, in, from);
230-
ensureAcknowledged(out, in);
231-
232-
sendFileContents(out, from);
233-
ensureAcknowledged(out, in);
234-
} finally {
235-
if (out != null) {
236-
out.close();
237-
}
238-
if (in != null) {
239-
in.close();
240-
}
241-
if (channel != null) {
242-
channel.disconnect();
243-
}
244-
}
127+
if (!www.canExecute()) {
128+
System.out.println("Problem accessing files in folder " + www);
129+
return false;
245130
}
246-
247-
public String scpHexToBoard(String buildPath, String className) throws JSchException, IOException {
248-
scpFile(new File(buildPath, className + ".hex"), SKETCH_FILE);
249-
return SKETCH_FILE;
131+
if (!ssh.execSyncCommand("special-storage-available")) {
132+
System.out.println("Problem accessing board folder /www/sd");
133+
return false;
250134
}
135+
return true;
136+
}
251137

252-
private void ensureAcknowledged(OutputStream out, InputStream in) throws IOException {
253-
out.flush();
254-
255-
int b = in.read();
256-
257-
if (b == 0) return;
258-
if (b == -1) return;
259-
260-
if (b == 1 || b == 2) {
261-
StringBuilder sb = new StringBuilder();
262-
sb.append("SCP error: ");
263-
264-
int c;
265-
do {
266-
c = in.read();
267-
sb.append((char) c);
268-
} while (c != '\n');
269-
270-
throw new IOException(sb.toString());
271-
}
272-
273-
throw new IOException("Uknown SCP error: " + b);
138+
private void recursiveSCP(File from, SCP scp) throws IOException {
139+
File[] files = from.listFiles();
140+
if (files == null) {
141+
return;
274142
}
275143

276-
private void sendFileContents(OutputStream out, File hex) throws IOException {
277-
FileInputStream fis = null;
278-
try {
279-
fis = new FileInputStream(hex);
280-
byte[] buf = new byte[4096];
281-
while (true) {
282-
int len = fis.read(buf, 0, buf.length);
283-
if (len <= 0) break;
284-
out.write(buf, 0, len);
285-
}
286-
287-
// \0 terminates file
288-
buf[0] = 0;
289-
out.write(buf, 0, 1);
290-
} finally {
291-
if (fis != null) {
292-
fis.close();
293-
}
144+
for (File file : files) {
145+
if (file.isDirectory() && file.canExecute()) {
146+
scp.startFolder(file.getName());
147+
recursiveSCP(file, scp);
148+
scp.endFolder();
149+
} else if (file.isFile() && file.canRead()) {
150+
scp.sendFile(file);
294151
}
295152
}
153+
}
296154

297-
private void sendFileSizeAndName(OutputStream out, InputStream in, File hex) throws IOException {
298-
long filesize = hex.length();
299-
String command = "C0644 " + filesize + " " + hex.getName() + "\n";
300-
out.write(command.getBytes());
301-
}
302-
155+
@Override
156+
public boolean burnBootloader() throws RunnerException {
157+
throw new RunnerException("Can't burn bootloader via SSH");
303158
}
304159

305160
}

0 commit comments

Comments
 (0)