This closes #106 pull request at github/apache/ant repomaster
@@ -14,6 +14,10 @@ Fixed bugs: | |||||
files. | files. | ||||
Bugzilla Report 63874 | Bugzilla Report 63874 | ||||
* sshexec, sshsession and scp now support a new sshConfig parameter. | |||||
It specified the SSH configuration file (typically ${user.home}/.ssh/config) | |||||
defining the username and keyfile to be used per host. | |||||
Other changes: | Other changes: | ||||
-------------- | -------------- | ||||
@@ -142,6 +142,14 @@ information. This task has been tested with <code>jsch-0.1.2</code> and later.< | |||||
<td>Passphrase for your private key.</td> | <td>Passphrase for your private key.</td> | ||||
<td>No; defaults to an empty string</td> | <td>No; defaults to an empty string</td> | ||||
</tr> | </tr> | ||||
<tr> | |||||
<td>sshConfig</td> | |||||
<td>Location of the file holding the OpenSSH style configuration (e.g. <code>${user.home}/.ssh/config</code>). | |||||
The username and the key file are read from the configuration file, | |||||
unless they are already specified in the task parameters. | |||||
<em>since Ant 1.10.8</em></td> | |||||
<td>No</td> | |||||
</tr> | |||||
<tr> | <tr> | ||||
<td>verbose</td> | <td>verbose</td> | ||||
<td>Determines whether SCP outputs verbosely to the user. Currently this means outputting | <td>Determines whether SCP outputs verbosely to the user. Currently this means outputting | ||||
@@ -104,6 +104,14 @@ JSCh earlier than 0.1.28.</p> | |||||
<td>Passphrase for your private key.</td> | <td>Passphrase for your private key.</td> | ||||
<td>No; defaults to an empty string</td> | <td>No; defaults to an empty string</td> | ||||
</tr> | </tr> | ||||
<tr> | |||||
<td>sshConfig</td> | |||||
<td>Location of the file holding the OpenSSH style configuration (e.g. <code>${user.home}/.ssh/config</code>). | |||||
The username and the key file are read from the configuration file, | |||||
unless they are already specified in the task parameters. | |||||
<em>since Ant 1.10.8</em></td> | |||||
<td>No</td> | |||||
</tr> | |||||
<tr> | <tr> | ||||
<td>suppresssystemout</td> | <td>suppresssystemout</td> | ||||
<td>Whether to suppress system out. <em>since Ant 1.9.0</em></td> | <td>Whether to suppress system out. <em>since Ant 1.9.0</em></td> | ||||
@@ -110,6 +110,14 @@ JSCh earlier than 0.1.28.</p> | |||||
<td>Passphrase for your private key.</td> | <td>Passphrase for your private key.</td> | ||||
<td>No; defaults to an empty string</td> | <td>No; defaults to an empty string</td> | ||||
</tr> | </tr> | ||||
<tr> | |||||
<td>sshConfig</td> | |||||
<td>Location of the file holding the OpenSSH style configuration (e.g. <code>${user.home}/.ssh/config</code>). | |||||
The username and the key file are read from the configuration file, | |||||
unless they are already specified in the task parameters. | |||||
<em>since Ant 1.10.8</em></td> | |||||
<td>No</td> | |||||
</tr> | |||||
<tr> | <tr> | ||||
<td>timeout</td> | <td>timeout</td> | ||||
<td>Give up if the connection cannot be established within the specified time (given in | <td>Give up if the connection cannot be established within the specified time (given in | ||||
@@ -18,6 +18,11 @@ | |||||
package org.apache.tools.ant.taskdefs.optional.ssh; | package org.apache.tools.ant.taskdefs.optional.ssh; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import com.jcraft.jsch.ConfigRepository; | |||||
import com.jcraft.jsch.OpenSSHConfig; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
@@ -42,6 +47,7 @@ public abstract class SSHBase extends Task implements LogListener { | |||||
private boolean failOnError = true; | private boolean failOnError = true; | ||||
private boolean verbose; | private boolean verbose; | ||||
private final SSHUserInfo userInfo; | private final SSHUserInfo userInfo; | ||||
private String sshConfig; | |||||
private int serverAliveCountMax = 3; | private int serverAliveCountMax = 3; | ||||
private int serverAliveIntervalSeconds = 0; | private int serverAliveIntervalSeconds = 0; | ||||
@@ -106,6 +112,24 @@ public abstract class SSHBase extends Task implements LogListener { | |||||
return verbose; | return verbose; | ||||
} | } | ||||
/** | |||||
* Get the OpenSSH config file (~/.ssh/config). | |||||
* @return the OpenSSH config file | |||||
* @since Ant 1.10.8 | |||||
*/ | |||||
public String getSshConfig() { | |||||
return sshConfig; | |||||
} | |||||
/** | |||||
* Set the OpenSSH config file (~/.ssh/config). | |||||
* @param sshConfig the OpenSSH config file | |||||
* @since Ant 1.10.8 | |||||
*/ | |||||
public void setSshConfig(String sshConfig) { | |||||
this.sshConfig = sshConfig; | |||||
} | |||||
/** | /** | ||||
* Set the serverAliveCountMax value. | * Set the serverAliveCountMax value. | ||||
* @param countMax int | * @param countMax int | ||||
@@ -235,6 +259,37 @@ public abstract class SSHBase extends Task implements LogListener { | |||||
this.port = SSH_PORT; | this.port = SSH_PORT; | ||||
} | } | ||||
/** | |||||
* Load the SSH configuration file. | |||||
* @throws BuildException on error | |||||
*/ | |||||
protected void loadSshConfig() throws BuildException { | |||||
if (sshConfig != null && (userInfo.getName() == null || userInfo.getKeyfile() == null)) { | |||||
if (!new File(sshConfig).exists()) { | |||||
throw new BuildException("The SSH configuration file specified doesn't exist: " + sshConfig); | |||||
} | |||||
log("Loading SSH configuration file " + sshConfig, Project.MSG_DEBUG); | |||||
ConfigRepository.Config config = null; | |||||
try { | |||||
config = OpenSSHConfig.parseFile(sshConfig).getConfig(host); | |||||
} catch (IOException e) { | |||||
throw new BuildException("Failed to load the SSH configuration file " + sshConfig, e); | |||||
} | |||||
host = config.getHostname(); | |||||
if (userInfo.getName() == null) { | |||||
userInfo.setName(config.getUser()); | |||||
} | |||||
if (userInfo.getKeyfile() == null) { | |||||
log("Using SSH key file " + config.getValue("IdentityFile") + " for host " + host, Project.MSG_INFO); | |||||
userInfo.setKeyfile(config.getValue("IdentityFile")); | |||||
} | |||||
} | |||||
} | |||||
/** | /** | ||||
* Open an ssh session. | * Open an ssh session. | ||||
* @return the opened session | * @return the opened session | ||||
@@ -284,6 +284,9 @@ public class SSHExec extends SSHBase { | |||||
if (getHost() == null) { | if (getHost() == null) { | ||||
throw new BuildException("Host is required."); | throw new BuildException("Host is required."); | ||||
} | } | ||||
loadSshConfig(); | |||||
if (getUserInfo().getName() == null) { | if (getUserInfo().getName() == null) { | ||||
throw new BuildException("Username is required."); | throw new BuildException("Username is required."); | ||||
} | } | ||||
@@ -132,6 +132,9 @@ public class SSHSession extends SSHBase { | |||||
if (getHost() == null) { | if (getHost() == null) { | ||||
throw new BuildException("Host is required."); | throw new BuildException("Host is required."); | ||||
} | } | ||||
loadSshConfig(); | |||||
if (getUserInfo().getName() == null) { | if (getUserInfo().getName() == null) { | ||||
throw new BuildException("Username is required."); | throw new BuildException("Username is required."); | ||||
} | } | ||||
@@ -435,19 +435,22 @@ public class Scp extends SSHBase { | |||||
throw new BuildException("no username was given. Can't authenticate."); | throw new BuildException("no username was given. Can't authenticate."); | ||||
} | } | ||||
if (getUserInfo().getPassword() == null | |||||
&& getUserInfo().getKeyfile() == null) { | |||||
throw new BuildException( | |||||
"neither password nor keyfile for user %s has been given. Can't authenticate.", | |||||
getUserInfo().getName()); | |||||
} | |||||
final int indexOfPath = uri.indexOf(':', indexOfAt + 1); | final int indexOfPath = uri.indexOf(':', indexOfAt + 1); | ||||
if (indexOfPath == -1) { | if (indexOfPath == -1) { | ||||
throw new BuildException("no remote path in %s", uri); | throw new BuildException("no remote path in %s", uri); | ||||
} | } | ||||
setHost(uri.substring(indexOfAt + 1, indexOfPath)); | setHost(uri.substring(indexOfAt + 1, indexOfPath)); | ||||
loadSshConfig(); | |||||
if (getUserInfo().getPassword() == null | |||||
&& getUserInfo().getKeyfile() == null) { | |||||
throw new BuildException( | |||||
"neither password nor keyfile for user %s has been given. Can't authenticate.", | |||||
getUserInfo().getName()); | |||||
} | |||||
String remotePath = uri.substring(indexOfPath + 1); | String remotePath = uri.substring(indexOfPath + 1); | ||||
if (remotePath.isEmpty()) { | if (remotePath.isEmpty()) { | ||||
remotePath = "."; | remotePath = "."; | ||||