#!/bin/sh
#
# $Id: nsr_shutdown.sh,v 6.68.22.1.30.3 2010/12/16 17:09:57 tompkb1 Exp $ Copyright 1995-2010 EMC Corporation.
#

#
# Copyright (c) 1995-2010 EMC Corporation.
#
# All rights reserved.  This is an UNPUBLISHED work, and
# comprises proprietary and confidential information of EMC.
# Unauthorized use, disclosure, and distribution are strictly
# prohibited.  Use, duplication, or disclosure of the software
# and documentation by the U.S. Government is subject to
# restrictions set forth in a license agreement between the
# Government and EMC or other written agreement specifying
# the Government's rights to use the software and any applicable
# FAR provisions, such as FAR 52.227-19.
#

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb; export PATH
UNIX95=Y; export UNIX95

NSR_RUN=/nsr/run
NSR_TMP=/nsr/tmp/sec
NSR_SERVICES=$NSR_TMP/services
NSR_KILLED=$NSR_TMP/killed
NSR_PROCLIST=$NSR_TMP/proclist

PROG=`basename $0`
UNAME_OS=`uname`
PIDFILE=$NSR_TMP/$PROG.pid
PIDFILE_EXPIRATION=300
MODE=NORMAL
TIMETOKILL=180



case $UNAME_OS in
    AIX | HP-UX | IRIX* | OSF1 | SunOS ) PSOPS="eo pid,ppid,pgid,comm";;
    Linux ) PSOPS="eo pid,ppid,pgrp,comm";;
    Darwin ) PSOPS="axco pid,ppid,pgid,command";;
esac

Usage() {

    printf "%s: Usage: %s [ -fq | -n | -l ] [ -t timeout ]  [ service ... ]\n" \
      $PROG $PROG >&2

    exit 1
}

Unsupported_Darwin() {

    printf "%s: Only the -l option is supported on Mac OS X platforms. " \
        $PROG >&2
    printf "To shutdown NetWorker daemons, use launchctl(1)\n" >&2

    exit 1
}


