X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=runtime%2Forg.argeo.slc.core%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2Ftasks%2FSystemCall.java;h=04da5242f41556d93ff439f371cec4578e6847b1;hb=d1298659fe6f179d1cbbc8c89f108a0bbc5b4edf;hp=49ccc9c57f3d22f7ecfc891519bfea7343f0a40b;hpb=db3956bb988a67ab966ca7f44b0058674dacb1f8;p=gpl%2Fargeo-slc.git diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java index 49ccc9c57..04da5242f 100644 --- a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java +++ b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Mathieu Baudier + * Copyright (C) 2007-2012 Argeo GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.argeo.slc.core.execution.tasks; import java.io.File; @@ -30,6 +29,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; + +import javax.security.auth.callback.CallbackHandler; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; @@ -52,6 +54,8 @@ import org.argeo.slc.core.test.SimpleResultPart; import org.argeo.slc.test.TestResult; import org.argeo.slc.test.TestStatus; import org.springframework.core.io.Resource; +import org.springframework.security.Authentication; +import org.springframework.security.context.SecurityContextHolder; /** Execute an OS specific system call. */ public class SystemCall implements Runnable { @@ -94,6 +98,8 @@ public class SystemCall implements Runnable { private Boolean exceptionOnFailed = true; private Boolean mergeEnvironmentVariables = true; + private Authentication authentication; + private String osConsole = null; private String generateScript = null; @@ -104,6 +110,20 @@ public class SystemCall implements Runnable { private ExecutionResources executionResources; + /** Sudo the command, as root if empty or as user if not. */ + private String sudo = null; + // TODO make it more secure and robust, test only once + private final String sudoPrompt = UUID.randomUUID().toString(); + private String askPassProgram = "/usr/libexec/openssh/ssh-askpass"; + private boolean firstLine = true; + private CallbackHandler callbackHandler; + /** Chroot to the this path (must not be empty) */ + private String chroot = null; + + // Current + /** Current watchdog, null if process is completed */ + ExecuteWatchdog currentWatchdog = null; + /** Empty constructor */ public SystemCall() { @@ -138,6 +158,8 @@ public class SystemCall implements Runnable { /** Executes the system call. */ public void run() { + authentication = SecurityContextHolder.getContext().getAuthentication(); + // Manage streams Writer stdOutWriter = null; OutputStream stdOutputStream = null; @@ -185,7 +207,7 @@ public class SystemCall implements Runnable { executorToUse = executor; else executorToUse = new DefaultExecutor(); - executorToUse.setWatchdog(new ExecuteWatchdog(watchdogTimeout)); + executorToUse.setWatchdog(createWatchdog()); if (redirectStreams) { // Redirect standard streams @@ -230,6 +252,8 @@ public class SystemCall implements Runnable { Thread.currentThread().interrupt(); return; } + // Sleep 1s in order to make sure error logs are flushed + Thread.sleep(1000); executeResultHandler.onProcessFailed(e1); } else @@ -252,10 +276,16 @@ public class SystemCall implements Runnable { public synchronized String function() { final StringBuffer buf = new StringBuffer(""); SystemCallOutputListener tempOutputListener = new SystemCallOutputListener() { + private Long lineCount = 0l; + public void newLine(SystemCall systemCall, String line, Boolean isError) { - if (!isError) + if (!isError) { + if (lineCount != 0l) + buf.append('\n'); buf.append(line); + lineCount++; + } } }; addOutputListener(tempOutputListener); @@ -300,11 +330,40 @@ public class SystemCall implements Runnable { throw new SlcException( "Specify the command either as a line or as a list."); else if (cmdToUse != null) { + if (chroot != null && !chroot.trim().equals("")) + cmdToUse = "chroot \"" + chroot + "\" " + cmdToUse; + if (sudo != null) { + environmentVariables.put("SUDO_ASKPASS", askPassProgram); + if (!sudo.trim().equals("")) + cmdToUse = "sudo -p " + sudoPrompt + " -u " + sudo + " " + + cmdToUse; + else + cmdToUse = "sudo -p " + sudoPrompt + " " + cmdToUse; + } + + // GENERATE COMMAND LINE commandLine = CommandLine.parse(cmdToUse); } else if (commandToUse != null) { if (commandToUse.size() == 0) throw new SlcException("Command line is empty."); + if (chroot != null && sudo != null) { + commandToUse.add(0, "chroot"); + commandToUse.add(1, chroot); + } + + if (sudo != null) { + environmentVariables.put("SUDO_ASKPASS", askPassProgram); + commandToUse.add(0, "sudo"); + commandToUse.add(1, "-p"); + commandToUse.add(2, sudoPrompt); + if (!sudo.trim().equals("")) { + commandToUse.add(3, "-u"); + commandToUse.add(4, sudo); + } + } + + // GENERATE COMMAND LINE commandLine = new CommandLine(commandToUse.get(0).toString()); for (int i = 1; i < commandToUse.size(); i++) { @@ -347,24 +406,50 @@ public class SystemCall implements Runnable { final Writer stdErrWriter, final InputStream stdInStream) { // Log writers - - PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( - stdOutputStream != null ? stdOutputStream - : new LogOutputStream() { - protected void processLine(String line, int level) { - if (line != null && !line.trim().equals("")) - logStdOut(line); - if (stdOutWriter != null) - appendLineToFile(stdOutWriter, line); - } - }, new LogOutputStream() { + OutputStream stdout = stdOutputStream != null ? stdOutputStream + : new LogOutputStream() { protected void processLine(String line, int level) { + // if (firstLine) { + // if (sudo != null && callbackHandler != null + // && line.startsWith(sudoPrompt)) { + // try { + // PasswordCallback pc = new PasswordCallback( + // "sudo password", false); + // Callback[] cbs = { pc }; + // callbackHandler.handle(cbs); + // char[] pwd = pc.getPassword(); + // char[] arr = Arrays.copyOf(pwd, + // pwd.length + 1); + // arr[arr.length - 1] = '\n'; + // IOUtils.write(arr, stdInSink); + // stdInSink.flush(); + // } catch (Exception e) { + // throw new SlcException( + // "Cannot retrieve sudo password", e); + // } + // } + // firstLine = false; + // } + if (line != null && !line.trim().equals("")) - logStdErr(line); - if (stdErrWriter != null) - appendLineToFile(stdErrWriter, line); + logStdOut(line); + + if (stdOutWriter != null) + appendLineToFile(stdOutWriter, line); } - }, stdInStream) { + }; + + OutputStream stderr = new LogOutputStream() { + protected void processLine(String line, int level) { + if (line != null && !line.trim().equals("")) + logStdErr(line); + if (stdErrWriter != null) + appendLineToFile(stdErrWriter, line); + } + }; + + PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdout, + stderr, stdInStream) { @Override public void stop() { @@ -393,9 +478,11 @@ public class SystemCall implements Runnable { testResult.addResultPart(new SimpleResultPart( TestStatus.PASSED, msg)); } + releaseWatchdog(); } public void onProcessFailed(ExecuteException e) { + String msg = "System call '" + commandLine + "' failed."; if (testResult != null) { forwardPath(testResult); @@ -407,10 +494,12 @@ public class SystemCall implements Runnable { else log.error(msg, e); } + releaseWatchdog(); } }; } + @Deprecated protected void forwardPath(TestResult testResult) { // TODO: allocate a TreeSPath } @@ -420,19 +509,10 @@ public class SystemCall implements Runnable { */ protected String getExecDirToUse() { try { - File dir = null; if (execDir != null) { return execDir; - // Replace '/' by local file separator, for portability - // execDir.replace('/', File.separatorChar); - // dir = new File(execDir).getCanonicalFile(); - // dir = execDir.; } - - if (dir == null) - return System.getProperty("user.dir"); - else - return dir.getPath(); + return System.getProperty("user.dir"); } catch (Exception e) { throw new SlcException("Cannot find exec dir", e); } @@ -452,6 +532,12 @@ public class SystemCall implements Runnable { /** Log from the underlying streams. */ protected void log(String logLevel, String line) { + // TODO optimize + if (SecurityContextHolder.getContext().getAuthentication() == null) { + SecurityContextHolder.getContext() + .setAuthentication(authentication); + } + if ("ERROR".equals(logLevel)) log.error(line); else if ("WARN".equals(logLevel)) @@ -532,6 +618,27 @@ public class SystemCall implements Runnable { return this; } + // CONTROL + public synchronized Boolean isRunning() { + return currentWatchdog != null; + } + + private synchronized ExecuteWatchdog createWatchdog() { +// if (currentWatchdog != null) +// throw new SlcException("A process is already being monitored"); + currentWatchdog = new ExecuteWatchdog(watchdogTimeout); + return currentWatchdog; + } + + private synchronized void releaseWatchdog() { + currentWatchdog = null; + } + + public synchronized void kill() { + if (currentWatchdog != null) + currentWatchdog.destroyProcess(); + } + /** */ public void setCmd(String command) { this.cmd = command; @@ -569,6 +676,10 @@ public class SystemCall implements Runnable { this.environmentVariables = environmentVariables; } + public Map getEnvironmentVariables() { + return environmentVariables; + } + public void setWatchdogTimeout(Long watchdogTimeout) { this.watchdogTimeout = watchdogTimeout; } @@ -638,6 +749,18 @@ public class SystemCall implements Runnable { this.executor = executor; } + public void setSudo(String sudo) { + this.sudo = sudo; + } + + public void setCallbackHandler(CallbackHandler callbackHandler) { + this.callbackHandler = callbackHandler; + } + + public void setChroot(String chroot) { + this.chroot = chroot; + } + private class DummyexecuteStreamHandler implements ExecuteStreamHandler { public void setProcessErrorStream(InputStream is) throws IOException {