#!/bin/sh

# Set and retrieve "platform serial number" using system EEPROM.
# (can also be used to store and retrieve other things from EEPROM)
#
# Uses explorer configuration file as alternate source of serial number
# and updates the serial number in the explorer config file if possible.
#
# Optionally uses simulation of SMS command line interface
# on systems which do not have SMS or FRUID .
#
#   The "native" interface
#	sneep -s serialnumber	# put serial number in EEPROM
#	sneep 			# retrieve serial numnber
#	sneep -t tag -s setting	# save entry in eeprom for "tag" with "value"
#	sneep -t tag 		# retrieve "value" from eeprom for "tag"
#
#  The SMS 1.4 interface for serial numbers
#	showplatform -p csn 	# print serial number
#	setcsn -c serial	# put serial number in EEPROM
#
# Stores the system serial number in the nvramrc as a FORTH "print" command :  
#	." string " cr
# Note: this does not require OBP variable use-nvramrc? to be "true"
#	but if true, OBP will print the serial number while booting
#

FullProg=$0		# save program pathname
Prog=`basename $0`	# save program name

# External version used in package, docs, usage
# note: the makefile alters the release_version keyword below
# which is the release used in the package and docs.
# The Revision is the internal file revision from SCCS
version="Release 2.5_R1.76"

# 	@(#) sneep 1.76@(#) 08/16/05 06:36:27
#		michael.shon@sun.com	2004/01

#	Use of FORTH "print" to store values:
#		bob.hilliard@sun.com   jacob.kirsch@sun.com

# enable tracing within functions
tracing=false
case "$-" in	# check command-line options
	*x*)	tracing=true ;;		# check for tracing flag "x"
esac


BACKUP=/etc/default/SUNWsneep	# backup of sneep data in the filesystem
BACKUP_USER=root:other		# owner:group for $BACKUP
BACKUP_PERMS=u=rw,go=r		# permissions on $BACKUP
				# must be writable for owner

PATH=/sbin:/usr/bin:/usr/sbin:$PATH	# be sure of what we are getting
export PATH

		
errmsg()	# emit error message on stderr
{
	echo "$@" 1>&2
}

# sneep uses 'tr' for several data format checks,
# but /usr/bin/tr does not work properly for UTF-8 locales .
# the XPG4 or XPG6 versions of tr are required for those locales.

# set $tr to a suitable version if possible
for tr in /usr/xpg6/bin/tr /usr/xpg4/bin/tr /usr/bin/tr
do
	test -x $tr && break
done

# warn of possible problems with tr and locale
case "$tr" in
	/usr/bin/tr )	# have to use default version : check for problems

		trout=`echo abc | $tr '[:lower:]' '[:upper:]' 2>&1 `
		if echo "$trout" | grep -i "bad string" > /dev/null ; then
			errmsg NOTE: $tr does not support your locale,
			errmsg "     " and no XPG4 alternative is available \
					on this system. 
			errmsg "  " Changing your locale may help:
			errmsg "  " example:  LC_ALL="C" $Prog -a 
			exit 1
		fi
		;;
esac


tab=`echo @ | $tr @ '\t' `	# create a real tab character

# Get the current username.
# Hope that id in this locale returns something like
# 	uid=0(root) gid=1(other)
# and split on the parenthesis
thisuser=`id | ( IFS="()" ; read x name y ; echo $name) `
log()	# send the command line parameters to syslog() with user details
{	# desirable for traceability of changes in audits

	$tracing && set -x

	l_message="$@"	# the message to log is passed on the command line

	# syslog facility.priority
	# this is not a daemon, but we need to be as sure as possible 
	# that this log data is saved, and user.* is often ignored.
	# User can override in environment variable SNEEP_SYSLOG
	SYSLOG_FAC_PRIO=${SNEEP_SYSLOG:-daemon.notice}

	# figure out who is using the program if possible
	l_idprog=/usr/xpg4/bin/id
	if test -x $l_idprog ; then
		l_user=`$l_idprog -u -r -n`	# get real user as a word
	else
		l_user=$thisuser
	fi
	
	# since this program is generally run by root (often via sudo)
	# see if we can find out if we were someone else before we became root
	#	check SUDO_USER, USER, and LOGNAME

	l_moreuser=${SUDO_USER:-${USER:-$LOGNAME}}
	if test "$l_moreuser" = "$l_user" ; then
		l_userval="$l_user"
	else
		l_userval="$l_user:$l_moreuser"
	fi
	
	logger -p $SYSLOG_FAC_PRIO $Prog:$l_userval "$l_message"
}


usage()
{
	usage_full=${1:-false}	# pass "true" for full usage

	cat 1>&2 << ENDUSAGE

	Save and retrieve Chassis Serial Number  ( CSN ) using EEPROM.

 usage:

   $Prog [-aFhlTvV] [-t tag] [-s setting ] [-P ds1:ds2...] \\
				[-d default] [-o separator]
   setcsn -c serialnumber
   showplatform -p csn

	-h		This help message
	For detailed information, consult the man page. Try
		man -M /opt/SUNWsneep/man $Prog

ENDUSAGE


}





# example of eeprom nvramrc output
#	|$ eeprom nvramrc
#	|nvramrc=devalias vx-rootdisk /sbus@3,0/SUNW,fas@3,8800000/sd@0,0:a
#	|devalias vx-rootmir /sbus@3,0/SUNW,fas@3,8800000/sd@1,0:a
#	|." ChassisSerialNumber FW02110044 " cr
#
# Note that if someone creates this entry without this script,
#	there may or may not be spaces before Chassis and after the serialnumber
#


# example of output format (from actual SC running SMS 1.4) :
#	|sc0:sms-user:> showplatform -p csn
#	|     CSN:
#	|     ====
#	|     Chassis Serial Number: 353A00053
#
# This is the de-facto API .
# for something simpler, use the "native" command usage . See the usage()


PATH=/usr/sbin:/usr/bin:$PATH	# find eeprom, avoid other PATH pollution
export PATH


# Solaris 10 has Zones.  Non-global zones have no eeprom access.
# This causes some problems, so figure out if this is running in a 
# non-global zone.  
# Succeed if running in a non-global zone, fail on anything else

