package codeine.utils.os_process; import codeine.configuration.PathHelper; import codeine.credentials.CredHelper; import codeine.model.Constants; import codeine.model.Result; import codeine.utils.ExceptionUtils; import codeine.utils.FilesUtils; import codeine.utils.StringUtils; import codeine.utils.TextFileUtils; import codeine.utils.os.OperatingSystem; import codeine.utils.os_process.ProcessExecuter.ProcessExecuterBuilder; import com.google.common.collect.Lists; import org.apache.log4j.Logger; import java.io.File; import java.io.IOException; import java.nio.file.attribute.PosixFilePermission; import java.util.List; import java.util.Map; public class ShellScript { private static final Logger log = Logger.getLogger(ShellScript.class); private String content; private String fileName; private String key; private String tmpDir; private OperatingSystem operatingSystem; private String runFromDir; private Map<String, String> env; private String cred; public ShellScript(String key, String content, OperatingSystem operatingSystem, String tmp_dir, String runFromDir, Map<String, String> env, String cred) { this.key = key; this.content = content; this.operatingSystem = operatingSystem; this.runFromDir = runFromDir; this.env = env; this.cred = cred; this.tmpDir = StringUtils.isEmpty(tmp_dir) ? System.getProperty("java.io.tmpdir") : tmp_dir; } private boolean windows() { return operatingSystem == OperatingSystem.Windows; } public String create() { content = content.replace("\n", System.lineSeparator()); String fileNameNoDir = "codeine_" + key.hashCode() + (windows() ? ".bat" : ".sh"); this.fileName = tmpDir + File.separator + fileNameNoDir; try { TextFileUtils.setContents(fileName, content); } catch (RuntimeException ex){ log.warn("failed to write to file " + fileName); if (ExceptionUtils.getRootCause(ex) instanceof IOException && !windows()) { String filename2 = Constants.getPersistentDir() + File.separator + fileNameNoDir; try { TextFileUtils.setContents(filename2, content); fileName = filename2; } catch (RuntimeException ex1){ log.warn("fail to write to backup file " + filename2); throw ex; } } else { throw ex; } } if (!windows()) { FilesUtils.setPermissions(fileName, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_EXECUTE, PosixFilePermission.OTHERS_READ ); } return fileName; } public void delete() { FilesUtils.delete(fileName); FilesUtils.delete(getOutputFile()); } public Result execute() { try { create(); Result result = executeInternal(); String outputFromFile = ""; if (FilesUtils.exists(getOutputFile())) { outputFromFile = TextFileUtils.getContents(getOutputFile()).trim(); } result.outputFromFile(outputFromFile); return result; } finally { delete(); } } private Result executeInternal() { env.put(Constants.EXECUTION_ENV_OUTPUT_FILE, getOutputFile()); List<String> cmd = null; switch (operatingSystem) { case Linux: if (!StringUtils.isEmpty(cred)) { cmd = Lists.newArrayList(PathHelper.getReadLogs(), encode(cred), encode("/bin/sh"), encode("-xe"), encode(fileName)); } else { cmd = Lists.newArrayList("/bin/sh", "-xe", fileName); } break; case Windows: cmd = Lists.newArrayList("cmd", "/c", "call", fileName); break; default: throw new RuntimeException("missing implementation for " + operatingSystem); } return new ProcessExecuterBuilder(cmd, runFromDir).timeoutInMinutes(4).env(env).user(cred).build().execute(); } private String getOutputFile() { return fileName + ".output"; } private String encode(final String value) { return CredHelper.encode(value); } }