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 }