ngzone()
{
	$tracing && set -x
	: ngz_havepkg is \""$ngz_havepkg"\"

	# all zones should have the zonename command; fail if missing
	test -f /sbin/zonename || return 1

	# if the command is present, make sure that it is the real thing
	# and we have the package SUNWzoneu

	pkginfo -q SUNWzoneu || return 1   # fail if no zone package
	
	ngz_name=`/sbin/zonename`
	: zone name is \""$ngz_name"\"
	case "$ngz_name" in
	global)		return 1 ;;	# fail on global zone
	"")		return 1 ;;	# something wrong or fake command
	*)		return 0 ;;	# some other zone name : non-global !
	esac

	return 1	# should never get here - handled in "case" above
}



get_eeprom_value()	# retrieve tag/value number from eeprom (or nothing)
{
	$tracing && set -x

	gev_tag=${1:-"$DESIREDTAG"}	# optional tag to look for
	gev_getval=${2:-true}		# default is to get value
	gev_limit1=${3:-true}		# 1 line returned, unless false

	# Non-Global zones have no eeprom, so we use the backup file instead.
	# We do this here to prevent a great deal of checking and
	# extra code elsewhere.  
	# Putting it here makes the eeprom a virtual thing
	if ngzone ; then

		: Virtual eeprom

		if $gev_getval ; then
			backup get "$gev_tag"	# use backup for that value
		else
			backup gettags "$gev_tag" # use backup for tags
		fi
		return $?	# return whatever backup() returned
	fi

	# Real eeprom

	# extract VALUE from <." TAG VALUE " cr>  
	# keep the last one if there are more than one (manual entry mistake?)
	#	We try to allow for some flexibility for whitespace 
	#	in  manually-entered tags.
	#
	#	It helps simplify the main pattern if we first remove the
	#	optional spaces before and after the final quote,
	#	as well as the somewhat optional "cr" at the end.
	#
	#	Assuming that the value might be a space-separated list,
	#	this leaves 	." tag value1 value2"
	#
	#	Then, to separate the tag from the value(s), 
	#	we convert the whitespace after the quote to a single tab,
	#	and the whitespace after the tag 
	#		(whitespace after something that is not a quote)
	#	to another tab, leaving the value(s) after that .
	#			."<tab>tag<tab>value1 value2"
	#
	#	Then we can easily tell the tag and value apart-
	#	the tag is the thing between the tabs .

	# To extract the TAG instead, we select the part of the pattern
	# that corresponds to the tag by enclosing it in \(..\)

	if $gev_getval ; then
		gev_tagmatch="$gev_tag"
	else
		gev_tagmatch='\('"$gev_tag"'\)'
	fi
		
	if $gev_limit1 ; then
		gev_limiter="tail -1"	# limit to final entry if duplicates
	else
		gev_limiter="cat"	# no limit on number of entries
	fi

	gev_val=`
		eeprom nvramrc 2> /dev/null | sed -n -e 's/^nvramrc=//' \
		-e "s/[ $tab]*\"[ $tab]*cr[ $tab]*\$/\"/" \
		-e "s/[ $tab]*\"[ $tab]*\$/\"/" \
		-e "s/\"[ $tab]*/\"$tab/" \
		-e 's/\([^"]\)'"[ $tab][ $tab]*/\\1$tab/" \
		-e "s/\.\"$tab$gev_tagmatch$tab\(.*\)\"/\1/p" \
			| $gev_limiter
	`

	test -z "$gev_val" && return 1	# fail if no value

	echo "$gev_val"			# output value from eeprom
	return 0
}


set_eeprom_value()	# update eeprom with tag and value embedded in nvramrc
{
	$tracing && set -x

	se_value="$1"		# value to set
	se_tag=${2:-$DESIREDTAG}  # eeprom tag with which to associate value
	

	# make sure the new value can be used
	#	should not contain control characters
	#	cannot contain quotes
	#	  convert others to quotes, look for quotes
	#	  (need '\c' to prevent newline, which would become quote)
	#	   '["*0]' means "a string of quotes of the proper length"

	se_tst=`echo "$se_value"'\c' | $tr '[:cntrl:]'   '["*0]'  `
	if echo "$se_tst" | grep '"' > /dev/null ; then
		errmsg The value for nvramrc cannot contain double quotes \
			or control characters .
		return 1
	fi

	# get the current value for this tag 
	se_oldval=`get_eeprom_value "$se_tag"`

	# if the new value is null, the value is to be removed.
	# if the old value is also null, (absent) this could indicate
	#	a mistake (incorrect tag)
	if test -z "$se_value" -a -z "$se_oldval" ; then
		errmsg \""$se_tag"\" was not present in nvram .

		# no need to go on with setting the eeprom:
		# might even eliminate potential problems
		return 0	# success: tag has been "removed"
	fi

	# if the old and new value are the same, dont bother
	if test "$se_value" = "$se_oldval" ; then
		errmsg \""$se_tag"\" no change to eeprom.

		# no need to go on with setting the eeprom:
		# might even eliminate potential problems
		return 0	# success: tag has not changed
	fi
	# 

	# non-global zone has no eeprom: pretend that setting it succeeds
	# both to reduce unhelpful error messages,
	# and to permit other changes to proceed (e.g. explorer and CST)
	#	Note that to properly virtualize the eeprom 
	#	using the backup file, we should call "backup set" here,
	#	but backup set is always called anyway if set_eeprom succeeds

	ngzone && return 0	# pretend that set_eeprom_value succeeded


	# Real eeprom update

	# remove old value from nvramrc (if present)
	se_oldnvram=`eeprom nvramrc 2> /dev/null | 
		sed	-e "/nvramrc:.*data not avail.*/d" \
			-e "/\.\"[ $tab]*$se_tag[ $tab][ $tab]*..*\"/d" \
			-e 's/nvramrc=//' `


	# start building the new nvramrc contents
	# from the remaining old contents 

	se_newnvram="$se_oldnvram"

	# only add the value to nvramrc if new value is non-null
	# This allows us to delete entries by giving null value.
	if test ! -z "$se_value" ; then
		# if nvramrc was empty, put tag/value in as only entry
		if test  -z "$se_oldnvram" ; then
			se_newnvram=".\" $se_tag $se_value \" cr"
		else
			# add new serial entry to what was already there
			se_newnvram=`	echo "$se_oldnvram" ; 
					echo ".\" $se_tag $se_value \" cr"
				`
		fi
	fi

	# ready to update the eeprom:
	# perform safety checks

	if check_nvramrc_limits "$se_newnvram" ; then
		: reasonable entry for this eeprom
	else
		errmsg New entry exceeds eeprom nvramrc limitations .
		return 1
	fi

	# 
	# update the eeprom
	#

	if eeprom nvramrc="$se_newnvram" 2> /dev/null
	then
		log Changed $se_tag in EEPROM nvramrc from \
			\"$se_oldval\" to \"$se_value\"

		#
		# seems to have been successful: double-check result
		#

		se_newval=`get_eeprom_value "$se_tag"`
		: v=\""$se_value"\"  nv=\""$se_newval"\"
		if test "$se_value" != "$se_newval" ; then
			return 1	# old and new value dont match
		fi			# fail
	else
		return 1
	fi

	return 0
}


