Keeping access to STDIN of a program run in the background via a FIFO - shell

So I have a docker image, containing a minecraft server. A minecraft server takes input from the STDIN, so after starting the server with java -jar server.jar I can feed it commands (to stop the server for example) by typing it in the console.
Ideally I want to start the server in a shellscript that looks something this:
#!/bin/sh
.. some initialization ..
exec java -Xmx$RAM -Xms$RAM -jar server.jar
This way the java executable takes over the process, so that it can be gracefully terminated when docker sends a SIGTERM.
The issue comes when I want to keep an artificial STDIN, that allows me to executes commands onto the server via another script. To do this I made a FIFO that represents the console like so:
rm -f console; mkfifo console
I'm not sure how I can redirect this FIFO into the process this way though. I'm reading the fifo using cat in a while-loop like so
while true; do cat console; done
And thought about doing something like this:
while true; do cat console; done | exec java -jar server.jar
Or something like this:
exec java -jar server.jar < <(while true; do cat console; done)
For the latter, I've looked at bash: pipe data into an exec'd command which unfortunately gives me syntax error: unexpected redirection
I've also tried this:
while true; do cat console; done >&0 &
exec java -jar server.jar
which seems to freeze the server while it's starting.
I have no idea why all of this is not working, and I don't know how I can make it work.
To be clear of what I want to achieve:
Have java (the server) take over the main process
Have a FIFO redirected to the STDIN of the server
It sounds so simple to me, but I just cannot make it happen. Any help would be greatly appreciated.

I just found out that the < <(command) syntax is not supported in /bin/sh but it is support in /bin/bash.
After switching to that, my final line is:
exec java -jar server.jar < <(tail -f console)
Sometimes you just have to accept that you have to install more dependencies..

Related

mvn jetty:run-forked inside a docker container?

I have an application that uses the jetty maven plugin "run-forked" goal that I need to dockerize. What happens is that maven starts, the container exists only for about 10 seconds and then dies when maven exits after it forks the child JVM process.
I have investigated many options. One option that I thought might work is to set "waitForChild" to true and then
do something like this:
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "jetty:run-forked > /tmp/log 2>&1" ]
But, though this keeps maven running, the image does not build, because Docker waits for a SIGTERM.
If you are wondering why I need to use jetty:run-forked, it is because the code requires a static linked library that needs a JVM.
I am ready to throw in the towel, because this seems impossible ...
I'm not entirely sure about your java set up, but a neat trick that works is something like this:
In your dockerfile, add a custom script like so:
COPY myscript.sh /bin/myscript.sh # Remember to make this executable!
then edit your ENTRYPOINT to reflect that:
ENTRYPOINT ["/bin/myscript.sh"]
Your myscript.sh could look a little something like this:
#!/bin/bash
# Run Java/mvn commands here
...
jetty:run-forked > /tmp/log 2>&1
# Throw in a shell command that simply executes forever
tail -f /dev/null
This will ensure your container keeps running even after your jetty/mvn stuff spawns another process and quits, because it is no longer PID 1 within the container, the myscript.sh shell script is. This shell script continues to run forever because of the tail -f.

prevent output from script in xinetd service

I have a bash script that starts Xvnc after doing some other processing, and it's launched from a xinetd service. However the script indirectly outputs some text to stdout and stderr, which gets sent back to the connecting client.
Is there some way to tell xinetd to ignore any ouput from the script and just let Xvnc take over the connection?
(I assume Xvnc somehow takes over the socket from xinetd, rather than just using stdout to communicate with xinetd?)
Put the following lines into the script at the beginning:
exec >/dev/null
exec 2>/dev/null

How to run shell script on VM indefinitely?

I have a VM that I want running indefinitely. The server is always running but I want the script to keep running after I log out. How would I go about doing so? Creating a cron job?
In general the following steps are sufficient to convince most Unix shells that the process you're launching should not depend on the continued existence of the shell:
run the command under nohup
run the command in the background
redirect all file descriptors that normally point to the terminal to other locations
So, if you want to run command-name, you should do it like so:
nohup command-name >/dev/null 2>/dev/null </dev/null &
This tells the process that will execute command-name to send all stdout and stderr to nowhere (instead of to your terminal) and also to read stdin from nowhere (instead of from your terminal). Of course if you actually have locations to write to/read from, you can certainly use those instead -- anything except the terminal is fine:
nohup command-name >outputFile 2>errorFile <inputFile &
See also the answer in Petur's comment, which discusses this issue a fair bit.

Buffered pipe in bash

