#!/bin/sh
## --------------------------------------------------------------------------------
##
## startstop_ramdisk - start/stop script to create or destroy a ramdisk
##
## The script can be used to create a ramdisk, optionally mount it, and optionally 
## copy initial data to the ramdisk.
##
## The script can also be used to destroy a ramdisk with optionally creating a 
## backup of the data on the ramdisk before destroying it.
##
## Author:
##
##	Bernd Schemmer (Bernd.Schemmer@gmx.de)
##
## History
##
##	22.11.2008 v1.0.0/bs
##		initial release
##
## Usage: 
##
## 	startstop_ramdisk [help|start|stop|status|viewconfig|backup {backupdir}]
##
## Environment variables used by this script:
##
##	VERBOSE
##		if set, print verbose messages
##
##	QUIET 
##		if set, print no messages at all
##
## Notes: 
##
## 	The script supports a config file. The name of the config file is
##
## 		/etc/{scriptname}.conf
##
## 	where {scriptname} is the name of the script without a leading K## or S##.
##
## 	Examples:
##
##	Scriptname			Config file
## 	-----------------------------------------------------------
##	/etc/rcS.d/S99ramdisk1	 	/etc/ramdisk1.conf
##	/etc/rc3.d/K01ramdisk1	 	/etc/ramdisk1.conf
##	/etc/rcS.d/S99ramdisk2	 	/etc/ramdisk2.conf
##      /etc/init.d/ramdisk2		/etc/ramdisk2.conf
##	/usr/sbin/startstop_ramdisk	/etc/startstop_ramdisk.con
##
##	To create the config file just copy the statements between 
##
##		--- configuration starts ----
##	and 
##		-- configuration ends ---
##
## 	to the config file and change the values of the variables to your need.
##
## 	To use more than one ramdisk create symbolic links with different names :
##
## 	e.g. if the script is in the directory /etc/init.d and called startstop_ramdisk
##
##	# This invocation reads the config from the file /etc/start_ramdisk1.conf
## 	ln -s ../init.d/startstop_ramdisk /etc/rc2.d/S05start_ramdisk1
##
##	# This invocation reads the config from the file /etc/start_ramdisk2.conf
## 	ln -s ../init.d/startstop_ramdisk /etc/rc2.d/S06start_ramdisk2
##
##
## In Solaris 10 you should create a SMF service to start and stop the ramdisk(s) 
## automatically
##
## --------------------------------------------------------------------------------
#
# License
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License").  You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2006, 2007 Bernd Schemmer  All rights reserved.
# Use is subject to license terms.


# --------------------------------------------------------------------------------
# init the variables for the name of the script ...
#
BASENAME="` basename $0 `"

# ... and the config file
#
CONFIG_FILE="/etc/` echo ${BASENAME} | sed \"s/^[KS][0-9][0-9]//g\" `.conf"

# init some constants
#
TRUE=0
FALSE=1

# binary for creating and destroying the ramdisk
#
RAMDISKADM="/usr/sbin/ramdiskadm"

# set the PATH
#
PATH=/usr/sbin:/usr/bin ; export PATH

# init the variables


# -- the following variables can be in the config file to override the defaulst ----


#		--- configuration starts ----

# name of the ramdisk (see ramdiskadm(1m) for valid names)
#
RAMDISK_NAME="ramdisk1"

# ramdisk size (see ramdiskadm(1m) for known values)
#
RAMDISK_SIZE="40m"

# type of the filesystem for the ramdisk
# (default: "" which means ufs)
#
# Note: If RAMDISK_FILESYSTEM is not equal "" mkfs is used to create the filesystem.
# 	In this case you must add all necessary options for mkfs in the 
#	variable MOUNT_FILESYSTEM_OPTIONS
#	If RAMDISK_FILESYSTEM is equal "" newfs is used to create the filesystem
#
RAMDISK_FILESYSTEM=""

# options for creating the filesystem on the ramdisk
# (use "" for none)
#
RAMDISK_FILESYSTEM_OPTIONS=""

# mount point for the ramdisk
# (use "" for none)
#
# Note: The mountpoint will be created if it does not exist
#
RAMDISK_MOUNTPOINT="/ramdisk1"

# options for mounting the ramdisk
# (use "" for none)
#
RAMDISK_MOUNT_OPTIONS=""

# directory with the initial data for the ramdisk
# (use "" for none)
#
LOAD_DATA_DIR=""

