public enum Event {

    id,
    queued,
    priority,
    running,
    niceness,
    retcode,
    cancelled,
    ping,
    error,
    exceed_tree,
    shutdown,
    maxrss,
    maxswap,
    starvation_relaunch,
    starvation_stop;
}
 public class PeerChannel<I extends Input> {

    private static final Logger LOGGER = Logger.getLogger(PeerChannel.class.getName());

    private final String user;
    private final I input;
    private final File procFile;
    private boolean closed = false;

    private final FileInputStream stdinIs;

    private final FileOutputStream eventsOs;
    private final FileOutputStream stdoutOs;
    private final FileOutputStream stderrOs;

    public PeerChannel(String user, I input, File namedPipesRoot) throws OrphanChannelException, IOException {
        this.user = user;
        this.input = input;
        this.procFile = new File("/proc/" + input.getClientPid());

        File stdinNamedPipe = new File(namedPipesRoot, NamedPipe.stdin.name());
        File eventsNamedPipe = new File(namedPipesRoot, NamedPipe.events.name());
        File stdoutNamedPipe = new File(namedPipesRoot, NamedPipe.stdout.name());
        File stderrNamedPipe = new File(namedPipesRoot, NamedPipe.stderr.name());

        final Bean<Boolean> initializedBean = new Bean<>();
        Thread timeoutThread = new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        Thread.sleep(10 * 1000);
                        synchronized (initializedBean) {
                            if (initializedBean.getValue() == null) { // peer not read all pipes already
                                if (isPeerAlive()) { // peer still running
                                    continue;
                                } else { // peer not running -> no process reading pipe
                                    try {
                                        new FileInputStream(eventsNamedPipe).close();
                                    } catch (Exception ex) {
                                    }
                                    try {
                                        new FileInputStream(stdoutNamedPipe).close();
                                    } catch (Exception ex) {
                                    }
                                    try {
                                        new FileInputStream(stderrNamedPipe).close();
                                    } catch (Exception ex) {
                                    }
                                    try {
                                        new FileOutputStream(stdinNamedPipe).close();
                                    } catch (Exception ex) {
                                    }
                                }
                            } else {
                                return;
                            }
                        }
                    }
                } catch (InterruptedException ex) {
                    LOGGER.log(Level.SEVERE, null, ex);
                }
            }
        };
        timeoutThread.setDaemon(true);
        timeoutThread.start();
        this.eventsOs = new FileOutputStream(eventsNamedPipe);
        this.stdoutOs = new FileOutputStream(stdoutNamedPipe);
        this.stderrOs = new FileOutputStream(stderrNamedPipe);
        this.stdinIs = new FileInputStream(stdinNamedPipe);
        try {
            synchronized (initializedBean) {
                if (initializedBean.getValue() != null) {
                    throw new OrphanChannelException();
                }
                initializedBean.setValue(true);
            }
        } finally {
            Miscellaneous.deleteDirectory(namedPipesRoot);
        }
    }

    public static boolean println(OutputStream os, String message) {
        try {
            byte[] bytes = message.getBytes();
            for (int i = 0; i < bytes.length; i++) {
                os.write(bytes[i]);
                if (i > 0 && i % 1024 == 0) {
                    os.flush();
                }
            }
            os.write("\n".getBytes());
            os.flush();
            return true;
        } catch (IOException ex) {
            // Peer closed
            return false;
        }
    }

    public String getUser() {
        return user;
    }

    public OutputStream getEventsOs() {
        return eventsOs;
    }

    public OutputStream getStdoutOs() {
        return stdoutOs;
    }

    public OutputStream getStderrOs() {
        return stderrOs;
    }

    public InputStream getStdinIs() {
        return stdinIs;
    }

    public synchronized boolean sendMessage(ANSICode color, String message) {
        if (closed) {
            return false;
        }
        if (color == null) {
            color = ANSICode.RESET;
        }
        return println(stderrOs, color + message + ANSICode.RESET);
    }

    public boolean isPeerAlive() {
        return procFile.exists();
    }

    public synchronized boolean sendEvent(Event event, Object value) {
        if (closed) {
            return false;
        }
        if (value == null) {
            return println(eventsOs, System.currentTimeMillis() + ":" + event.name());
        } else {
            return println(eventsOs, System.currentTimeMillis() + ":" + event.name() + ":" + value);
        }
    }

    public I getInput() {
        return input;
    }

    public synchronized void close() throws IOException {
        this.closed = true;
        this.eventsOs.flush();
        this.stdoutOs.flush();
        this.stderrOs.flush();
        this.stdinIs.close();
        this.eventsOs.close();
        this.stdoutOs.close();
        this.stderrOs.close();
    }
}
 public class Config {

    private final File tempFolder;
    private static volatile Config instance;
    private final ConfigImpl impl;

    private Config() {
        try {
            File cfgFile = new File(WavaHome.getInstance().getFile(), "cfg/wava.json");
            if (!cfgFile.exists()) {
                throw new RuntimeException("Config file not found " + cfgFile.getAbsolutePath());
            }
            String str = Miscellaneous.toString(new FileInputStream(cfgFile), "UTF-8");
            this.impl = JsonCodec.getInstance().parse(replaceEnvEntries(str), ConfigImpl.class);
            if (impl.getTempFolder() != null) {
                this.tempFolder = new File(impl.getTempFolder() + "/" + WavaHome.getInstance().getId());
            } else {
                this.tempFolder = null;
            }
        } catch (Throwable th) {
            throw new Error(th);
        }
    }

    private static String replaceEnvEntries(String str) {
        StringBuilder sb = new StringBuilder(str);
        for (String envEntry : System.getenv().keySet()) {
            String entryName = "$" + envEntry;
            int i;
            while ((i = sb.indexOf(entryName)) >= 0) {
                sb.replace(i, i + entryName.length(), System.getenv().get(envEntry));
            }
        }
        return sb.toString();
    }

    public static Config getInstance() {
        if (instance == null) {
            synchronized (Config.class) {
                if (instance == null) {
                    instance = new Config();
                }
            }
        }
        return instance;
    }

    public SchedulerCfg getSchedulerCfg() {
        return impl.getSchedulerCfg();
    }

    public ProcessCfg getProcessCfg() {
        return impl.getProcessCfg();
    }

    public GroupCfg getGroupCfg() {
        return impl.getGroupCfg();
    }

    public UICfg getuICfg() {
        return impl.getuICfg();
    }

    public File getTempFolder() {
        return this.tempFolder;
    }
}
 public class LinuxCommands {

    private static final Logger LOGGER = Logger.getLogger(LinuxCommands.class.getName());
    private static final File FILE_MEMINFO = new File("/proc/meminfo");
    private static final File MEMORY_CGROUP_ROOT = new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/memory/" + WavaHome.getInstance().getId());
    private static final File CPUACCT_CGROUP_ROOT = new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/cpuacct/" + WavaHome.getInstance().getId());
    private static final File BLKIO_CGROUP_ROOT = new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/blkio/" + WavaHome.getInstance().getId());

    private static String executeBashCommand(String command) throws ProcessException, InterruptedException {
        String[] cmd = {"/bin/bash", "-c", command};
        return ProcessUtils.executeProcess(cmd);
    }

    public static boolean createWavaCgroups(long totalManagedRss) {
        try {
            createMemoryCgroup(totalManagedRss);
            createCpuCgroup();
            createIOCgroup();
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    private static void createMemoryCgroup(long totalManagedRss) throws Exception {
        removeLeafFolder(MEMORY_CGROUP_ROOT);
        String[] cmd = {"mkdir", MEMORY_CGROUP_ROOT.getAbsolutePath()};
        ProcessUtils.executeProcess(cmd);
        Miscellaneous.writeStringToFile(new File(MEMORY_CGROUP_ROOT, "memory.limit_in_bytes"), String.valueOf(totalManagedRss), "UTF-8");
        long maxTotalSwapBytes = Miscellaneous.parseHumanReadableByteCount(Config.getInstance().getSchedulerCfg().getMaxSwap());
        if (maxTotalSwapBytes > 0) {
            File swapLimitFile = new File(MEMORY_CGROUP_ROOT, "memory.memsw.limit_in_bytes");
            if (swapLimitFile.exists()) {
                Miscellaneous.writeStringToFile(swapLimitFile, String.valueOf(totalManagedRss + maxTotalSwapBytes), "UTF-8");
            } else {
                LOGGER.warning("Swap limit is not enabled");
            }
        }
        Miscellaneous.writeStringToFile(new File(MEMORY_CGROUP_ROOT, "memory.oom_control"), Config.getInstance().getSchedulerCfg().isOutOfMemoryKillerEnabled() ? "0" : "1", "UTF-8");
        Miscellaneous.writeStringToFile(new File(MEMORY_CGROUP_ROOT, "memory.use_hierarchy"), "1", "UTF-8");
    }

    private static void createCpuCgroup() throws Exception {
        removeLeafFolder(CPUACCT_CGROUP_ROOT);
        String[] cmd = {"mkdir", CPUACCT_CGROUP_ROOT.getAbsolutePath()};
        ProcessUtils.executeProcess(cmd);
    }

    private static void createIOCgroup() throws Exception {
        removeLeafFolder(BLKIO_CGROUP_ROOT);
        String[] cmd = {"mkdir", BLKIO_CGROUP_ROOT.getAbsolutePath()};
        ProcessUtils.executeProcess(cmd);
    }

    private static void removeLeafFolder(File folder) throws ProcessException, InterruptedException {
        if (!folder.exists()) {
            return;
        }
        File[] children = folder.listFiles();
        if (children != null) {
            for (int i = 0; i < children.length; i++) {
                File ch = children[i];
                if (ch.isDirectory()) {
                    removeLeafFolder(ch);
                }
            }
        }
        String[] cmd = {"rmdir", folder.getAbsolutePath()};
        ProcessUtils.executeProcess(cmd);
    }

    public static void createJobCgroups(String groupName, int jobId, long maxJobRSSBytes) {
        createJobMemoryCgroup(groupName, jobId, maxJobRSSBytes);
        createJobCpuCgroup(groupName, jobId);
        createJobIOCgroup(groupName, jobId);
    }

    public static void createGroupCgroups(String groupName) {
        createGroupMemoryCgroup(groupName);
        createGroupCpuCgroup(groupName);
        createGroupIOCgroup(groupName);
    }

    private static void createGroupMemoryCgroup(String groupName) {
        try {
            File f = new File(MEMORY_CGROUP_ROOT, groupName);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void createGroupCpuCgroup(String groupName) {
        try {
            File f = new File(CPUACCT_CGROUP_ROOT, groupName);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void createGroupIOCgroup(String groupName) {
        try {
            File f = new File(BLKIO_CGROUP_ROOT, groupName);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void createJobMemoryCgroup(String groupName, int jobId, long maxJobRSSBytes) {
        try {
            File f = new File(MEMORY_CGROUP_ROOT, groupName + "/" + jobId);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
            Miscellaneous.writeStringToFile(new File(f, "memory.soft_limit_in_bytes"), String.valueOf(maxJobRSSBytes), "UTF-8");
            Miscellaneous.writeStringToFile(new File(f, "memory.limit_in_bytes"), String.valueOf(Miscellaneous.parseHumanReadableByteCount(Config.getInstance().getSchedulerCfg().getMaxJobSize())), "UTF-8");

        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void createJobCpuCgroup(String groupName, int jobId) {
        try {
            File f = new File(CPUACCT_CGROUP_ROOT, groupName + "/" + jobId);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void createJobIOCgroup(String groupName, int jobId) {
        try {
            File f = new File(BLKIO_CGROUP_ROOT, groupName + "/" + jobId);
            String[] cmd = {"mkdir", f.getAbsolutePath()};
            ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
    
    public static int getUserHz() {
        try {
            return Integer.valueOf(ProcessUtils.executeProcess("getconf", "CLK_TCK"));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static MemoryStats getCgroupMemoryStats(String groupName, Integer jobId) {
        File f;
        if (jobId == null) {
            f = new File(MEMORY_CGROUP_ROOT, "/memory.stat");
        } else {
            f = new File(MEMORY_CGROUP_ROOT, groupName + "/" + jobId + "/memory.stat");
        }
        try {
            long nanos1 = System.nanoTime();
            String content = Miscellaneous.toString(new FileInputStream(f), "UTF-8");
            String[] lines = content.split("\n");
            MemoryStats ret = new MemoryStats();
            for (int i = 0; i < lines.length; i++) {
                String line = lines[i];
                String[] tokens = line.split("\\s+");
                if (tokens[0].equals("rss")) {
                    ret.rssBytes = Long.valueOf(tokens[1]);
                } else if (tokens[0].equals("swap")) {
                    ret.swapBytes = Long.valueOf(tokens[1]);
                    break;
                }
            }
            long nanos2 = System.nanoTime();
            //  if elasped less than a ms (discard wrong timings due to garbage collection)
            if (nanos2 < nanos1 + 1000000) {
                ret.nanos = nanos2;
            }
            return ret;
        } catch (FileNotFoundException ex) {
            return null;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static CpuStats getCgroupCpuStats(String groupName, Integer jobId) {
        CpuStats ret = new CpuStats();
        File f;
        if (jobId == null) {
            f = new File(CPUACCT_CGROUP_ROOT, "/cpuacct.stat");
        } else {
            f = new File(CPUACCT_CGROUP_ROOT, groupName + "/" + jobId + "/cpuacct.stat");
        }
        try {
            long nanos1 = System.nanoTime();
            String content = Miscellaneous.toString(new FileInputStream(f), "UTF-8");
            Scanner sc = new Scanner(content);
            sc.next();
            ret.userJiffies = Long.valueOf(sc.next());
            sc.next();
            ret.systemJiffies = Long.valueOf(sc.next());
            long nanos2 = System.nanoTime();
            //  if elasped less than a ms (discard wrong timings due to garbage collection)
            if (nanos2 < nanos1 + 1000000) {
                ret.nanos = nanos2;
            }
            return ret;
        } catch (FileNotFoundException ex) {
            return null;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static IOStats getCgroupIOStats(String groupName, Integer jobId) {

        File f;
        if (jobId == null) {
            f = new File(BLKIO_CGROUP_ROOT, "/blkio.throttle.io_service_bytes");
        } else {
            f = new File(BLKIO_CGROUP_ROOT, groupName + "/" + jobId + "/blkio.throttle.io_service_bytes");
        }
        try {
            long nanos1 = System.nanoTime();
            Scanner sc = new Scanner(f);
            String line = null;
            while (sc.hasNextLine()) {
                line = sc.nextLine();
            }
            if (line != null) {
                IOStats ret = new IOStats();
                ret.ioBytes = Long.valueOf(line.split("\\s+")[1]);
                long nanos2 = System.nanoTime();
                //  if elasped less than a ms (discard wrong timings due to garbage collection)
                if (nanos2 < nanos1 + 1000000) {
                    ret.nanos = nanos2;
                }
                return ret;
            }
            return null;
        } catch (FileNotFoundException ex) {
            return null;
        }
    }

    public static void removeGroupCgroups(String groupName) {
        try {
            File memCgroup = new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/memory/" + WavaHome.getInstance().getId() + "/" + groupName);
            executeBashCommand("echo 0 > " + new File(memCgroup, "memory.force_empty").getAbsolutePath());
            removeLeafFolder(memCgroup);
            removeLeafFolder(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/cpuacct/" + WavaHome.getInstance().getId() + "/" + groupName));
            removeLeafFolder(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/blkio/" + WavaHome.getInstance().getId() + "/" + groupName));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void removeJobCgroups(String groupName, int jobId) {
        try {
            File memCgroup = new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/memory/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId);
            executeBashCommand("echo 0 > " + new File(memCgroup, "memory.force_empty").getAbsolutePath());
            removeLeafFolder(memCgroup);
            removeLeafFolder(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/cpuacct/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId));
            removeLeafFolder(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/blkio/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void setNiceness(int pId, int niceness) {
        try {
            String[] cmd = {"renice", "-n", String.valueOf(niceness), "-p", String.valueOf(pId)};
            ProcessUtils.executeProcess(cmd);
            String output = ProcessUtils.executeProcess(new String[]{"ps", "-o", "pid", "--no-headers", "--ppid", String.valueOf(pId)});
            if (output != null) {
                String[] childrenIds = output.split("\n");
                for (String childrenId : childrenIds) {
                    setNiceness(Integer.valueOf(childrenId.trim()), niceness);
                }
            }
        } catch (ProcessException ex) {
            // Silently continue if executed command doesn't return 0
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void killTree(int pId) {
        List<Integer> visitedIds = new ArrayList<>();
        getTree(visitedIds, pId, true);
        sendSignal(visitedIds, 9); // SIGKILL
    }

    private static void getTree(List<Integer> visited, int pId, boolean stop) {
        try {
            if (stop) {
                // needed to stop quickly forking parent from producing children between child killing and parent killing
                ProcessUtils.executeProcess(new String[]{"kill", "-stop", String.valueOf(pId)});
            }
            String output = ProcessUtils.executeProcess(new String[]{"ps", "-o", "pid", "--no-headers", "--ppid", String.valueOf(pId)});
            if (output != null) {
                String[] pIds = output.split("\n");
                for (int i = 0; i < pIds.length; i++) {
                    getTree(visited, Integer.valueOf(pIds[i].trim()), stop);
                }
            }
        } catch (ProcessException ex) {
            // Silently continue if executed command doesn't return 0
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        } finally {
            visited.add(pId);
        }
    }

    private static void sendSignal(List<Integer> pIds, int signal) {
        try {
            String[] cmd = new String[3 + pIds.size()];
            cmd[0] = "kill";
            cmd[1] = "-s";
            cmd[2] = String.valueOf(signal);
            Iterator<Integer> it = pIds.iterator();
            for (int i = 3; i < cmd.length; i++) {
                cmd[i] = String.valueOf(it.next());
            }
            ProcessUtils.executeProcess(cmd);
        } catch (ProcessException ex) {
            // Silently continue if executed command doesn't return 0
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }

//    public static TreeStats[] getTreeStats(int[] pIds) {
//        TreeStats[] ret = new TreeStats[pIds.length];
//        Map<Integer, Integer> indexes = new HashMap<>();
//        for (int i = 0; i < pIds.length; i++) {
//            indexes.put(pIds[i], i);
//        }
//        String[] cmd = {"ps", "axo", "pid,ppid,rss,%cpu", "--no-headers", "--sort=start_time"};
//        String stdout;
//        try {
//            stdout = ProcessUtils.executeProcess(cmd);
//        } catch (ProcessException ex) {
//            // no processes exist retcode=1
//            return null;
//        } catch (InterruptedException ex) {
//            throw new RuntimeException(ex);
//        }
//        if (stdout != null) {
//            String[] lines = stdout.split("\n");
//            for (String line : lines) {
//                String[] cols = line.trim().split("\\s+");
//                Integer pid = Integer.valueOf(cols[0].trim());
//                Integer index = indexes.get(pid);
//                if (index == null) {
//                    Integer ppid = Integer.valueOf(cols[1].trim());
//                    index = indexes.get(ppid);
//                    if (index != null) {
//                        indexes.put(pid, index);
//                    }
//                }
//                if (index != null) {
//                    TreeStats st = ret[index];
//                    if (st == null) {
//                        st = new TreeStats();
//                        ret[index] = st;
//                    }
//                    st.rssBytes += Long.valueOf(cols[2].trim()) * 1000;
//                    st.cpuPercentage += Double.valueOf(cols[3].trim());
//                }
//            }
//        }
//        return ret;
//    }
    public static String[] decorateWithCPUAffinity(String[] cmd, String affinity) {
        String[] ret = new String[cmd.length + 3];
        ret[0] = "taskset";
        ret[1] = "-c";
        ret[2] = affinity;
        for (int i = 3; i < ret.length; i++) {
            ret[i] = cmd[i - 3];
        }
        return ret;
    }

    public static String[] decorateWithBatchSchedulerPolicy(String[] cmd) {
        String[] ret = new String[cmd.length + 3];
        ret[0] = "chrt";
        ret[1] = "-b";
        ret[2] = "0";
        for (int i = 3; i < ret.length; i++) {
            ret[i] = cmd[i - 3];
        }
        return ret;
    }

    public static long[] getMemInfo() {
        long[] ret = {-1, -1};
        try (Scanner scanner = new Scanner(FILE_MEMINFO).useDelimiter("\\s*:\\s*|\n")) {
            while (scanner.hasNext()) {
                String token = scanner.next();
                if (token.equals("MemTotal")) {
                    ret[0] = Miscellaneous.parseHumanReadableByteCount(scanner.next());
                } else if (token.equals("MemAvailable")) {
                    ret[1] = Miscellaneous.parseHumanReadableByteCount(scanner.next());
                }
                if (ret[0] != -1 && ret[1] != -1) {
                    break;
                }
            }
            return ret;
        } catch (FileNotFoundException e) {
            throw new Error(FILE_MEMINFO.getPath() + " not found");
        }
    }

    public static String[] decorateRunAsCommand(String[] cmd, String user) {
        StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < cmd.length; i++) {
            if (i > 0) {
                sb.append(" ");
            }
            sb.append("\"").append(cmd[i].replaceAll("\"", "\\\\\"")).append("\"");
        }
        return new String[]{"runuser", "-p", user, "-c", sb.toString()};
    }

    public static String[] decorateRunInCgroup(String[] cmd, String groupName, int jobId) {
        StringBuilder sb = new StringBuilder("echo $$ >");
        sb.append(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/memory/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId + "/cgroup.procs").getAbsolutePath());
        sb.append(" && ");
        sb.append("echo $$ >");
        sb.append(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/cpuacct/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId + "/cgroup.procs").getAbsolutePath());
        sb.append(" && ");
        sb.append("echo $$ >");
        sb.append(new File(Config.getInstance().getSchedulerCfg().getCgroupRootPath() + "/blkio/" + WavaHome.getInstance().getId() + "/" + groupName + "/" + jobId + "/cgroup.procs").getAbsolutePath());
        sb.append(" && ");
        for (int i = 0; i < cmd.length; i++) {
            if (i > 0) {
                sb.append(" ");
            }
            sb.append("\"").append(cmd[i].replaceAll("\"", "\\\\\"")).append("\"");
        }
        return new String[]{"/bin/bash", "-c", sb.toString()};
    }

    public static String getRunningUser() {
        try {
            String[] cmd = {"id", "-un"};
            return ProcessUtils.executeProcess(cmd);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static String getFileOwner(File f) {
        try {
            return executeBashCommand("ls -ld \"" + f.getAbsolutePath() + "\" | awk 'NR==1 {print $3}'");
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
 public interface GroupCfg {

    public int getDynamicGroupIdleSeconds();

    public Group[] getPredefinedGroups();

    public interface Group {

        public String getName();

        public int getPriority();

        public int getTimeToIdleSeconds();
        
        public File getStatsDirectory();
    }
}
 public abstract class NicenessHandler {

    private volatile static NicenessHandler instance;

    public static NicenessHandler getInstance() {
        if (instance == null) {
            synchronized (NicenessHandler.class) {
                if (instance == null) {
                    try {
                        instance = (NicenessHandler) Class.forName(Config.getInstance().getSchedulerCfg().getNicenessHandlerClassName()).newInstance();
                    } catch (Exception ex) {
                        throw new Error(ex);
                    }
                }
            }
        }
        return instance;
    }

    /**
     * Returns the niceness of the processPosition-th process out of a total of
     * {@code total}
     *
     * @param processPosition from {@code 0} to {@code processCount -1}, in decreasing
     * order of priority
     * @param processCount total number of running processes
     * @param groupPosition from {@code 0} to {@code groupCount -1}, in decreasing
     * order of priority
     * @param groupCount total number of groups with running processes
     * @param minNiceness
     * @param maxNiceness
     * @return a value within the range [minNiceness,maxNiceness]
     */
    public abstract int getNiceness(int processPosition, int processCount, int groupPosition, int groupCount, int minNiceness, int maxNiceness);
}
 public class CpuStats {
    public long nanos;
    public long userJiffies;
    public long systemJiffies;
} public class IOStats {
    public long nanos;
    public long ioBytes;
} public class MemoryStats {
    public long nanos;
    public long rssBytes;
    public long swapBytes;
} public enum EnvEntry {
    WAVA_HOME,
    WAVA_JOB_ID,
    STDIN_TTY
}
 public class CancelInput extends Input {

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
 public class GroupInput extends Input {

    private String groupName;
    private Integer priority;
    private Integer timetoIdleSeconds;
    private boolean delete;
    private boolean list;
    private boolean noHeaders;
    private File statsDirectory;

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public Integer getPriority() {
        return priority;
    }

    public void setPriority(Integer priority) {
        this.priority = priority;
    }

    public Integer getTimetoIdleSeconds() {
        return timetoIdleSeconds;
    }

    public void setTimetoIdleSeconds(Integer timetoIdleSeconds) {
        this.timetoIdleSeconds = timetoIdleSeconds;
    }

    public boolean isDelete() {
        return delete;
    }

    public boolean isNoHeaders() {
        return noHeaders;
    }

    public void setNoHeaders(boolean noHeaders) {
        this.noHeaders = noHeaders;
    }

    public void setDelete(boolean delete) {
        this.delete = delete;
    }

    public boolean isList() {
        return list;
    }

    public void setList(boolean list) {
        this.list = list;
    }

    public File getStatsDirectory() {
        return statsDirectory;
    }

    public void setStatsDirectory(File statsDirectory) {
        this.statsDirectory = statsDirectory;
    }
} public class ExtendedSubmitInput extends SubmitInput {

    private Integer parentId;

    public ExtendedSubmitInput() {
    }

    public ExtendedSubmitInput(SubmitInput si) {
        setCommand(si.getCommand());
        setEnvironment(si.getEnvironment());
        setGroupName(si.getGroupName());
        setIdempotent(si.isIdempotent());
        setMaxRSS(si.getMaxRSS());
        setWorkingDirectory(si.getWorkingDirectory());
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

}
 public class ListJobsInput extends Input {

    private boolean noHeaders;

    public boolean isNoHeaders() {
        return noHeaders;
    }

    public void setNoHeaders(boolean noHeaders) {
        this.noHeaders = noHeaders;
    }
}
 public enum ANSICode {

    BLACK("\u001B[30m"),
    RED("\u001B[31m"),
    GREEN("\u001B[32m"),
    YELLOW("\u001B[33m"),
    BLUE("\u001B[34m"),
    PURPLE("\u001B[35m"),
    CYAN("\u001B[36m"),
    WHITE("\u001B[37m"),
    BG_GREEN("\u001b[42m"),
    BG_BLACK("\u001b[40m"),
    BG_RED("\u001b[41m"),
    BG_YELLOW("\u001b[43m"),
    BG_BLUE("\u001b[44m"),
    BG_MAGENTA("\u001b[45m"),
    BG_CYAN("\u001b[46m"),
    BG_WHITE("\u001b[47m"),
    RESET("\u001B[0m"),
    BOLD("\u001b[1m"),
    UNDERLINED("\u001b[4m"),
    BLINK("\u001b[5m"),
    REVERSED("\u001b[7m"),
    INVISIBLE("\u001b[8m"),
    END_OF_LINE("\u001b[K"),
    MOVE_TO_TOP("\u001b[0;0f"),
    NO_WRAP("\u001b[?7l"),
    WRAP("\u001b[?7h"),
    CLEAR("\u001b[2J");

    private final String code;

    private static final ThreadLocal<Boolean> ACTIVE = new ThreadLocal<Boolean>() {

        @Override
        protected Boolean initialValue() {
            return Config.getInstance().getuICfg().isAnsiColors();
        }

    };

    ANSICode(String code) {
        this.code = code;
    }

    public static void setActive(boolean active) {
        ACTIVE.set(active && Config.getInstance().getuICfg().isAnsiColors());
    }

    public static boolean isActive() {
        return ACTIVE.get();
    }

    public String getCode() {
        if (isActive()) {
            return code;
        } else {
            return "";
        }
    }

    @Override
    public String toString() {
        return getCode();
    }
}
 public class NonRootUserException extends RuntimeException {
} public enum RetCode {

    ERROR(201601),
    CORE_NOT_RUNNING(201602),
    NOT_WAVA_HOME(201603),
    NOT_ROOT_USER(201604),
    CANCELLED(201605);

    private final int code;

    private RetCode(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

import java.io.File;
import org.brutusin.wava.io.Event;
import org.brutusin.wava.core.io.PeerChannel;
import org.brutusin.wava.cfg.Config;
import org.brutusin.wava.utils.LinuxCommands;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.apache.commons.lang3.StringUtils;
import org.brutusin.commons.utils.ErrorHandler;
import org.brutusin.commons.utils.Miscellaneous;
import org.brutusin.json.spi.JsonCodec;
import org.brutusin.wava.cfg.GroupCfg;
import org.brutusin.wava.core.plug.NicenessHandler;
import org.brutusin.wava.core.stats.CpuStats;
import org.brutusin.wava.core.stats.IOStats;
import org.brutusin.wava.core.stats.MemoryStats;
import org.brutusin.wava.env.EnvEntry;
import org.brutusin.wava.input.CancelInput;
import org.brutusin.wava.input.GroupInput;
import org.brutusin.wava.input.ExtendedSubmitInput;
import org.brutusin.wava.input.ListJobsInput;
import org.brutusin.wava.utils.ANSICode;
import org.brutusin.wava.utils.NonRootUserException;
import org.brutusin.wava.io.RetCode;

            JobSet.QueueIterator queuedIt = jobSet.getQueue();
            while (queuedIt.hasNext()) {
                Integer id = queuedIt.next();
                JobInfo ji = jobMap.get(id);
                if (ji.getSubmitChannel().getInput().getMaxRSS() > availableMemory) {
                    return;
                }
                queuedIt.moveToRunning();
                changeQueuedChildren(ji.getSubmitChannel().getInput().getParentId(), false);
                changeRunningChildren(ji.getSubmitChannel().getInput().getParentId(), true);
                execute(id, ji);
                availableMemory -= ji.getSubmitChannel().getInput().getMaxRSS();
            }
        }
    }

    private long getAvailableManagedMemory(long allocatedManagedMemory) {
        long availableManagedMemory = totalManagedRss - allocatedManagedMemory;
        long systemAvailableMemory = LinuxCommands.getMemInfo()[1];
        if (systemAvailableMemory < 0) { // Unable to get available memory
            return availableManagedMemory;
        }
        if (availableManagedMemory > systemAvailableMemory) {
            availableManagedMemory = systemAvailableMemory;
        }
        return availableManagedMemory;
    }

    private void onStateChanged() throws IOException, InterruptedException {
        distributeNiceness();
        checkStarvation();
        dequeueJobs();
        sendQueuePositionEventsToParentJobs();
        takeStats();
    }

    private void takeStats() throws IOException, InterruptedException {
        synchronized (jobSet) {
            long allocatedManagedMemory = getAllocatedManagedMemory();
            long availableManagedMemory = getAvailableManagedMemory(allocatedManagedMemory);
            GaugeStats stats = getStats();
            this.jobList = createJobList(false, availableManagedMemory, allocatedManagedMemory, stats);
            if (statsLogger != null) {
                currentStatRecord.running = jobSet.countRunning();
                currentStatRecord.queded = jobSet.countQueued();
                currentStatRecord.cpu = stats.cpuGaugeStats.getCpuPercent();
                currentStatRecord.rss = stats.memStats.rssBytes;
                currentStatRecord.swap = stats.memStats.swapBytes;
                currentStatRecord.io = stats.iOGaugeStats.ioBps;
                if (isWriteStatRecord(previousStatRecord, currentStatRecord)) {
                    currentStatRecord.start = previousStatRecord.end;
                    currentStatRecord.end = System.currentTimeMillis();
                    writeGlobalStatsRecord(this.statsLogger, currentStatRecord);
                    StatRecord tmp = previousStatRecord;
                    previousStatRecord = currentStatRecord;
                    currentStatRecord = tmp;
                }
            }
        }
    }

    /**
     *
     * @param pi
     * @return ret[0]: job position, ret[1]: group position
     * @throws IOException
     * @throws InterruptedException
     */
    private int[] getRunningPosition(ProcessInfo pi) throws IOException, InterruptedException {
        synchronized (jobSet) {
            JobSet.RunningIterator running = jobSet.getRunning();
            int pos = 0;
            int gpos = 0;
            ProcessInfo prev = null;
            while (running.hasNext()) {
                Integer id = running.next();
                ProcessInfo p = processMap.get(id);
                if (p == null) {
                    continue;
                }
                if (prev != null && !prev.getJobInfo().getSubmitChannel().getInput().getGroupName().equals(p.getJobInfo().getSubmitChannel().getInput().getGroupName())) {
                    gpos++;
                }
                if (pi.getJobInfo().getId() == p.getJobInfo().getId()) {
                    return new int[]{pos, gpos};
                }
                pos++;
                prev = p;
            }
            return null;
        }
    }

    private GaugeStats getStats() throws IOException, InterruptedException {
        synchronized (jobSet) {
            GaugeStats ret = new GaugeStats();
            Iterator<Integer> running = jobSet.getRunning();
            for (GroupInfo gi : groupMap.values()) {
                if (gi.getStatsLogger() != null) {
                    gi.getCurrentStatRecord().start = 0;
                    gi.getCurrentStatRecord().end = 0;
                    gi.getCurrentStatRecord().cpu = 0;
                    gi.getCurrentStatRecord().swap = 0;
                    gi.getCurrentStatRecord().io = 0;
                    gi.getCurrentStatRecord().rss = 0;
                    gi.getCurrentStatRecord().queded = 0;
                    gi.getCurrentStatRecord().running = 0;
                    gi.getCurrentStatRecord().io = 0;
                }
            }
            while (running.hasNext()) {
                Integer id = running.next();
                ProcessInfo pi = processMap.get(id);
                if (pi == null) {
                    continue;
                }
                MemoryStats memStats = LinuxCommands.getCgroupMemoryStats(pi.getJobInfo().getSubmitChannel().getInput().getGroupName(), id);
                if (memStats == null) {
                    continue;
                }

                CpuStats cpuStats = LinuxCommands.getCgroupCpuStats(pi.getJobInfo().getSubmitChannel().getInput().getGroupName(), id);
