Monday, October 06, 2008

Shell Script Consolidation

My ex-colleague had been creating a lot of web instances under the Sun ONE Web Server (S1WS) installation. However, for every instance of the web server, he also created a separated script to start/stop/query the web instance. Basically all these scripts are more or less the same except the instance directory and listening port. BTW, the way he used to find out the process ID of the web instance is not really foolproof because it is based on the listing of the processes and grep the instance name (ps -ef | grep instance).

If you were to look under the 'hood', you will realise that S1WS start/stop scripts actually make use of a utililty called 'parsexml' to get information related to it's instance. 'parsexml' is located at $INSTALL_DIR/lib/parsexml, but there isn't any documentation on how to use it. Thanks to the UNIX utility - strings, basically it prints the strings of printable characters in file. By 'strings' the parsexml file, we discovered the usage and it's corresponding variables name. So, to find out the PID of the web instance, you need to display the content in the PID_FILE: $INSTALL_DIR/lib/parsexml -g PID_FILE

# $INSTALL_DIR/parsexml -h
failure: CONF1115: Error opening -h/server.xml (File not found)

# $INSTALL_DIR//parsexml --help
failure: CONF1115: Error opening --help/server.xml (File not found)

# $INSTALL_DIR/parsexml
Usage: parsexml path -g option
       parsexml path

# strings $INSTALL_DIR/lib/parsexml
...

Usage: parsexml path -g option
       parsexml path
server.xml
SERVER_PID_FILE
SERVER_TEMP_DIR
SERVER_USER
SERVER_PLATFORM_SUBDIR
SERVER_JVM_LIBPATH
%s: invalid option
Options: PID_FILE
         TEMP_DIR
         USER
         JVM_LIBPATH
         PLATFORM_SUBDIR
...

# $INSTALL_DIR/lib/parsexml -g PID_FILE
/tmp/https-chihungchan.blogspot.com-9d93b4d6-1/pid

# cat /tmp/https-chihungchan.blogspot.com-9d93b4d6-1/pid
5266

Since all these scripts are similar, we should be able to 'considerate' them together to a single generic script (I called it s1ws.sh). By doing so, we need to supply the script with the instance name. However, the instance name can be pretty long and it would not be user-friendly if you were to ask the user to supply that every time he/she needs to run it. I cannot remember where I came across the auto-complete trick in bash shell, but basically it allows you to auto-complete the name of the file/directory in your current working directory. In our case, we cannot limit the user from running our script (s1ws.sh) from the $INSTALL_DIR directory. The trick is to run some unix commands (ls -1d ... | sed ...) to extract the instance names and supply that to the 'complete' command which is a bash shell builtin command.

_COMP=($(ls -1d /opt/SUNWwbsvr/admin-server /opt/SUNWwbsvr/https-* | sed 's#.*/##'))
complete -o default -W "${_COMP[*]}" s1ws.sh

In the s1ws.sh script, it allows user to query the status of the web instance. Status includes process id, process name and arguments, listening port and it's corresponding netstat LISTENing output. It is possible to get hold of listening port number from the '$INSTALL_DIR/$INSTANCE/config/server.xml'. With xml_grep command in Linux, you can extract the port number by using XPath notation. In our case, the XPath will be /server/http-listener/port. Here is a snippet of the server.xml

<server>
  ...
  <http-listener>
    <name>http-listener-1</name>
    <ip>192.168.1.2</ip>
    <port>80</port>
    <server-name>chihungchan.blogspot.com</server-name>
    <default-virtual-server-name>chihungchan.blogspot.com</default-virtual-server-name>
  </http-listener>
  ...
</server>

Here is the generic "s1ws.sh":

#! /bin/sh


INSTALL_DIR="/opt/SUNWwbsvr"
if [ ! -d "$INSTALL_DIR" ]; then
        echo "Error: \"$INSTALL_DIR\" directory does not exist"
        exit 1
fi
PARSEXML="$INSTALL_DIR/lib/parsexml"


usage()
{
        echo "Usage: $0 <instance> <start|stop|status>"
        echo ""
}