check_nvramrc_limits()
{
	$tracing && set -x

	cnl_newentry="$1"

	cnl_rval=0		# return value: assume success
	cnl_checkoverride=false	# check safety override true

	# set default value for maximum size of nvram contents
	cnl_maxsize=500		# by default, this is the most nvramrc we allow.
				# Slightly more than 512 is available on Sparc2.
				# Newer platforms generally provide more.
				# overflow causes a drop into OBP,
				# and extreme cases can cause failure to boot

	# find out how big the given entry will be
	cnl_nsize=`echo "$cnl_newentry" | wc -c`

	# check for hardware platform limitations
	# uname -i returns something like "SUNW,Ultra-250" or "i86pc"
	case `uname -i` in
	*)	# assume defaults are OK for any platform which we
		# do not specifically know of another restriction
		;;
	esac

	# check for limitations of current host's ISA or processor type
	# uname -p returns something like "sparc" or "i386"
	case `uname -p` in
	i386 | *386* | *x86* )
		# x86 platform has multiple restrictions (and no true eeprom)
		# The system won't boot if the emulated eeprom is too large, so
		cnl_maxsize=500		# this has tested OK on solaris10

		# Solaris 10 x86 at FCS could only accept one line for nvramrc .
		#  we assume that prior versions were just as restrictive
		cnl_nlines=`echo "$cnl_newentry" | wc -l`
		if test $cnl_nlines -gt 1 ; then
			errmsg This platform is limited to one \
				entry in EEPROM nvramrc.
			errmsg To store this entry, remove any others first .
			cnl_rval=1
		fi
		;;

	sparc)
		# Need more details on the hardware for sparc.
		# "uname -m", "arch", and "mach" are all discouraged,
		# but uname is preferred.

		case `uname -m` in
		sun4 | sun4c | sun4d )
			cnl_maxsize=500 ;;	# just over 500 available
		sun4u | sun4m )	
			cnl_maxsize=5000 ;;	# somewhat over 5000 available
		esac
		;;

	*)	cnl_isa=`uname -p`
		errmsg $cnl_isa : Unknown platform ISA or \
					processor type "(uname -p)" .
		cnl_errmsg=`echo Cannot know eeprom limitations from \
			uname -p of $cnl_isa .`

		cnl_checkoverride=true	# check for safety override
		cnl_rval=1		# otherwise fail
		;;
	esac

	# make sure that the entry will safely fit in nvram

	# As the current size could already be more than the default,
	# we will allow as much as we already have.
	cnl_cursize=`eeprom nvramrc 2> /dev/null | sed 's/nvramrc=//' | wc -c`
	test $cnl_cursize -gt $cnl_maxsize && cnl_maxsize=$cnl_cursize

	# make sure it doesnt exceed the maximum, unless overridden by user
	if test $cnl_nsize -gt $cnl_maxsize ; then
		# new value is larger than the supposed maximum.
		errmsg EEPROM nvramrc may not be large enough .
		cnl_errmsg=`echo Overriding nvramrc size limit of \
			$cnl_maxsize to save $cnl_nsize characters .`

		cnl_checkoverride=true	# check for safety override
		cnl_rval=1		# otherwise fail
	fi
	
	if $cnl_checkoverride ; then
		# allow user to override, but log it if they do.
		if $override ; then
			log Safety override option has been used:
			log "$cnl_errmsg"
			sleep 1	# allow syslog to process the messages
			sync	# save any outstanding IO in case of crash
			sleep 1	# let the IO complete

			cnl_rval=0	# override chosen: must be OK
		fi
	fi

	return $cnl_rval
}

get_explorerconfig_serial()	# print serial entry from explorer config
{
	myid=`hostid`

	# look for EXP_SERIAL_<hostid>="<serial>"
	for expfile in	$explorer_configs
	do
		test -r $expfile || continue
		sed -n -e 's/"//g' -e "s/^EXP_SERIAL_${myid}=//p" $expfile
	done | tail -1		# keep only the last one if multiple
}

# Message included in explorer defaults file if created by sneep (RFE 00058)
new_explorer_message='
# Created by SUNWsneep so that explorer can obtain the Chassis Serial Number.
# Note that in order for explorer to execute successfully, several settings
# are necessary, which SUNWsneep does not provide.
# Be sure to run "explorer -g" to configure this file properly.
'

set_explorerconfig_serial()	# update explorer config files with serialnumber
{				# return success if all existing files updated
				# (still success if no files exist)

	ses_serial="$1"		

	myid=`hostid`
	ses_stat=0	# assume success: still OK if no explorer files present

	ses_hasexplorer=false	# assume that explorer is not installed
	pkginfo -q SUNWexplo && ses_hasexplorer=true	# for RFE 00058

	# set SERIAL config parameter in any and all explorer config files
	for expfile in	$explorer_configs
	do
		if test ! -f $expfile ; then
			# No explorer defaults file present.

			# RFE 00058: allow update even if no defaults file
			# as long as explorer is installed.

			if $ses_hasexplorer ; then
				# make dir for defaults file if necessary
				ses_expdir=`dirname $expfile`
				mkdir -p -m 0755 $ses_expdir 2> /dev/null

				# try to create empty explorer defaults file
				if touch $expfile 2> /dev/null ; then
					# created explorer defaults file,

					# Provide initial comment.
					echo "$new_explorer_message" > $expfile

					# fix permissions on file and dirs
					(
					  chmod 0644 $expfile 
					  chmod 0555 $ses_expdir $ses_expdir/..
					  chown root:bin $ses_expdir \
								$ses_expdir/..
					) 2> /dev/null

					# continue as though $expfile
					# had already been present:
					# update with serial
				else
					# file missing and cannot create it
					continue	# skip missing file
				fi
			else
				continue	# missing file and explorer
			fi
		fi

		# format of entry is 
		#	EXP_SERIAL_<hostid>="<serial>"
		ses_protoentry="EXP_SERIAL_${myid}="
		ses_newentry="$ses_protoentry\"$ses_serial\""

		ses_contents=`cat $expfile`	# capture entire file contents

		if grep "^$ses_newentry" $expfile > /dev/null ; then
			: entry already present: no need to update file
			continue
		fi

		# check file for update by writing to it
		# ( "touch" is not enough as it may only change timestamp)
		if ( echo "$ses_contents" > $expfile ) 2> /dev/null ; then
			: OK - we should be able to update the file
		else
			errmsg Cannot update $expfile as $thisuser .
			ses_stat=1
			continue
		fi

		# get desired contents into $ses_contents

		if grep "^$ses_protoentry" $expfile > /dev/null ; then
			: different entry present
			# get the old value, dropping any quotes
			ses_oldserial=` 
				sed -n	-e 's/"//g' \
					-e "s/^$ses_protoentry//p" $expfile `

			# change entry
			ses_contents=`sed -e \
				  "s/^$ses_protoentry.*/$ses_newentry/" $expfile `
		else
			: no entry present : append a new one to contents
			ses_oldserial=""
			ses_contents=`	echo "$ses_contents" ;
					echo ""
					echo \# Serial number for hostid $myid
					echo "$ses_newentry" `
		fi

		# update the explorer config file
		if (echo "$ses_contents" > $expfile)  2> /dev/null ; then
			: no problem updating
		else
			ses_stat=1	# could not update $expfile
		fi
		log Changed Serial Number in $expfile \
			from \"$ses_oldserial\" to \"$ses_serial\"
	done 
	
	return $ses_stat
}


