[SOLVED] BASH scripting, creating a custom service (CentOS)
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
BASH scripting, creating a custom service (CentOS)
I am trying to create a custom service to manage a minecraft server what runs in a screen session (this is to allow easy console access as opposed to running it as a nohup background process). I am hitting two main issues with this script so far and wondering if anybody is able to shed some light on it, considering I am far from the greatest BASH scripter on Earth.
The issues are related to start and stop. the first issue with start is there is a command run "screen -dmS minecraft java -Xms1024M -Xmx1024M -jar /minecraft/minecraft.jar nogui" what doesn't appear to work, however when run from console (copy and pasted) this command seems to run perfectly. I am uncertain as to why this is not working within the script. Also if run from a script in /minecraft it also appears to work but in the minecraftd script I have created at /etc/init.d it does not work. I think the issue might be related to the location but am not certain on it, nor how to resolve it.
the second issue with stop is that it works sometimes but at other times it does not, overall it seems to work about 30~50% of the time. When it works it passes a couple of disconnection messages to the screen session and then gives the stop command (the command to gracefully terminate the minecraft server). It seems to work more often when using screen that I attach to the minecraft session while it is running but really not sure if this is an issue with my scripting or an issue with how I am passing things to screen.
The full code in /etc/init.d/minecraftd is below
Code:
#!/bin/bash
## Script Information
## Script Author: Berwick East
## Script Version: --------
## Last Updated: 07-MAY-2011
## Note
## this script is only a free service management tool for minecraft and can be redistribed, Minecraft itself however is not as per copyrights
## (http://www.minecraft.net/copyright.jsp).
##http://it.megocollector.com/?p=805
SERVICE="Minecraft"
RETVAL=0
PROCESS=`ps aux | grep minecraft | egrep -v 'SCREEN|grep|etc/init.d|service'`
start()
{
if [ -z "$PROCESS" ]
then
cd /minecraft
service iptables restart > /dev/null
echo "$SERVICE is being started"
iptables -I RH-Firewall-1-INPUT -p tcp --dport 25565 -j ACCEPT
screen -dmS minecraft java -Xms1024M -Xmx1024M -jar /minecraft/minecraft.jar nogui
else
echo "$SERVICE appears to already be running"
fi
}
stop()
{
if [ "$PROCESS" ]
then
echo "$SERVICE is now sending disconnection messages"
screen -d minecraft > /dev/null
echo "sending first disconnect message (60 seconds left)"
screen -S minecraft -X stuff $'say Server is preparing to shutdown in 60 seconds please disconnect.\n'
sleep 30
echo "sending second disconnect message (30 seconds left)"
screen -S minecraft -X stuff $'say Server will shutdown in 30 seconds, disconnect immediately.\n'
sleep 20
echo "sending final disconnect message (10 seconds left)"
screen -S minecraft -X stuff $'say Server is about to shutdown, you will be automatically disconnected in 10 seconds.\n'
screen -S minecraft -X stuff $'stop\n'
service iptables restart > /dev/null
echo "$SERVICE is now stopping"
sleep 20
RETEST=`ps aux | grep minecraft | egrep -v 'SCREEN|grep|etc/init.d|service'`
if [ "$RETEST" ]
then
echo "$SERVICE failed to stop in expected time, please enter 'screen -r minecraft' into the console and then enter 'STOP' manually."
exit 1
else
echo "$SERVICE stopped successfully"
fi
else
echo "$SERVICE not found, if running please kill manually"
exit 1
fi
}
status()
{
##http://www.anyexample.com/linux_bsd/bash/check_if_program_is_running_with_bash_shell_script.xml
if [ "$PROCESS" ]
then
echo "$SERVICE is running"
else
echo "$SERVICE is stopped"
fi
}
restart()
{
if [ "$PROCESS" ]
then
stop
else
echo "$SERVICE was already stopped"
fi
start
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 1
esac
exit $RETVAL
Any help would be appreciated,
Thanks in advanced.
Last edited by r3sistance; 05-07-2011 at 04:42 AM.
A couple of thoughts. Red Hat/Centos has it's own way of managing
services. To see a good example, look at /etc/init.d/crond. Notice
that it has several commented lines starting with the keyword chkconfig.
These are required to properly use the service management tools, ntsysv
and chkconfig. It's a good idea to start with this as a template and
update it to match your new service. It's more work, but it ensures
that your service will be compatible with the OS.
Second thought. I'll bet the issue with it not starting is that bash
knows whether or not it is being run interactively, and screen is
expecting to be run from an interactive shell. What's the difference?
In an interactive shell your keyboard is set up as input and output
goes to your display (unless redirected). In a non-interactive shell
there is no input and output is buffered (unless redirected). I hope
this makes sense.
I'll look into configuring it to the template once I have things working (don't want it to be for nothing if I still can't get it working).
I am not sure I get the issue with interactive mode, I mean I can understand that when you run a script file it runs it in a separate shell, however in another script where I have copied and pasted the exact line from, it works fine, so I am not sure why it'd have that problem with one script file and not another. expect for testing I always start this process via script.
When you start a shell interactively, it assumes that the input
is coming from a keyboard and output is going to a screen. When
it is started non-interactively (either in a startup script or
an at or cron job), it assumes there is no keyboard and that the
output is not being seen in real time by a human. So it's all
about input and output. Let me know if this still doesn't make
sense.
Some programs such as sudo check to see if the shell is interactive.
I'll bet that screen does such a check and it is refusing to run.
Try searching for "linux screen non-interactive shell" and see
if it sheds any light.
Just to follow up on your last sentence. Any time you run a script
it starts in a shell (unless you use the . command). It's the parent
shell that is important here. When you type the command, the parent
shell is an interactive bash shell. When the command is run as a
service, the parent shell is a non-interactive shell launched by
the startup scripts.
Basically it's not a question of how the script was written, it's
a question of whether or not the invoking shell is interactive.
There may be a work around for this by adding some parameter to
screen. Try the search from my last reply.
You can start screen without a controlling terminal, in detached mode. Then you have to use the '-X stuff' command to stuff characters into the detached session, and you can do that from your script, once you've launched screen. . I use this method quite a bit to start processes that have an interactive aspect to them, and to which I want to later attach and do interactive stuff.
Ok I have figured out all of my issues. Thank you carltm, while your suspicious appears to have been wrong you put me in the right mind set to figure this all out and I thank you for that!
I have figured out what is causing the stop issue and have resolved it dirtily. The screen session will only close if I have attached to it (via screen -r minecraft) either before or during the shut down, if I have never attached to the screen session it will not run, I have already found one thread on this and after reading this I had a brainstorm. From that I have figured out my own method, a second screen session that attaches to the first, this appears to work. The code now looks like
Code:
stop()
{
if [ "$PROCESS" ]
then
screen -dms dirtyhack screen -r minecraft
echo "$SERVICE is now sending disconnection messages"
screen -d minecraft > /dev/null
echo "sending first disconnect message (60 seconds left)"
screen -S minecraft -X stuff $'say Server is preparing to shutdown in 60 seconds please disconnect.\n'
sleep 30
echo "sending second disconnect message (30 seconds left)"
screen -S minecraft -X stuff $'say Server will shutdown in 30 seconds, disconnect immediately.\n'
sleep 20
echo "sending final disconnect message (10 seconds left)"
screen -S minecraft -X stuff $'say Server is about to shutdown, you will be automatically disconnected in 10 seconds.\n'
screen -S minecraft -X stuff $'stop\n'
service iptables restart > /dev/null
echo "$SERVICE is now stopping"
sleep 20
RETEST=`ps aux | grep minecraft | egrep -v 'SCREEN|grep|etc/init.d|service'`
if [ "$RETEST" ]
then
echo "$SERVICE failed to stop in expected time, please enter 'screen -r minecraft' into the console and then enter 'STOP' manually."
exit 1
else
echo "$SERVICE stopped successfully"
fi
else
echo "$SERVICE not found, if running please kill manually"
exit 1
fi
}
As for the start script, I never thought to try the script by doing "/etc/init.d/minecraftd start"! This method works so it very much appears that service is responsible for it not running. The problem can only be made more confusing however, the above stop routine also creates a screen session in very much the same way but this works fine by running service.
My suspicious became that service does not use the same path as the user that calls it and since when I installed java I only made the path for root I simpled switched "java" for the route to where java is actually held and that works. So this appears all solved, but if anybody has any cleaner way to handle the attach issue for the stop script I would like to hear it.
it appears Service has the path "/sbin:/usr/sbin:/bin:/usr/bin", what is definitely not the same as I have configure for root and thus my prolbem, Java does not exist in any of these, I will probably symbolic link java to /sbin now (probably crossing partitions but allows me to write the code a bit neater).
Last edited by r3sistance; 05-08-2011 at 06:55 AM.
Reason: Service's Path
There are many ways to handle paths. My suggestion would be
to export the path that you need from inside the startup
script. Doing it this way will prevent surprises later if
you install another instance of java or if a security update
makes some change to the default version of java. Just add
a line like this.
You can probably get around the 'screen in screen' method by using the -p 0 option in your command that stuffs strings into the detached screen.
--- rod.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.