Home | History | Annotate | Download | only in shell
      1 #!/bin/ksh
      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 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24 # Use is subject to license terms.
     25 #
     26 
     27 [ -f /lib/svc/share/smf_include.sh ] || exit 1
     28 
     29 . /lib/svc/share/smf_include.sh
     30 
     31 SVCADM=/usr/sbin/svcadm
     32 SVCCFG=/usr/sbin/svccfg
     33 SVCPROP=/bin/svcprop
     34 SVCS=/usr/bin/svcs
     35 MFSTPG=manifestfiles
     36 MFSTSCAN=/lib/svc/bin/mfstscan
     37 MCLEANUPFILE=/etc/svc/volatile/mcleanup.$$
     38 IGNORELIST="system/install-discovery smf/manifest"
     39 MFSTHISTORY=/lib/svc/share/mfsthistory
     40 UPLIST=0
     41 
     42 #
     43 # Create a list of service to manifest pairs for the upgrade
     44 # process to determine what files are associated with a service
     45 #
     46 function create_list {
     47 	for cl_mfile in `find /var/svc/manifest -name "*.xml"`
     48 	do
     49 		for cl_invent in `svccfg inventory $cl_mfile`
     50 		do
     51 			cl_invent=${cl_invent#svc:/*}
     52 
     53 			cl_instance=${cl_invent#*:}
     54 			cl_instance=${cl_instance##*/*}
     55 			[ $cl_instance ] && continue
     56 
     57 			cl_invent=${cl_invent%:*}
     58 			cl_invent=`echo $cl_invent | sed -e 's/[-\/\,]/_/g'`
     59 
     60 
     61 			eval $cl_invent=\"\$$cl_invent $cl_mfile\"
     62 		done
     63 	done
     64 	UPLIST=1
     65 }
     66 
     67 #
     68 # Inventory the instances listed with a manifest file
     69 #
     70 function get_instances {
     71 	gi_mfile=$1
     72 
     73 	lst=""
     74 	for gi_invent in `svccfg inventory $gi_mfile`
     75 	do
     76 		gi_tmp=${gi_invent#svc:/*}
     77 		gi_tmp=${gi_tmp#*:}
     78 		gi_tmp=${gi_tmp##*/*}
     79 
     80 		[ $gi_tmp ] && lst="$lst $gi_invent"
     81 	done
     82 
     83 	echo $lst
     84 }
     85 
     86 function pid_timeout {
     87 	pt_pid=$1
     88 
     89 	pt_cnt=0
     90 	while [ `ps -p $pt_pid -o pid | grep -v PID` -a $pt_cnt -lt 30 ]
     91 	do
     92 		sleep 1
     93 		cnt=`expr $pt_cnt + 1`
     94 	done
     95 	if [ $pt_cnt -eq 30 -a "`ps -p $pt_pid -o pid | grep -v PID`" ]; then
     96 		return 1
     97 	else
     98 		return 0
     99 	fi
    100 }
    101 
    102 #
    103 # Process a service to ensure that it's manifests exist
    104 # and are in sync with the service.
    105 #
    106 function process_service {
    107 	ps_service=$1
    108 
    109 	#
    110 	# Throw away unsupported services, if there is a false listing
    111 	# for manifestfiles support
    112 	#
    113 	$SVCPROP -p $MFSTPG/support $ps_service 2>/dev/null | grep false > /dev/null
    114 	[ $? -eq 0 ] && return
    115 
    116 	#
    117 	# Create the list of instances for this service.
    118 	#
    119 	$SVCPROP -p $MFSTPG $ps_service > $MCLEANUPFILE
    120 	set -A ps_mfiles `grep astring $MCLEANUPFILE | awk '{print $3}'`
    121 
    122 	#
    123 	# Check to see if the manifest files associated with the service are
    124 	# missing, or if the manifest file has changed, either caught here
    125 	# or by the caller.
    126 	#
    127 	ps_x=`$MFSTSCAN ${ps_mfiles[@]} 2>&1`
    128 	if [ $? -eq 0 ]; then
    129 		if [ "$force" != "true" -a ! "$ps_x" ]; then
    130 			ps_ret=0
    131 			for ps_f in ${ps_mfiles[@]}
    132 			do
    133 				echo "$force" | grep -v $ps_f > /dev/null 2>&1
    134 				ps_ret=`expr $ps_ret + $?`
    135 			done
    136 			[ $ps_ret -eq 0 ] && return
    137 		fi
    138 	fi
    139 
    140 	ps_refresh=0
    141 	ps_mfiles_tmp=""
    142 	ps_mfiles_cnt=${#ps_mfiles[@]}
    143 	ps_instances=`$SVCS -H -oFMRI $ps_service 2>/dev/null`
    144 
    145 	#
    146 	# For each manifest file that is listed by the service
    147 	# check for its existance.  If it exists, then check that
    148 	# the instances of the service are supported by at least
    149 	# one of the manifest files listed.
    150 	#
    151 	for mf in ${ps_mfiles[@]}
    152 	do
    153 		#
    154 		# This is an unsupported service just return
    155 		# skipping the service.
    156 		#
    157 		[ ${mf%/var/svc/manifest*} ] && return
    158 
    159 		if [ ! -f $mf ]; then
    160 			ps_mfiles_tmp="$ps_mfiles_tmp $mf"
    161 			continue
    162 		fi
    163 
    164 		inst=`get_instances $mf`
    165 
    166 		set -A ps_inst_list
    167 		for i in $inst
    168 		do
    169 			ps_inst_tmp=""
    170 			for j in $ps_instances
    171 			do
    172 				if [ "$i" == "$j" ]; then
    173 					set -A ps_inst_list ${ps_inst_list[*]} $j
    174 					continue
    175 				else
    176 					ps_inst_tmp="$ps_inst_tmp $j"
    177 				fi
    178 			done
    179 			#
    180 			# If there are any instances not accounted for add
    181 			# them to the list to be cleaned up.
    182 			#
    183 			ps_instances=$ps_inst_tmp
    184 		done
    185 	done
    186 	#
    187 	# If there are any manifest files set them to the list
    188 	# to be cleaned up.
    189 	#
    190 	set -A ps_mfiles $ps_mfiles_tmp
    191 
    192 	#
    193 	# For each manifest file that was not found remove it from
    194 	# the service's list of manifest files.
    195 	#
    196 	for mf in ${ps_mfiles[@]}
    197 	do
    198 		#
    199 		# Need to remove the file from the smf/manifest
    200 		# list.
    201 		#
    202 		ps_refresh=1
    203 		mf_nw=`echo "$needwork" | grep -v $mf`
    204 		needwork="$mf_nw"
    205 		mf_srch=`echo $mf | sed -e 's/\./\\\./g'`
    206 		mf_pg=`grep "$mf_srch" $MCLEANUPFILE | awk '{print $1}'`
    207 		[ $ps_mfiles_cnt -ne ${#ps_mfiles[@]} ] && \
    208 		    $SVCCFG -s $ps_service delprop $mf_pg > /dev/null 2>&1
    209 		mf_pg=`echo $mf_pg | awk -F'/' '{print $2}'`
    210 		$SVCCFG -s smf/manifest delpg $mf_pg > /dev/null 2>&1
    211 	done
    212 
    213 	#
    214 	# If all the manifest files that were listed in the service have now
    215 	# been removed, delete the service.
    216 	#
    217 	if [ $ps_mfiles_cnt -eq ${#ps_mfiles[@]} ]; then
    218 		#
    219 		# Disable each of the instances for the service
    220 		# then delete the service.
    221 		#
    222 		# If the restarter is not startd then the service
    223 		# will not be online at this point and we need
    224 		# to not wait on the disable.
    225 		#
    226 		# Set the delete opt to -f if the disable is not
    227 		# synchronous.
    228 		#
    229 		$SVCPROP -q -p general/restarter $ps_service
    230 		if [ $? -ne 0 ]; then
    231 			DISOPT="-s"
    232 			DELOP=""
    233 		else
    234 			DISOPT=""
    235 			DELOP="-f"
    236 		fi
    237 
    238 		for i in `$SVCS -H -oFMRI $ps_service`
    239 		do
    240 			$SVCADM disable $DISOPT $i &
    241 			CPID=$!
    242 
    243 			pid_timeout $CPID
    244 			if [ $? -ne 0 ]; then
    245 				DELOPT="-f"
    246 				kill $CPID
    247 			fi
    248 		done
    249 
    250 		echo "$SVCCFG delete $ps_service"
    251 		$SVCCFG delete $DELOPT $ps_service
    252 		return
    253 	fi
    254 
    255 	#
    256 	# Need to only cleanup instances that are no longer supported
    257 	# by the manifest files associated with the service.
    258 	#
    259 	for i in $ps_instances
    260 	do
    261 		#
    262 		# Ignore any instances that are hand created
    263 		#
    264 		ps_refresh=1
    265 		$SVCCFG -s $i selectsnap last-import > /dev/null 2>&1
    266 		[ $? -ne 0 ] && continue
    267 
    268 		#
    269 		# If the restarter is not startd then the service
    270 		# will not be online at this point and we need
    271 		# to not wait on the disable.
    272 		#
    273 		$SVCPROP -q -p general/restarter $ps_service
    274 		if [ $? -ne 0 ]; then
    275 			DELOP=""
    276 			$SVCADM disable -s $i &
    277 			CPID=$!
    278 
    279 			pid_timeout $CPID
    280 			if [ $? -ne 0 ]; then
    281 				DELOPT="-f"
    282 				kill $CPID
    283 			fi
    284 		else
    285 			DELOP="-f"
    286 			$SVCADM disable $i
    287 		fi
    288 
    289 		echo "$SVCCFG delete $i"
    290 		$SVCCFG delete $DELOP $i
    291 	done
    292 
    293 	#
    294 	# If instances of the services were removed, refresh the
    295 	# additional instances, or cleanup any leftover services.
    296 	#
    297 	if [ $ps_refresh -ne 0 ]; then
    298 		if [ ${#ps_inst_list[@]} -gt 0 ]; then
    299 			for i in ${ps_inst_list[@]}
    300 			do
    301 				$SVCCFG -s $i refresh
    302 			done
    303 		else
    304 			ps_support=0
    305 			for ps_mfile in `awk '{print $3}' $MCLEANUPFILE`
    306 			do
    307 				$SVCCFG inventory $ps_mfile | grep $ps_service > /dev/null 2>&1
    308 				[ $? -eq 0 ] && ps_supprt=1
    309 			done
    310 			[ $ps_support -eq 0 ] && $SVCCFG delete $ps_service
    311 		fi
    312 	fi
    313 }
    314 
    315 #
    316 # Upgrade a service to have the manifest files associated with
    317 # listed in the manifestfiles property group.
    318 #
    319 # If the first argument is FALSE, then check to see if the service
    320 # has any previous import indications.  If so then delete the
    321 # service, otherwise set the service as a non-supported service
    322 # for the automated manifest deletion process.
    323 #
    324 function add_manifest {
    325 	am_service=$1
    326 	shift
    327 
    328 	$SVCCFG -s $am_service addpg $MFSTPG framework
    329 
    330 	if [ "$1" == "FALSE" ]; then
    331 		am_lisnap=1
    332 		am_inst=`svcs -H -oFMRI $am_service 2>/dev/null`
    333 
    334 		#
    335 		# Check for a last-import snapshot, if there is not
    336 		# one then the service was hand crafted and the support
    337 		# should be set to false.
    338 		#
    339 		if [ $? -eq 0 ]; then
    340 			for i in $am_inst
    341 			do
    342 				$SVCCFG -s $i selectsnap last-import > /dev/null 2>&1
    343 				[ $? -eq 0 ] && am_lisnap=0
    344 			done
    345 		fi
    346 
    347 		if [ $am_lisnap -ne 0 ]; then
    348 			$SVCCFG -s $am_service setprop $MFSTPG/support = boolean: 0
    349 
    350 			return
    351 		fi
    352 
    353 		#
    354 		# If the service was not hand crafted then check to see if
    355 		# the service has ever been installed in the /var/svc/manifest
    356 		# directory and therefore a known removed service.
    357 		#
    358 		grep "$am_service " $MFSTHISTORY | grep -v "^#" > /dev/null 2>&1
    359 		if [ $? -eq 0 ]; then
    360 			echo "$SVCCFG delete $am_service"
    361 			$SVCCFG delete -f $am_service
    362 		else
    363 			#
    364 			# Do not know where the service came from so set
    365 			# it to false.
    366 			#
    367 			$SVCCFG -s $am_service setprop $MFSTPG/support = boolean: 0
    368 		fi
    369 	else
    370 		for am_mfile in $@
    371 		do
    372 			CF=${am_mfile#/*}
    373 			CF=`echo $CF | sed -e 's/[\/\,\.]/_/g'`
    374 			$SVCCFG -s $am_service setprop $MFSTPG/$CF = astring: $am_mfile
    375 		done
    376 	fi
    377 }
    378 
    379 #
    380 # upgrade the entries in the smf/manifest table to have
    381 # a pointer to the actual manifest file.
    382 #
    383 function upgrade_smfmanifest {
    384 	us_unfnd=""
    385 
    386 	for us_E in `$SVCPROP smf/manifest | grep md5sum | grep var_svc_manifest | awk '{print $1}' | awk -F'/' '{print $1}'`
    387 	do
    388 		$SVCPROP -q -p $us_E/manifestfile smf/manifest
    389 		[ $? -eq 0 ] && continue
    390 
    391 		us_S=`echo $us_E | sed -e 's/_xml/.xml/'`
    392 		us_S=`echo $us_S | sed -e 's/var_svc_manifest_/var\/svc\/manifest\//'`
    393 
    394 		us_R=""
    395 		while [ ! -f $us_S -a ! "$us_R" ]
    396 		do
    397 			us_S=`echo $us_S | sed -e 's/_/\//'`
    398 			us_R=${us_S##*_*}
    399 		done
    400 
    401 		us_S="/$us_S"
    402 		if [ -f $us_S ]; then
    403 			us_R=`$MFSTSCAN $us_S`
    404 			[ ! "$R" ] && \
    405 				$SVCCFG -s smf/manifest setprop ${us_E}/manifestfile = astring: $us_S
    406 		else
    407 			us_unfnd="$us_unfnd $us_E"
    408 		fi
    409 	done
    410 
    411 	echo "$us_unfnd"
    412 }
    413 
    414 function manifest_cleanup {
    415 	#
    416 	# If manifest-import had activity then need to make checks to override
    417 	# a mfstscan that returns no modifications.  This is because the hash
    418 	# table will already have been updated by the manifest-import run, 
    419 	# therefor manifest-cleanup will not see those changes in the mfstscan
    420 	# call.
    421 	#
    422 	# activity indicates changes and overrides the needwork check.
    423 	# force can be a list of files that will only be processed
    424 	# 	or force can be set to true, so that all files are checked
    425 	# 	regardless.
    426 	#
    427 	arg1=$1
    428 	activity=${arg1:-true}
    429 	[ "$1" ] && shift
    430 	argrest=$@
    431 	force=${argrest:-false}
    432 
    433 	#
    434 	# Check the smf/manifest table to see if it needs upgrading
    435 	#
    436 	md5c=`$SVCPROP smf/manifest | grep var_svc_manifest | grep -c md5sum`
    437 	mfc=`$SVCPROP smf/manifest | grep var_svc_manifest | grep -cw manifestfile`
    438 	if [ $md5c -ne $mfc ]; then
    439 		unfnd_upgrade=`upgrade_smfmanifest`
    440 		if [ "$force" == false ]; then
    441 			activity="true"
    442 			force="true"
    443 		fi
    444 	fi
    445 
    446 	smfmfiles=`svcprop smf/manifest | grep manifestfile | grep astring | awk '{print $3}'`
    447 	needwork=`/lib/svc/bin/mfstscan $smfmfiles 2>&1 1>/dev/null`
    448 	if [ ! "$needwork" ]; then
    449 		[ "$activity" == false ] && return
    450 	
    451 		[ "$activity" == true -a "$force" == false ] && return
    452 	fi
    453 
    454 	#
    455 	# Walk the list of services...
    456 	#
    457 	export SVCCFG_CHECKHASH=1
    458 	for service in `$SVCCFG list`
    459 	do
    460 		svcprop -q -p $MFSTPG $service
    461 		if [ $? -ne 0 ]; then
    462 			mc_igchk=`eval expr \"$IGNORELIST \" : "'.*\($service \)'"`
    463 			if [[ -n $mc_igchk ]]; then
    464 				echo "add_manifest $service FALSE"
    465 				add_manifest $service FALSE
    466 				continue
    467 			fi
    468 			
    469 			[ $UPLIST -eq 0 ] && create_list
    470 
    471 			CS=`echo $service | sed -e 's/[-\/\,]/_/g'`
    472 
    473 			eval manifestlist=\$$CS
    474 			if [ -n "$manifestlist" ]; then
    475 				echo "add_manifest $service $manifestlist"
    476 				add_manifest $service $manifestlist
    477 			else
    478 				echo "add_manifest $service FALSE"
    479 				add_manifest $service FALSE
    480 			fi
    481 		else
    482 			process_service $service
    483 		fi
    484 	done
    485 
    486 	rm -f $MCLEANUPFILE
    487 	unset SVCCFG_CHECKHASH
    488 
    489 	#
    490 	# Check to make sure all work was processed and 
    491 	# that all the files were removed correctly from
    492 	# the smf/manifest table.
    493 	#
    494 	leftover=`echo "$needwork" | grep "cannot stat" | awk '{print $4}'`
    495 	for f in $leftover $unfnd_upgrade
    496 	do
    497 		f_srch=`echo $f | sed -e 's/\./\\\./g; s/:$//'`
    498 		f_entry=`$SVCPROP smf/manifest | grep "$f_srch" | awk -F'/' '{print $1}'`
    499 		[ "$f_entry" ] && $SVCCFG -s smf/manifest delpg $f_entry
    500 	done
    501 }
    502