Skip to content

Instantly share code, notes, and snippets.

@RadoBuransky
Last active March 24, 2020 20:26
Init.d shell script for Play framework distributed application. Provides start, stop, restart and status commands to control applications packaged using standard "play dist" packaging command.
#!/bin/bash
#
# =========================================================================
# Copyright 2014 Rado Buransky, Dominion Marine Media
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================================================================
#
#
# Check this blog post I wrote with detailed information:
# http://buransky.com/play-framework/init-d-shell-script-for-play-framework-distributed-application/
#
#
# Script to start, stop and check status of a Play framework application. It requires
# the Play application to be packaged using the "dist" command. Before you run the script,
# you have to set values of NAME, PORT and APP_DIR variables.
#
# NAME – name of the application, must be the same as the name of shell script
# generated by Play framework to run the app
# PORT – port number at which the app should run
# APP_DIR – path to directory where you have unzipped the packaged app
#
#
# Usage: control.sh {start|stop|status|restart}
# port - requred for start and restart commands
#
# Example: control.sh restart app-name 9000
#
#
# The script uses RUNNING_PID file generated by Play framework which contains ID of the
# application server process.
#
#
# START YOUR APPLICATION WHEN MACHINE STARTS
# ==========================================
#
# The script uses RUNNING_PID file generated by Play framework which contains ID of
# the application server process.
#
#
# SAFE START
# ==========
#
# After starting the application the script checks whether the RUNNING_PID file has
# been created and whether the process is really running. After that it uses wget
# utility to issue an HTTP GET request for root document to do yet another check
# whether the server is alive. Of course this assumes that your application serves
# this document. If you don’t like (or have) wget I have provided curl version for
# your convenience as well.
#
#
# SAFE STOP
# =========
#
# Stop checks whether the process whose ID is in the RUNNING_PID file really belongs
# to your application. This is an important check so that we don’t kill an innocent
# process by accident. Then it sends termination signals to the process starting
# with the most gentle ones until the process dies.
#
#
# Script arguments (start, stop, restart or status)
COMMAND=$1
# ***********************************************
# ************* Set these variables ***********
NAME=
PORT=
APP_DIR=
# Example:
NAME=jugjane
PORT=9000
APP_DIR=/home/rado/bin/jugjane-1.1-SNAPSHOT
# ***********************************************
# ***********************************************
# Additional arguments to be passed to the Play application
APP_ARGS=-Dhttp.port=${PORT}
# Path to the RUNNING_PID file containing process ID
PID_FILE=$APP_DIR/RUNNING_PID
# Helper functions
echoProgress()
{
setColor 6
printf "%-70s" "$1..."
resetColor
return 0
}
echoError()
{
setColor 6
printf "ERROR"
if [ ! -z "$1" ]
then
resetColor
printf " [$1]"
fi
printf "\n"
resetColor
return 0
}
echoOK()
{
setColor 2
printf "OK"
if [ ! -z "$1" ]
then
resetColor
printf " [$1]"
fi
printf "\n"
resetColor
return 0
}
checkResult()
{
if [ "$1" -ne 0 ]
then
echoError "$2"
exit 1
fi
}
setColor()
{
tput setaf $1 2>/dev/null
}
resetColor()
{
tput sgr0 2>/dev/null
}
# Checks if RUNNING_PID file exists and whether the process is really running.
checkPidFile()
{
if [ -f $PID_FILE ]
then
if ps -p `cat $PID_FILE` > /dev/null
then
# The file exists and the process is running
return 1
else
# The file exitsts, but the process is dead
return 2
fi
fi
# The file doesn't exist
return 0
}
# Gently kill the given process
kill_softly()
{
SAFE_CHECK=`ps $@ | grep [-]Duser.dir=$APP_DIR`
if [ -z "$SAFE_CHECK" ]
then
# Process ID doesn't belong to expected application! Don't kill it!
return 1
else
# Send termination signals one by one
for sig in TERM HUP INT QUIT PIPE KILL; do
if ! kill -$sig "$@" > /dev/null 2>&1 ;
then
break
fi
sleep 2
done
fi
}
# Get process ID from RUNNING_PID file and print it
printPid()
{
PID=`cat $PID_FILE`
printf "PID=$PID"
}
# Check port input argument
checkPort()
{
if [ -z "$PORT" ]
then
echoError "Port not set!"
return 1
fi
}
# Check input arguments
checkArgs()
{
# Check command
case "$COMMAND" in
start | stop | restart | status) ;;
*)
echoError "Unknown command"
return 1
;;
esac
# Check application name
if [ -z "$NAME" ]
then
echoError "Application name not set!"
return 1
fi
# Check application directory
if [ -z "$APP_DIR" ]
then
echoError "Application installation directory not set!"
return 1
fi
# Check port
case "$COMMAND" in
start | restart)
checkPort
if [ $? != 0 ]
then
return 1
fi
;;
esac
}
checkAppStarted()
{
# Wait a bit
sleep 3
# Check if RUNNING_PID file exists and if process is really running
checkPidFile
if [ $? != 1 ]
then
echoError
cat $TMP_LOG 1>&2
exit 1
fi
local HTTP_RESPONSE_CODE
# Issue HTTP GET request using wget to check if the app is really started. Of course this
# command assumes that your server supports GET for the root URL.
HTTP_RESPONSE_CODE=`wget -SO- "http://localhost:$PORT/" 2>&1 | grep "HTTP/" | awk '{print $2}'`
# The same functionality but using curl. For your convenience.
#HTTP_RESPONSE_CODE=`curl --connect-timeout 20 --retry 3 -o /dev/null --silent --write-out "%{http_code}" http://localhost:$PORT/`
checkResult $? "no response from server, timeout"
if [ $HTTP_RESPONSE_CODE != 200 ]
then
echoError "HTTP GET / = $HTTP_RESPONSE_CODE"
exit 1
fi
}
# Check input arguments
checkArgs
if [ $? != 0 ]
then
echo "Usage: $0 {start|stop|status|restart}"
exit 1
fi
case "${COMMAND}" in
start)
echoProgress "Starting $NAME at port $PORT"
checkPidFile
case $? in
1) echoOK "$(printPid) already started"
exit ;;
2) # Delete the RUNNING_PID FILE
rm $PID_FILE ;;
esac
SCRIPT_TO_RUN=$APP_DIR/bin/$NAME
if [ ! -f $SCRIPT_TO_RUN ]
then
echoError "Play script doesn't exist!"
exit 1
fi
# * * * Run the Play application * * *
TMP_LOG=`mktemp`
PID=`$SCRIPT_TO_RUN $APP_ARGS > /dev/null 2>$TMP_LOG & echo $!`
# Check if successfully started
if [ $? != 0 ]
then
echoError
exit 1
else
checkAppStarted
echoOK "PID=$PID"
fi
;;
status)
echoProgress "Checking $NAME at port $PORT"
checkPidFile
case $? in
0) echoOK "not running" ;;
1) echoOK "$(printPid) running" ;;
2) echoError "process dead but RUNNING_PID file exists" ;;
esac
;;
stop)
echoProgress "Stopping $NAME"
checkPidFile
case $? in
0) echoOK "wasn't running" ;;
1) PRINTED_PID=$(printPid)
kill_softly `cat $PID_FILE`
if [ $? != 0 ]
then
echoError "$PRINTED_PID doesn't belong to $NAME! Human intervention is required."
exit 1
else
echoOK "$PRINTED_PID stopped"
fi ;;
2) echoError "RUNNING_PID exists but process is already dead" ;;
esac
;;
restart)
$0 stop $NAME $PORT
if [ $? == 0 ]
then
$0 start $NAME $PORT
if [ $? == 0 ]
then
# Success
exit
fi
fi
exit 1
;;
esac
@Evildethow
Copy link

