提交 380ec734 编写于 作者: K kohsuke

factored out the code that does "sudo" for Callable.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@17016 71c3de6d-444a-0410-be80-ed276b4c234a
上级 3e913994
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* 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.os;
import com.sun.solaris.EmbeddedSu;
import hudson.Launcher.LocalLauncher;
import hudson.Util;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Launcher;
import hudson.remoting.Which;
import hudson.util.ArgumentListBuilder;
import static hudson.util.jna.GNUCLibrary.LIBC;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
/**
* Executes {@link Callable} as the super user, by forking a new process and executing the closure in there
* if necessary.
*
* <p>
* A best effort is made to execute the closure as root, but we may still end up exeucting the closure
* in the non-root privilege, so the closure should expect that and handle it gracefully.
*
* <p>
* Still very much experimental. Subject to change.
*
* @author Kohsuke Kawaguchi
*/
public abstract class SU {
private SU() { // not meant to be instantiated
}
public static <V,T extends Throwable> V execute(final TaskListener listener, final String rootUsername, final String rootPassword, Callable<V, T> closure) throws T, IOException, InterruptedException {
if(File.pathSeparatorChar==';')
return closure.call(); // TODO: perhaps use RunAs to run as an Administrator?
String os = Util.fixNull(System.getProperty("os.name"));
if(os.equals("Linux"))
return new UnixSu() {
protected String sudoExe() {
return "/usr/bin/sudo";
}
protected Process sudoWithPass(ArgumentListBuilder args) throws IOException {
ProcessBuilder pb = new ProcessBuilder(args.prepend(sudoExe(),"-S").toCommandArray());
Process p = pb.start();
new PrintStream(p.getOutputStream()).println(rootPassword);
return p;
}
}.execute(closure,listener, rootPassword);
if(os.equals("SunOS"))
return new UnixSu() {
protected String sudoExe() {
return "/usr/bin/pfexec";
}
protected Process sudoWithPass(ArgumentListBuilder args) throws IOException {
listener.getLogger().println("Running with embedded_su");
ProcessBuilder pb = new ProcessBuilder(args.prepend(sudoExe()).toCommandArray());
return EmbeddedSu.startWithSu(rootUsername, rootPassword, pb);
}
}.execute(closure,listener, rootPassword);
// TODO: Mac?
// unsupported platform, take a chance
return closure.call();
}
private static abstract class UnixSu {
protected abstract String sudoExe();
protected abstract Process sudoWithPass(ArgumentListBuilder args) throws IOException;
<V,T extends Throwable>
V execute(Callable<V, T> task, TaskListener listener, String rootPassword) throws T, IOException, InterruptedException {
final int uid = LIBC.geteuid();
if(uid==0) // already running as root
return task.call();
String javaExe = System.getProperty("java.home") + "/bin/java";
String slaveJar = Which.jarFile(Launcher.class).getAbsolutePath();
// otherwise first attempt pfexec, as that doesn't require password
Channel channel;
Process proc=null;
ArgumentListBuilder args = new ArgumentListBuilder().add(javaExe, "-jar", slaveJar);
if(rootPassword==null) {
// try sudo, in the hope that the user has the permission to do so without password
channel = new LocalLauncher(listener).launchChannel(
args.prepend(sudoExe()).toCommandArray(),
listener.getLogger(), null, Collections.<String, String>emptyMap());
} else {
// try sudo with the given password. Also run in pfexec so that we can elevate the privileges
proc = sudoWithPass(args);
channel = new Channel(args.toString(), Computer.threadPoolForRemoting,
proc.getInputStream(), proc.getOutputStream(), listener.getLogger());
}
try {
return channel.call(task);
} finally {
channel.close();
if(proc!=null)
proc.destroy();
}
}
}
}
......@@ -25,19 +25,15 @@ package hudson.os.solaris;
import com.sun.akuma.Daemon;
import com.sun.akuma.JavaVMArguments;
import com.sun.solaris.EmbeddedSu;
import hudson.FilePath;
import hudson.Launcher.LocalLauncher;
import hudson.Util;
import hudson.Extension;
import hudson.os.SU;
import hudson.model.AdministrativeMonitor;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Launcher;
import hudson.remoting.Which;
import hudson.util.ForkOutputStream;
import hudson.util.HudsonIsRestarting;
import hudson.util.StreamTaskListener;
......@@ -59,7 +55,6 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -166,7 +161,7 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
// this is the actual creation of the file system.
// return true indicating a success
Callable<String,IOException> task = new Callable<String,IOException>() {
return SU.execute(listener, rootUsername, rootPassword, new Callable<String,IOException>() {
public String call() throws IOException {
PrintStream out = listener.getLogger();
......@@ -207,42 +202,7 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
}
return hudson.getName();
}
};
// if we are the root user already, we can just do it here.
// if that fails, no amount of pfexec and embedded_sudo would do.
if(uid==0)
return task.call();
String javaExe = System.getProperty("java.home") + "/bin/java";
String slaveJar = Which.jarFile(Launcher.class).getAbsolutePath();
// otherwise first attempt pfexec, as that doesn't require password
Channel channel;
Process proc=null;
if(rootPassword==null) {
// try pfexec, in the hope that the user has the permission
channel = new LocalLauncher(listener).launchChannel(
new String[]{"/usr/bin/pfexec", javaExe, "-jar", slaveJar},
listener.getLogger(), null, Collections.<String, String>emptyMap());
} else {
// try sudo with the given password. Also run in pfexec so that we can elevate the privileges
listener.getLogger().println("Running with embedded_su");
ProcessBuilder pb = new ProcessBuilder("/usr/bin/pfexec",javaExe,"-jar",slaveJar);
proc = EmbeddedSu.startWithSu(rootUsername, rootPassword, pb);
channel = new Channel("zfs migration thread", Computer.threadPoolForRemoting,
proc.getInputStream(), proc.getOutputStream(), listener.getLogger());
}
try {
return channel.call(task);
} finally {
channel.close();
if(proc!=null)
proc.destroy();
}
});
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册