提交 dae1565e 编写于 作者: K Kohsuke Kawaguchi

service installation is made UAC aware

上级 cf9b3d93
......@@ -737,7 +737,7 @@ THE SOFTWARE.
<dependency>
<groupId>com.sun.winsw</groupId>
<artifactId>winsw</artifactId>
<version>1.8</version>
<version>1.9</version>
<classifier>bin</classifier>
<type>exe</type>
<scope>provided</scope><!-- this isn't really a dependency that Maven should care about, so putting 'provided' -->
......
......@@ -30,7 +30,6 @@ import hudson.AbortException;
import hudson.Extension;
import hudson.util.StreamTaskListener;
import hudson.util.jna.DotNet;
import hudson.Launcher.LocalLauncher;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
......@@ -127,9 +126,8 @@ public class WindowsInstallerLink extends ManagementLink {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamTaskListener task = new StreamTaskListener(baos);
task.getLogger().println("Installing a service");
int r = new LocalLauncher(task).launch()
.cmds(new File(dir, "hudson.exe").getPath(), "install")
.stdout(task.getLogger()).pwd(dir).join();
int r = WindowsSlaveInstaller.runElevated(
new File(dir, "hudson.exe"), "install", task, dir);
if(r!=0) {
sendError(baos.toString(),req,rsp);
return;
......@@ -197,8 +195,8 @@ public class WindowsInstallerLink extends ManagementLink {
}
LOGGER.info("Starting a Windows service");
StreamTaskListener task = StreamTaskListener.fromStdout();
int r = new LocalLauncher(task).launch().cmds(new File(installationDir, "hudson.exe"), "start")
.stdout(task).pwd(installationDir).join();
int r = WindowsSlaveInstaller.runElevated(
new File(installationDir, "hudson.exe"), "start", task, installationDir);
task.getLogger().println(r==0?"Successfully started":"start service failed. Exit code="+r);
} catch (IOException e) {
e.printStackTrace();
......
......@@ -23,30 +23,37 @@
*/
package hudson.lifecycle;
import com.sun.jna.Native;
import hudson.Launcher.LocalLauncher;
import hudson.Util;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Engine;
import hudson.remoting.jnlp.MainDialog;
import hudson.remoting.jnlp.MainMenu;
import hudson.util.StreamTaskListener;
import hudson.util.jna.DotNet;
import hudson.util.jna.Kernel32Utils;
import hudson.util.jna.SHELLEXECUTEINFO;
import hudson.util.jna.Shell32;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import javax.swing.*;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import static hudson.util.jna.SHELLEXECUTEINFO.*;
import static javax.swing.JOptionPane.*;
/**
* @author Kohsuke Kawaguchi
*/
......@@ -89,6 +96,44 @@ public class WindowsSlaveInstaller implements Callable<Void,RuntimeException>, A
return null;
}
/**
* Invokes slave.exe with a SCM management command.
*
* <p>
* If it fails in a way that indicates the presence of UAC, retry in an UAC compatible manner.
*/
static int runElevated(File slaveExe, String command, TaskListener out, File pwd) throws IOException, InterruptedException {
try {
return new LocalLauncher(out).launch().cmds(slaveExe, command).stdout(out).pwd(pwd).join();
} catch (IOException e) {
if (e.getMessage().contains("CreateProcess") && e.getMessage().contains("=740")) {
// fall through
} else {
throw e;
}
}
// error code 740 is ERROR_ELEVATION_REQUIRED, indicating that
// we run in UAC-enabled Windows and we need to run this in an elevated privilege
SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO();
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = "runas";
sei.lpFile = slaveExe.getAbsolutePath();
sei.lpParameters = "/redirect redirect.log "+command;
sei.lpDirectory = pwd.getAbsolutePath();
sei.nShow = SW_HIDE;
if (!Shell32.INSTANCE.ShellExecuteEx(sei))
throw new IOException("Failed to shellExecute: "+ Native.getLastError());
try {
return Kernel32Utils.waitForExitProcess(sei.hProcess);
} finally {
FileInputStream fin = new FileInputStream(new File(pwd,"redirect.log"));
IOUtils.copy(fin,out.getLogger());
fin.close();
}
}
/**
* Called when the install menu is selected
*/
......@@ -131,7 +176,7 @@ public class WindowsSlaveInstaller implements Callable<Void,RuntimeException>, A
// install as a service
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamTaskListener task = new StreamTaskListener(baos);
r = new LocalLauncher(task).launch().cmds(slaveExe, "install").stdout(task).pwd(dir).join();
r = runElevated(slaveExe,"install",task,dir);
if(r!=0) {
JOptionPane.showMessageDialog(
dialog,baos.toString(),"Error", ERROR_MESSAGE);
......@@ -148,7 +193,7 @@ public class WindowsSlaveInstaller implements Callable<Void,RuntimeException>, A
public void run() {
try {
StreamTaskListener task = StreamTaskListener.fromStdout();
int r = new LocalLauncher(task).launch().cmds(slaveExe, "start").stdout(task).pwd(dir).join();
int r = runElevated(slaveExe,"start",task,dir);
task.getLogger().println(r==0?"Successfully started":"start service failed. Exit code="+r);
} catch (IOException e) {
e.printStackTrace();
......
/*
* The MIT License
*
* Copyright (c) 2010, CloudBees, Inc.
*
* 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.util.jna;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
/**
*
* @author Kohsuke Kawaguchi
*/
public class Kernel32Utils {
/**
* Given the process handle, waits for its completion and returns the exit code.
*/
public static int waitForExitProcess(Pointer hProcess) throws InterruptedException {
while (true) {
if (Thread.interrupted())
throw new InterruptedException();
Kernel32.INSTANCE.WaitForSingleObject(hProcess,1000);
IntByReference exitCode = new IntByReference();
exitCode.setValue(-1);
Kernel32.INSTANCE.GetExitCodeProcess(hProcess,exitCode);
int v = exitCode.getValue();
if (v !=Kernel32.STILL_ACTIVE) {
return v;
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册