##########################################################
#
# Backup file interface
#

backup()	# save/retrieve tag and value from backup file
{
	bup_action="$1"	# action is first parameter
	bup_tag="$2"	# tag
	bup_val="$3"	# value for tag

	# backup file format has (potentially) multi-line entries
	# each of the form :
	#	<tag><tab><value>
	#
	# Note that is may be more flexible than the eeprom,
	# so the backup file may accept things which cannot be
	# put in the eeprom.
	#
	# Note that ****the eeprom and the backup may not match****
	#
	# but the backup will always have the "best" information.
	# Getting it from backup and putting back in eeprom will
	# put the best possible thing in eeprom that it can handle.

	case "$bup_action" in
	"")	errmsg Internal error: no directions for backup .
		return 1
		;;
	set)
		#
		# Set up the backup file: take care with owner and perms
		#

		# set permissions on existing file before 
		# we try to write it.
		test -f $BACKUP && (
			chown $BACKUP_USER $BACKUP
			chmod $BACKUP_PERMS $BACKUP
		) 2> /dev/null
		if touch $BACKUP 2> /dev/null ; then
			: $BACKUP is writable
			# set permissions in case it was just created
			chmod $BACKUP_PERMS $BACKUP 2> /dev/null
			chown $BACKUP_USER $BACKUP 2> /dev/null
		else
			errmsg Cannot update $BACKUP as $thisuser .
			return 1
		fi

		#
		# Update the backup file
		#


		# get all lines except those for $bup_tag and hostid
		bup_other=`sed	-e '/^sneep hostid/d' \
				-e "/^$bup_tag$tab/d" $BACKUP`
		
		# empty the backup file
		cp /dev/null $BACKUP 2> /dev/null || return 1

		# put the hostid of this host in the backup file
		# for boot-time autoload check
		echo "sneep hostid$tab`hostid`" > $BACKUP

		# if there is a new value, put it in the backup file
		# as the first entry.
		# Each line of the value starts with <tag><tab> .
		test ! -z "$bup_val" && 
			echo "$bup_val" | sed "s/^/$bup_tag$tab/" >> $BACKUP

		# put any other stuff back into the backup file
		test ! -z "$bup_other" && echo "$bup_other" >> $BACKUP

		return 0	# update succeeded
		;;
	get)
		#
		# Retrieve value for tag from the backup file
		#

		test -r $BACKUP || return 1		# fail if no backup file
	
		# return the value by stripping the tag and tab from 
		# the relevant lines.
		bup_val=` sed -n "s/^$bup_tag$tab//p" $BACKUP `

		test -z "$bup_val" && return 1		# fail if no value

		echo "$bup_val"		# return value
		return 0		# return success
		;;

	gettags)
		#
		# Retrieve all tags from the backup file
		#

		test -r $BACKUP || return 0	# no backup, but thats OK
	
		# return the tags by stripping the tab and value from 
		# the relevant lines.
		#	do NOT return tags like "sneep hostid"
		sed -n -e '/^sneep /d' -e "s/$tab.*//p" $BACKUP	# return value
		return 0
		;;

	esac
}






##########################################################
#
# CST Configuration Service Tracker interface
#

CSTPKG=${CSTPKG:-SUNWcstu}		# package name for CST
CSTVAR=${CSTVAR:-/var/opt/SUNWcst}	# where to find CST user data and config
CSTUSERDATA=$CSTVAR/user_data		# Serial stored in this file by CST

cst_available()		# return 0 if CST is installed and usable
{			# also return package install dir (generally /opt)

	if CSTBASE=`/usr/bin/pkginfo -r SUNWcstu 2> /dev/null` ; then
		echo $CSTBASE
		return 0	# assume it is up
	fi			# app_event can tell if it is down later

	return 1	# cannot find or use CST
}


: get_cst_serial

get_cst_serial()	# get serial from CST user data if possible
{

	# no real need to check for CST installed- just look for user_data
	# gcs_dir=`cst_available` || return 0	# Keep quiet if no CST
	

	test -r "$CSTUSERDATA" || return 1	# fail if we cannot read it

	# user_data file is tab separated,
	# serial number is on a line like this:
	#	Serial # of System<tab>819fc481

	if gcs_serline=`grep -i "serial.*system" $CSTUSERDATA ` ; then
		echo "$gcs_serline" | cut -sf 2	# return serial number
		return 0			# and success
	fi

	return	1				# failed to get serial from CST
}


: set_cst_serial