if [ $# -ne 2 ]; then
        usage
        exit 1
fi


INSTANCE="$1"
ACTION="$2"


isRunning()
{
        IS_RUNNING=0

        CONFIG_DIR="$INSTALL_DIR/$INSTANCE/config"
        if [ -d "$CONFIG_DIR" ]; then
                PID_FILE=`$PARSEXML "$CONFIG_DIR" -g PID_FILE`
                if [ -f "$PID_FILE" ]; then
                        PID=`cat "$PID_FILE"`
                else
                        echo "***WARNING*** Web instance \"$INSTANCE\" is not running"
                        return
                fi
        else
                echo "***ERROR*** Web instance \"$INSTANCE\" does not contain any configuration file"
                return
        fi

        #
        # check if process exist, via /proc
        #
        if [ -d /proc/$PID ]; then
                IS_RUNNING=1
                if [ $VERBOSE == 1 ]; then
                        echo "Process ID: $PID"
                        echo -e "`cat /proc/$PID/cmdline | tr '\000' ' '`\n"

                        # listen to port
                        port=`xml_grep --cond /server/http-listener/port --text_only $INSTALL_DIR/$INSTANCE/config/server.xml`
                        echo "Listening at port: $port"
                        netstat -an | awk '$6 == "LISTEN" && $4 ~/:'$port'$/{print}'

                fi
        fi
}


VERBOSE=0
case "$ACTION" in
status)
        VERBOSE=1
        isRunning
        if [ "$IS_RUNNING" -eq 0 ]; then
                exit 1
        else
                exit 0
        fi
        ;;
start)
        isRunning
        if [ "$IS_RUNNING" -eq 1 ]; then
                echo "***WARNING*** Web instance \"$INSTANCE\" is still running. No need to start"
        else
                $INSTALL_DIR/$INSTANCE/bin/startserv
                isRunning
                if [ "$IS_RUNNING" -eq 1 ]; then
                        echo "***INFO**** Successfully started."
                fi
        fi
        ;;
stop)
        isRunning
        if [ "$IS_RUNNING" -eq 0 ]; then
                echo "***WARNING*** Web instance \"$INSTANCE\" is stopped. No need to stop"
        else
                $INSTALL_DIR/$INSTANCE/bin/stopserv
                isRunning
                if [ "$IS_RUNNING" -eq 0 ]; then
                        echo "***INFO**** Successfully stopped."
                fi
        fi
        ;;
*)
        usage
        exit 1
        ;;
esac

and "s1ws.sh" in action:

# s1ws.sh
Usage: /usr/local/bin/s1ws.sh <instance> <start|stop|status>

# s1ws.sh <tab>
admin-server https-chihungchan.blogspot.com https-chihungchan.blogspot.com-dev1 https-chihungchan.blogspot.com-dev2 https-chihungchan.blogspot.com-test1 https-chihungchan.blogspot.com-test2 https-chihungchan.blogspot.com-uat

# s1ws.sh admin-server status
Process ID: 7215
webservd-wdog -d /opt/SUNWwbsvr/admin-server/config -r /opt/SUNWwbsvr -t /tmp/admin-server-9d93b4d6-1 -u root

Listening at port: 8989
tcp        0      0 0.0.0.0:8989                0.0.0.0:*                   LISTEN

# s1ws.sh admin-server start
***WARNING*** Web instance "admin-server" is still running. No need to start

# s1ws.sh admin-server stop
server has been shutdown
***WARNING*** Web instance "admin-server" is not running
***INFO**** Successfully stopped.

# s1ws.sh admin-server status
***WARNING*** Web instance "admin-server" is not running

# s1ws.sh admin-server start
***WARNING*** Web instance "admin-server" is not running
Sun Java System Web Server 7.0U1 B06/12/2007 21:21
info: CORE3016: daemon is running as super-user
info: CORE5076: Using [Java HotSpot(TM) Server VM, Version 1.5.0_09] from [Sun Microsystems Inc.]
info: WEB0100: Loading web module in virtual server [admin-server] at [/admingui]
info: WEB0100: Loading web module in virtual server [admin-server] at [/jmxconnector]
info: HTTP3072: admin-ssl-port: https://chihungchan.blogspot.com:8989 ready to accept requests
info: CORE3274: successful server startup
***INFO**** Successfully started.

# s1ws.sh admin-server status
Process ID: 7331
webservd-wdog -d /opt/SUNWwbsvr/admin-server/config -r /opt/SUNWwbsvr -t /tmp/admin-server-9d93b4d6-1 -u root

Listening at port: 8989
tcp        0      0 0.0.0.0:8989                0.0.0.0:*                   LISTEN

# s1ws.sh https-chihungchan.blogspot.com status
Process ID: 5266
webservd-wdog -d /opt/SUNWwbsvr/https-chihungchan.blogspot.com/config -r /opt/SUNWwbsvr -t /tmp/https-chihungchan.blogspot.com-9d93b4d6-1 -u webservd

Listening at port: 80
tcp        0      0 192.168.1.2:80             0.0.0.0:*                   LISTEN

After this 'shell script consolidation' exercise, we only need to remember and maintain one script instead of a half-a-dozen scripts or more.

Labels:

0 Comments:

Post a Comment

<< Home