1

I am writing a shell script to make an application I have developped run as a service on Linux. The shell script I wrote works perfectly when I used the ./smpp-daemon.sh start. However when I want to use the following syntax : sh smpp-daemon.sh start I got the following error:/srv/smpp/bin/smpp-daemon.sh: 26: /srv/smpp/bin/smpp-daemon.sh: function: not found /srv/smpp/bin/smpp-daemon.sh: 27: local: not in a function.

This is the shell script:

#!/bin/bash
#
# Unity SMPP Daemon script
#
# Arsene Tochemey GANDOTE

# Set this to your Java installation
JAVA_HOME=/usr/lib/jvm/java-7-oracle

appNameLo="smpp"                               # application name with the first letter in lowercase
appName="SMPP"                                 # application name
osUser="root"                                    # OS user name for the service
osGroup="root"                                  # OS group name for the service
appHome="/srv/$appNameLo"                       # home directory of the service application
osUserHome="/$osUser"                           # home directory of the service user
appLogFile="/var/log/$appNameLo.log"                    # log file for StdOut/StdErr
maxShutdownTime=15                                  # maximum number of seconds to wait for the daemon to terminate normally
pidFile="/var/run/$appNameLo.pid"                   # name of PID file (PID = process ID number)
javaCommand="java"                                  # name of the Java launcher without the path
javaExe="$JAVA_HOME/bin/$javaCommand"               # file name of the Java application launcher executable
javaArgs="-jar $appHome/smsgh-smpp.jar"            # arguments for Java launcher
javaCommandLine="$javaExe $javaArgs"                # command line to start the Java service application
javaCommandLineKeyword="smsgh-smpp.jar"              # a keyword that occurs on the command line, used to detect an already running service process and to distinguish it from others

# Makes the file $1 writable by the group $osGroup.
function makeFileWritable {
   local filename="$1"
   touch $filename || return 1
   chgrp $osGroup $filename || return 1
   chmod g+w $filename || return 1
   return 0;
}

# Returns 0 if the process with PID $1 is running.
function checkProcessIsRunning {
   local pid="$1"
   if [ -z "$pid" -o "$pid" == " " ]; then return 1; fi
   if [ ! -e /proc/$pid ]; then return 1; fi
   return 0;
}

# Returns 0 if the process with PID $1 is our Java service process.
function checkProcessIsOurService {
   local pid="$1"
   if [ "$(ps -p $pid --no-headers -o comm)" != "$javaCommand" ]; then return 1; fi
   grep -q --binary -F "$javaCommandLineKeyword" /proc/$pid/cmdline
   if [ $? -ne 0 ]; then return 1; fi
   return 0;
}

# Returns 0 when the service is running and sets the variable $pid to the PID.
function getServicePID {
   if [ ! -f $pidFile ]; then return 1; fi
   pid="$(<$pidFile)"
   checkProcessIsRunning $pid || return 1
   checkProcessIsOurService $pid || return 1
   return 0;
}

function startServiceProcess {
   cd $appHome || return 1
   rm -f $pidFile
   makeFileWritable $pidFile || return 1
   makeFileWritable $appLogFile || return 1
   cmd="nohup $javaCommandLine >>$appLogFile 2>&1 & echo \$! >$pidFile"
   su -m $osUser -s $SHELL -c "$cmd" || return 1
   sleep 0.1
   pid="$(<$pidFile)"
   if checkProcessIsRunning $pid; then :; else
      echo -ne "\n$appName start failed, see logfile."
      return 1
   fi
   return 0; 
}

function stopServiceProcess {
   kill $pid || return 1
   for ((i=0; i<maxShutdownTime*10; i++)); do
      checkProcessIsRunning $pid
      if [ $? -ne 0 ]; then
         rm -f $pidFile
         return 0
         fi
      sleep 0.1
      done
   echo -e "\n$appName did not terminate within $maxShutdownTime seconds, sending SIGKILL..."
   kill -s KILL $pid || return 1
   local killWaitTime=15
   for ((i=0; i<killWaitTime*10; i++)); do
      checkProcessIsRunning $pid
      if [ $? -ne 0 ]; then
         rm -f $pidFile
         return 0
         fi
      sleep 0.1
      done
   echo "Error: $appName could not be stopped within $maxShutdownTime+$killWaitTime seconds!"
   return 1; 
}

function startService {
   getServicePID
   if [ $? -eq 0 ]; then echo -n "$appName is already running"; RETVAL=0; return 0; fi
   echo -n "Starting $appName   "
   startServiceProcess
   if [ $? -ne 0 ]; then RETVAL=1; echo "failed"; return 1; fi
   echo "started PID=$pid"
   RETVAL=0
   return 0; 
}

function stopService {
   getServicePID
   if [ $? -ne 0 ]; then echo -n "$appName is not running"; RETVAL=0; echo ""; return 0; fi
   echo -n "Stopping $appName   "
   stopServiceProcess
   if [ $? -ne 0 ]; then RETVAL=1; echo "failed"; return 1; fi
   echo "stopped PID=$pid"
   RETVAL=0
   return 0; 
}

function checkServiceStatus {
   echo -n "Checking for $appName:   "
   if getServicePID; then
  echo "running PID=$pid"
  RETVAL=0
   else
  echo "stopped"
  RETVAL=3
   fi
   return 0; 
}

function main {
   RETVAL=0
   case "$1" in
      start)                                               # starts the Java program as a Linux service
         startService
         ;;
      stop)                                                # stops the Java program service
         stopService
         ;;
      restart)                                             # stops and restarts the service
         stopService && startService
         ;;
      status)                                              # displays the service status
         checkServiceStatus
         ;;
      *)
         echo "Usage: $0 {start|stop|restart|status}"
         exit 1
         ;;
      esac
   exit $RETVAL
}

main $1

Please assist because I am not shell script geek. I can only get my way around.

2 Answers 2

1

The issue is twofold:

  1. Bash is not sh on Debian, Ubuntu, etc., so Bashishms won't work when you execute your script with sh. Debian uses dash for sh instead.
  2. The local keyword is not, in fact, standard POSIX sh. Dash thinks it is a function call.

Easy workaround: do not use local, make sure your variable names are unique. Sh scripting is not my forte, but from what I can tell it looks like simply removing the offending local keyword will do in your case.

Sign up to request clarification or add additional context in comments.

1 Comment

Actually, dash understands local just fine, but the local var=value syntax is not supported. Split it into local var; var=value and it should work with Dash. It's still not POSIX, though. See, however, also apenwarr.ca/log/?m=201102#28 (specifically item #4).
0

Running as ./script uses the shell stated in the #! "shebang" line, or your default shell if no such line exists - the latter most likely being Bash.

Running as sh script overrides the default / shebang shell, and uses the Bourne shell instead - or, more likely, Bash in backward compatibility mode.

If your script uses Bash-isms (like local), they won't work in the second case.

You could, of course, call bash script...

2 Comments

Thank you DevSolar. I use the bash command and it worked. I also remove the local keyword.
@BigToch: Well, if you execute it as Bash, there is nothing wrong in using Bash's features. To the contrary! That would be like a C++ programm using char[] instead of std::string so it "might" be compatible with a C compiler (which it doesn't use). ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.