# directory to which the data on the ramdisk should be copied before
# destroying the ramdisk. The directory will be created if it dos not exist
# (use "" for none)
#
# The script creates a logfile of the backup in the backup directory with the name
# 	{scriptname}.backup.log
#
BACKUP_DIR=""

# automatically backup the ramdisk before destroying it? (${TRUE} = yes, else not )
#
AUTO_BACKUP_DATA=${TRUE}

# Remove existing files and directories in the backup directory before copying the 
# data from the ramdisk? (${TRUE}= yes, else = no)
#
CLEANUP_BACKUP_DIR=${TRUE}

# log file for the backup
#
BACKUP_LOGFILE="/var/log/${BASENAME}.backup.log"

#		-- configuration ends ---

# ------------------ end of config variables for the config file ------------------


# --------------------------------------------------------------------------------
# sub routines

logMsg() {
	[ "${QUIET}"x  = ""x ] && echo "${BASENAME}: $*"
}

logInfo() {
	[ "${VERBOSE}"x != ""x ] && logMsg "$*" >&2
}
  
  
die() {
	THISRC=$1
	if [ $# -ne 0 ] ; then
		shift
		[ ${THISRC} -gt 1 ] && logMsg "ERROR: $* !" >&2 || logMsg "$*"
	fi
	exit ${THISRC}
}


BackupData() {				
	CUR_BACKUP_DIR="$1"
	
	if [ ! -d "${CUR_BACKUP_DIR}" ] ; then
		logInfo "Creating the backup directory \"${CUR_BACKUP_DIR}\" ..."
		mkdir -p "${CUR_BACKUP_DIR}" || \
			die 151 "Error creating the backup directory \"${CUR_BACKUP_DIR}\" "
	fi	
	if [ ${CLEANUP_BACKUP_DIR} = ${TRUE} ] ; then
		logInfo "Cleaning the directory \"${CUR_BACKUP_DIR}\" ..."
		rm -r ${CUR_BACKUP_DIR}\* 2>/dev/null
	fi
	logMsg "Backing up the data of the ramdisk to \"${CUR_BACKUP_DIR}\" ..."

	echo "
--------------------------------------------------------------------------------	
Backup of the ramdisk \"${RAMDISK_NAME}\" started at ` date ` 
" >>"${BACKUP_LOGFILE}"

	cd "${CUR_RAMDISK_MOUNPOINT}" && find . | cpio -pdumv "${CUR_BACKUP_DIR}"  >>"${BACKUP_LOGFILE}" 2>&1 
	CPIO_RC=$?
	
	echo "
Backup of the ramdisk \"${RAMDISK_NAME}\" ended at ` date ` ; the RC is ${CPIO_RC}
--------------------------------------------------------------------------------
" >>"${BACKUP_LOGFILE}"
	
		[ ${CPIO_RC} != 0 ] && die 155 "Error backing up the data from the ramdisk to \"${CUR_BACKUP_DIR}\" "
	return 0
}

viewRamdiskConfig() {
	logMsg "The name of the config file is \"${CONFIG_FILE}\" "
	if [ ! -r "${CONFIG_FILE}" ] ; then
		logMsg "The config file does not exist or is not readable"
	fi
	logMsg "The ramdisk to process is \"${RAMDISK_NAME}\" "
	logMsg "The size of the ramdisk is \"${RAMDISK_SIZE}\"" 
	logMsg "The filesystem for the ramdisk is \"${RAMDISK_FILESYSTEM}\" (\"\" = ufs)"
	logMsg "The options for creating the filesystem are \"${RAMDISK_FILESYSTEM_OPTIONS}\" "
	logMsg "The mount point for the ramdisk is \"${RAMDISK_MOUNTPOINT}\" "
	logMsg "The options for mounting the filesystem are \"${RAMDISK_MOUNT_OPTIONS}\" "
	logMsg "The directory for the initial data load is \"${LOAD_DATA_DIR}\" "
	logMsg "The backup directory is \"${BACKUP_DIR}\" "
	logMsg "Automatically backup the data on shutdown: ${AUTO_BACKUP_DATA} (0 = yes)"
	logMsg "Cleanup the backup directory before backing up the data: ${CLEANUP_BACKUP_DIR} (0 = yes)"
}

# --------------------------------------------------------------------------------
# main routine starts here

# check for a config file
#
logInfo "The configfile to use is \"${CONFIG_FILE}\" "

CONFIG_FILE_FOUND=${FALSE}
if [ "${CONFIG_FILE}"x != ""x ] ; then
	if [ -r "${CONFIG_FILE}" ] ; then
		logMsg "Config file \"${CONFIG_FILE}\" found - now reading it ..."
		. "${CONFIG_FILE}"
		CONFIG_FILE_FOUND=${TRUE}
	fi
fi

# init the return code of the script
#
MAINRC=200

# save the parameter
#
ACTION="$1"
[ $# -ne 0 ] && shift
ACTION_PARAMETER="$*"

# check the variables
#
[ "${RAMDISK_NAME}"x = ""x ] && die 10 "The variable RAMDISK_NAME is not defined"
[ "${RAMDISKADM}"x = ""x ] && die 15 "The variable RAMDISKADM is not defined"

[ ! -x "${RAMDISKADM}" ] && die 35 "Binary \"${RAMDISKADM}\" not found or not executable"

# check if the script is executed by root
#
 [ "` id | sed 's/uid=\([0-9]*\)(.*/\1/' `" != 0 ] && \
	die 45 "You must be root to execute this script"

# get the status of the ramdisk
#
RAMDISK_DEFINED=${FALSE}
${RAMDISKADM} |  grep -v grep | grep "/dev/ramdisk/${RAMDISK_NAME}" >/dev/null 2>/dev/null
if [ $? -eq 0 ] ; then
	RAMDISK_DEFINED=${TRUE}

# read the ramdisk configuration	
	CUR_RAMDISK_FSTYP="` fstyp "/dev/rramdisk/${RAMDISK_NAME}" 2>/dev/null `"
	if [ $? -eq 0 ] ; then
		set ` df -k "/dev/ramdisk/${RAMDISK_NAME}" 2>/dev/null | tail -1 ` >/dev/null
		CUR_RAMDISK_SIZE="$2"
		CUR_RAMDISK_USED="$3"
		CUR_RAMDISK_FREE="$4"
		CUR_RAMDISK_USED_REL="$5"
		CUR_RAMDISK_MOUNPOINT="$6"
	fi
fi

# now process the parameter
#
case ${ACTION} in

	start )
		logMsg "Creating the ramdisk \"${RAMDISK_NAME}\" ..."
		if [ ${RAMDISK_DEFINED} = ${TRUE} ] ; then
			logMsg "The ramdisk \"${RAMDISK_NAME}\" is already defined."
			MAINRC=0
		else
			logInfo "Creating the ramdisk \"${RAMDISK_NAME}\" with the size ${RAMDISK_SIZE} .."
			${RAMDISKADM} -a ${RAMDISK_NAME} ${RAMDISK_SIZE} || \
				die 105 "Error creating the ramdisk \"${RAMDISK_NAME}\" (RC=$?)"

			logInfo "Creating a filesystem on the ramdisk ..."
			
			if [ "${RAMDISK_FILESYSTEM}"x != ""x  ] ; then
				mkfs -F "${RAMDISK_FILESYSTEM}" ${RAMDISK_FILESYSTEM_OPTIONS} "/dev/rramdisk/${RAMDISK_NAME}" || \
					die 107 "Error creating the filesystem on the ramdisk (RC=$?)"
			else
				yes | newfs  ${RAMDISK_FILESYSTEM_OPTIONS} "/dev/rramdisk/${RAMDISK_NAME}" || \
					die 108 "Error creating the filesystem on the ramdisk (RC=$?)"
			fi
			
			logMsg "ramdisk \"${RAMDISK_NAME}\" created."
			if [ "${RAMDISK_MOUNTPOINT}"x != ""x ] ; then
				if [ ! -d "${RAMDISK_MOUNTPOINT}" ] ; then
					logInfo "Creating the mount point \"${RAMDISK_MOUNTPOINT}\" ..."
					mkdir -p "${RAMDISK_MOUNTPOINT}" || \
						die 109 "Error creating the mount point \"${RAMDISK_MOUNTPOINT}\" (RC=$?)"
				fi
				logInfo "Mounting the ramdisk to \"${RAMDISK_MOUNTPOINT}\" with the options \"${RAMDISK_MOUNT_OPTIONS}\" ..."
				[ "${RAMDISK_FILESYSTEM}"x != ""x ] && MOUNT_PARAMETER="-F ${RAMDISK_FILESYSTEM}" || MOUNT_PARAMETER=""
				mount ${MOUNT_PARAMETER} ${RAMDISK_MOUNT_OPTIONS} "/dev/ramdisk/${RAMDISK_NAME}" "${RAMDISK_MOUNTPOINT}" || \
					die 111 "Error mounting the ramdisk to \"${RAMDISK_MOUNTPOINT}\" (RC=$?)"
				if [ "${LOAD_DATA_DIR}"x != ""x -a  -d "${LOAD_DATA_DIR}/." ] ; then
					logInfo "Copying the data from the directory \"${LOAD_DATA_DIR}\" to the ramdisk ..."
					cd "${LOAD_DATA_DIR}" && find . | cpio -pdum "${RAMDISK_MOUNTPOINT}" || \
						die 113 "Error copying the data from the directory \"${LOAD_DATA_DIR}\" to the ramdisk"
				fi
				logMsg "ramdisk mounted at \"${RAMDISK_MOUNTPOINT}\"."
			fi
			MAINRC=0
		fi
		;;
	
	stop )
		logMsg "Destroying the ramdisk \"${RAMDISK_NAME}\" ..."
		if [ ${RAMDISK_DEFINED} = ${FALSE} ] ; then
			logMsg "The ramdisk \"${RAMDISK_NAME}\" is not defined."
			MAINRC=0
		else
			if [ "${CUR_RAMDISK_MOUNPOINT}"x = ""x ] ; then
				logMsg "The ramdisk is not mounted; will not backup the data from the ramdisk"
			else
				[ "${BACKUP_DIR}"x != ""x  -a ${AUTO_BACKUP_DATA} = ${TRUE} ] && \
					BackupData "${BACKUP_DIR}"
				cd /
				logInfo "Umounting the ramdisk ...."
				umount "${CUR_RAMDISK_MOUNPOINT}" || \
					die 157 "Error umounting the ramdisk (RC=$?)"
			fi
			logInfo "Destroying the ramdisk ..."
			${RAMDISKADM} -d "${RAMDISK_NAME}"  || \
				die 159 "Error destroying the ramdisk (RC=$?)"
			MAINRC=0
		fi
		;;

	status | viewconfig )		
		if [ "${ACTION}"x = "status"x ] ; then
			logMsg "Checking the status of the ramdisk \"${RAMDISK_NAME}\" ..."
		else
			logMsg "The configuration of the ramdisk \"${RAMDISK_NAME}\" is:"
			viewRamdiskConfig
		fi
		
		if [ ${RAMDISK_DEFINED} = ${TRUE} ] ; then
			logMsg "The ramdisk \"${RAMDISK_NAME}\" is already defined."
			[ "${CUR_RAMDISK_SIZE}"x != ""x ] && \
				logMsg "The size of the ramdisk is ${CUR_RAMDISK_SIZE} kb, ${CUR_RAMDISK_USED} kb are used (${CUR_RAMDISK_USED_REL}) and ${CUR_RAMDISK_FREE} kb are free" || \
				logMsg "There is no known filesystem on the ramdisk"
			[ "${CUR_RAMDISK_MOUNPOINT}"x != ""x ] && logMsg "The ramdisk is mounted to \"${CUR_RAMDISK_MOUNPOINT}\"; the filesystem is \"${CUR_RAMDISK_FSTYP}\" " || logMsg "The ramdisk is not mounted."
			MAINRC=0
		else
			logMsg "The ramdisk \"${RAMDISK_NAME}\" is not defined."
			MAINRC=1
		fi
		;;

	backup )
		logMsg "Backing up the data from the ramdisk \"${RAMDISK_NAME}\" ..."
		TEMP_BACKUP_DIR="${BACKUP_DIR}"
		[ "${ACTION_PARAMETER}"x != ""x ] && TEMP_BACKUP_DIR="${ACTION_PARAMETER}"
		if [ "${TEMP_BACKUP_DIR}"x != ""x ] ; then
			BackupData "${TEMP_BACKUP_DIR}"
		else
			die 165 "No backup directory defined for the ramdisk \"${RAMDISK_NAME}\""
		fi
		MAINRC=0
		;;
		
	help )
		grep "^##" "$0" | cut -c2- 
		MAINRC=5
		;;
		
	*  ) 
		logMsg "Usage: ${BASENAME} [help|start|stop|status|viewconfig|backup {backupdir}]"	
		MAINRC=5

esac	

exit ${MAINRC}

