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