You've hard-coded the port at line 264

@RadoBuransky
Copy link
Author

@Evildethow Thanks, fixed.

@BenBestmann
Copy link

Does this work with Play 2.3.x ? When I try it I get the following Error: ERROR [Play script doesn't exist!]

@iandow
Copy link

iandow commented Sep 10, 2014

I had to put a sleep on line 259 to give the web server a few seconds to start.

@levinotik
Copy link

this is great, thank you 👍

@debugging
Copy link

very nice, thanks!

P.S if you are adding more items to APP_ARGS make sure you put in in quotes:

APP_ARGS=" ... "

@ultra00
Copy link

ultra00 commented Jan 13, 2015

very nice, thank you.

@cciotti-ge
Copy link

Good stuff, thanks for posting

@danielnegri
Copy link

Hey BenBestmann, with Play 2.3.x I had kinda the same issue.

Ubuntu 12.04
---------------------------------------------------------------------------------------
$ ./activator debian:packageBin
$ sudo dpkg -i target/MobileAPI-1.0-SNAPSHOT.deb

$ tail /var/log/syslog
Feb 16 17:02:17 vagrant kernel: [  892.834802] init: /etc/init/mobileapi.conf:13: Expected operator

$ vim /etc/init/mobileapi.conf

Change the parenthesis in line 13 and 17:

From:  start on started (networking)
To: start on (started networking)

From: stop on stopping (networking)
To: stop on (stopping networking)

@danielnegri
Copy link

@dsugden
Copy link

dsugden commented Apr 8, 2015

Many Thanks!

@iamloivx
Copy link

iamloivx commented Jul 2, 2015

Thank you very much!

@amirkarimi
Copy link

Thanks a lot.

It works perfectly but I get line 263: [: !=: unary operator expected on start

@yeewang
Copy link

yeewang commented Aug 23, 2015

Good!

@rupebac
Copy link

rupebac commented Nov 11, 2015

I have improved a bit the checkAppStarted so that it does not sleep for a fixed amount:

241 checkAppStarted()
242 {
243         # Wait a bit
244         sleep 2
245
246         # Check if RUNNING_PID file exists and if process is really running
247         checkPidFile
248         if [ $? != 1 ]
249         then
250                 echoError
251                 cat $TMP_LOG 1>&2
252                 exit 1
253         fi
254
255         local HTTP_RESPONSE_CODE
256         COUNTER=0
257
258         while [ "$HTTP_RESPONSE_CODE" != "200" ] && [ $COUNTER -lt 10 ]; do
259                 sleep 1
260                 # Issue HTTP GET request using wget to check if the app is really started. Of course this
261                 # command assumes that your server supports GET for the root URL.
262                 HTTP_RESPONSE_CODE=`wget -SO- "http://localhost:$PORT/" 2>&1 | grep "HTTP/" | awk '{print $2}'`
263
264                 # The same functionality but using curl. For your convenience.
265                 #HTTP_RESPONSE_CODE=`curl --connect-timeout 20 --retry 3 -o /dev/null --silent --write-out "%{http_code}" http://localhost:$PORT/`
266
267                 checkResult $? "no response from server, timeout"
268                 let COUNTER+=1
269         done
270
271         if [ "$HTTP_RESPONSE_CODE" != "200" ]
272         then
273                 echoError "HTTP GET / = $HTTP_RESPONSE_CODE"
274                 exit 1
275         fi
276 }

@skkarthi87
Copy link

I am using Play 2.3.x. I am getting the below issue:
ERROR [Play script doesn't exist!]
How do I get it work?

@ricky-wong
Copy link

@skkarthi87 add the variable to that print, like echoError "Play script $SCRIPT_TO_RUN doesn't exist!", then you should be able to see why. For me, it was because I used $HOME in $SCRIPT_TO_RUN, but $HOME wasn't defined in this context.

@camilosampedro
Copy link

Thank you very much.:metal: It is really useful :metal:

@andiwi
Copy link

andiwi commented Jun 3, 2016

+1 for @RadoBuranski and @Iqbweb!! <3

@MrLesh
Copy link

MrLesh commented Jun 5, 2016

Had to add "" to APP_ARGS but other then that works great
Thanks!

@atais
Copy link

atais commented Sep 2, 2016

With @lqbweb modification it works like a charm! Thanks!

Forked here to working solution: https://gist.github.com/atais/2634d84bdec20593454a3cbae0cd3598

@arsenanai
Copy link

unable to set two different services for two different play servers running in single VPS on ubuntu? any help could be appreciated

@Technowise
Copy link

I use a much shorter script to do this, you can find it here: https://gist.github.com/Technowise/47f71f1b2c128d1f2759d4027341f4dc

@maxyermayank
Copy link

Have anyone tried to run this script into Docker container?

@jmooo
Copy link

jmooo commented Mar 8, 2017

Does this only run the play app as the root user?

@maxyermayank
Copy link

Have anyone tried to run this script into Docker Container in daemon mode?? I have script modified and we have been running in production for couple years now but can't make it to work in Docker Container. https://gist.github.com/maxyermayank/09f932a5aa0ea052914ba9e69c623841

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment