How to issue command to server through command line - java

I am working on an application that starts a minecraft server with one click of a button. I've successfully got the server to startup, but now I am trying to figure out a way to stop the server through the same cmd process.
Here's my code for starting the server...
public static void startServer() {
System.out.println("Starting server...");
try {
server = Runtime.getRuntime().exec(
"java -jar -Xmx1024M -Xms1024M minecraft_server.jar nogui");
output = server.getOutputStream();
input = server.getInputStream();
writer = new BufferedWriter(new OutputStreamWriter(output));
} catch (IOException e) {
e.printStackTrace();
}
}
This tells the runtime to execute a run.bat file that is in the same directory of the application. This method also initialized the OutputStream and InputStream objects that I created at the top of this class.
Here's my code for stopping the server...
public static void stopServer() {
System.out.println("Stopping server...");
// server.destroy();
try {
writer.write("stop\n\r");
} catch (IOException e) {
e.printStackTrace();
}
}
"stop" is a command that I'm trying to issue to the server to stop it, but for some reason the command is never being issued to the server.
More info:
The server is being run in cmd.exe, and therefore all server cmds need to be issued in cmd.
The server is named minecraft_server.jar so I have to use the command line to run the server and get output from the server and write input to it.
The run.bat file contains the text java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui.
My main goal is to write the command "stop" to the server to stop it.

As #clearlyspam23 stated, you are killing the process the moment you write anything to it.
Second, you are writing to the process' output stream, you want to write in the input stream. Edit: nope
Also, any server command is usually validated with a 'Enter' keystroke, so you might need to add a carriage return ('\r') right after your command to simulate that.

Related

How to fix TERM environment not set in Intellij?

