Shubham Khatri
Shubham Khatri

Reputation: 281606

Execute a local script on a remote machine through Java JSch library

I am working on monitoring the logs for an error on a set of remote servers. For that I am monitoring logs that were modified within some time period.

I am using Jsch Java library to make a SSH connection and excute the commands. As I have searched, multiple commands can be remote machine by separating different commands by ;

For that I have a text file which contains the list on remote hosts on which I am establishing the connection. I realise that I can get the list of all files that were modified by the following command

find . -mmin -60 -type f -exec ls {} +

No I can loop over the files and grep for the Error. For this I have written the following script

logTestScript.sh

#!/bin/bash

cd /log
searchString='<Error ErrorCode="java.sql.SQLException"'
files=`find . -mmin -60 -type f -exec ls {} +`
pwd
hostname
echo ${files}
for file in ${files[@]}
do
        if grep -Fq "$searchString" $file
        then
                echo "String found in $file"
        fi
done

Now this script is present in the current package from which I am running a java program and I wish to run this script on each of the remote host.

Below is my current code making use of ChannelExec

private void getSSHConnection() throws IOException, JSchException {

        username = props.getProperty("host_username");
        password = props.getProperty("host_password");
        log_traceback_interval = props.getProperty("fetch_logs_interval_minutes");
        errorString = props.getProperty("error_message_log");

        System.out.println(username + " " + password + " " + errorString);

        // Get the hostnames resource file
        String hostnameResourcePath = envObj.getAgentHostnamesConfig(TargetEnvironment, ManagementSystem);
        InputStream hostInpStream = this.getClass().getResourceAsStream(hostnameResourcePath);
        BufferedReader hostnameReader = new BufferedReader(new InputStreamReader(hostInpStream));

        String host = null;

        while((host = hostnameReader.readLine()) != null) {

            System.out.println(host);

            // get a JSch connection object
            JSch jschObj = new JSch();
            Session session = jschObj.getSession(username, host);

            // Disable StrictHost key checking
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            session.setPassword(password);
            session.connect();

            //Execute channel instance
            ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
            InputStream in = channelExec.getInputStream();

            //Bash script command with arguments
            StringBuilder sb = new StringBuilder();
            String command = sb.append("cd /log").append(";")
                                .append("echo `find . -mmin -60 -type f -exec ls {} +`").append(";")
                                .toString();   

            System.out.println(command);

            //Run the script on host
            channelExec.setCommand(command);    //I want to run the script here instead of each command as it uses for loop

            channelExec.setInputStream(null);

            channelExec.setErrStream(System.err);

            InputStream monitorLogs = channelExec.getInputStream();

            channelExec.connect();

            //Read the logs
            BufferedReader logReader = new BufferedReader(new InputStreamReader(monitorLogs));
            String logLine;

            while((logLine = logReader.readLine()) != null) {

                System.out.println(logLine);
            }

            //Close the connections
            channelExec.disconnect();
            session.disconnect();
        }




    }

One solution that I can think of if to SCP the script on to the remote host and then executing it there.

However Is there some simple solution, with which I can acieve the same result even without writing the script and only executing commands or executing the script on the remote server.

Thanks for help

Upvotes: 1

Views: 4150

Answers (2)

Martin Prikryl
Martin Prikryl

Reputation: 202088

You cannot execute a local script on a server.

You have two options (that you are obviously aware of):

  • Upload the script and execute it (and delete afterwards, if needed).
  • Execute the contents of the script. As you already know, just concatenate all lines with ;.

    Though be careful about the for and if. There should be no ; after the do and then.

    cd /log ; searchString='<Error ErrorCode="java.sql.SQLException"' ; files=`find . -mmin -60 -type f -exec ls {} +` ; echo `pwd` ; echo `hostname` ; echo ${files} ; for file in ${files[@]} ; do if grep -Fq "$searchString" $file ; then echo "String found in $file" ; fi ; done
    
  • Similarly to the previous approach, you can execute the bash -s and write the contents of the script (the commands) to its input.

    ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
    channelExec.setCommand("bash -s");
    channelExec.connect();
    OutputStream out = channel.getOutputStream();
    FileInputStream fis = new FileInputStream("testScript.sh");
    byte[] buf = new byte[1024];
    while (true)
    {
        int len = fis.read(buf, 0, buf.length);
        if (len <= 0) break;
        out.write(buf, 0, len);
    }
    

    The above is basically copied from seemingly unrelated SCP upload example - That example actually feeds a local file to remote scp process - So it's actually pretty related.


Ntb, "echo `pwd`" and "echo `hostname`" do not make sense. Use "pwd" and "hostname" directly.

Upvotes: 3

VIPIN KUMAR
VIPIN KUMAR

Reputation: 3127

In order to execute the script script.sh on a remote server, you can use below code -

#!/usr/bin/expect
export PASSD="User@123"
expect -c 'spawn sftp [email protected]; 
expect "*Password: ";
send "$env(PASSD)\r";
expect "ssh>";
send "cd /home/user \r";
expect "ssh>";
send "sh -x script.sh \r";
expect "ssh>";
send "exit \r"'

You can put above code in a script and then add after the success condition of your if loop.

Upvotes: -1

Related Questions