set_cst_serial()	# send CST event with serial number
{			# to be picked up and used to set CST user data

	$tracing && set -x

	scs_serial="$1"		# new serial number is pased in


	# As of 200411, the only agent-side changes to user data 
	# which are propagated to the CST middleware are those made 
	# using the GUI.
	#
	# Note :  by using the the CST User Data Editor (uded)
	#	available from Sun Field Customer Advocacy,
	#	edits made to the user data on the agent CAN be propagated
	#	to the middleware.  uded will also automatically detect
	#	and leverage the application events described below.
	# 
	# As a result, without uded there is little value in 
	# editing the user_data file as we edit the explorer config file.
	#
	#	The "value" is that if the serial number is erased,
	#	and then requested again via sneep, it will be unknown.
	#	If we do not edit it out of the file, then the old
	#	serial is returned again. This can come as a surprise.
	#
	# Our most valuable CST activity then, is to create a special 
	# application event on the agent which is passed to the middleware
	# and can be processed later to create a simulated user_data edit
	# if desired. 
	# (this in handled in conjunction with the "uded" utility, not part
	#	of the standard CST installation)
	

	# first, update local cst user data file
	# whether or not CST is running

	update_cst_udata "$scs_serial"	# update local file with serial

	#
	# Send CST event to record serial locally, and on middleware server
	#

	# check for usable CST
	scs_dir=`cst_available` || return 0	# Keep quiet if no CST
						# complain if CST not working
	# check for CST app_event utility installed correctly
	scs_appev=$scs_dir/SUNWcstu/bin/app_event
	if test ! -x $scs_appev ; then
		errmsg CST app_event utility not found .
		return 1
	fi

	# send CST app_event of type "Serial Number" with new value.
	# Capture output, as we have found the app_event exit value unreliable.

	scs_tmp=`$scs_appev "Serial Number" "<CSN>$scs_serial</CSN>" 2>&1 `
	scs_stat=$?

	# check app_event output to see if all went OK
	if echo "$scs_tmp" | grep -i DONE > /dev/null ; then
		: DONE seems to mean CST is OK
	else
		errmsg CST may be down .
		return 1
	fi
	return 0
}


#
# Update CST user_data file
#
update_cst_udata()	# upate the CST user_data file
{
	ucu_serial="$1"	# pass in the serial number to update in the file


	if test ! -f $CSTUSERDATA ; then
		# There is no CST user file (perhaps no CST directory)
		# on this system.
		# If CST is installed, we will update the user file anyway.

		# if CST is not installed, return success anyway
		# because CST is completely optional. 
		pkginfo -q SUNWcstu || return 0  # not installed, return
		
		# file missing, but CST is installed. 
		# create the missing file and set the serial in it
		# RFE 00056 (extension): update missing serial

		# create CST data directory if missing
		ucu_dir=`dirname $CSTUSERDATA`
		test -d $ucu_dir || mkdir -m 0755 -p $ucu_dir 2> /dev/null

		# create empty user data file, or return
		#	return success because CST is not required
		touch $CSTUSERDATA || return 0

		chmod 644 $CSTUSERDATA 2> /dev/null	# set safe permissions
	fi

	# if the old and new values are the same (even if null)
	# then no need to update
	ucu_oldserial=`get_cst_serial`
	test "$ucu_serial" = "$ucu_oldserial" && return 0

	# user_data file is tab separated,
	# serial number is on a line like this:
	#	Serial # of System<tab>819fc481

	
	# save everything EXCEPT the line containing the Serial entry
	ucu_otherlines=`grep -v -i "serial.*system" $CSTUSERDATA `

	
	if touch $CSTUSERDATA 2> /dev/null ; then
		: no problem 
	else
		errmsg Cannot write $CSTUSERDATA as $thisuser .
		return 1
	fi

	# Put new value (or null value) into tab-separated file
	echo 'Serial # of System'"$tab$ucu_serial" > $CSTUSERDATA || return 1

	# Put the other entries into the file
	echo "$ucu_otherlines" >> $CSTUSERDATA || return 1

	log Changed Serial Number in CST user data $CSTUSERDATA \
		from \"$ucu_oldserial\" to \"$ucu_serial\"

	return 0
}



##########################################################




: get_value 


get_value()	# get value from eeprom (or explorer/CST) and report on stdout
{
			# first param is whether or not this is serial number
	gv_tag="${1:-$DESIREDTAG}"	# eeprom tag should be passed in
	gv_sources=${2:-$SNEEPPATH}	# sources to check in sequence

	gv_status=0	# assume success
	gv_found=false	# true if value has been found

	case "$gv_tag" in
		"$SERIAL_TAG" | serial | csn | CSN )
		
			gv_tag="$SERIAL_TAG" # use standard tag
			gv_serial=true		# processing a serial number:
			;;
		* )
			gv_serial=false		# not the serial number
			;;
	esac

	for gv_src in `(IFS=: ; echo $gv_sources)` default
	do
		: check $gv_src

		gv_sourcename=$gv_src	# printable source name

		case "$gv_src" in
		fruid|FRUID|fru*|FRU*)
			# check $gv_serial before lookup
			: $gv_src not implemented yet
			;;
		sms|SMS)
			# check $gv_serial before lookup
			: $gv_src not implemented yet
			;;

		eeprom|EEPROM|eep*|EEP*|nvram*)
			gv_value=`get_eeprom_value "$gv_tag" `
			;;

		backup|bac*)
			gv_value=`backup get "$gv_tag" ""`
			gv_sourcename="$gv_src : $BACKUP"
			;;

		explorer|exp*)
			# check $gv_serial before lookup
			$gv_serial && gv_value=`get_explorerconfig_serial`
			;;
		cst|CST)
			# check $gv_serial before lookup
			$gv_serial && gv_value=`get_cst_serial `
			;;

		default|def*)
			# provide default value
			# (unless value has already been found
			#  as it might be with -a option report all sources )

			if $gv_found ; then
				: skipping default because already found
			else
				gv_value=$defaultval
				gv_sourcename="default value"

				# fail if serial cannot be determined
				gv_status=1	
			fi
			;;

		*)	errmsg Unknown data source: \""$gv_src"\" .
			;;
		esac

		# report when we find a value
		if test ! -z "$gv_value" ; then
			gv_found=true

			$verbose && 
				echo "   " "$gv_tag" from $gv_sourcename : 
			echo "$gv_value"
			gv_value=""

			# stop looking as soon as we find a value
			# unless we are reporting all sources
			$allsources || break
		fi
	done

	return $gv_status
}

: showplat

showplat()	# emulate showplatform -p csn output
{
	serial="$@"	# pass in serial number

	cat << END
     CSN:
     ====
     Chassis Serial Number: $serial
END
}

: set_value