I am currently working on a java automation application incorporating Jsch. When I run my code however, it passes back an error saying that the TERM environment is not set up.
I already tried to manually add the environment in intellij by choosing environment variables. Then I add TERM=xterm. Though when I run that, it still fails.
import com.jcraft.jsch.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Driver {
public static void main(String[] args) throws Exception {
JSch jsch = new JSch();
Session session;
try {
// Open a Session to remote SSH server and Connect.
// Set User and IP of the remote host and SSH port.
session = jsch.getSession("username", "host", 22);
// When we do SSH to a remote host for the 1st time or if key at the remote host
// changes, we will be prompted to confirm the authenticity of remote host.
// This check feature is controlled by StrictHostKeyChecking ssh parameter.
// By default StrictHostKeyChecking is set to yes as a security measure.
session.setConfig("StrictHostKeyChecking", "no");
//Set password
session.setPassword("password");
session.connect();
// create the execution channel over the session
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
// Set the command to execute on the channel and execute the command
channelExec.setCommand("./script.sh");
channelExec.connect();
// Get an InputStream from this channel and read messages, generated
// by the executing command, from the remote side.
InputStream in = channelExec.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// Command execution completed here.
// Retrieve the exit status of the executed command
int exitStatus = channelExec.getExitStatus();
if (exitStatus > 0) {
System.out.println("Remote script exec error! " + exitStatus);
}
//Disconnect the Session
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Make sure that the variable is set in your current shell by either exporting it or running your code with a set variable for TERM.
Something similar to the following should work:
TERM=linux /path/to/your/executable --some-arguments
The following could possibly only be relevant to bash but there is also a way to export a variable so as to make it global.
After exporting a variable, you can verify its value using:
echo $TERM
Empty response means variable is not set. Else, well... you get it i am sure. In order to export it globally, in bash that is, you can use the command line directly or add the export command into your dotfiles, which should be loaded upon login
export TERM=linux
Either way you choose, the command stays the same. There are multiple terminals and types, 'linux' being a very very generic one. A more color-friendly solution could be to try using 'xterm-256color' instead.
export TERM=xterm-256color
You should check out the basics of terminal if you wish to learn more. I hope this can help you achive your desired outcome.
Cheers
IntelliJ IDEA Run console is not a real Terminal, hence the problem.
You can run the code manually outside of IntelliJ IDEA or in the Terminal tool window.
For debugging you can use the Remote debug.
Related request: Add option to run configuration to launch in real console.

Invoking Seagull Diameter Client using Java

i need to send some messages from my java web application to some servers using Diameter protocol, in particular CCR-CCA scenario. I had a look at jdiameter opensource project, but my usecase does not require such complexity, since that i just need to send a single request and log the response (actually i don't even need the CER-CEA part).
So i thought i could just have used Seagull running under my webapp. I downloaded Seagull (for Windows), and what i'm trying to do is basically to run the .bat file coming from Seagull for the diameter environment from my java environment.
That's what i've done till now..
1) A simple test to invoke the client.. Here wrapper simply sets working dir and starts the process
public static void main(String[] args) {
List<String> cmd=new ArrayList<>();
cmd.add("cmd.exe");
cmd.add("/c");
cmd.add("my_start_client.bat");
JavaProcessBuilderWrapper wrapper = new JavaProcessBuilderWrapper();
Process p = wrapper.createProcess(RedirectErrorsTo.STDERR,
new HashMap<>(), new File("my_working_dir"), cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null) {
output.append(line);
}
System.out.println(line);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2) I modified the client's and server's .bat files coming from Seagull to use CCR-CCA protocol.
Running Java main with this configuration caused a
Fatal: Keyboard saved configuration failure error
on my logs.
3) So, as mentioned here i further modified my client's .bat file to run in background mode, adding -bg at the end. Now my client's bat look like this
#ECHO OFF
rem
"Diameter Start Script Sample"
"Local env"
SET RUN_DIR=C:\Program Files\Seagull
set PATH=%PATH%;%RUN_DIR%
set LD_LIBRARY_PATH=%RUN_DIR%
set RUN_DIR=%RUN_DIR%\diameter-env\run
cd %RUN_DIR%
cls
mode 81,25
echo "Seagull Diameter Client Sample Start"
seagull -conf ..\config\conf.client.xml -dico ..\config\base_ro_3gpp.xml -scen ..\scenario\ccr-cca.ro.client.xml -log ..\logs\ccr-cca.client.log -llevel ETM -bg
pause
Since i was facing some troubles, to keep things simple, i just tried to make it work at least via cmd (not using my java method), but i think background mode is messing around, because now when i start my server and then my client in bg mode, sometimes i get a
Fatal: Forking error
but the most of the times, the client send a single message and then on my console i see that my software is causing connection abort (error code -1), and from the log i see that the channel just get closed, and my client does not even receive an answer. (NB for now i left the configuration files untouched)
Has any of you faced this behaviour? Is something else closing the connection (firewall perhaps)? Do i have to provide other configurations to make this work?
Once i can get this working, can i use my java web app (with a method similar to the one i already mentioned) to make diameter calls?
Thanks in advance, any help is really welcomed.

How to run a shell script from Java and have it continue running after JVM shutdown?

I'm writing a plugin in order to restart a server application on Linux (though I'm testing on OSX). The way I'm doing this is using a shell script which commands the application to stop, and then oversees the death of the process, safely starting a new one when the time comes.
My script works when I execute it manually from the command line. However, when I execute it from within the application, the shell process is killed along with the application.
I've tried two different methods of running the process from Java:
String scriptArgs[] = {"sh", "restart.sh", "&"};
try {
Runtime.getRuntime().exec(scriptArgs);
} catch (IOException e) {
e.printStackTrace();
}
and
ProcessBuilder processBuilder = new ProcessBuilder("sh", "restart.sh");
try {
processBuilder.directory(new File(System.getProperty("user.dir")));
processBuilder.redirectErrorStream(false);
processBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
Both of these methods gave the same result: the script was called, it successfully shut down the application, and then it died before it could continue. Is there any method to start a completely independent process from Java?
When you run a process from java you are creating a shell instance which then runs the process. The shell will only exit once this process has finished even if it is being run in the background &
To run a process in headless mode you need to use the nohup command. For details, see here.
A usage could look like this:
ProcessBuilder processBuilder = new ProcessBuilder("nohup", "sh", "restart.sh");
try {
processBuilder.directory(new File(System.getProperty("user.dir")));
processBuilder.redirectErrorStream(false);
processBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}

Java-OutputStreamWriter "Spam"

I have built a program that sends command to another Jar(After running it). After playing around with Streams , I figured how to prevent the second Jar from crashing with every command sent. But now , another error popped up! The second jar prints Uknown Command if the command sent is not valid. After sending the command reload for example , I get feedback that the command was excecuted , but then my log wont stop printing Unknown Command !
I am sending the commands like this:
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(f.outputStream));
try {
writer.write("reload");
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
This is inside a thread. Then I run the thread.

Killing a process through sshj

Im using sshj and im trying to tail a file, but my problem is that the remote process is never killed.
In the following example code you can see that i try to tail /var/log/syslog, and then i send a kill signal to the process. However after the application has stopped and i list out all the processes on the server, i can still see an active tail process.
Why will not this code kill the process? and what can i do to remedy that?
SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier());
try {
ssh.connect("localhost");
ssh.authPassword("xxx", "xxx");
final Session session = ssh.startSession();
try {
final Command cmd = session.exec("tail -f /var/log/syslog");
cmd.signal(Signal.KILL);
System.out.println("\n** exit status: " + cmd.getExitStatus());
} catch (IOException e) {
e.printStackTrace();
}finally{
session.close();
}
} finally{
ssh.disconnect();
}
EDIT
also tried sending all available signals.
for(Signal s : Signal.values()){
cmd.signal(s);
}
Allocating a PTY and sending a Ctrl+C character code did the trick for me:
final Session session = ssh.startSession();
session.allocateDefaultPTY();
try {
final Command cmd = session.exec("tail -f /var/log/syslog");
// Send Ctrl+C (character code is 0x03):
cmd.getOutputStream().write(3);
cmd.getOutputStream().flush();
// Wait some time for the process to exit:
cmd.join(1, TimeUnit.SECONDS);
// If no exception has been raised yet, then the process has exited
// (but the exit status can still be null if the process has been killed).
System.out.println("\n** exit status: " + cmd.getExitStatus());
} catch (IOException e) {
e.printStackTrace();
}finally{
session.close();
}
Of course, being able to send signals would be better, but if even the OpenSSH server does not support it, there's no hope there :/
openssh doesn't support it https://bugzilla.mindrot.org/show_bug.cgi?id=1424
Just use cmd.close(), that should term the process as well
This is most likely a problem with the ssh server implementation, as i have tried using two different ssh clients and getting the same result. My solution ended up being a client-side tail logic, instead of "tail -f" to prevent free roaming processes.
Had a similar issue recently. In my specific case it was the OpenSSH issue mentioned by #shikhar.
My solution was to run start another session (sharing the connection) and run a kill command pgrep mycommand | xargs kill.

Categories