1 #!/bin/ksh -p
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 #
24 # Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 # Use is subject to license terms.
26 #
27
28 #
29 #This script is a tool which applies various service states to either
30 #system services (or) test services according to usage.
31 #
32
33 bname=`basename $0`
34
35 ###############################################################################
36 #This section is configurable section. If you want you can add some more
37 #validstates to variable "test_state". Also make sure you are going to add
38 #matching 'output_state' for each test_state. Increase lib_wait_time
39 #if default value is not sufficient.
40 ###############################################################################
41
42 lib_wait_time=${DEFAULT_WAIT_TIME:-30}
43 test_state="enable disable refresh restart maintenance" # Add more valid states if needed
44 output_state="online disabled online online maintenance" # Add more valid output states
45 # if needed
46
47 ###############################################################################
48 # Initialize variables
49 ###############################################################################
50
51
52 typeset child_process="/var/tmp/child_script.$$"
53 set -A rarray $output_state
54 typeset pid=$$
55 typeset ignore_not_online=0
56 typeset continue_after_fails=0
57 typeset filename=
58 typeset time=
59 typeset global_service_identifier=
60 typeset bstime= # service STIME before restart
61 typeset astime= # service STIME after restart
62 typeset bppid= # process STIME before restart
63 typeset appid= # process STIME after restart
64 typeset fmri=
65
66 ###############################################################################
67 # Usage of this script
68 ###############################################################################
69
70 function usage {
71
72 cat >&2 << EOF
73 Usage: $PROG [-M <instance_FMRI>] [-f <filename>] [-t <timeout_in_seconds>] [-i] [-n <num_of_iter>]
74 [-c]
75
76 Options:
77 -M: Instance FMRI
78 -f: filename contains list of online services
79 -t: timeout in seconds to restrict runtime of this script.
80 -i: to ignore services which are not online
81 -n: to iterate the whole process given number of times
82 -c: continue the transition even if some services fails
83 EOF
84 }
85
86 ###############################################################################
87 # Generic cleanup; called when exit 0, 1, 2 and 15.
88 ###############################################################################
89
90 function cleanup {
91 print "$bname: Bring back pending service state transition to online"
92 rm -f $child_process
93 bring_service_back_to_online $global_service_identifier
94 if [ $? -ne 0 ]; then
95 return 1
96 fi
97 }
98
99 ###############################################################################
100 # Cleanup_usr2 called when current process gets killed by signal
101 # "SIGUSR2". SIGUSR2 is sent after <time> seconds if <-t time> is set
102 # when starting this script.
103 ###############################################################################
104
105 function cleanup_usr2 {
106 print "$bname: This script ran for \"$time\" seconds; Now aborting due
107 to SIGUSR2 as per requirement"
108 rm -f $child_process
109 bring_service_back_to_online $global_service_identifier
110 if [ $? -ne 0 ]; then
111 return 1
112 fi
113 exit 0
114 }
115
116 ###############################################################################
117 # Validate the given file
118 # - Check if file size > 0
119 # - Check if it is readable
120 ###############################################################################
121
122 function validate_file {
123 typeset fname="$bname: function - validate_file:"
124
125 [[ $# -ne 1 ]] && {
126 print -u2 "$fname: requires one argument - $# passed"
127 return 1
128 }
129 input_file="$1"
130
131 if [[ ! -s $input_file || ! -r $input_file ]]; then
132 print -u2 "$fname: $input_file is not readable (or) zero size"
133 return 1
134 fi
135
136 return 0
137 }
138
139 ###############################################################################
140 # Validate each service
141 # - Verify that service state is "online"
142 ###############################################################################
143
144 function validate_eachservice {
145 typeset fname="$bname: function - validate_eachservice:"
146
147 [[ $# -ne 1 ]] && {
148 print -u2 "$fname: requires one argument - $# passed"
149 return 1
150 }
151
152 service=$1
153 state=`svcprop -p restarter/state $service 2>/dev/null`
154 if [ "$state" != "online" ]; then
155 return 1
156 fi
157 return 0
158 }
159
160 ###############################################################################
161 # Verify that given service exists in the system
162 ###############################################################################
163
164 function service_exists {
165 typeset fname="$bname: function - service_exists:"
166
167 [[ $# -ne 1 ]] && {
168 print "$fname : function requires one argument $# passed"
169 return 1
170 }
171 typeset service=$1
172
173 /usr/sbin/svccfg select $service > /dev/null 2>&1
174 ret=$?
175 return $ret
176 }
177
178 ###############################################################################
179 # This function checks service state
180 # Two arguments : 1. Service name 2. Service state
181 # Return value:
182 # Return 1 if Service's state is not equal to expected state
183 ###############################################################################
184
185
186 function service_check_state {
187 typeset fname="$bname: function - service_check_state:"
188 typeset quiet=
189
190
191 if [ -n "$1" -a "$1" = "-q" ]; then
192 quiet=1
193 shift
194 fi
195
196 [[ $# -ne 2 ]] && {
197 print -u2 "$fname: function requires two arguments - $# passed"
198 return 2
199 }
200
201 typeset service=$1
202 typeset statetocheck=$2
203
204 typeset state=
205 typeset nstate=
206
207 service_exists $service || {
208 print -u2 "$fname: entity $service does not exist"
209 return 2
210 }
211
212 state=`svcprop -p restarter/state $service 2>/dev/null`
213 [[ -z $state ]] && {
214 print -u2 "$fname: svcs did not return a state for service \
215 $state"
216 return 2
217 }
218
219 nstate=`svcprop -p restarter/state $service 2>/dev/null`
220 [[ -z $nstate ]] && {
221 print -u2 "$fname: svcs did not return have nstate = -"
222 return 2
223 }
224
225 if [[ "$state" != "$statetocheck" || "$nstate" != "-" ]]; then
226 [ -z "$quiet" ] && \
227 print -u2 "$fname: service $service returned state $state, \
228 not $statetocheck"
229
230 return 1
231 fi
232 return 0
233 }
234
235 ###############################################################################
236 # This function waits until service gets transited to given state.
237 # Two arguments : 1. Service name 2. Service state
238 # Return value:
239 # Return 1 if Service's state didn't transit to given state in
240 # wait_time
241 ###############################################################################
242
243 function service_wait_state {
244 typeset fname="$bname: function - service_wait_state:"
245
246 [[ $# -ne 2 && $# -ne 3 ]] && {
247 print -u2 "$fname: function requires two or three arguments - $# passed"
248 return 2
249 }
250
251
252 typeset service=$1
253 typeset state=$2
254 typeset wait_time=${3:-$lib_wait_time}
255 typeset nsec=0
256
257 while [ $nsec -le $wait_time ]; do
258 service_check_state -q $service $state
259 [[ $? -eq 0 ]] && {
260 print -u2 "$fname: $service transitioned to $state"
261 return 0
262 }
263 sleep 1
264 nsec=$((nsec + 1))
265 done
266
267
268 print -u2 "$fname: service did not transition to state $state within
269 $wait_time seconds"
270 return 1
271 }
272
273 ###############################################################################
274 # Function name : stop_after_signaled
275 #
276 # Description : This function is called if this script invoked
277 # with <-t time>. Basically, this function forks
278 # one more process, which sleeps for given time
279 # and sends SIGUSR2 to parent process. This is
280 # to restrict the run time of script if user opts.
281 #
282 # Argument : 2 arguments ; 1. Time (in seconds) 2. pid (pid of parent)
283 ###############################################################################
284
285
286 function stop_after_signaled {
287 typeset fname="$bname: function - stop_after_signaled:"
288
289 [[ $# -ne 2 ]] && {
290 print -u2 "$fname: requires two argument - $# passed"
291 return 1
292 }
293
294 typeset wait_time="$1"
295 typeset parent_pid="$2"
296
297 typeset child_pid=
298
299 cat > /var/tmp/child_script.$$ << EOF
300 #!/bin/ksh -p
301 sleep $wait_time
302 ps -p $parent_pid >/dev/null 2>&1
303 if [ $? -eq 0 ]; then
304 kill -17 $parent_pid >/dev/null 2>&1
305 fi
306 exit 0
307 EOF
308
309 chmod 755 /var/tmp/child_script.$$ >/dev/null 2>&1
310 if [ $? -ne 0 ]; then
311 print -u2 "$fname: chmod 755 /var/tmp/child_script.$$ failed"
312 return 1
313 fi
314
315 # Call the script
316
317 /var/tmp/child_script.$$ &
318 child_pid=$!
319 if [ -z $child_pid ]; then
320 print -u2 "$fname: Unable to run $childprocess"
321 return 1
322 fi
323 return 0
324 }
325
326 ###############################################################################
327 # Function name : transit_state
328 #
329 # Description : This function is called over each service to
330 # apply various states and expect the transition
331 # to happen.
332 #
333 # Argument : 1 argument ; 1. Servicename
334 ###############################################################################
335
336 function transit_state {
337 typeset fname="$bname: function - transit_state:"
338 typeset atime=
339 typeset btime=
340
341 bstime=""
342 astime=""
343 bppid=""
344 appid=""
345
346 [[ $# -ne 1 ]] && {
347 print -u2 "$fname: requires one argument - $# passed"
348 return 1
349 }
350
351 typeset count=0
352 typeset service=$1
353 global_service_identifier=$service
354
355 print "\n\nFMRI: $service"
356
357 for eachstate in $test_state
358 do
359
360 print " Attempted command: $eachstate"
361 expected_state=${rarray[$count]}
362 print " Expected state: $expected_state"
363 # If state = restart, then calculate stime of the
364 # service before restart
365
366 if [ "$eachstate" = "restart" ]; then
367 calculate_pid "before" $service
368 if [ $? -ne 0 ]; then
369 return 1
370 fi
371 fi
372
373 # Could use 'svcadm enable -r' here, perhaps
374 if [ "$eachstate" = "enable" ]; then
375 /usr/sbin/svcadm enable $service > /dev/null 2>&1
376 elif [ "$eachstate" = "maintenance" ]; then
377 /usr/sbin/svcadm mark maintenance $service > /dev/null 2>&1
378 else
379 /usr/sbin/svcadm $eachstate $service >/dev/null 2>&1
380 fi
381
382 if [ $? -ne 0 ]; then
383 print -u2 "$fname: svcadm $eachstate $service failed"
384 return 1
385 fi
386
387 service_wait_state $service $expected_state >/dev/null 2>&1
388 if [ $? -ne 0 ]; then
389 tstate=`svcprop -p restarter/state $service 2>/dev/null`
390 print " Actual state: $tstate"
391 print -u2 "$fname: Service transition to $expected_state failed"
392 return 1
393 fi
394 print " Actual state: $expected_state"
395 print " -----------------------------"
396
397
398 #Verify state transition of dependencies
399 check_for_dependencies $service $expected_state
400 if [ $? -ne 0 ]; then
401 return 1
402 fi
403
404 # If state = restart, then calculate stime of the
405 # service after restart
406
407 if [ "$eachstate" = "restart" ]; then
408 calculate_pid "after" $service
409 if [ $? -ne 0 ]; then
410 return 1
411 fi
412 fi
413
414
415 if [ "$expected_state" = "maintenance" ]; then
416 print " Attempted command: clear"
417
418 svcadm clear $service >/dev/null 2>&1
419 if [ $? -ne 0 ]; then
420 print -u2 "$fname: svcadm clear $service failed"
421 return 1
422 fi
423 print " Expected state: online"
424
425 service_wait_state $service "online" >/dev/null 2>&1
426 if [ $? -ne 0 ]; then
427 tstate=`svcprop -p restarter/state $service 2>/dev/null`
428 print " Actual state: $tstate"
429 print -u2 "$fname: Service transition to online failed"
430 return 1
431 fi
432
433 check_for_dependencies $service "online"
434 if [ $? -ne 0 ]; then
435 return 1
436 fi
437 print " Actual state: online"
438 print " -----------------------------"
439 fi
440
441 #If following states attempted, bring service to
442 #online
443
444
445 if [[ $expected_state = "degraded" || \
446 $expected_state = "disabled" ]]
447 then
448 bring_service_back_to_online $service
449 if [ $? -ne 0 ]; then
450 return 1
451 fi
452 fi
453 count=`expr $count + 1`
454 done
455 return 0
456 }
457
458 ###############################################################################
459 # Function name : bring_service_back_to_online
460 #
461 # Description : This function is called if the service state is
462 # - disabled
463 # - maintenance
464 # - degraded
465 # Expected : To bring services back to online.
466 #
467 # Argument : 1 argument : Servicename
468 ###############################################################################
469
470 function bring_service_back_to_online
471 {
472 typeset fname="$bname: function - transit_state:"
473 [[ $# -ne 1 ]] && {
474 print -u2 "$fname: requires one argument - $# passed"
475 return 1
476 }
477
478 typeset service="$1"
479
480 tmp_state=`svcprop -p restarter/state $service 2>/dev/null`
481 if [ "$tmp_state" = "maintenance" ]; then
482 /usr/sbin/svcadm clear $service >/dev/null 2>&1
483 if [ $? -ne 0 ]; then
484 print -u2 "$fname: svcadm clear $service failed"
485 return 1
486 fi
487 else
488 /usr/sbin/svcadm enable $service >/dev/null 2>&1
489 if [ $? -ne 0 ]; then
490 print -u2 "$fname: svcadm enable $service failed"
491 return 1
492 fi
493 fi
494
495 service_wait_state $service "online" >/dev/null 2>&1
496 if [ $? -ne 0 ]; then
497 print -u2 "$fname: service_wait_state to online failed"
498 return 1
499 fi
500
501 return 0
502 }
503
504 ###############################################################################
505 # Function name : calculate_pid
506 #
507 # Description : This function is called to verify the different
508 # in stime of process and service before restart
509 # and after restart
510 # Argument : 1 argument : mode and Servicename
511 ###############################################################################
512
513
514 function calculate_pid {
515 typeset fname="$bname: function - calculate_pid:"
516 typeset mode=$1
517 typeset service=$2
518 typeset stime=
519 typeset ppid=
520 typeset line_count=
521
522 stime=`svcs -Ho STIME $service 2>/dev/null`
523 if [[ $? -ne 0 || -z $stime ]]; then
524 print -u2 "$fname: Error in calculating \
525 STIME of $service"
526 return 1
527 fi
528 line_count=`svcs -Hp $service | wc -l 2>/dev/null`
529 if [ $? -ne 0 ]; then
530 print -u2 "$fname: Error in calculating svcs -Hp $service"
531 return 1
532 fi
533
534 if [ $line_count -ge 2 ]; then
535 ppid=`svcs -Hp $service | grep -v $service | \
536 awk '{print $1}' 2>/dev/null`
537 if [[ $? -ne 0 || -z $ppid ]]; then
538 print -u2 "$fname: Error calculating \
539 pid of $service's process"
540 return 1
541 fi
542 fi
543
544 if [ "$mode" = "before" ]; then
545 bstime=$stime
546 if [ ! -z $ppid ]; then
547 bppid=$ppid
548 fi
549 fi
550
551 if [ "$mode" = "after" ]; then
552 astime=$stime
553 if [ ! -z $ppid ]; then
554 appid=$ppid
555 fi
556
557 if [ $bstime = $astime ]; then
558 print -u2 "$fname: ERROR Service time unchanged upon \
559 restart of $service $bstime $astime"
560 return 1
561 fi
562
563 if [[ ! -z $appid && ! -z $bppid ]]; then
564 set -A array $appid
565 typeset tcount=0
566 for eachbppid in $bppid
567 do
568 if [ "$eachbppid" = "${array[$tcount]}" ]; then
569 print -u2 "$fname: ERROR: Process id\
570 unchanged upon restart \
571 of service $service $appid $bppid"
572 return 1
573 fi
574 tcount=`expr $tcount + 1`
575 done
576 fi
577 fi
578 return 0
579 }
580
581
582 # aditya: This function is incorrect, *dependent*
583 # instances may be disabled by default in which case
584 # they will never come online.
585 ###############################################################################
586 # Function name : check_for_dependencies
587 #
588 # Description : This function verifies corresponding states for
589 # dependencies also.
590 #
591 # Argument : 2: 1. Servicename 2. State
592 ###############################################################################
593
594 function check_for_dependencies {
595
596 return 0
597
598 typeset service=$1
599 typeset state=$2
600 typeset fname="$bname: function - check_for_dependencies"
601 typeset dependencies=
602
603 dependencies=`svcs -HD -o FMRI $service 2>/dev/null`
604 if [ $? -ne 0 ]; then
605 print -u2 "$fname: Error: Calculating dependencies for $service"
606 return 1
607 fi
608 if [ -z $dependencies ]; then
609 return 0
610 fi
611
612 if [[ "$state" = "disabled" || "$state" = "maintenance" ]]; then
613 state="offline"
614 fi
615
616 for eachdependencies in $dependencies
617 do
618 service_wait_state $eachdependencies $state >/dev/null 2>&1
619 if [ $? -ne 0 ]; then
620 astate=`svcprop -p restarter/state $eachdependencies 2>/dev/null`
621 print -u2 "$fname: For dependency $eachdependencies"
622 print -u2 "Expected state : $state"
623 print -u2 "Actual state : $astate"
624 return 1
625 fi
626 done
627
628 return 0
629 }
630
631
632 ##############################################################################
633 #main
634 ##############################################################################
635
636 # Make sure we run as root
637 if ! /usr/bin/id | grep "uid=0(root)" > /dev/null 2>&1
638 then
639 print -u2 "$bname: This script must be run as root."
640 exit $STF_UNRESOLVED
641 fi
642
643 # Make sure /usr/bin is first in our path
644 export PATH=/usr/bin:$PATH
645
646 typeset max=1
647 while getopts :M:f:t:icn: opt
648 do
649 case $opt in
650 M)
651 # Use supplied argument as instance FMRI.
652 fmri=$OPTARG
653 ;;
654 f)
655 #Validate input filename
656 filename=$OPTARG
657 validate_file $filename
658 if [ $? -ne 0 ]; then
659 exit 1
660 fi
661 ;;
662 t)
663 #Stop the process after time "t" with signal SIGUSR2
664 time=$OPTARG
665 stop_after_signaled $time $pid
666 if [ $? -ne 0 ]; then
667 exit 1
668 fi
669 ;;
670 i)
671 # to ignore services which are not online
672 ignore_not_online=1
673 ;;
674 c)
675 # to ignore services which are not online
676 continue_after_fails=1
677 ;;
678 n)
679 #Number of iterations.
680 max=$OPTARG
681 ;;
682 *)
683 usage
684 exit 2
685 ;;
686 esac
687 done
688
689 # Handle signals
690 trap cleanup_usr2 USR2
691 trap cleanup 0 1 2 15
692
693 typeset -i PASS=0
694 typeset -i UNRESOLVED=1
695 typeset -i FAIL=2
696
697 typeset -i result=$PASS
698
699 typeset iter=0
700
701 while [ $iter -lt $max ]
702 do
703
704 # If filename is given then get services from file
705 if [ ! -z $filename ]; then
706 for eachservice in `cat $filename`
707 do
708 validate_eachservice $eachservice
709 if [ $? -ne 0 ]; then
710 if [ $ignore_not_online -eq 1 ]; then
711 print -u2 "\n\n$bname: $eachservice is not online, continuing ..."
712 result=$UNRESOLVED
713 print -u2 "\nResult: UNRESOLVED\n"
714 continue;
715 else
716 print -u2 "\n$bname: $eachservice is not online\n"
717 bring_service_back_to_online $eachservice
718 if [ $? -ne 0 ]; then
719 print -u2 "\n$bname: Couldnt bring $eachservice online, continuing on ...\n"
720 result=$FAIL
721 print -u2 "\nResult: FAIL\n"
722 if [ $continue_after_fails -eq 1 ]; then
723 continue
724 fi
725 exit 1
726 fi
727 fi
728 fi
729
730 transit_state $eachservice
731 if [ $? -ne 0 ]; then
732 result=$FAIL
733 print -u2 "\nResult: FAIL\n"
734 if [ $continue_after_fails -eq 1 ]; then
735 continue
736 fi
737 exit 1
738 fi
739 print -u2 "\nResult: PASS\n"
740 done
741 elif [ ! -z $fmri ]
742 then
743 validate_eachservice $fmri
744 if [ $? -ne 0 ]; then
745 if [ $ignore_not_online -eq 1 ]; then
746 print -u2 "\n\n$bname: $fmri is not online, continuing ..."
747 result=$UNRESOLVED
748 print -u2 "\nResult: UNRESOLVED\n"
749 exit 1
750 else
751 print -u2 "\n$bname: $fmri is not online\n"
752 bring_service_back_to_online $fmri
753 if [ $? -ne 0 ]; then
754 print -u2 "\n$bname: Couldnt bring $fmri online, continuing on ...\n"
755 result=$FAIL
756 print -u2 "\nResult: FAIL\n"
757 exit 1
758 fi
759 fi
760 fi
761
762 transit_state $fmri
763 if [ $? -ne 0 ]; then
764 result=$FAIL
765 print -u2 "\nResult: FAIL\n"
766 exit 1
767 fi
768 print -u2 "\nResult: PASS\n"
769 else
770 # If filename is not given, then get system's online service
771 # and play with them
772 for eachservice in `svcs -Ho STATE,FMRI | grep online | awk '{print $2}'`
773 do
774 transit_state $eachservice
775 if [ $? -ne 0 ]; then
776 result=$FAIL
777 print -u2 "\nResult: FAIL\n"
778 else
779 print -u2 "\nResult: PASS\n"
780 continue;
781 fi
782 if [ $continue_after_fails -eq 1 ]; then
783 continue;
784 else
785 exit 1
786 fi
787 done
788 fi
789
790 iter=`expr $iter + 1`
791 done
792
793 exit 0