1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License (the "License").
   6 # You may not use this file except in compliance with the License.
   7 #
   8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9 # or http://www.opensolaris.org/os/licensing.
  10 # See the License for the specific language governing permissions
  11 # and limitations under the License.
  12 #
  13 # When distributing Covered Code, include this CDDL HEADER in each
  14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15 # If applicable, add the following below this CDDL HEADER, with the
  16 # fields enclosed by brackets "[]" replaced with your own identifying
  17 # information: Portions Copyright [yyyy] [name of copyright owner]
  18 #
  19 # CDDL HEADER END
  20 #
  21 
  22 #
  23 # Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 #
  26 
  27 smf_installed_fn() { # void
  28     #
  29     #           Will return 0 if smf appears to be installed and should be
  30     #           usable, otherwise it will return != 0.
  31     #
  32     #           This function will check and rely on the presence or absence
  33     #           of /lib/svc/share/smf_include.sh.
  34     #
  35 
  36     # Some debugging stuff....
  37 
  38     echo :${__DEBUG}: | /usr/xpg4/bin/grep -q ':smf_cmd_FMRI_wait:' && \
  39         set -v && \
  40         set -x
  41 
  42     [ ! -r /lib/svc/share/smf_include.sh ] && return 1
  43 
  44     . /lib/svc/share/smf_include.sh
  45     smf_present
  46 
  47 }
  48 
  49 _scFw_children_of_fn() { # pid
  50 
  51     /bin/ptree $1 | \
  52         /bin/awk "BEGIN {seen=0;} { if ( \$1 == $1 ) seen=1;} \
  53                 (seen == 1) {print \$1}"
  54 
  55 }
  56 
  57 _scFw_cleanup_fn() {
  58 
  59     ##########################################################
  60     #
  61     #           This function is private -- Hands off!
  62     #
  63     ##########################################################
  64 
  65         # Some debugging stuff....
  66 
  67         echo :${__DEBUG}: | /usr/xpg4/bin/grep -q 'smf_cmd_FMRI_wait::' && \
  68             set -v && \
  69             set -x
  70 
  71         # Clean up whatever has been created.
  72 
  73         if [ -n "$scFw_timeout_id" ]; then
  74             echo :${__DEBUG}: | /usr/xpg4/bin/grep -q ':smf_cmd_FMRI_wait:' && \
  75                 /bin/ptree $scFw_timeout_id
  76 
  77             pl=`_scFw_children_of_fn $scFw_timeout_id`
  78             [ -n "$pl" ] && kill -9 $pl > /dev/null 2>&1
  79 
  80         fi
  81         if [ -n "$scFw_svcprop_id" ]; then
  82             echo :${__DEBUG}: | /usr/xpg4/bin/grep -q 'smf_cmd_FMRI_wait::' && \
  83                  /bin/ptree $scFw_svcprop_id
  84 
  85             pl=`_scFw_children_of_fn $scFw_svcprop_id`
  86             [ -n "$pl" ] && kill -9 $pl > /dev/null 2>&1
  87 
  88         fi
  89 
  90         [ -n "$scFw_FIFO" ] && /bin/rm -f $scFw_FIFO
  91 
  92 }
  93 
  94 smf_cmd_FMRI_wait_fn() { # [-v] "quoted command" FMRI status timeout
  95     #
  96     #           Will invoke the quoted command, then use svcprop -w to wait
  97     #           for up to timeout seconds for the restarter/state property of
  98     #           the FMRI named to have the status passed. The command may be
  99     #           empty, and the timeout may be 0 to indicate not to wait or -1
 100     #           to indicate a wait forever.
 101     #
 102     #           '$FMRI' embedded within the quoted command will be replaced
 103     #           with the FMRI parameter passed to this function. Note: Since
 104     #           '$FMRI' references a shell variable, the '$' must be suitably
 105     #           escaped or redundantly quoted to ensure that it remains intact
 106     #           until substitution occurs when the quoted command is
 107     #           invoked. e.g. smf_cmd_FMRI_wait "svcadm enable \$FMRI" svc:."
 108     #
 109     #           -v will enable progress messages.
 110     #
 111     #           If the quoted command returns 0, and the FMRI named reaches
 112     #           the state specified within the timeout allowed, then
 113     #           smf_cmd_FMRI_wait will return 0.
 114     #
 115     #           If the quoted command does not return 0, then
 116     #           smf_cmd_FMRI_wait will return the value returned by the
 117     #           command.
 118     #
 119     #           If the FMRI status changes, but does not match the specified
 120     #           status, then smf_cmd_FMRI_wait will return 35 (ENOMSG).
 121     #
 122     #           If the FMRI status does not change within the timeout
 123     #           specified, then smf_cmd_FMRI_wait will return 62 (ETIME).
 124     #
 125     #           If SMF does not appear to be installed, then smf_cmd_FMRI_wait
 126     #           will return 2 (ENOENT).
 127     #
 128     #           If invalid or insufficient parameters are passed, then
 129     #           smf_cmd_FMRI_wait will return 22 (EINVAL).
 130     #
 131     #           If this routine is interrupted by the user then
 132     #           smf_cmd_FMRI_wait will return 4 (EINTR).
 133     #
 134 
 135     # Some debugging stuff....
 136 
 137     echo :${__DEBUG}: | /usr/xpg4/bin/grep -q ':smf_cmd_FMRI_wait:' && \
 138         set -v && \
 139         set -x
 140 
 141     # Collect my parameters....
 142 
 143     scFw_verbose=0
 144     if [ x:-v = x:$1 ]; then
 145         shift
 146         scFw_verbose=1
 147 
 148     fi
 149 
 150     [ ${#} -ne 4 ] && return 22
 151 
 152     cmd="$1"
 153     FMRI=$2
 154     FMRI_status=$3
 155     timeout=$4
 156 
 157     scFw_interrupted=0
 158     export scFw_interrupted
 159 
 160     svcprop_w_cmd="/bin/svcprop -w -p restarter/state $FMRI"
 161     svcprop_cmd="/bin/svcprop -p restarter/state $FMRI"
 162 
 163     # Make sure smf is installed, and usable...
 164 
 165     smf_installed_fn || return 2
 166 
 167     # Catch errors.
 168 
 169     trap "_scFw_cleanup_fn; scFw_interrupted=1; trap 2" 2
 170 
 171     # Execute the command. If it returns non-zero, then clean up and bail!
 172 
 173     eval $cmd
 174     status=$?
 175 
 176     if [ $status -ne 0 ]; then
 177         _scFw_cleanup_fn
 178         return $status
 179 
 180     fi
 181 
 182     # Spawn a timer to wait for the status change.
 183 
 184     scFw_FIFO=/tmp/smf_cmd_FMRI_wait.FIFO.$$
 185 
 186     /bin/rm -f $scFw_FIFO
 187     /bin/mkfifo $scFw_FIFO || return
 188 
 189     (/bin/sleep $timeout; echo ETIME) > $scFw_FIFO < /dev/null &
 190     status=$?
 191     scFw_timeout_id=$!
 192 
 193     if [ $status -ne 0 ]; then
 194         _scFw_cleanup_fn
 195         return $status
 196 
 197     fi
 198 
 199     # Start a svcprop -w to wait for the property to change.
 200 
 201     $svcprop_w_cmd > $scFw_FIFO < /dev/null &
 202     status=$?
 203     scFw_svcprop_id=$!
 204 
 205     if [ $status -ne 0 ]; then
 206         _scFw_cleanup_fn
 207         return $status
 208 
 209     fi 
 210 
 211     # Before waiting, see if the state is already what was expected. It might
 212     # have changed before the svcprop -w, above, was started.
 213 
 214     while smf_result=`$svcprop_cmd` && \
 215         [ x:$FMRI_status != x:$smf_result ] && \
 216         read smf_result < $scFw_FIFO; do
 217 
 218         # Did the user try to kill this off?
 219 
 220         if [ $scFw_interrupted -ne 0 ]; then
 221             [ $scFw_verbose -ne 0 ] && echo "Interrupted."
 222             return 4
 223 
 224         fi
 225 
 226         if [ x:$smf_result = "x:ETIME" ]; then
 227             _scFw_cleanup_fn
 228 
 229             # The sleep timer and svcprop -w may have been racing to the
 230             # pipe. So check for the result of the propery change before
 231             # actually declaring this a timeout condition.
 232 
 233             [ x:$FMRI_status = x:`$svcprop_cmd` ] && return 0
 234             [ $scFw_verbose -ne 0 ] && echo "Timeout."
 235             return 62
 236 
 237         fi
 238 
 239         [ $scFw_verbose -ne 0 ] && echo "$FMRI moved to $smf_result"
 240 
 241         # So, we've read one status that was not from the timer, so it must
 242         # have been from the svcprop -w -- need to start another one waiting
 243         # for the property to change.
 244 
 245         pl=`_scFw_children_of_fn $scFw_svcprop_id`
 246         [ -n "$pl" ] && kill -9 $pl > /dev/null 2>&1
 247         $svcprop_w_cmd > $scFw_FIFO < /dev/null &
 248         status=$?
 249         scFw_svcprop_id=$!
 250 
 251         if [ $status -ne 0 ]; then
 252             _scFw_cleanup_fn
 253             return $status
 254 
 255         fi
 256 
 257     done
 258     status=$?
 259     [ $status -eq 0 ] && \
 260         [ $scFw_verbose -ne 0 ] && \
 261         echo "$FMRI status is `$svcprop_cmd`"
 262 
 263     _scFw_cleanup_fn
 264     return $status
 265 
 266 }
 267 
 268 smf_get_state() { # fmri
 269         #
 270         # prints the current state of the instance specified by the
 271         # supplied FMRI.  Returns 22 if usage was incorrect.  Otherwise,
 272         # returns the exit status of svcprop.
 273 
 274         # Some debugging stuff....
 275 
 276         echo :${__DEBUG}: | /usr/xpg4/bin/grep -q ':smf_get_state:' && \
 277                 set -v && \
 278                 set -x
 279 
 280         # check the number of parameters
 281         [ ${#} -ne 1 ] && return 22
 282 
 283         fmri="$1"
 284 
 285         echo "`/usr/bin/svcprop -p restarter/state ${fmri}`"
 286         return $?
 287 }
 288 
 289 
 290 smf_fmri_transition_state() { # args
 291         # Usage:
 292         #       smf_fmri_state_transition "do" $fmri $state $timeout
 293         # OR
 294         #       smf_fmri_state_transition "check" $fmri $state $timeout
 295         #
 296         # For more details, see the detailed comments at the end of the
 297         # function, or the README file in the parent directory
 298 
 299         # Some debugging stuff....
 300 
 301         echo :${__DEBUG}: | /usr/xpg4/bin/grep -q ':smf_fmri_transition_state:' && \
 302                 set -v && \
 303                 set -x
 304 
 305         # check the number of parameters
 306         [ ${#} -ne 4 ] && return 22
 307 
 308         # Get the input parameters
 309         sfts_do_or_check=$1
 310         sfts_target_fmri=$2
 311         sfts_target_state=$3
 312         sfts_timeout=$4
 313 
 314         # Short-circuit the proceedings if already in the target state
 315         [ "`smf_get_state $sfts_target_fmri`" = "$sfts_target_state" ] && \
 316                 return 0
 317 
 318         # if the user requested that we *do* (v/s *check*) the transition, then,
 319         # just do it!
 320         if [ "$sfts_do_or_check" != "check" ]; then
 321                 # determine the relevant svcadm subcommand to issue
 322                 sfts_svcadm_subcmd="disable"
 323                 case $sfts_target_state in
 324                         online) sfts_svcadm_subcmd="enable -s" ;;
 325                         offline|disabled) sfts_svcadm_subcmd="disable -s" ;;
 326                         maintenance|degraded)
 327                                 sfts_svcadm_subcmd="mark -I $sfts_target_state" ;;
 328                         clear) sfts_svcadm_subcmd="clear" ;;
 329                         refresh) sfts_svcadm_subcmd="$sfts_target_state"
 330                                 sfts_target_state="`smf_get_state $fmri`"
 331                                 ;;
 332                         restart) sfts_svcadm_subcmd="$sfts_target_state"
 333                                 sfts_target_state="online"
 334                                 if [ `smf_get_state $sfts_target_fmri` = "maintenance" ]; then
 335                                         sfts_target_state="maintenance"
 336                                 fi
 337                                 ;;
 338                         *) return 22 ;;
 339                 esac
 340 
 341                 # Invoke svcadm to perform the required state transition
 342                 /usr/sbin/svcadm $sfts_svcadm_subcmd $sfts_target_fmri
 343                 if [ $? -ne 0 ]; then
 344             # svcadm invocation failed
 345             return 2
 346         fi
 347 
 348     fi # if $stfs_do_or_check != check
 349 
 350         # 'clear' subcommand needs special handling: we can't predict what
 351         # the next state should be; just that it shouldn't be maintenance
 352         if [ "$sfts_target_state" = "clear" ]; then
 353                 /bin/sleep 1
 354                 [ "`smf_get_state $sfts_target_fmri`" != "maintenance" ] && \
 355                         return 0 || return 1
 356         fi
 357 
 358         # For all other subcommands, loop until the desired state is reached
 359         # or timeout expires
 360     sfts_waited_time=0
 361     while [ $sfts_waited_time -lt $sfts_timeout -a \
 362         "`smf_get_state $sfts_target_fmri`" != "$sfts_target_state" ]
 363     do
 364         /bin/sleep 1
 365         sfts_waited_time=`/bin/expr $sfts_waited_time + 1`
 366     done
 367 
 368     # Give the FMRI one more chance to complete the transition
 369     if [ "`smf_get_state $sfts_target_fmri`" != "$sfts_target_state" ]; then
 370                 # timed out
 371                 return 1
 372     fi
 373 
 374     return 0
 375         # END
 376 
 377         # DEVELOPER NOTES:
 378         # ================
 379         # Usage:
 380         #
 381         # This function can be used in two modes: 
 382         #       (1) as a utility that waits for the specified state transition to occur
 383         #       (2) as a tool that affects the desired state transition
 384         #
 385         # Invocation:
 386         # -----------
 387         # (1) "check" (waiter-only) mode
 388         #               smf_fmri_transition_state "check" $fmri $state $timeout
 389         # (2) "do" mode: 
 390         #               smf_fmri_transition_state "do" $fmri $state $timeout
 391         #
 392         # In the 'check' mode, the function merely waits for the target fmri
 393         # to attain the desired state in the specified timeout (seconds).  It
 394         # is up to the invoking process to ensure the issuance of appropriate
 395         # svcadm commands to actually affect the desired state transition.
 396         # In the 'do' mode, the function itself issues an svcadm command to 
 397         # cause the state transition to the desired state and then waits for
 398         # the fmri to reach the desired state in the specified timeout period
 399         # (seconds).  In both cases, a return status of 0 (zero) indicates 
 400         # success and a non-zero return status indicates failure.
 401         #
 402         # Note that this is a 'state transition' function, not a substitute
 403         # for svcadm commands.  Specifically, this function does not issue
 404         # *all* possible svcadm commands; only those that are needed for the
 405         # desired state transition to be executed as immediately as possible.  
 406         # For example, the function cannot be told to issue commands such as
 407         # the following:
 408         #       svcadm enable -r FMRI    # recursive enable
 409         #       svcadm {en,dis}able -t FMRI # temporary
 410         #       svcadm mark -t maintenance FMRI # temporary
 411         #
 412         # However, for one thing, these types of invocations are quite rare,
 413         # and, for another, when developers need to issue these svcadm sub-
 414         # commands, they can invoke them explicitly and simply use this 
 415         # function in the "check" mode.  Alternatively, users are invited to
 416         # explore the smf_cmd_wait_FMRI function defined elsewhere in this file.
 417 }