/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.processtools; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.atomic.AtomicBoolean; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; public class ProcessData { private final Session m_ssh_session; private final Channel m_channel; private final StreamWatcher m_out; private final StreamWatcher m_err; private Thread m_sshThread; public enum Stream { STDERR, STDOUT; } public static class OutputLine { public OutputLine(String processName, Stream stream, String value) { this.processName = processName; this.stream = stream; this.message = value; } public final String processName; public final Stream stream; public final String message; } class StreamWatcher extends Thread { final BufferedReader m_reader; final String m_processName; final Stream m_stream; final OutputHandler m_handler; final AtomicBoolean m_expectDeath = new AtomicBoolean(false); StreamWatcher(BufferedReader reader, String processName, Stream stream, OutputHandler handler) { assert(reader != null); m_reader = reader; m_processName = processName; m_stream = stream; m_handler = handler; } void setExpectDeath(boolean expectDeath) { m_expectDeath.set(expectDeath); } @Override public void run() { while (true) { String line = null; try { line = m_reader.readLine(); } catch (IOException e) { if (!m_expectDeath.get()) { e.printStackTrace(); System.err.print("Err Stream monitoring thread exiting."); System.err.flush(); } return; } if (line != null) { OutputLine ol = new OutputLine(m_processName, m_stream, line); if (m_handler != null) { m_handler.update(ol); } else { final long now = (System.currentTimeMillis() / 1000) - 1256158053; System.out.println("(" + now + ")" + m_processName + ": " + line); } } else { return; } } } } ProcessData(String processName, OutputHandler handler, Session ssh_session, final String command) throws JSchException, IOException { m_ssh_session = ssh_session; m_channel=ssh_session.openChannel("exec"); ((ChannelExec)m_channel).setCommand(command); // Set up the i/o streams m_channel.setInputStream(null); BufferedReader out = new BufferedReader(new InputStreamReader(m_channel.getInputStream())); BufferedReader err = new BufferedReader(new InputStreamReader(((ChannelExec) m_channel).getErrStream())); m_out = new StreamWatcher(out, processName, Stream.STDOUT, handler); m_err = new StreamWatcher(err, processName, Stream.STDERR, handler); /* * Execute the command non-blocking. */ m_sshThread = new Thread() { @Override public void run() { try { m_channel.connect(); m_out.start(); m_err.start(); } catch (Exception e) { e.printStackTrace(); System.err.print("Err SSH long-running execution thread exiting."); System.err.flush(); } } }; m_sshThread.start(); } public int kill() { m_out.m_expectDeath.set(true); m_err.m_expectDeath.set(true); int retval = -255; synchronized(m_channel) { m_channel.disconnect(); m_ssh_session.disconnect(); } m_sshThread.interrupt(); return retval; } public boolean isAlive() { synchronized(m_channel) { return (m_ssh_session.isConnected() && m_channel.getExitStatus() == -1); } } }