set_value()	# set value in eeprom, explorer config files, CST
{		# we allow null serial so that it can be removed

	sv_tag="$1"	# eeprom tag to use
	sv_value="$2"	# value to set

	sane_tag "$sv_tag" || return 1		# reject illegal tags

	case "$sv_tag" in
		"$SERIAL_TAG" | serial | csn | CSN )
		
			sv_serial=true			# setting serial number
			sv_force_ucase=$force_ucase	# take user view of case
			sv_tag=$SERIAL_TAG		# use standard tag
			;;
		* )
			sv_serial=false		# not the serial number
			sv_force_ucase=false	# no need to change value case 
			;;
	esac

	if $sv_force_ucase ; then
		# we need a standard to simplify comparisons everywhere else -
		# I choose upper case.  
		# hostids are different - they are hex numbers.

		# convert lower case to upper case for serial numbers
		sv_value=`echo "$sv_value" | $tr '[:lower:]' '[:upper:]' `
	fi

	# if we are setting a serial number, sanity check the contents
	if $sv_serial ; then
		if sane_serial "$sv_value" ; then
			: no problem
		else
			errmsg Cannot use this value for serial number .
			return 1
		fi
	else
		# not setting a serial number.

		# if the value is null, then we are deleting it
		# and that is always permissible

		if test -z "$sv_value" ; then
			: deleting $sv_tag
		else
			: setting a non-serial tag
			# Refuse to set value if Serial is not already set.
			#	This policy is intended to help make certain 
			#	that eeprom.out contains serial even on x86
			#	where only 1 entry is allowed, and also ensures
			# 	that sneep serves its primary function first
			#	rather than just being a handy utility 
			#	that is used for other purposes.
			#
			#	Of course, we cannot be sure that 
			#	the serial is right...

			# Find out if the serial has been set
			sv_eeserial=`get_eeprom_value "$SERIAL_TAG" ` 

			if test -z "$sv_eeserial" ; then
				errmsg You must set the Chassis Serial Number \
					before setting any other tags.
				return 1
			fi
		fi
		
	fi
	
	if set_eeprom_value "$sv_value" "$sv_tag" ; then
		: no problem
		sv_eefailed=false	# eeprom update succeeded
		sv_stat=0
	else
		errmsg Could not save $sv_tag in system EEPROM as $thisuser .
		sv_stat=1
		sv_eefailed=true	# eeprom update failed
	fi

	# only update explorer and CST if this is for a serial number
	# and we were able to set it in EEPROM
	if $sv_serial && test $sv_eefailed = false ; then
		for sv_source in `(IFS=: ; echo $SNEEPPATH)`
		do
			case "$sv_source" in
			explorer)
				if set_explorerconfig_serial "$sv_value" ; then
					: no problem
				else
					echo Could not save serial number in \
						explorer configuration file 1>&2 
					sv_stat=1
				fi
				;;
			cst)
				if set_cst_serial "$sv_value" ; then
					: no problem
				else
					errmsg Could not send CST change event .
				fi
				;;
			esac
		done
	fi


	# Current policy is:
	# update backup file even if eeprom update failed
	# to overcome eeprom limitations.
	#	Can lead to surprises when values dont match,
	#	but mostly does what you would want it to do

	# If eeprom update failed and backup did not,
	# then notify the user so there is less surprise later.

	if backup set "$sv_tag" "$sv_value" && $sv_eefailed ; then
		errmsg ""
		errmsg Saved "$sv_tag" in backup .
		errmsg ""
	fi


	return $sv_stat
}

: sane_serial

sane_serial()	# succeed if the string passed in seems reasonable for a serial
{
	$tracing && set -x

	ss_value="$1"

	# Serial must be alphanumeric and fairly short
	# delete alphanumerics and see what is left: should be nothing left
	ss_other=`echo "$ss_value" | $tr  -d '[:alnum:]' `
	if test ! -z "$ss_other" ; then 
		errmsg Serial Number must contain only alphanumerics .
		return 1
	fi

	ss_len=`echo "$ss_value"'\c' | wc -c `
	if test $ss_len -gt 13 ; then
		if test $ss_len -gt 20 ; then
			errmsg Setting provided is too long for a serial number .
			return 1
		fi

		errmsg \""$ss_value"\" is unusually long for a serial number .
	fi

	return 0
}

: sane_tag

sane_tag()	# succeed if the passed value is a reasonable tag value
{
	$tracing && set -x

	st_tag="$1"	# get tag passed as argument

	st_special="@#_+=-"	# special characters allowed in tags


	# must be alphanumeric and fairly short,
	# with a few other characters allowed.
	# Delete the allowable characters and see if anything remains.

	st_other=`echo "$st_tag" | $tr  -d "[:alnum:]$st_special" `
	if test ! -z "$st_other" ; then
		errmsg Only alphanumerics and \"$st_special\" \
				are allowed in the tag .
		return 1
	fi
	return 0
}

: sneep_startup