I'm running a Bukkit (Minecraft) server on a Linux machine and I want to have the server gracefully shut down using the server's stop command and the computer suspend at a certain time using pm-suspend from the command line. Here's what I've got:
me#comp~/dir$ perl -e 'sleep [time]; print "stop\\n";' | ./server && sudo pm-suspend
(I've edited by /etc/sudoers so I don't have to enter my password when I suspend.)
The thing is, while the perl -e is sleeping, the server is expecting a constant stream of bytes, (That's my guess. I could be misunderstanding something.) so it prints out all of the nothings it receives, taking up precious resources:
me#comp~/dir$ ...
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>...
Is there any such thing as a buffered pipe? If not, are there any ways to send delayed input to a script?
You may want to have a look at Bukkit's wiki, which recommends an init script for permanently running servers.
This init script uses rather unconventional approach to communicate with running server. The server is started in screen session, then all commands are send to the server console via screen, e.g.
screen -p 0 -S $SCREEN -X eval 'stuff \"stop\"\015'
See https://github.com/Ahtenus/minecraft-init/blob/master/minecraft
This approach suggest that bukkit may be expecting standard input to be attached to a terminal, thus requiring screen wrapper (which is itself terminal emulator) for unattended runs.

Send command to a background process

I have a previously running process (process1.sh) that is running in the background with a PID of 1111 (or some other arbitrary number). How could I send something like command option1 option2 to that process with a PID of 1111?
I don't want to start a new process1.sh!
Named Pipes are your friend. See the article Linux Journal: Using Named Pipes (FIFOs) with Bash.
Based on the answers:
Writing to stdin of background process
Accessing bash command line args $# vs $*
Why my named pipe input command line just hangs when it is called?
Can I redirect output to a log file and background a process at the same time?
I wrote two shell scripts to communicate with my game server.
This first script is run when computer start up. It does start the server and configure it to read/receive my commands while it run in background:
start_czero_server.sh
#!/bin/sh
# Go to the game server application folder where the game application `hlds_run` is
cd /home/user/Half-Life
# Set up a pipe named `/tmp/srv-input`
rm /tmp/srv-input
mkfifo /tmp/srv-input
# To avoid your server to receive a EOF. At least one process must have
# the fifo opened in writing so your server does not receive a EOF.
cat > /tmp/srv-input &
# The PID of this command is saved in the /tmp/srv-input-cat-pid file
# for latter kill.
#
# To send a EOF to your server, you need to kill the `cat > /tmp/srv-input` process
# which PID has been saved in the `/tmp/srv-input-cat-pid file`.
echo $! > /tmp/srv-input-cat-pid
# Start the server reading from the pipe named `/tmp/srv-input`
# And also output all its console to the file `/home/user/Half-Life/my_logs.txt`
#
# Replace the `./hlds_run -console -game czero +port 27015` by your application command
./hlds_run -console -game czero +port 27015 > my_logs.txt 2>&1 < /tmp/srv-input &
# Successful execution
exit 0
This second script it just a wrapper which allow me easily to send commands to the my server:
send.sh
half_life_folder="/home/jack/Steam/steamapps/common/Half-Life"
half_life_pid_tail_file_name=hlds_logs_tail_pid.txt
half_life_pid_tail="$(cat $half_life_folder/$half_life_pid_tail_file_name)"
if ps -p $half_life_pid_tail > /dev/null
then
echo "$half_life_pid_tail is running"
else
echo "Starting the tailing..."
tail -2f $half_life_folder/my_logs.txt &
echo $! > $half_life_folder/$half_life_pid_tail_file_name
fi
echo "$#" > /tmp/srv-input
sleep 1
exit 0
Now every time I want to send a command to my server I just do on the terminal:
./send.sh mp_timelimit 30
This script allows me to keep tailing the process on your current terminal, because every time I send a command, it checks whether there is a tail process running in background. If not, it just start one and every time the process sends outputs, I can see it on the terminal I used to send the command, just like for the applications you run appending the & operator.
You could always keep another open terminal open just to listen to my server server console. To do it just use the tail command with the -f flag to follow my server console output:
./tail -f /home/user/Half-Life/my_logs.txt
If you don't want to be limited to signals, your program must support one of the Inter Process Communication methods. See the corresponding Wikipedia article.
A simple method is to make it listen for commands on a Unix domain socket.
For how to send commands to a server via a named pipe (fifo) from the shell see here:
Redirecting input of application (java) but still allowing stdin in BASH
How do I use exec 3>myfifo in a script, and not have echo foo>&3 close the pipe?
You can use the bash's coproc comamnd. (avaliable only in 4.0+) - it's like ksh's |&
check this for examples http://wiki.bash-hackers.org/syntax/keywords/coproc
you can't send new args to a running process.
But if you are implementing this process or its a process that can take the args from a pipe, then the other answer would help.

Resources