#!/bin/sh # /usr/bin/dphys-config - install/update config files and trigger commands # author Neil Franklin, last modification 2010.01.21 # copyright ETH Zuerich Physics Departement, # use under either modified/non-advertising BSD or GPL license # this script is intended to be run as an normal user ### ------ configuration for this site # first CONF_* various site or user dependant user config variables # then DEBUG_* various debugging settings # last SYS_* various system internal values # some of these are overridable by command line options # --- CONF_* various site or subnet dependant user config variables # base directory for temporary files # some prefer /var/tmp for size and safety, works everywhere # some prefer /tmp for speed or boot-time auto-delete CONF_TMP_DIR=/var/tmp # base directory for config files on server # set to something that will produce an error message without site config CONF_BASEURL=http://not-configured-server.example.net/not/configured/directory # configuration name to use, usually host name, unless chroot or vhost or test CONF_CONFNAME=`hostname` # only process lines matching this grep regexp, default do all CONF_LINEFILTER=".*" # log what we have done CONF_LOG_DONE=yes # --- DEBUG_*, various debugging settings # these can be set to "yes" by -D option, followed by name without DEBUG_ # such as like this: dphys-config -D PRINT_STEP ... # set this to sleep after displaying each steps header #DEBUG_SLEEP=yes # set this to output debug state info after each step #DEBUG_PRINT_STEP=yes # set this to wait after each debug state info #DEBUG_WAIT_STEP=yes # set this to have debug dry runs install not in /, avoid killing system #DEBUG_INSTALL_BASE=${CONF_TMP_DIR}/dphys-config-debug-install # set this to leave temporary directories undeleted after working #DEBUG_LEAVE_TEMPDIRS=yes # set this to leave wget output file that was parsed # this requires also DEBUG_LEAVE_TEMPDIRS, else file gone with directories #DEBUG_LEAVE_WGET_OUTPUT=yes # --- SYS_*, various system internal values # name of config file list file on server SYS_CONFLIST_NAME=dphys-config.list # where we want to do all the download/merge/diff-test stuff # this must be a directory that root can access (not a root-squash NFS mount) SYS_WORKDIR=${CONF_TMP_DIR}/dphys-config-$$-work ### ------ actual implementation from here on # no user settings any more below this point # --- get ready to work # report as many called program errors as possible set -e # sanitise this place, else some commands may fail # must be before any commands are executed, incl config/input processing # /usr/local added for FreeBSD, because their ports end up in there PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin export PATH # also sanitise this, else wget output parsing fails on some systems LANG=C export LANG # --- tidy up some commands, make systematic # stuff that goes wrong, not expected by user, not in data output, use >&2 # so also with $0 in case this script was called by an other script # something within the system that user does not expect, give up CMD_FATAL="echo $0: FATAL:" # something from users input, user will correct this and continue CMD_ERROR="echo $0: ERROR:" # something we can continue with, but may be wrong, and user may not suspect it CMD_WARNING="echo $0: WARNING:" # something most likely not wrong, but tell user for the odd case it is wrong CMD_NOTE="echo $0: NOTE:" # normal stuff users expect, so to stdout as normal output, no $0, no marking CMD_INFO="echo" # stuff users asked for, so add to stdout as normal output, no $0, but mark it CMD_DEBUG="echo DEBUG:" # and also put out some permanent messages, in case running as cron/init.d job BASENAME="`basename "$0"`" # something within the system that user does not expect, give up CMD_LOG_FATAL="logger -t ${BASENAME} -p user.error FATAL:" # something from users input, user can possibly correct this and continue CMD_LOG_ERROR="logger -t ${BASENAME} -p user.error ERROR:" # something we can continue with, but may be wrong, and user may not suspect it CMD_LOG_WARNING="logger -t ${BASENAME} -p user.warning WARNING:" # something most likely not wrong, but tell user for the odd case it is wrong CMD_LOG_NOTE="logger -t ${BASENAME} -p user.notice NOTE:" # normal stuff users expect, so no marking CMD_LOG_INFO="logger -t ${BASENAME} -p user.info" # stuff users asked for, but mark it as special CMD_LOG_DEBUG="logger -t ${BASENAME} -p user.debug DEBUG:" # other stuff we may want to use CMD_SLEEP="sleep 2" CMD_WAIT="read -p ---DEBUG-wait-after-step--- dummy" # set config option controllable stuff CMD_INFO_PRINT="${CMD_INFO}" CMD_VERBOSE_PRINT=true # set debug option controllable stuff if [ "${DEBUG_SLEEP}" = yes ] ; then CMD_DEBUG_SLEEP="${CMD_SLEEP}" else CMD_DEBUG_SLEEP=true fi if [ "${DEBUG_PRINT_STEP}" = yes ] ; then CMD_DEBUG_PRINT="${CMD_DEBUG}" else CMD_DEBUG_PRINT=true fi if [ "${DEBUG_WAIT_STEP}" = yes ] ; then CMD_DEBUG_WAIT="${CMD_WAIT}" else CMD_DEBUG_WAIT=true fi # --- config file stuff # what we are, program and package name NAME=dphys-config PNAME=dphys-config # check user config file(s), let user override settings if [ -e /etc/"${PNAME}" ] ; then . /etc/"${PNAME}" fi if [ -e ~/."${PNAME}" ] ; then . ~/."${PNAME}" fi # --- control variable output # set config option controllable stuff if [ "${CONF_LOG_DONE}" = yes ] ; then CMD_LOG_IF_DONE="${CMD_LOG_INFO}" else CMD_LOG_IF_DONE=true fi # --- parse command line # so long next parameter is a set of options (= begins with an - character) while [ "`echo "$1" | cut -c 1`" = - ] ; do # extract options from parameter (cut off the "-") OPTS="`echo "$1" | cut -c 2-`" shift 1 # so long still unprocessed option characters while [ "${OPTS}" != "" ] ; do # first option to process OPT="`echo "${OPTS}" | cut -c 1`" # and rest of options for later OPTS="`echo "${OPTS}" | cut -c 2-`" case "${OPT}" in c) # configname: use this set of files config instead of hostname set CONF_CONFNAME="$1" shift 1 ;; f) # filter: only process lines matching this grep regexp CONF_LINEFILTER="$1" shift 1 ;; q) # quiet: don't inform user what we are doing CMD_INFO_PRINT=true ;; v) # verbose: detailed inform user what we are doing CMD_VERBOSE_PRINT="${CMD_INFO}" ;; D) # Debug: set an debug option to yes eval "DEBUG_$1"=yes if [ "DEBUG_$1" = DEBUG_SLEEP ] ; then CMD_DEBUG_SLEEP="${CMD_SLEEP}" fi if [ "DEBUG_$1" = DEBUG_PRINT_STEP ] ; then CMD_DEBUG_PRINT="${CMD_DEBUG}" fi if [ "DEBUG_$1" = DEBUG_WAIT_STEP ] ; then CMD_DEBUG_WAIT="${CMD_WAIT}" fi shift 1 ;; h) # help: give out help how this script can be used cat << END-HELP-TEXT Usage is: $0 [options] options: -c configname: use this set of config files instead of hostname set -f filter filter: only process lines matching this grep regexp -q quiet: don't give user an running report of what we are doing -v verbose: give user detailed running report -D Debug: activate an debug option, see source for operation -h help: output this text, and then abort operation mode none or "none" or "init" (run directly) or "cron" (delay 0..59min) END-HELP-TEXT exit 0 ;; *) # not one of our recognized options ${CMD_ERROR} "unknown option: ${OPT}" >&2 ${CMD_INFO} >&2 # call self with -h to display help "$0" -h >&2 exit 1 ;; esac done done # do not use elif [ "" = "" -o "" = "" ] ; as some shells test fails on this if [ "$1" = "" ] ; then MODE=none elif ( [ "$1" = cron ] || [ "$1" = init ] ) ; then MODE="$1" else ${CMD_ERROR} "unknown mode (or even multiple) given: $1" >&2 ${CMD_INFO} >&2 # call self with -h to display help "$0" -h >&2 exit 1 fi ${CMD_DEBUG_PRINT} "options parsed" ${CMD_DEBUG_WAIT} # --- spread config file server load # avoid load peak on the file server from over 100 machines at same time # delay cron jobs of various machines up to 1 hour, do not affect init jobs # therefore we random wait for 0..3599 seconds before working if [ "${MODE}" = cron ] ; then DELAY=`perl -e 'print int(rand(3600)), "\n"'` ${CMD_INFO} "waiting ${DELAY}s (0-3599s) for our time slot ..." sleep "${DELAY}" ${CMD_INFO} "waiting done, continuing ..." fi # --- wget handle unreliable network, DNS faillures or DNS not reachable broken_net_wget_or_die () { # our many thanks to the net admins for their unreliable net # and the resulting unreachable DNS, for client->host and host client auth # with 150 hosts times 30 files, is produces up to 5 failed hosts per run # later this subroutine also got used to "fix" the missing file: URLs in wget local URL URL="$1" local DIR DIR="$2" ${CMD_DEBUG_PRINT} "URL=${URL} DIR=${DIR}" ${CMD_DEBUG_WAIT} # support for file: URLs which wget does not know about if [ "`echo "${URL}" | cut -c 1-5`" = "file:" ] ; then local FILE FILE="`echo "${URL}" | cut -c 6-`" # URL ends in /, this is directory existance test, generate fake index.html if [ "`echo "${FILE}" | rev | cut -c 1`" = "/" ] ; then # we later only test for file existance of index.html, so non-HTML is OK (cd "${DIR}"; ls -al "${FILE}" > index.html 2> /dev/null) else local FILEBASE FILEBASE="`basename "${FILE}"`" (cd "${DIR}"; cp -p "${FILE}" "${FILEBASE}") fi return 0 fi # check if we can work if ! which wget > /dev/null ; then ${CMD_FATAL} "failed to find wget, can not work, aborting ..." >&2 ${CMD_LOG_FATAL} "failed to find wget, can not work, aborting ..." exit 1 fi local TRY for TRY in 1 2 3 ; do # wget gives back 1 for both DNS failure (bad) and simple 404 (expected) # also we need to expect 403 if we managed to reach the server # but it then failed to resolve our IP address # and also for any other access violation problems # such as admin giving wrong file permissions # better only continue if we got 200 (got file) or 404 (no file) # this fails safe, can never break config files # uses an temp file for wget output, split on that to see which happened local WGET_OUTPUT WGET_OUTPUT="${SYS_WORKDIR}/@wget.output" # -N so we do not re-fetch unchanged files, important when large files # -N and -O do not like each other, so use an (cd ; wget) (cd "${DIR}"; wget -N -o "${WGET_OUTPUT}" "${URL}" || true) local DNS_STAT DNS_STAT="`grep '^Resolving .*... failed: Host not found.$' \ "${WGET_OUTPUT}" | tr -d ' '`" # the 6th field is the numeric HTTP return code # do not depend on 7th field, textual code, can vary depending on server local HTTP_STAT HTTP_STAT="`grep '^HTTP request sent, awaiting response... ' \ "${WGET_OUTPUT}" | tail -n 1 | cut -f 6 -d ' '`" if [ "${DEBUG_LEAVE_WGET_OUTPUT}" != yes ] ; then # offer to leave this to investigate bugs rm "${WGET_OUTPUT}" fi ${CMD_DEBUG_PRINT} "DNS_STAT=${DNS_STAT} HTTP_STAT=${HTTP_STAT}" # 2 similar "elif" as some shells built-in [ command fail on -o operator if [ "${DNS_STAT}" != "" ] ; then ${CMD_WARNING} "failed try ${TRY} to resolve server for URL ${URL}" >&2 ${CMD_LOG_WARNING} "failed try ${TRY} to resolve server for URL ${URL}" elif ( [ "${HTTP_STAT}" = 200 ] || [ "${HTTP_STAT}" = 404 ] ) ; then # we managed to get a file, or there is no file, both are OK for us return 0 else ${CMD_WARNING} "failed try ${TRY} to retrieve file from" \ "URL ${URL}, with HTTP status ${HTTP_STAT}" >&2 ${CMD_LOG_WARNING} "failed try ${TRY} to retrieve file from" \ "URL ${URL}, with HTTP status ${HTTP_STAT}" fi done ${CMD_FATAL} "failed entirely to retrieve file from URL ${URL}," \ "with HTTP status ${HTTP_STAT}, aborting ..." >&2 ${CMD_LOG_FATAL} "failed entirely to retrieve file from URL ${URL}," \ "with HTTP status ${HTTP_STAT}, aborting ..." exit 1 } # --- check for neccessary configuration done by user ${CMD_INFO_PRINT} "checking configuration for user setting CONF_BASEURL ..." ${CMD_DEBUG_SLEEP} if `echo "${CONF_BASEURL}" | \ grep -q 'http://not-configured-server.example.net/'` ; then # CONF_BASEURL still has not configured setting, so we can not work with it ${CMD_ERROR} "user config variable CONF_BASEURL has not been set to an" \ "valid base directory for config files on your server, aborting ..." >&2 ${CMD_LOG_ERROR} "user config variable CONF_BASEURL has not been set to an" \ "valid base directory for config files on your server, aborting ..." exit 1 fi # --- check for config file server ${CMD_INFO_PRINT} "checking file server for configuration ${CONF_CONFNAME} ..." ${CMD_DEBUG_SLEEP} # use mktemp if users system has it installed # some systems need $$ PIDs as no utility is present, works everywhere # others can use mktemp, preferable as this prevents race conditions # in this case just add mktemp on to end of name with $$ in it, use both if [ "`which mktemp 2> /dev/null`" != "" ]; then SYS_WORKDIR="`mktemp -d "${SYS_WORKDIR}.XXXXXXXXXX"`" fi # make work space for this and other temporary files mkdir -p "${SYS_WORKDIR}" # prevent access to just-wget-ed temporary files with wrong permissions chmod 700 "${SYS_WORKDIR}" # check for valid server URL, read directory with all hosts in it rm -f "${SYS_WORKDIR}/index.html" broken_net_wget_or_die "${CONF_BASEURL}/" "${SYS_WORKDIR}" # do not rely on the contents of index.html being actual HTML # can be ls -al output in case of file:/ URLs # so only use this file for existance test, no parsing its content if [ ! -f "${SYS_WORKDIR}/index.html" ] ; then ${CMD_FATAL} "failed to find server dir ${CONF_BASEURL}, aborting ..." >&2 ${CMD_LOG_FATAL} "failed to find server dir ${CONF_BASEURL}, aborting ..." exit 1 fi # server subdirectory for this hosts configuration CONFHOST_URL="${CONF_BASEURL}/${CONF_CONFNAME}" # check for valid host name, read directory of this host rm -f "${SYS_WORKDIR}/index.html" broken_net_wget_or_die "${CONFHOST_URL}/" "${SYS_WORKDIR}" if [ ! -f "${SYS_WORKDIR}/index.html" ] ; then ${CMD_FATAL} "failed to find host dir for ${CONFHOST_URL}, aborting ..." >&2 ${CMD_LOG_FATAL} "failed to find host dir for ${CONFHOST_URL}, aborting ..." exit 1 fi ${CMD_DEBUG_PRINT} "`ls -al "${SYS_WORKDIR}/index.html"`" ${CMD_DEBUG_WAIT} # --- fetch config file from server to spool and preprocess it fetch_and_preprocess_config_file () { local URL URL="$1" local WORK WORK="$2" ${CMD_DEBUG_PRINT} "URL=${URL} WORK=${WORK}" ${CMD_DEBUG_WAIT} # ensure first that work dir is there, in case config file in an new subdir local DIR DIR="`dirname "${WORK}"`" mkdir -p "${DIR}" # get the file broken_net_wget_or_die "${URL}" "${DIR}" if [ ! -f "${WORK}" ] ; then # config file not fetched, so we can not work on it ${CMD_ERROR} "no config file ${URL} fetched to ${WORK}" >&2 ${CMD_LOG_ERROR} "no config file ${URL} fetched to ${WORK}" exit 1 fi ${CMD_DEBUG_PRINT} "`ls -al "${WORK}"`" ${CMD_DEBUG_WAIT} # preprocessor stuff # header calls for preprocessing, with syntax similar to the #! shell trigger local HEAD HEAD="`head -n 1 "${WORK}"`" if `echo "${HEAD}" | grep -q '^#@dphys-config-preprocess'` ; then # use rest of file, purged of header, which is never part of output sed -ne '2,$p' "${WORK}" > "${WORK}.temp" mv "${WORK}.temp" "${WORK}" # what preprocessing actions the user wants, and in which row to run them # extract from HEAD format: #@dphys-config-preprocess [preprocessor...] # add an space after "${HEAD}" to circumvent cut -f 2- -d ' ' brokenness # if no preprocessor was listed by user, and so possibly no space in line local PREPROCS PREPROCS="`echo "${HEAD} " | cut -f 2- -d ' ' | tr -c -d 'a-z '`" ${CMD_DEBUG_PRINT} "HEAD=${HEAD} PREPROCS=${PREPROCS}" # no "${PREPROCS}", else intervening and trailing spaces end up in PREPROC # and that would then really confuse case # split junk will simply feed nonsense to case, and get caught by *) local PREPROC for PREPROC in ${PREPROCS} ; do case "${PREPROC}" in backtick) # replace backtick expressions with stdout generated by running them ${CMD_VERBOSE_PRINT} "preprocessing for backtick ..." ${CMD_DEBUG_SLEEP} local LINE # IFS= to avoid read doing separator processing (mangles spaces) # and -r to prevent \ interpretation (mangles line contents) cat "${WORK}" | while IFS= read -r LINE ; do # syntax taken from the shell backtick, which is also used here # may have multiple substitutions on one line, so use while loop while echo "${LINE}" | grep -q '`' ; do # the '\` here need the \ because of the `` subshell local FRONT FRONT="`echo "${LINE}" | cut -f 1 -d '\`' `" local TICK TICK="`echo "${LINE}" | cut -f 2 -d '\`' `" local REST REST="`echo "${LINE}" | cut -f 3- -d '\`' `" # prevent non zero return from user given command from killing us set +e local SUBST SUBST="`"${TICK}"`" RETVAL="$?" set -e if [ "${RETVAL}" != 0 ] ; then ${CMD_WARNING} "backtick returned non zero: ${RETVAL}" >&2 ${CMD_LOG_WARNING} "backtick returned non zero: ${RETVAL}" fi LINE="${FRONT}${SUBST}${REST}" done echo "${LINE}" >> "${WORK}.temp" done mv "${WORK}.temp" "${WORK}" ;; if) # if condition met, leave rest of line, else delete it ${CMD_VERBOSE_PRINT} "preprocessing for if ..." ${CMD_DEBUG_SLEEP} local LINE cat "${WORK}" | while IFS= read -r LINE ; do # extract from LINE format: #@if condition ; line-to-leave if echo "${LINE}" | grep -q '^#@if ' ; then # end of command part with ; analog to shell if/while/for if ! echo "${LINE}" | grep -q ';' ; then ${CMD_ERROR} "#@if config line \"${LINE}\" has no \";\"" >&2 ${CMD_LOG_ERROR} "#@if config line \"${LINE}\" has no \";\"" exit 1 fi local COND COND="`echo "${LINE}" | cut -f 1 -d ';'`" LINE="`echo "${LINE}" | cut -f 2- -d ';'`" # purge command, which is not part of condition COND="`echo "${COND}" | cut -f 2- -d ' '`" # strip leading spaces after ; from line while [ "`echo "${LINE}" | cut -c 1`" = " " ] ; do LINE="`echo "${LINE}" | cut -c 2-`" done set +e if sh -c "${COND}" ; then echo "${LINE}" >> "${WORK}.temp" fi set -e else echo "${LINE}" >> "${WORK}.temp" fi done mv "${WORK}.temp" "${WORK}" ;; include) # replace line with contents of section file named on rest of line ${CMD_VERBOSE_PRINT} "preprocessing for include ..." ${CMD_DEBUG_SLEEP} local LINE cat "${WORK}" | while IFS= read -r LINE ; do if echo "${LINE}" | grep -q '^#@include ' ; then local SECTION SECTION="`echo "${LINE}" | cut -f 2 -d ' '`" broken_net_wget_or_die "${URL}.${SECTION}" "${DIR}" if [ -f "${WORK}.${SECTION}" ] ; then ${CMD_DEBUG_PRINT} "`ls -al "${WORK}.${SECTION}"`" fi ${CMD_DEBUG_WAIT} if [ -f "${WORK}.${SECTION}" ] ; then cat "${WORK}.${SECTION}" >> "${WORK}.temp" #else # NOTE: this output is more annoying than useful, disable it #${CMD_NOTE} "no config file section ${URL}.${SECTION} ..." >&2 #${CMD_LOG_NOTE} "no config file section ${URL}.${SECTION} ..." fi else echo "${LINE}" >> "${WORK}.temp" fi done mv "${WORK}.temp" "${WORK}" ;; *) ${CMD_WARNING} "unknown preprocessor \"${PREPROC}\", " \ "ignoring it ..." >&2 ${CMD_LOG_WARNING} "unknown preprocessor \"${PREPROC}\"," \ "ignoring it ..." ;; esac ${CMD_DEBUG_PRINT} "`ls -al "${WORK}"`" ${CMD_DEBUG_WAIT} done fi return 0 } # --- download config file list ${CMD_INFO_PRINT} "downloading configuration list ${SYS_CONFLIST_NAME} ..." ${CMD_DEBUG_SLEEP} # file list url on server CONFLIST_URL="${CONFHOST_URL}/${SYS_CONFLIST_NAME}" # temp file here on host CONFLIST="${SYS_WORKDIR}/${SYS_CONFLIST_NAME}" # get the config file list, it lists what config files are to be processed fetch_and_preprocess_config_file "${CONFLIST_URL}" "${CONFLIST}" # file here on host if [ "`whoami`" != root ] ; then # being run by an user for personal files, not as root for system # FreeBSD sh fails to substitute if the ~ is inside the "" CONFLIST_PLACE=~/."${SYS_CONFLIST_NAME}" else CONFLIST_PLACE="/etc/${SYS_CONFLIST_NAME}" fi # if file nonexistant generate, empty, hopefully that will not break something # need to prevent diff below from complaining about missing previous file if [ ! -e "${CONFLIST_PLACE}" ] ; then ${CMD_INFO_PRINT} "will be installing new config list at ${CONFLIST_PLACE}" ${CMD_DEBUG_SLEEP} # empty file ensures it is different (new one from server is not empty) touch "${CONFLIST_PLACE}" fi # only if file has changed do we do anything # diff is missnamed, successful finding difference regarded as faillure # that makes ist an "equal" test, not an "difference" test :-) # so ! in here despite wanting to do the if stuff in case of difference if ! diff "${CONFLIST_PLACE}" "${CONFLIST}" > /dev/null ; then ${CMD_INFO_PRINT} "updating old or installing new config" \ "${SYS_CONFLIST_NAME} as ${CONFLIST_PLACE} ..." ${CMD_DEBUG_SLEEP} cp -pf "${CONFLIST}" "${CONFLIST_PLACE}" ${CMD_DEBUG_PRINT} "`ls -al "${CONFLIST_PLACE}"`" ${CMD_DEBUG_WAIT} fi # --- process each config file ${CMD_INFO_PRINT} "processing individual config files from the list ..." ${CMD_DEBUG_SLEEP} # cut get rid of comments and grep empty lines (may be emptied comment lines) # and allow user to select/filter subset of lines to be processed # IFS= to avoid read doing separator processing (mangles spaces) # and -r to prevent \ interpretation (mangles line contents) cut -f 1 -d '#' "${CONFLIST_PLACE}" | grep -v '^ *$' | \ grep "${CONF_LINEFILTER}" | while IFS= read -r LINE ; do ${CMD_DEBUG_PRINT} "LINE=${LINE}" ${CMD_DEBUG_WAIT} # extract from LINE format: file-name:place-on-target[:command-to-trigger] if ! echo "${LINE}" | grep -q ':' ; then ${CMD_ERROR} "config line \"${LINE}\" has no \":\" to split on" >&2 ${CMD_LOG_ERROR} "config line \"${LINE}\" has no \":\" to split on" exit 1 fi # file-name: work on this file # if this is set to -, then remove an now unused config file CONFFILE_NAME="`echo "${LINE}" | cut -f 1 -d ':'`" # needs to be set, but may be unset if an : is right at begin of line if [ "`echo "${CONFFILE_NAME}" | tr -d ' '`" = "" ] ; then ${CMD_ERROR} "config line \"${LINE}\" has no file name before \":\"" >&2 ${CMD_LOG_ERROR} "config line \"${LINE}\" has no file name before \":\"" exit 1 fi # place-on-target: put result here CONFFILE_PLACE="`echo "${LINE}" | cut -f 2 -d ':'`" # needs to be set, but may be unset if mothing between the first 2 : if [ "`echo "${CONFFILE_PLACE}" | tr -d ' '`" = "" ] ; then ${CMD_ERROR} "config line \"${LINE}\" has no config place" \ "between first and second \":\"" >&2 ${CMD_LOG_ERROR} "config line \"${LINE}\" has no config place" \ "between first and second \":\"" exit 1 fi # command-to-trigger: run this facultatively # run post installing (or pre removing) an config file # if no second : on line or nothing after it this is automatically blank # if more than 2 : on line include the rest of them into this with 3- CMDTRIG="`echo "${LINE}" | cut -f 3- -d ':'`" ${CMD_DEBUG_PRINT} "CONFFILE_NAME=${CONFFILE_NAME}" \ "CONFFILE_PLACE=${CONFFILE_PLACE} CMDTRIG=${CMDTRIG}" ${CMD_DEBUG_WAIT} ${CMD_VERBOSE_PRINT} "processing config file ${CONFFILE_NAME} ..." ${CMD_DEBUG_SLEEP} # final file place here on host stuff # ensure that it is an absolute filename if [ "`echo "${CONFFILE_PLACE}" | cut -c 1`" != / ] ; then ${CMD_WARNING} "correcting non-absolute \"${CONFFILE_PLACE}\", add /" >&2 ${CMD_LOG_WARNING} "correcting non-absolute \"${CONFFILE_PLACE}\", add /" fi # allow debug runs as normal non-root user, and without overwriting stuff CONFFILE_PLACE="${DEBUG_INSTALL_BASE}${CONFFILE_PLACE}" # test for only directory name (ends in /), add base file name to it if [ "`echo "${CONFFILE_PLACE}" | rev | cut -c 1`" = / ] ; then # user gave directory, add filename to it # add no / after "${CONFFILE_PLACE}" as it is already contained in it # explicit user request that no // is displayed in resulting names CONFFILE_PLACE="${CONFFILE_PLACE}${CONFFILE_NAME}" else # user gave no directory, but check for forgotten / if [ -d "${CONFFILE_PLACE}" ] ; then ${CMD_WARNING} "place without / at end, but is existing directory" >&2 ${CMD_LOG_WARNING} "place without / at end, but is existing directory" CONFFILE_PLACE="${CONFFILE_PLACE}/${CONFFILE_NAME}" fi fi # expand an {} in commands to trigger to the full name of the file installed # syntax in copied from the find -exec ; command if echo "${CMDTRIG}" | grep -q '\{\}' ; then # hide slashes in path, to not confuse sed s command SED_CONFFILE_PLACE="`echo "${CONFFILE_PLACE}" | sed -e '/\//s@@\\\/@g'`" CMDTRIG="`echo "${CMDTRIG}" | sed -e "/{}/s//${SED_CONFFILE_PLACE}/g"`" fi if [ "${CONFFILE_NAME}" != - ] ; then # install an config file # file url on server CONFFILE_URL="${CONFHOST_URL}/${CONFFILE_NAME}" # temp file here on host CONFFILE="${SYS_WORKDIR}/${CONFFILE_NAME}" # get the actual config file to be processed fetch_and_preprocess_config_file "${CONFFILE_URL}" "${CONFFILE}" # ensure first that target dir is there, case new config file in new dir mkdir -p "`dirname "${CONFFILE_PLACE}"`" # prevent diff complaining fix, as for config list if [ ! -e "${CONFFILE_PLACE}" ] ; then ${CMD_INFO_PRINT} "will be installing new config file list at" \ "${CONFFILE_PLACE}" ${CMD_DEBUG_SLEEP} # empty file ensures it is different (new one from server is not empty) touch "${CONFFILE_PLACE}" fi # only if file has changed do we do anything, as for config list if ! diff "${CONFFILE_PLACE}" "${CONFFILE}" > /dev/null ; then ${CMD_INFO_PRINT} "updating old or installing new config" \ "${CONFFILE_NAME} as ${CONFFILE_PLACE} ..." ${CMD_DEBUG_SLEEP} cp -pf "${CONFFILE}" "${CONFFILE_PLACE}" ${CMD_DEBUG_PRINT} "`ls -al "${CONFFILE_PLACE}"`" ${CMD_DEBUG_WAIT} # run the facultative postinstall command trigger stuff if [ "`echo "${CMDTRIG}" | tr -d ' '`" != "" ] ; then ${CMD_INFO_PRINT} "triggering postinstall command \"${CMDTRIG}\" ..." ${CMD_DEBUG_SLEEP} # prevent non zero return from user given command from killing us set +e sh -c "${CMDTRIG}" RETVAL="$?" set -e if [ "${RETVAL}" != 0 ] ; then ${CMD_WARNING} "command returned non zero: ${RETVAL}" >&2 ${CMD_LOG_WARNING} "command returned non zero: ${RETVAL}" fi ${CMD_DEBUG_PRINT} "`ls -al "${CONFFILE_PLACE}"`" ${CMD_DEBUG_WAIT} fi fi else # remove an config file # only if file still exists do we do anything if [ -e "${CONFFILE_PLACE}" ] ; then ${CMD_INFO_PRINT} "removing old config file ${CONFFILE_PLACE} ..." ${CMD_DEBUG_SLEEP} # test for only directory name (ends in /), possibly dangerous leftover if [ "`echo "${CONFFILE_PLACE}" | rev | cut -c 1`" = "/" ] ; then ${CMD_WARNING} "would remove entire directory ${CONFFILE_PLACE}," \ "may be partially edited list file entry, DANGEROUS, leaving it," \ "add the files name after the / to delete an single file," \ "remove / after dir name to really delete entire directory" >&2 ${CMD_LOG_WARNING} "would remove entire directory ${CONFFILE_PLACE}," \ "may be partially edited list file entry, DANGEROUS, leaving it," \ "add the files name after the / to delete an single file," \ "remove / after dir name to really delete entire directory" else # run the facultative preremove command trigger stuff if [ "`echo "${CMDTRIG}" | tr -d ' '`" != "" ] ; then ${CMD_INFO_PRINT} "triggering preremove command \"${CMDTRIG}\" ..." ${CMD_DEBUG_SLEEP} set +e sh -c "${CMDTRIG}" RETVAL="$?" set -e if [ "${RETVAL}" != 0 ] ; then ${CMD_WARNING} "command returned non zero: ${RETVAL}" >&2 ${CMD_LOG_WARNING} "command returned non zero: ${RETVAL}" fi fi ${CMD_INFO_PRINT} "removing config file ${CONFFILE_PLACE} ..." ${CMD_DEBUG_SLEEP} ${CMD_DEBUG_PRINT} "`ls -ald "${CONFFILE_PLACE}"`" ${CMD_DEBUG_WAIT} rm -rf "${CONFFILE_PLACE}" fi fi fi done # --- finish off ${CMD_INFO_PRINT} "tidying up from working ..." ${CMD_DEBUG_SLEEP} ${CMD_DEBUG_PRINT} "+++ begin workdirectory listing +++" if [ "${DEBUG_PRINT_STEP}" = yes ] ; then ls -al "${SYS_WORKDIR}" 2> /dev/null fi ${CMD_DEBUG_PRINT} "+++ end workdirectory listing +++" ${CMD_DEBUG_WAIT} # remove temporary directory and its contents, to save space # must be after debug printout if [ "${DEBUG_LEAVE_TEMPDIRS}" != yes ] ; then # but offer to leave this to investigate bugs rm -rf "${SYS_WORKDIR}" fi ${CMD_LOG_IF_DONE} "has updated/installed/removed config files" exit 0