# perform data integrity checks for system startup
# (assuming we have been called as a startup script)
sneep_startup()		# check serial integrity e.g. at system startup
{			# and fix if possible

	# check (eeprom) serial against other sources and complain of mismatch
	# fix other sources if eeprom is OK.

	repairinfo=""
	sneeppath=$FullProg	# default path to sneep is to this script

	# look for sneep package and set up official path if possible,
	# as well as a message to assist with repairs
	if sneepdir=`pkginfo -r SUNWsneep 2> /dev/null` ; then
		sneeppath=$sneepdir/bin/sneep
		repairinfo="Repair Chassis Serial with $sneeppath"
	fi

	ssu_problems=false		# no problem so far
	ssu_attempt_repair=false	# if true when finished, update serial

	# find out if backup is valid for this system: 
	#	does it contain the right hostid?
	ssu_goodbackup=false
	if ssu_hid=`backup get "sneep hostid"` ; then
		if test "$ssu_hid" = "`hostid`" ; then
			ssu_goodbackup=true
		else
			log $BACKUP is from another system: hostid $ssu_hid
			# save original backup for the other system
			# in case we have to sort it out later
			if test ! -f $BACKUP.$ssu_hid ; then
				cp -p $BACKUP $BACKUP.$ssu_hid
			fi
			# serial in backup cannot be correct- delete it
			backup set "$SERIAL_TAG" ""
		fi
	fi


	# get serial from eeprom to check other sources against.
	# If it is set, then proceed to loop below to check other sources.
	# If it is not set, then load from backup if backup is from this system.

	if ssu_eeprom=`get_value $SERIAL_TAG eeprom` ; then
		: serial is set in eeprom - check the remaining sources
	else
		: serial is not in eeprom - may have been reset or never set

		# if backup file has the hostid from this system,
		# and has the serial in it, then reload the eeprom from backup

		if $ssu_goodbackup ; then
			: backup is from this system, check for serial
		   	if ssu_bupserial=`backup get $SERIAL_TAG`  ; then
				: serial is available from backup, load eeprom

				log Restoring eeprom data from backup file
				# we choose to load all values, not just serial

				# Report commands to set values, send to shell,
				# ignore stderr in case of complaints on x86
				# about having room for only one eeprom entry

				$sneeppath -Tv | sh 2> /dev/null

				# continue with checks, just to be sure
				ssu_eeprom=`get_value $SERIAL_TAG eeprom`
			else
				: serial is not in backup, cannot load
				# missing serial will be reported in loop below
			fi
		else
			: backup may not be from this system
			log cannot use backup to restore missing eeprom values
			# missing serial will be reported in loop below
		fi
	fi


	# for each other serial source, check individually
	# 	check eeprom first so that missing eeprom value will
	#	get us out of here early (see below).
	#	Also, we know that we can try to fix other sources	
	#	because eeprom was done first and must have been OK 
	#	or we wouldnt still be in this loop.

	for ssu_source in eeprom `(IFS=: ; echo $SNEEPPATH)`
	do
		: check source $ssu_source against $ssu_eeprom

		if ssu_val=`get_value $SERIAL_TAG $ssu_source` ; then
			: got a value successfully

			# see if it matches eeprom   value
			if test "$ssu_val" != "$ssu_eeprom" ; then
				log Serial in eeprom \"$ssu_eeprom\" \
					does not match serial in \
					$ssu_source \"$ssu_val\"
				ssu_problems=true
				ssu_attempt_repair=true	 # try to fix it later
			fi
		else
			: could not get a serial value from $ssu_source
			case "$ssu_source" in
			eeprom)
				log Chassis Serial not available \
							from system eeprom
				ssu_problems=true
				ssu_attempt_repair=false # not repairable
					# (should already have been fixed above)
				break 2		# do not continue this loop
				;;
			backup)
				log Chassis Serial is not in backup file
				ssu_problems=true
				ssu_attempt_repair=true	 # try to fix it later
				;;
			explorer)
				# RFE 00056: on startup, sneep should "fix" 
				# 	null serial in explorer
				# update missing serial if explorer installed
				pkginfo -q SUNWexplo && ssu_attempt_repair=true
				;;
			cst)
				# RFE 00056 (extension): update missing serial
				# update missing serial if CST installed
				pkginfo -q SUNWcstu && ssu_attempt_repair=true
				;;
			esac
		fi
	done
	# log hint for how to fix problems manually
	$ssu_problems && test ! -z "$repairinfo" && log $repairinfo

	# if it looked like it was fixable, give it a try
	# (eeprom valid but other sources do not match)
	if $ssu_attempt_repair ; then
		log Attempting automatic repair using eeprom 

		if $ssu_goodbackup ; then
			: should go smoothly: eeprom and backup should align
		else
			log Warning: backup file is questionable and could
			log introduce unexpected tags into eeprom.
			log Please check eeprom tags with $sneeppath -T
		fi
			

		# Recover from eeprom.
		# Set all tags found in eeprom *and backup*
		# to recover backup and other sources from eeprom.
		# (settings only in the backup will be unchanged, but
		#  will be introduced into eeprom)

		# Report commands to set values, send to shell,
		# ignore stderr in case of complaints on x86
		# about having room for only one eeprom entry

		$sneeppath -Tv | sh 2> /dev/null
	fi

	return 0	# everything went as well as could be expected
}


: report_tags

report_tags()	# report tags and values; for documentation and recovery
{
	rt_tag="$1"			# optional tag passed in
	rt_specifictag=${2:-false}	# if true, just do that one tag
					# otherwise do them all

	verbose_output=$verbose		# remember whether we want verbose form

	verbose=false			# turn verbose off to prevent
					# extra messages
	if $rt_specifictag ; then

		# tag could be a comma-separated list of tags.
		# split on commas and get unique members
		rt_tags=`( IFS=, ; echo $rt_tag ) `
		rt_alltags=`
			for rt_t in $rt_tags
			do
				# if a short form of $SERIAL_TAG was used,
				# convert to the full tag name

				case "$rt_t" in
					serial | csn | CSN )	
						echo $SERIAL_TAG ;;
				*)
					echo $rt_t ;;
				esac
			done | sort -u
		`
	else
		# No specific tags provided:
		# collect unique tags from eeprom and backup; 
		# other sources are just for serial, so we dont check them.

		# get any number of tags from eeprom
		rt_eeptags=`get_eeprom_value '.*' false false`
		# get all tags from backup
		rt_backuptags=`backup gettags '.*' ""`

		# make list of all tags, making sure that serial is in there
		rt_alltags=`(
			echo $SERIAL_TAG
			echo "$rt_eeptags"
			echo "$rt_backuptags"
		) | sort -u `
	fi

	# if SERIAL_TAG is in the list, make sure that it is first in the output
	# because there is a rule that serial must be set before other tags.
	# If this is a recovery from backup, we have to put serial in eeprom
	# before we try anything else.

	case "$rt_alltags" in
	*${SERIAL_TAG}* )
		# remove serial tag
		rt_alltags=`echo "$rt_alltags" | 
				grep -v "^$SERIAL_TAG\$" `
		# insert serial tag at beginning of list
		rt_alltags="$SERIAL_TAG $rt_alltags"
		;;
	esac


	# if the sneep priority path is not the default path,
	# include it in -Tv output  later, so that the sneep commands
	# obey the user's priorities and affect only desired data sources
	if test "$SNEEPPATH" != "$SNEEPPATHDEFAULT" ; then
		rt_prio="-P \"$SNEEPPATH\""
	else
		rt_prio=""
	fi

	# for each tag, print tag and value
	# for verbose printing, include command and parameters to set the value
	for rt_tag in $rt_alltags
	do
		: print value for \""$rt_tag"\"

		rt_val=`get_value "$rt_tag" `

		if $verbose_output ; then
			# output sneep command in verbose mode

			$erase && rt_val=""	# erase value if directed

			echo $FullProg $rt_prio \
				-t \""$rt_tag"\""$tab""-s \"$rt_val\""
		else
			# output tags and values
			echo \""$rt_tag"\""$tab"\""$rt_val"\"
		fi
	done
	return 0
}

: get_multi

