/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.cygpath;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.LauncherDecorator;
import hudson.Proc;
import hudson.model.Node;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.remoting.Callable;
import hudson.util.IOException2;
import hudson.util.jna.JnaException;
import hudson.util.jna.RegistryKey;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
/**
* If we are on Windows, convert the path of the executable via Cygwin.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class CygpathLauncherDecorator extends LauncherDecorator {
public Launcher decorate(final Launcher base, Node node) {
if(base.isUnix()) return base; // no decoration on Unix
return new Launcher(base) {
@Override
public boolean isUnix() {
return base.isUnix();
}
public Proc launch(ProcStarter starter) throws IOException {
starter = starter.copy();
List<String> cmds = starter.cmds();
starter.cmds(cygpath(cmds.toArray(new String[cmds.size()])));
return base.launch(starter);
}
@Override
public Channel launchChannel(String[] cmd, OutputStream out, FilePath workDir, Map<String, String> envVars) throws IOException, InterruptedException {
return base.launchChannel(cygpath(cmd),out,workDir,envVars);
}
@Override
public void kill(Map<String, String> modelEnvVars) throws IOException, InterruptedException {
base.kill(modelEnvVars);
}
private String[] cygpath(String[] cmds) throws IOException {
try {
String exe = cmds[0];
if (exe.indexOf('/')<0 && exe.indexOf('\\')<0)
return cmds; // if the executable is a single token, it'll be found in PATH. "cygpath -w" would append the current directory in front, which won't work.
ByteArrayOutputStream out = new ByteArrayOutputStream();
if(base.launch().cmds(getCygpathExe(),"-w",exe).stdout(out).join()==0) {
// replace by the converted path
String cmd = out.toString().trim();
if(cmd.length()>0)
// Maybe a bug in cygwin 1.7, or maybe a race condition that I'm not aware of yet,
// but until I get around to investigate that, I'm putting this defensive check
cmds[0] = cmd;
}
} catch (InterruptedException e) {
// handle the interrupt later
Thread.currentThread().interrupt();
}
return cmds;
}
private String getCygpathExe() throws IOException, InterruptedException {
VirtualChannel ch = base.getChannel();
if (ch==null) return "cygpath"; // fall back
return ch.call(new GetCygpathTask());
}
};
}
/**
* Where is Cygwin installed?
*/
private static class GetCygpathTask implements Callable<String,IOException> {
private File getCygwinRoot() throws IOException {
try {
RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly("SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/");
try {
return new File(key.getStringValue("native"));
} finally {
key.dispose();
}
} catch (JnaException e) {
throw new IOException2("Failed to locate Cygwin installation. Is Cygwin installed?",e);
}
}
public String call() throws IOException {
return new File(getCygwinRoot(),"bin\\cygpath").getPath();
}
}
}