Cleanup() {

    rm -f $NSR_SERVICES \
          $NSR_SERVICES.tmp \
          $NSR_KILLED \
          $NSR_PROCLIST \
          $NSR_TMP/*.dep \
          $PIDFILE
}

GetTime() {

    set -- `date -u '+%Y %j %H %M %S'`

    CURRENT_MINS=`expr 60 \* $3 + $4`
    CURRENT_SECS=`expr 3600 \* $3 + 60 \* $4 + $5`

    YEARSSINCE1970=`expr $1 - 1970`
    YEARSSINCE1969=`expr $1 - 1969`

    LEAPYEARS=`expr $YEARSSINCE1969 / 4`
    
    EPOCH_DAYS=`expr 365 \* $YEARSSINCE1970 + $LEAPYEARS + $2 - 1`
    EPOCH_MINS=`expr 1440 \* $EPOCH_DAYS + $CURRENT_MINS`
    EPOCH_SECS=`expr 86400 \* $EPOCH_DAYS + $CURRENT_SECS`

    echo $EPOCH_SECS 
} 

GetPIDsFromProcess() {

    ps -$PSOPS | grep $1 | while read PID pPID PGID COMM; do

        if [ "`basename $COMM`" = "$1" ] && [  $PID -eq $PGID ]; then
		
            echo $PID
        fi
    done
}

GetChildPIDsFromPPID() {

	ps -$PSOPS | grep $1 | while read PID pPID PGID COMM; do

		if [ $pPID -eq $1 ] || \
                    [ $pPID -eq 1 -a $PGID -eq $1 -a $PID -ne $1 ]; then
		
			echo $PID
		fi
	done
}

GetPIDsFromGPID() {

	ps -$PSOPS | grep $1 | while read PID pPID PGID COMM; do

		if [ $PGID -eq $1 ]; then
		
			echo $PID
		fi
	done
}


GetProcessFromPID() {

	ps -$PSOPS | grep $1 | while read PID pPID PGID COMM; do

		if [ $PID -eq $1 ]; then
		
			echo `basename $COMM`
		fi
	done
}

GetNameFromRunfile() {

	echo $1 | cut -f1 -d.
}

GetPIDFromRunfile() {

	echo $1 | cut -f2 -d.
}

AlreadyRunning() {

    if [ -f $PIDFILE ]; then

        read PID CREATION < $PIDFILE

        if [ "`GetProcessFromPID $PID`" -a \
            `expr $CREATION + $PIDFILE_EXPIRATION` -gt `GetTime` ]; then

            echo $PID
        else
            rm -f $PIDFILE
        fi
    fi
}

ListProcessTree() {

    if [ ! "$3" ] || \
        [ ! "`GetProcessFromPID $3`" -a ! "`GetPIDsFromGPID $3`" ]; then

        return

    elif [ $1 -eq 0 ]; then

        if [ "$MODE" != "LIST" ]; then

            printf "$3\n"
        else
            printf "+--o %s " $2

            [ "`GetProcessFromPID $3`" ] && printf "(%d)\n" $3 || \
                printf "(%s)\n" "???"
        fi

	for PID in `GetChildPIDsFromPPID $3`; do

            ListProcessTree `expr $1 + 1` `GetProcessFromPID $PID` $PID
        done
    else
       
	DEPTH=`expr $1 + 1` 
	OFFSET=`expr $DEPTH \* 4`

        if [ "$MODE" != "LIST" ]; then

            printf "$3\n"
        else

            printf "%`expr $OFFSET - 1`s %s (%d)\n" "+--o" \
                `GetProcessFromPID $PID` $PID
        fi
	
        for PID in `GetChildPIDsFromPPID $3`; do

            ListProcessTree $DEPTH `GetProcessFromPID $PID` $PID
        done
    fi
}

RemoveService() {

    rm -f $NSR_TMP/$1.dep

    for DEPFILE in `find $NSR_TMP -name *.dep 2> /dev/null`; do

        sed -e s/$1//g $DEPFILE > $DEPFILE.tmp

        mv -f $DEPFILE.tmp $DEPFILE
    done

    if [ -f "$NSR_SERVICES" ]; then

        cat $NSR_SERVICES | grep -v $1 > $NSR_SERVICES.tmp

        mv -f $NSR_SERVICES.tmp $NSR_SERVICES
    fi
}

KillService() {

    [ -f "$NSR_TMP/$1.$2.dep" ] && DEPENDS="`cat $NSR_TMP/$1.$2.dep`" || \
         DEPENDS=""

    if [ ! "`GetProcessFromPID $2`" -a ! "`GetPIDsFromGPID $2`" ]; then

        [ "$MODE" != "QUIET" ] && [ "`grep $1.$2 $NSR_SERVICES`" ] && \
            printf "Service %s (%d) shutdown.\n" $1 $2

        RemoveService $1.$2

        rm -f $NSR_RUN/$1.$2

     elif [ "$DEPENDS" ]; then

        for SERVICE in $DEPENDS; do

            if [ -f "$NSR_TMP/$SERVICE.dep" ] && \
                [ "`grep $1.$2 $NSR_TMP/$SERVICE.dep`" ]; then

                printf \
                  "%s: Circular dependency found between services %s and %s.\n"\
                  $PROG `GetNameFromRunfile $SERVICE` $1

                printf "%s: Removing service %s from %s's dependency file.\n" \
                    $PROG `GetNameFromRunfile $SERVICE` $1
                
                sed -e s/$1.$2//g $NSR_TMP/$1.$2.dep > $NSR_TMP/$1.$2.dep.tmp
                mv -f $NSR_TMP/$1.$2.dep.tmp $NSR_TMP/$1.$2.dep

            else

                for RUNFILE in $NSR_RUN/$SERVICE.*; do

                    KillService `GetNameFromRunfile $SERVICE` \
                        `GetPIDFromRunfile $SERVICE`
                done 
            fi
        done

    elif [ ! "`grep $1.$2 $NSR_KILLED`" ]; then

        [ "$MODE" != "QUIET" ] && printf "Stopping service: %s (%s)\n" $1 $2

        echo $1.$2 >> $NSR_KILLED

        if [ "$MODE" = "NORUN" ]; then 

            RemoveService $1.$2
        else

            if [ "`GetProcessFromPID $2`" = "$1" ]; then

                /bin/kill $2

            elif [ ! "`GetProcessFromPID $2`" ]; then 

                /bin/kill -TERM -$2
            else 

                RemoveService $1.$2

                rm -f $NSR_RUN/$1.$2
            fi
        fi
    else 
    
        [ "$MODE" != "QUIET" ] && \
            printf "Waiting for service: %s (%s)\n" $1 $2
    fi
}


if [ "`whoami`" != "root" ]; then

    printf "%s: You must be root to execute %s.\n" $PROG $PROG >&2
    exit 1
fi

trap "Cleanup" 1 2 13 15

TIMESTAMP=`GetTime`

while getopts Aacdflnst:qv ARG
do
    case $ARG in
        f) 
           FORCE=TRUE;;
        l) 
           [ "$MODE" != "NORUN" ] && [ "$MODE" != "QUIET" ] \
            && MODE=LIST || Usage;;
        n) 
           [ "$MODE" != "LIST" ] && [ "$MODE" != "QUIET" ] \
            && MODE=NORUN || Usage;;
        t)
           [ `echo $OPTARG | awk '{print $1 + 0}'` -ne 0 ] || Usage;
           TIMETOKILL=$OPTARG;;
        q) 
           [ "$MODE" != "LIST" ] && [ "$MODE" != "NORUN" ] \
            && MODE=QUIET || Usage;;
        A | a | c | d | s | v)
           printf "%s: Option no longer supported -- %s\n" $PROG $ARG >&2 && \
           Usage;;
        \?) 
           Usage;;
    esac
done

umask 022

if [ -d "$NSR_RUN" ]; then

    printf "" > $NSR_SERVICES.tmp
    printf "" > $NSR_SERVICES

    for SERVICE in `ls $NSR_RUN`; do

        [ "$SERVICE" = ".nsr" ] && continue

        ENTRY=`echo $SERVICE | \
            sed -n "/^[a-zA-Z][a-zA-Z]*\.[0-9][0-9]*$/p" `

        if [ -f "$NSR_RUN/$ENTRY" ]; then

            printf "%s\n" $ENTRY >> $NSR_SERVICES.tmp
        else

            if [ "$MODE" != "QUIET" ]; then

    	        printf "%s: Removing corrupt/illegal run file: %s\n" \
                    $PROG $SERVICE >&2
            fi

            rm -f $NSR_RUN/$SERVICE
        fi
    done
else
    if [ "$MODE" != "QUIET" ]; then

        printf \
            "%s: /nsr directory hierarchy does not exist or is incomplete.\n" \
            $PROG >&2
    fi

    exit 1
fi

if [ ! "`cat $NSR_SERVICES.tmp`" ]; then

    if [ "$MODE" != "QUIET" ]; then

        printf "%s: There are currently no running NetWorker processes.\n" \
                $PROG >&2
    fi

    exit 1
fi

PID="`AlreadyRunning`"

if [ "$PID" ]; then

    printf "Found %s process (%d) already running.\n" $PROG $PID >&2

    exit 1
else
    printf "%d %d" $$ `GetTime` > $PIDFILE
fi

printf "" > $NSR_KILLED

shift `expr $OPTIND - 1`

if [ "$*" ]; then

    for SERVICE in $*; do

        SERVICE=`basename $SERVICE`
   
        cat $NSR_SERVICES.tmp | sed -n "/^$SERVICE\.[0-9][0-9]*$/p" \
            >> $NSR_SERVICES

        ADDED=`grep "^$SERVICE\.[0-9]" $NSR_SERVICES`

        if [ ! "$ADDED" ]; then

            printf "%s: Invalid service or parent process: %s\n" \
                $PROG `basename $SERVICE` >&2
        fi
    done
else

    mv $NSR_SERVICES.tmp $NSR_SERVICES
fi


for SERVICE in `cat $NSR_SERVICES`; do 

    rm -f $NSR_TMP/$SERVICE.dep

    for DEPENDENCY in `cat $NSR_RUN/$SERVICE | uniq`; do

        for LISTING in $NSR_RUN/$DEPENDENCY.*; do
        
            if [ -f "$LISTING" ]; then

                LISTING=`basename $LISTING`

                echo $LISTING >> $NSR_TMP/$SERVICE.dep
            fi
        done
    done
done

TIMEOUT=`expr $TIMESTAMP + $TIMETOKILL`

case $MODE in

    LIST )

            if [ "`cat $NSR_SERVICES`" ]; then	

                for SERVICE in `cat $NSR_SERVICES`; do

                    ListProcessTree 0 `GetNameFromRunfile $SERVICE` \
                        `GetPIDFromRunfile $SERVICE`

                    RemoveService $SERVICE
                done

                exit 0
            else

                exit 1
            fi
	;;

    * )

        if [ "$UNAME_OS" = "Darwin" ]; then
            Unsupported_Darwin
        fi
             
        while [ "`cat $NSR_SERVICES`" ] && [ $TIMEOUT -gt `GetTime` ]; do

            while read SERVICE; do
          
                KillService `GetNameFromRunfile $SERVICE` \
                    `GetPIDFromRunfile $SERVICE`
                
            done <$NSR_SERVICES

            if [ "$MODE" != "NORUN" ]; then

                sleep 5
            fi
       
        done;;
esac

[ -f "$NSR_SERVICES" ] && LIVING_SERVICES=`cat $NSR_SERVICES`

Cleanup

if [ "$LIVING_SERVICES" ]; then

    EXIT_CODE=0

    for SERVICE in $LIVING_SERVICES; do

        SERVICE_PID=`GetPIDFromRunfile $SERVICE`
        SERVICE_NAME=`GetNameFromRunfile $SERVICE`

        if [ "$FORCE" = "TRUE" ]; then 

            if [ "$MODE" != "QUIET" ]; then

                 printf "Forcefully killing service: %s (%s)\n" $SERVICE_NAME \
                    $SERVICE_PID
            fi

            echo "`ListProcessTree 0 $SERVICE_NAME $SERVICE_PID`" > \
                $NSR_PROCLIST

            for PID in `cat $NSR_PROCLIST`; do

                /bin/kill $PID 2> /dev/null
            done

            sleep 2 

            for PID in `cat $NSR_PROCLIST`; do

                /bin/kill -KILL $PID 2> /dev/null
            done

            for PID in `cat $NSR_PROCLIST`; do

                NAME=`GetProcessFromPID $PID`
                
                if [ "$NAME" ]; then
                    printf \
                        "%s: Process %s (%d) not responding to KILL signal\n" \
                        $PROG $NAME $PID >&2

                    EXIT_CODE=1
                fi
            done

            if [ ! "`GetProcessFromPID $SERVICE_PID`" ]; then 

                RemoveService $SERVICE

                rm -f $NSR_RUN/$SERVICE
            else

                printf "%s: Failed to shutdown service: %s (%d)\n" \
                    $PROG $SERVICE_NAME $SERVICE_PID >&2
            
                EXIT_CODE=1
            fi

            rm -f $NSR_PROCLIST
        else
            printf "%s: Failed to shutdown service: %s (%d)\n" \
                $PROG $SERVICE_NAME $SERVICE_PID >&2

            EXIT_CODE=1
        fi
    done

    exit $EXIT_CODE
fi

exit 0