get_multi()	# print list of values with field separator
{		# generally for CSV output to spreadsheet

	gm_tags="$1"	# comma-separated list of tags and keywords to get
	gm_fieldsep="$2"	# field separator; may be null

	: get multiple fields and print with field separator
	gm_fieldvalues=""
	for gm_field in `(IFS="," ; echo $gm_tags )`  # split tags on commas
	do
		: get val for tag \"$gm_field\"

		sane_tag "$gm_field" || continue	# skip bad tags

		# handle a few special keywords here
		case "$gm_field" in

		# handle special pseudo-tags for commonly needed information
		hostid)		gm_thisval=`hostid` ;;
		hostname)	gm_thisval=`hostname` ;;

		serial | csn | CSN )	# handle aliases for serial number tag
				gm_thisval=`get_value "$SERIAL_TAG" ` ;;


		*)			# handle all other tags
				gm_thisval=`get_value "$gm_field" `
				;;
		esac

		if test -z "$gm_fieldvalues" ; then
			gm_fieldvalues="$gm_thisval"
		else
			gm_fieldvalues="$gm_fieldvalues$gm_fieldsep$gm_thisval"
		fi
	done
	echo "$gm_fieldvalues"
}





: Begin MAIN

SERIAL_TAG="ChassisSerialNumber"	# tag for serial number in eeprom
DESIREDTAG="$SERIAL_TAG"		# tag to use by default
specified_tag=false			# if true, tag option was used

serialin=""		# input serial number for setcsn


# If force_ucase is true then force serial numbers into upper case.
# This is the norm, but it can be overridden in the environment : $FORCE_UCASE
force_ucase=${FORCE_UCASE:-true}
test "$FORCE_UCASE" = true || FORCE_UCASE=false	# make certain of true or false

defaultdflt=unknown	# what we get back if we cant find what we are after
			# this can be changed on the command line
defaultval=$defaultdflt	

verbose=false		# if true, tell more about where the data comes from

settingvalue=false	# assume that we are NOT setting a value
override=false		# if true, user can override safety restrictions
			
allsources=false	# if true, report from all sources

report_tags_too=false	# if true, do nothing but report tags and values

fieldsep=,		# output field separator

erase=false		# if true and used with -T -v, erase data


# the search order for retrieving the data
SNEEPPATHDEFAULT="fruid:sms:eeprom:backup:explorer:cst"
# local override of default path can be stored in backup file as "sneep path"
sneeplocalpath=`backup get "sneep path" ` && SNEEPPATHDEFAULT="$sneeplocalpath"

SNEEPPATH=${SNEEPPATH:-$SNEEPPATHDEFAULT}	# use environment or default

SNEEP_SYSLOG=${SNEEP_SYSLOG:-`backup get "sneep syslog"`}


# list of explorer configuration files in order of least to most desirable
# (generally speaking, oldest location to newest)
#	(only put files in this list for which there is code that understands
#		the format of the serial settings in that file)
explorer_configs="
	/etc/opt/SUNWexplo/default/explorer
"

# if the path to an alternate explorer config file has been provided
# in the local backup file, then use that instead of the usual path(s)

altexplopath=`backup get "sneep explorerdefaults"`
test ! -z "$altexplopath" && explorer_configs="$altexplopath"

while getopts aeFd:t:Tho:p:P:c:s:Vvx arg
do
	: arg=\""$arg"\"
	case "$arg" in
	
	a)	allsources=true
		verbose=true
		;;

	e)	erase=true ;;		# use with -T -v

	h|\?)	usage 
		exit 0
		;;


	F)	override=true
		;;


	t)		# set tag to set or find in EEPROM
		specified_tag=true
		DESIREDTAG="$OPTARG"
		if test -z "$DESIREDTAG" ; then
			usage
			errmsg Cannot use null tag with -t option .
			exit 1
		fi
		;;

	T)		# report tags in eeprom and backup
		report_tags_too=true
		;;

	d)		# set tag to use in EEPROM
		defaultval="$OPTARG"	# choose what we get back
					# if we find nothing
		;;

	o)		# set output field separator for multiple fields (-f)
		fieldsep="$OPTARG"
		;;

	P)	# select data source(s)
		SNEEPPATH="$OPTARG"
		;;

	p)		# print CSN per SMS 1.4 and above
			# showplatform -p csn

		case "$Prog" in 
		*showplatform)	 ;;	# OK - called as showplatform

		*)	errmsg " -p" is reserved for \"showplatform\" interface
			exit 1
			;;
		esac

		pword="$OPTARG"
		case "$pword" in
			csn)
				# always insist on serial with "-p csn"
				# SMS returns "Unknown" if unknown
				defaultval=Unknown
				csn=`get_value $SERIAL_TAG `
				showplat "$csn"
				exit 0
			;;
		*)
			usage 
			exit 1
			;;
		esac
		;;

	c )		# setcsn -c <serialnumber>

		# We will set the serial number if "setcsn" is the name
		# but otherwise, this is a mistake

		value="$OPTARG"
		case "$Prog" in
		*setcsn)	# set serial if name is setcsn
			set_value "$SERIAL_TAG" "$value"
			exitval=$?
			;;
		*)
			errmsg " -c" is reserved for \"setcsn\" interface
			exitval=1
			;;
		esac
		exit $exitval
		;;

	s)	# set an arbitrary value for an arbitrary tag
		# <anyname> [-t anytag] -s value

		value="$OPTARG"
		settingvalue=true
		;;

	v)	verbose=true ;;

	V)	echo "$version" ; exit 0 
		;;

	x)	set -x ; tracing=true ;;	# secret debugging option

	esac
done
shift `expr $OPTIND - 1`

	

: arg 1 is \"$1\"

if $report_tags_too ; then
	allsources=false	# report only one source per tag/value
	report_tags "$DESIREDTAG" $specified_tag	# report tags and values
	exit $?
fi


# This script can be linked into the system startup script directories
# such as /etc/init.d and /etc/rc2.d .
# Perform start/stop functionality if called with correct keyword
case "$1" in
	stop)			exit 0 ;;	# no stop action
	start | restart)	sneep_startup $1 ; exit $? ;;
esac


if $settingvalue ; then

	if $override ; then
		errmsg You have chosen to override safety features,
		errmsg and accept full responsibility for any negative effects.

	fi

	trap "" 2 15		# ignore signals INT and TERM
				# to prevent loss of data from interrupts
				# in the middle of multi-step updates

	set_value "$DESIREDTAG" "$value"
	exit $?
else
	# get value for each tag and then output as a list
	get_multi "$DESIREDTAG" "$fieldsep"
	exit $?
fi

