ShellScriptExecutorImpl.java 12.4 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Binary
 *  javax.jcr.Node
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  org.apache.commons.io.IOUtils
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.jcr.api.SlingRepository
 *  org.apache.sling.settings.SlingSettingsService
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.monitoring.impl;

import com.adobe.granite.monitoring.impl.ScriptConfig;
import com.adobe.granite.monitoring.impl.ShellScriptExecutor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.Calendar;
import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.settings.SlingSettingsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=0)
@Service(value={ShellScriptExecutor.class})
public class ShellScriptExecutorImpl
implements ShellScriptExecutor {
    private static final String SERVICE_USER = "monitoringScripts";
    private static final Logger log = LoggerFactory.getLogger(ShellScriptExecutorImpl.class);
    public static final String[] SCRIPTS_PATHS = new String[]{"/libs/granite/monitoring/scripts"};
    @Reference
    private SlingRepository repository = null;
    @Reference
    private SlingSettingsService settings = null;
    private File execDirectory;

    @Activate
    private void activate() {
        try {
            this.execDirectory = new File(this.settings.getSlingHomePath(), "monitoring").getCanonicalFile();
            if (!this.execDirectory.exists()) {
                this.execDirectory.mkdirs();
            }
            log.info("Scrip executor started. Exec directory is {}", (Object)this.execDirectory.getPath());
        }
        catch (IOException e) {
            log.info("Scrip executor invalid exec directory", (Throwable)e);
            this.execDirectory = null;
        }
    }

    public ShellScriptExecutor.Result execute(ScriptConfig config) {
        File scriptFile;
        ShellScriptExecutor.Result r;
        if (this.execDirectory == null) {
            return null;
        }
        try {
            scriptFile = new File(this.execDirectory, config.getScriptFilename()).getCanonicalFile();
        }
        catch (IOException e) {
            log.error("Error sanitizing script file name.", (Throwable)e);
            return null;
        }
        try {
            String path = config.getScriptPath();
            if (path != null && path.length() > 0) {
                this.extractScript(path, scriptFile);
            }
        }
        catch (IOException e) {
            log.error("Unable to extract script '{}' to '{}'", new Object[]{config.getScriptPath(), scriptFile.getPath(), e});
        }
        ProcessExecutor ex = new ProcessExecutor(scriptFile);
        Thread t = new Thread((Runnable)ex, "Process Executor for " + scriptFile.getName());
        t.setDaemon(true);
        t.start();
        try {
            t.join(5000);
        }
        catch (InterruptedException e) {
            log.error("Error while waiting for process executor termination.", (Throwable)e);
        }
        if (t.isAlive()) {
            log.warn("Script {} did not complete within 5 seconds. Aborting.", (Object)scriptFile.getPath());
            ex.kill();
            try {
                t.join();
            }
            catch (InterruptedException e) {
                log.error("Error while waiting for process executor termination.", (Throwable)e);
            }
        }
        if ((r = ex.getResult()) != null && r.getExitCode() != 0) {
            this.logOut(scriptFile.getName() + "-exit", String.valueOf(r.getExitCode()));
            this.logOut(scriptFile.getName() + "-stdout", r.getStdout());
            this.logOut(scriptFile.getName() + "-stderr", r.getStderr());
        }
        return r;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void extractScript(String relPath, File targetFile) throws IOException {
        Session session = null;
        try {
            session = this.repository.loginService("monitoringScripts", null);
            Node content = null;
            for (String rootPath : SCRIPTS_PATHS) {
                StringBuilder b = new StringBuilder(rootPath);
                if (!relPath.startsWith("/")) {
                    b.append("/");
                }
                b.append(relPath).append("/").append("{http://www.jcp.org/jcr/1.0}content");
                if (!session.nodeExists(b.toString()) || (content = session.getNode(b.toString())).getPath().startsWith(rootPath)) continue;
                throw new IOException("Invalid absolute location " + content.getPath() + " of script " + relPath);
            }
            if (content == null) {
                throw new FileNotFoundException(relPath);
            }
            Calendar cal = content.getProperty("{http://www.jcp.org/jcr/1.0}lastModified").getDate();
            if (targetFile.isFile() && targetFile.lastModified() > cal.getTimeInMillis() - 1000) {
                return;
            }
            Class len$ = this.getClass();
            synchronized (len$) {
                InputStream in = null;
                FileOutputStream out = null;
                Binary bin = null;
                try {
                    bin = content.getProperty("{http://www.jcp.org/jcr/1.0}data").getBinary();
                    in = bin.getStream();
                    out = new FileOutputStream(targetFile);
                    IOUtils.copy((InputStream)in, (OutputStream)out);
                }
                finally {
                    if (bin != null) {
                        bin.dispose();
                    }
                    IOUtils.closeQuietly((InputStream)in);
                    IOUtils.closeQuietly((OutputStream)out);
                }
                targetFile.setLastModified(cal.getTimeInMillis());
                this.makeScriptExecutable(targetFile);
            }
        }
        catch (RepositoryException e) {
            IOException ioe = new IOException(e.getMessage());
            ioe.initCause((Throwable)e);
            throw ioe;
        }
        finally {
            if (session != null) {
                session.logout();
            }
        }
    }

    private void makeScriptExecutable(File scriptFile) {
        Method m = null;
        boolean didSetBit = false;
        try {
            m = scriptFile.getClass().getDeclaredMethod("setExecutable", Boolean.TYPE);
        }
        catch (Exception e) {
            log.debug("Can not set executable bit of {} to true because not supported by the current JDK!", (Object)scriptFile.getAbsolutePath());
        }
        if (m != null) {
            try {
                m.invoke(scriptFile, true);
                didSetBit = true;
            }
            catch (Exception e) {
                log.warn("Couldn't set executable bit of {} to true", (Object)scriptFile.getAbsolutePath(), (Object)e);
            }
        }
        if (!didSetBit) {
            try {
                Process p = Runtime.getRuntime().exec(new String[]{"chmod", "+x", scriptFile.getPath()});
                p.waitFor();
                if (p.exitValue() != 0) {
                    this.logOut("chmod-stdout", IOUtils.toString((InputStream)p.getInputStream(), (String)"utf-8"));
                    this.logOut("chmod-stderr", IOUtils.toString((InputStream)p.getErrorStream(), (String)"utf-8"));
                }
            }
            catch (Exception e) {
                log.warn("Unable to make script {} executable: {}", (Object)scriptFile.getPath(), (Object)e.toString());
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void logOut(String name, String output) {
        if (output == null) {
            log.info("{}: (null)", (Object)name);
            return;
        }
        try {
            BufferedReader rdr = new BufferedReader(new StringReader(output));
            try {
                String line;
                while ((line = rdr.readLine()) != null) {
                    log.info("{}: {}", (Object)name, (Object)line);
                }
            }
            finally {
                rdr.close();
            }
        }
        catch (IOException e) {
            // empty catch block
        }
    }

    protected void bindRepository(SlingRepository slingRepository) {
        this.repository = slingRepository;
    }

    protected void unbindRepository(SlingRepository slingRepository) {
        if (this.repository == slingRepository) {
            this.repository = null;
        }
    }

    protected void bindSettings(SlingSettingsService slingSettingsService) {
        this.settings = slingSettingsService;
    }

    protected void unbindSettings(SlingSettingsService slingSettingsService) {
        if (this.settings == slingSettingsService) {
            this.settings = null;
        }
    }

    private static class ResultImpl
    implements ShellScriptExecutor.Result {
        private final int exitCode;
        private final String stdout;
        private final String stderr;

        private ResultImpl(int exitCode, String stdout, String stderr) {
            this.exitCode = exitCode;
            this.stdout = stdout;
            this.stderr = stderr;
        }

        public int getExitCode() {
            return this.exitCode;
        }

        public String getStdout() {
            return this.stdout;
        }

        public String getStderr() {
            return this.stderr;
        }
    }

    private static class ProcessExecutor
    implements Runnable {
        private final File scriptFile;
        private ShellScriptExecutor.Result result;
        private Process process;

        private ProcessExecutor(File scriptFile) {
            this.scriptFile = scriptFile;
        }

        /*
         * WARNING - Removed try catching itself - possible behaviour change.
         */
        public void run() {
            InputStream stdout = null;
            InputStream stderr = null;
            OutputStream stdin = null;
            try {
                this.process = Runtime.getRuntime().exec(new String[]{this.scriptFile.getPath()}, null, this.scriptFile.getParentFile());
                stdout = this.process.getInputStream();
                stderr = this.process.getErrorStream();
                stdin = this.process.getOutputStream();
                this.process.waitFor();
                String stdoutStr = null;
                try {
                    stdoutStr = IOUtils.toString((InputStream)stdout, (String)"utf-8");
                }
                catch (IOException e) {
                    // empty catch block
                }
                String stderrStr = null;
                try {
                    stderrStr = IOUtils.toString((InputStream)stderr, (String)"utf-8");
                }
                catch (IOException e) {
                    // empty catch block
                }
                this.result = new ResultImpl(this.process.exitValue(), stdoutStr, stderrStr);
                this.process = null;
            }
            catch (Exception e) {
                log.error("Error while executing script {}", (Object)this.scriptFile.getPath(), (Object)e);
            }
            finally {
                IOUtils.closeQuietly((InputStream)stdout);
                IOUtils.closeQuietly((OutputStream)stdin);
                IOUtils.closeQuietly((InputStream)stderr);
            }
        }

        public void kill() {
            if (this.process != null) {
                this.process.destroy();
            }
        }

        public ShellScriptExecutor.Result getResult() {
            return this.result;
        }
    }

}