#!/bin/bash help () { { echo "Usage: iocsh [options] [files] [macro=value] ..." echo "Start an EPICS iocsh and load files" echo echo "Options:" echo " -?, -h, --help Show this page and exit." echo " -v, --version Show version and exit." echo " -win Run Windows softIOC via WINE." echo " -32 Force 32 bit version (on 64 bit systems)." echo " -x[.z[.y]] Select EPICS base version x.z.y (e.g. 3.14.8, 3.15, 7)." echo " -d, --debug Run IOC with gdb." echo " -dv Run IOC with valgrind." echo " -dp Run IOC with perf record." echo " -c 'cmd args' Ioc shell command." echo " -s 'prog m=v' Sequencer program (and arguments), run with 'seq'." echo " This forces an 'iocInit' before running the program." echo " -r module[,ver] Modue (optionally with version) loaded via 'require'." echo " -n name Name of the IOC, used for prompt and \${IOC} variable." echo " Default: dirname if parent dir is \"ioc\" otherwise hostname." echo " @file More arguments are read from file." echo echo "Supported filetypes:" echo " *.db, *.dbt, *.template loaded via 'dbLoadRecords'" echo " *.subs, *.subst loaded via 'dbLoadTemplate'" echo " *.dbd loaded via 'dbLoadDatabase'" echo " *.so loaded via 'dlload' (or 'ld' before 3.14.12)" echo "All other files are executed as startup scripts by the EPICS shell." echo "After a file you can specify substitutions like m1=v1 m2=v1 for that file." echo echo "Examples:" echo " iocsh st.cmd" echo " iocsh my_database.template P=XY M=3" echo " iocsh -r my_module,version -c 'initModule()'" echo " iocsh -3.15.4 -dp st.cmd" echo " iocsh -c 'var requireDebug 1' st.cmd" } >&2 exit } version () { { echo "iocsh by Dirk Zimoch" } >&2 exit } # realpath and readlink are not available on all systems, let's try what works... rp() { ( realpath $1 || readlink -f $1 || readlink $1 || (cd -P $1 && echo $PWD) || (x=$(\ls -ld $1) && echo ${x##* }) || echo $1 ) 2>/dev/null } # if EPICS_HOST_ARCH is not set guess it if [ -z "$EPICS_HOST_ARCH" ] then EPICS_HOST_ARCH=$(basename $(dirname $(rp $(which caRepeater)))) if [ -n "$EPICS_HOST_ARCH" ] then echo "Guessing EPICS_HOST_ARCH=$EPICS_HOST_ARCH" >&2 else echo "EPICS_HOST_ARCH is not set" >&2 exit 1 fi fi if read BASE < EPICSVERSION || read BASE < cfg/EPICSVERSION then unset EPICS_BASE fi 2> /dev/null while true do case $1 in ( -win ) EPICS_HOST_ARCH=windows-x64 ;; ( -32 ) EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64} ;; ( -[1-9]* ) unset EPICS_BASE BASE=${1#-} ;; ( * ) break ;; esac shift done # Either EPICS or EPICS_BASE should be set to the install directory if [ -z "$EPICS_BASE" ] then if [ -z "$EPICS" ] then # look for some standard install directories for EPICS in /usr/local/epics /opt/epics /epics do if [ -d $EPICS ] then break fi done if [ ! -d "$EPICS" ] then EPICS=$(dirname $(dirname $(dirname $(dirname $(ldd $(which caRepeater) | awk '/libca/ {print $3}'))))) echo "Guessing EPICS=$EPICS" fi if [ ! -d "$EPICS" ] then echo "Cannot find EPICS installation directory." >&2 echo "Try setting EPICS environment variable." >&2 exit 1 fi fi if [ -z "$BASE" ] then EPICS_BASE=$(\ls -1vrd $EPICS/base/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1) else # find highest (requested) EPICS version that supports our architecture (or its 32 bit version) EPICS_BASE=$(\ls -1vrd $EPICS/base-$BASE/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1) if [ -z "$EPICS_BASE" ] then unset FILTER if [ -f $EPICS/ignore ] then FILTER=$(echo "|grep -Ev '/base-($(sed < $EPICS/ignore -zr 's/\./\\./g;s/[ \t\r\n]+/|/g;s/\|$//'))/'") fi EPICS_BASE=$(eval \ls -1vrd $EPICS/base-$BASE*/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null $FILTER | head -n1) fi fi if [ -z "$EPICS_BASE" ] then if [ -z "$(\ls -1vrd $EPICS/base-$BASE*/ 2>/dev/null)" ] then echo "No EPICS $BASE installed." >&2 exit 1 fi echo EPICS $BASE not available for EPICS_HOST_ARCH=$EPICS_HOST_ARCH. >&2 exit 1 fi # maybe we need to change from 64 bit to 32 bit if [ $EPICS_HOST_ARCH != ${EPICS_BASE#*/bin/} ] then EPICS_HOST_ARCH=${EPICS_BASE#*/bin/} echo "No 64 bit version in ${EPICS_BASE%bin*}." >&2 echo "Switching to 32 bit version $EPICS_HOST_ARCH." >&2 fi EPICS_BASE=$(rp ${EPICS_BASE%bin*}) fi if [ ! -d $EPICS_BASE ] then echo "Cannot find EPICS_BASE directory." >&2 echo "Try setting EPICS_BASE environment variable to full path" >&2 exit 1 fi case $(uname) in ( Darwin ) LIBPREFIX=lib; LIBPOSTFIX=.dylib ;; ( * ) LIBPREFIX=lib; LIBPOSTFIX=.so; EXEPOSTFIX= # assume we need to run Windows softIoc on UNIX via WINE if [[ "$EPICS_HOST_ARCH" == win* ]]; then LIBPREFIX=; LIBPOSTFIX=.dll; EXEPOSTFIX=.exe fi ;; esac # Get actual EPICS revision if [ -f $EPICS_BASE/configure/CONFIG_BASE_VERSION ] then eval $(awk -F '[ \t]*=[ \t]*' ' /^[ \t]*EPICS_VERSION[ \t]*=/ {v=$2} /^[ \t]*EPICS_REVISION[ \t]*=/ {r=$2} /^[ \t]*EPICS_MODIFICATION[ \t]*=/ {m=$2+0} END {print "XBASE="v"."r"."m";BASECODE="v*10000+r*100+m} ' < $EPICS_BASE/configure/CONFIG_BASE_VERSION) elif [ -f $EPICS_BASE/lib/$EPICS_HOST_ARCH/${LIBPREFIX}Com$LIBPOSTFIX ] then eval $(strings $EPICS_BASE/lib/$EPICS_HOST_ARCH/${LIBPREFIX}Com$LIBPOSTFIX | awk -F'[.R-]' ' /EPICS R[0-9]/ {print "XBASE="$2"."$3"."$4";BASECODE="$2*10000+$3*100+$4 }') else echo "Cannot guess EPICS base version." >&2 exit 1; fi if [ ${BASE#$XBASE} = $BASE ] then BASE=$XBASE fi # IOC name derives from hostname # (trailing possible '\r' under cygwin) IOC=$(hostname|tr -d '\r') # trailing possible domain name IOC=${IOC%%.*} # or get IOC name from start directory following PSI convention if [ $(basename $(dirname $PWD)) = "ioc" ] then IOC=$(basename $PWD) fi export IOC # Check for 64 bit versions, default to 32 bit if [ ! -d $EPICS_BASE/lib/${EPICS_HOST_ARCH} -a -d $EPICS_BASE/lib/${EPICS_HOST_ARCH%_64} ] then echo "No 64 bit EPICS installation found. Defaulting to 32 bit" >&2 EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64} fi export EPICS_HOST_ARCH # setup search path for require ODIR=O.${BASE}_$EPICS_HOST_ARCH EPICS_DRIVER_PATH=.:bin/$EPICS_HOST_ARCH:bin:snl:../snl:$ODIR:src/$ODIR:snl/$ODIR:../snl/$ODIR:${EPICS_DRIVER_PATH#:} #Special PSI: find installation base for libs from working directory D=$(rp $PWD) I=${D%/iocBoot/*} if [ $I != $D ] then INSTBASE=$I fi EPICS_DRIVER_PATH=${EPICS_DRIVER_PATH%:}:${EPICS_MODULES:=/ioc/modules}:${INSTBASE:=/work}/iocBoot/R$BASE/$EPICS_HOST_ARCH export INSTBASE # convert for win32-x86 arch if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH ] then EPICS_DRIVER_PATH=$(cygpath -wp $EPICS_DRIVER_PATH) DBD=$(cygpath -wp $DBD) fi if [ ${EPICS_HOST_ARCH#cygwin-} != $EPICS_HOST_ARCH ] then DBD=$(cygpath -wp $DBD) fi export EPICS_DRIVER_PATH loadFiles () { while [ "$#" -gt 0 ] do file=$1 case $file in ( -32 ) echo "-32 option must be set earlier" >&2 exit 1 ;; ( -[1-9]* ) echo "EPICS version $file option must be set earlier" >&2 exit 1 ;; ( -h | "-?" | -help | --help ) help ;; ( -v | -ver | --ver | -version | --version ) version ;; ( @* ) loadFiles $(cat ${file#@}) ;; ( -d | -dg | --debug ) LOADER="gdb --eval-command run --args $LOADER" ;; ( -dv ) LOADER="valgrind --leak-check=full $LOADER" ;; ( -dp ) LOADER="perf record $LOADER" ;; ( -c ) shift case $1 in ( seq* ) if [ "$init" != NO ] then echo "iocInit" init=NO fi ;; ( iocInit ) init=NO ;; esac echo $1 ;; ( -s ) shift if [ "$init" != NO ] then echo "iocInit" init=NO fi echo "seq $1" ;; ( -r ) shift echo "require $1" ;; ( -n ) shift IOC="$1" ;; ( -* ) echo "Unknown option $1" >&2 echo "Try: $(basename $0) --help" >&2 exit 1 ;; ( *$LIBPOSTFIX ) if [ "$BASECODE" -ge 31412 ] then echo "dlload \"$file\"" else echo "ld \"$file\"" fi ;; ( *=* ) echo -n $file | awk -F '=' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}' ;; ( * ) subst="" while [ "$#" -gt 1 ] do case $2 in ( *=* ) subst="$subst,$2"; shift ;; ( * ) break ;; esac done subst=${subst#,} case $file in ( *.db | *.template) echo "dbLoadRecords '$file','$subst'" ;; ( *.subs | *.subst ) echo "dbLoadTemplate '$file','$subst'" ;; ( *.dbd ) # some dbd files must be loaded before main to take effect echo "dbLoadDatabase '$file','$DBD','$subst'" ;; ( * ) if [ "$BASECODE" -ge 31500 ] then echo "iocshLoad '$file','$subst'" else echo -n $subst | awk -F '=' -v 'RS=,' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}' echo "< '$file'" fi if grep -q iocInit $file; then init=NO; fi ;; esac ;; esac shift done } startup=/tmp/iocsh.startup.$$ # clean up and kill the softIoc when killed by any signal cleanup() { trap EXIT rm -f $startup stty sane 2> /dev/null echo } trap "kill -s SIGTERM 0; cleanup" EXIT { echo "# date=\"$(date)\"" echo "# user=\"${USER:-$(whoami)}\"" for var in IOC PWD BASE EPICS_HOST_ARCH SHELLBOX EPICS_CA_ADDR_LIST EPICS_DRIVER_PATH do echo "# $var=\"${!var}\"" done if [ -d $EPICS_MODULES/${REQUIRE:=require} ] then # new module pool model REQUIRE_LIB=$(ls -1rv $EPICS_MODULES/$REQUIRE/${REQUIRE_VERSION:-*.*.*}/R$BASE/lib/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX | head -n 1) REQUIRE_DBD=${REQUIRE_LIB%/lib/*}/dbd/$REQUIRE.dbd else # old driver pool model REQUIRE=misc${REQUIRE_VERSION:+-}$REQUIRE_VERSION REQUIRE_LIB=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX REQUIRE_DBD=$INSTBASE/iocBoot/R$BASE/dbd/$REQUIRE.dbd fi if [ "$BASECODE" -ge 31412 ] then if [ -x $EPICS_BASE/bin/$EPICS_HOST_ARCH/softIocPVA$EXEPOSTFIX ] then EXE=$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIocPVA$EXEPOSTFIX ARGS="-D $EPICS_BASE/dbd/softIocPVA.dbd" if [[ $EXEPOSTFIX == ".exe" ]]; then echo "dlload $EPICS_BASE/bin/$EPICS_HOST_ARCH/${LIBPREFIX}nt$LIBPOSTFIX" echo "dlload $EPICS_BASE/bin/$EPICS_HOST_ARCH/${LIBPREFIX}pvDatabase$LIBPOSTFIX" else echo "dlload $EPICS_BASE/lib/$EPICS_HOST_ARCH/${LIBPREFIX}nt$LIBPOSTFIX" echo "dlload $EPICS_BASE/lib/$EPICS_HOST_ARCH/${LIBPREFIX}pvDatabase$LIBPOSTFIX" fi else EXE=$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc$EXEPOSTFIX ARGS="-D $EPICS_BASE/dbd/softIoc.dbd" fi LDCMD="dlload" else # get rid of the compiled-in rpath because at PSI that is a link pointing to current EPICS version. LOADER="$LOADER /lib/ld-linux.so.2" LOADERARGS="--library-path $EPICS_BASE/lib/$EPICS_HOST_ARCH --inhibit-rpath ''" APP=ioc EXE=$EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH/$APP DBD=$EPICS_EXTENSIONS/dbd LDCMD="ld" echo "dbLoadDatabase \"$APP.dbd\",\"$DBD\"" echo "${APP}_registerRecordDeviceDriver(pdbbase)" fi # use WINE to run softIo, and convert EPICS_DRIVER_PATH to windows format if [[ $EXEPOSTFIX == ".exe" ]] then LOADER="wine" EPICS_DRIVER_PATH_WIN= while read -d ':' p; do EPICS_DRIVER_PATH_WIN="$EPICS_DRIVER_PATH_WIN;${p/#\//z:/}" done <<< "$EPICS_DRIVER_PATH:" EPICS_DRIVER_PATH=${EPICS_DRIVER_PATH_WIN#;} fi if [ ! -x $EXE ]; then echo "$EXE not found or not executable." >&2 exit 1 fi if [ ! -f "$REQUIRE_LIB" ] then echo "Library $REQUIRE_LIB not found." >&2 echo "Command 'require' is not available." >&2 else echo "$LDCMD $REQUIRE_LIB" echo "dbLoadDatabase $REQUIRE_DBD" echo "${REQUIRE%-*}_registerRecordDeviceDriver" echo "require misc $MISC_VERSION" fi loadFiles "$@" if [ "$init" != NO ] then echo "iocInit" fi echo 'epicsEnvSet IOCSH_PS1,"${IOC}> "' } > $startup # convert startup script file name for win32-x86 if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH ] then startup=`cygpath -w $startup` fi if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH -o ${EPICS_HOST_ARCH#cygwin-} != $EPICS_HOST_ARCH ] then PATH=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH:$EPIC_BASE/bin/$EPICS_HOST_ARCH:$EPICS_BASE/../seq/bin/$EPICS_HOST_ARCH:$PATH fi echo $EXE $ARGS $startup #enable core dumps ulimit -c unlimited eval "$LOADER $LOADERARGS $EXE" $ARGS "$startup" 2>&1 STATUS=$? cleanup exit $STATUS