# Copyright (c) 2008, Christopher Cowart and contributors # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $Id$ # # This file provides shell libraries to confman REPO_ACTION="${REPO_DB}/last_action" REPO_SYNC_STATE="${REPO_DB}/state" RECIPE_NAME="$(cat "$RECIPE_FILE" 2>/dev/null)" WCOPY_DIRTY="false" CONF_EXPORT="false" LOG_MESSAGE_SET="false" VERSION='@VERSION@' REPO_VERSION="2" # Prints the repository URI function conf_repo_uri { local proto="${REPO_PROTOCOL%:/*}" local uri user # When we're running in export mode and the user has configured # a separate REPO location for it if ${CONF_EXPORT} && [ -n "$EXPORT_REPO_PATH" ] ; then # Determine which options must be set if [ "x${EXPORT_REPO_PROTOCOL}" = "xfile://" ] ; then MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_PATH" else MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_HOSTNAME EXPORT_REPO_PATH" fi REPO_PROTOCOL="$EXPORT_REPO_PROTOCOL" REPO_HOSTNAME="$EXPORT_REPO_HOSTNAME" REPO_PATH="$EXPORT_REPO_PATH" else # Determine which options must be set if [ "x${REPO_PROTOCOL}" = "xfile://" ] ; then MUST_SET="REPO_PROTOCOL REPO_PATH" else MUST_SET="REPO_PROTOCOL REPO_HOSTNAME REPO_PATH" fi fi # Verify that required options have been set for option in $MUST_SET ; do conf_verify_option_set $option done user=${REPO_REMOTE_USER:+${REPO_REMOTE_USER}@} case $proto in file) uri="${REPO_PROTOCOL}${REPO_PATH}" ;; *) uri="${REPO_PROTOCOL}${user}${REPO_HOSTNAME}${REPO_PATH}" ;; esac echo $uri } function conf_meta_dir { echo "${WORK_PATH}/meta" } function conf_recipe_dir { echo "$(conf_meta_dir)/recipes" } function conf_recipe_path { echo "$(conf_recipe_dir)/${RECIPE_NAME}" } function conf_abswork { ${readlink_cmd} -m "$WORK_PATH" } # Checks out the conf tree if we don't already have it. Updates it if we do. function conf_checkout_tree { if [ ! -d ${WORK_PATH}/.svn ] ; then local path=`echo ${WORK_PATH} | ${sed_cmd} -e 's:/[^/]+$::'` mkdir -p ${WORK_PATH} # This makes sure nobody can enter your working copy. We relax the # permissions during the rollout, and restrict them again at # the end. chmod 700 ${WORK_PATH} @SVN@ checkout `conf_repo_uri`/trunk ${WORK_PATH} else @SVN@ update ${WORK_PATH} fi } # I update the working copy of a given module to the repository's copy. function conf_update_module { local module="$1" @SVN@ update ${WORK_PATH}/${module} } # Updates the whole source tree function conf_update_tree { if [ -z "$*" ] ; then @SVN@ update ${WORK_PATH} else @SVN@ update "$@" fi } # Functions for locking and unlocking both the system and the working copy function conf_wcopy_lockfile { echo "${WORK_PATH}/confman.lock" } function conf_lock_wcopy { local locktype conf_require_wcopy # If this process already has a lock on the wcopy if conf_wcopy_is_locked; then locktype="RECURSIVE" else locktype="ORIGINAL" fi ${LOCK} lock $$ $(conf_wcopy_lockfile) || die WCOPY_DIRTY="true" echo $locktype } function conf_unlock_wcopy { local locktype="$1" if [ "$locktype" = "RECURSIVE" ] ; then return 0 elif [ "$locktype" = "ORIGINAL" ] ; then ${LOCK} unlock $$ $(conf_wcopy_lockfile) return $? else echo "Invalid lock type in conf_unlock_wcopy" >&4 die fi } function conf_wcopy_is_locked { ${LOCK} haslock $$ $(conf_wcopy_lockfile) } function conf_lock_system { local locktype # If this process already has a lock on the wcopy if conf_system_is_locked; then locktype="RECURSIVE" else locktype="ORIGINAL" fi ${SUDO} ${LOCK} lock $$ "$LOCKFILE" || die echo $locktype } function conf_unlock_system { local locktype="$1" if [ "$locktype" = "RECURSIVE" ] ; then return 0 elif [ "$locktype" = "ORIGINAL" ] ; then ${SUDO} ${LOCK} unlock $$ "$LOCKFILE" return $? else echo "Invalid lock type in conf_unlock_system" >&2 die fi } function conf_system_is_locked { ${LOCK} haslock $$ "$LOCKFILE" } function conf_lock_is_recursive { local locktype="$1" case "$locktype" in ORIGINAL) return 1;; RECURSIVE) return 0;; *) echo "Invalid lock type in conf_lock_is_recursive" >&2 die ;; esac } # Revert a working file function conf_revert { @SVN@ revert $* } function conf_create_modules { local module local newmodules local rc=0 for module in "$@"; do if conf_create_module "$module"; then # Keep track of the modules that were successfully created # avoid inadvertently committing outstanding changes to # pre-existing modules if [ -z "$newmodules" ]; then newmodules="$module" else newmodules="$newmodules,$module" fi else rc=1 fi done # $newmodules is in the form foo,bar,baz. This eval trick forces # curly brace expansion. if echo "$newmodules" | grep -q ','; then @SVN@ commit $(eval echo ${WORK_PATH}/{$newmodules}) \ $(eval echo ${WORK_PATH}/${REPO_CHECKPTS}/{$newmodules}) -m \ "Created directory structure for $newmodules" elif [ -n "$newmodules" ]; then # We test -n for this case because $newmodules might # be empty, i.e. no new modules were created @SVN@ commit ${WORK_PATH}/$newmodules \ ${WORK_PATH}/${REPO_CHECKPTS}/$newmodules -m \ "Created directory structure for $newmodules" fi return $rc } # Assumes that we already have core setup in our work path. function conf_create_module { local module=$1 if [ -d ${WORK_PATH}/$module -a -d ${WORK_PATH}/${REPO_CHECKPTS}/$module ] then echo "Module already exists: $module" >&2 return 1 fi @SVN@ mkdir ${WORK_PATH}/$module @SVN@ mkdir ${WORK_PATH}/${REPO_CHECKPTS}/$module return 0 } function conf_commit_file { local msg="$1" shift local files for file in "$@"; do if [ -L "$file" ]; then files="$files `${readlink_cmd} -m $file`" fi files="$files $file" done @SVN@ commit -F "$msg" $files } function conf_fetch { local tmpfile case ${CONF_EXPORT_STYLE} in repository) tmpfile=$(conf_tmp_file) conf_fetch_file $tmpfile || return 1 ;; recipe) tmpfile=$(conf_tmp_file) conf_fetch_file $tmpfile $(conf_get_recipe_name) || return 1 ;; module) tmpfile=$(conf_tmp_dir) conf_fetch_file $tmpfile meta || return 1 tar -xzf $tmpfile/meta.tgz -C ${WORK_PATH} for layer in $(conf_get_recipe); do conf_fetch_file $tmpfile $layer || return 1 done ;; *) echo "Unsupported export style in conf_fetch" >&2 return 1 ;; esac echo $tmpfile return 0 } function conf_fetch_file { local proto="${CONF_EXPORT_URI%:/*}" local tmpfile=$1 local file=$2 local remote # If file is defined, munge it appropriately if [ -n "$file" ]; then file="/$file.tgz" fi case $proto in sftp|scp) remote="${CONF_EXPORT_URI#*://}" remote="${remote/\//:/}" # XXX: Remove for confman-3.0 if [ -z "$CONF_FETCH_SSH_KEY" ] && [ -n "$CONF_SSH_KEY" ] ; then CONF_FETCH_SSH_KEY="$CONF_SSH_KEY" conf_warn "CONF_SSH_KEY is deprecated. Please define " \ "CONF_FETCH_SSH_KEY instead." fi scp -p -i ${CONF_FETCH_SSH_KEY} ${CONF_FETCH_SSH_FLAGS} \ "$remote$file" $tmpfile ;; http|https|ftp) $fetch_cmd "$tmpfile$file" "${CONF_EXPORT_URI}$file" ;; file) remote="${CONF_EXPORT_URI#*://}" cp -p "$remote$file" $tmpfile ;; *) echo "Unsupported Protocol in conf_fetch_file" >&2 return 1 ;; esac return 0 } # A way to utilize the svn status feature. function conf_status { @SVN@ status $* } # This exports a working copy of the repository function conf_export { local exportpath="$1" local modules if [ -z "${CONF_EXPORT_WORK_PATH}" ]; then WORK_PATH=$(conf_tmp_dir) else WORK_PATH="${CONF_EXPORT_WORK_PATH}" fi conf_checkout_tree case ${CONF_EXPORT_STYLE} in repository) tar -czf "$exportpath" -C "${WORK_PATH}" . ;; module) modules=$(ls -1 ${WORK_PATH} | egrep -v '^(\.svn|checkpoints)$') # until ticket $40 is resolved for module in $modules; do tar -hczf "$exportpath/$module.tgz" -C "${WORK_PATH}" $module done ;; recipe) for recipefile in $(conf_recipe_dir)/*; do modules="meta $(conf_get_recipe $recipefile)" tar -hczf "$exportpath/${recipefile#$(conf_recipe_dir)/}.tgz" -C "${WORK_PATH}" $modules done ;; *) echo "Unsupported export style in conf_export" >&2 return 1 ;; esac if [ -z "${CONF_EXPORT_WORK_PATH}" ]; then rm -rf ${WORK_PATH} fi return 0 } # Roll out the specified module, optionally at the specified checkpoint # eg: conf_rollout MODULE [checkpoint] function conf_rollout { local module="$1" if [ -z $2 ] ; then local moduledir="${WORK_PATH}/$module" else local moduledir="${WORK_PATH}/checkpoints/${module}/${2}" fi # This is a hack to work around NFS home dirs, for now: ${NFS_HACK:-false} && chmod o+rx ${WORK_PATH} # Test to see if the SVN working copy is out of date if ! @SVN@ info $moduledir >/dev/null ; then echo "Error: $moduledir isn't usable by svn." >&2 exit 1 fi for directory in `find -L $moduledir -mindepth 1 -type d | grep -v "\.svn"`; do local livedir=`echo $directory | ${sed_cmd} -e "s:$moduledir::"` livedir="${LIVE_ROOT}${livedir}" local owner=`conf_get_prop ${directory} owner` local group=`conf_get_prop ${directory} group` local mode=`conf_get_prop ${directory} mode` local opts="-d -o $owner -g $group -m $mode" local cmd="$SUDO $install_cmd $opts $livedir" echo $cmd $cmd done for file in `find -L $moduledir -type f | grep -v "\.svn"` ; do local livefile=`echo "$file" | ${sed_cmd} -e "s:$moduledir::"` local owner=`conf_get_prop ${file} owner` local group=`conf_get_prop ${file} group` local mode=`conf_get_prop ${file} mode` local symlink="`conf_get_prop ${file} symlink`" file=`$readlink_cmd -m $file` if [ -n "$symlink" ]; then local cmd="$SUDO ln -snf $symlink ${LIVE_ROOT}$livefile" echo $cmd $cmd else local opts="-o $owner -g $group -m $mode" local cmd="$SUDO $install_cmd $opts $file ${LIVE_ROOT}$livefile" echo $cmd $cmd fi done # This is a hack to work around NFS home dirs, for now: ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH} return 0 } # Takes a working copy file path and returns the corresponding live file path function conf_livefile { local file="$1" local path=$(conf_rel_path "$file") || return echo /${path#*/} } # Takes a working copy file path and returns whether it is a singularity function conf_wfile_is_singularity { local file=$(${readlink_cmd} -m "$1") local module=$(conf_wfile_module $file) local livefile=$(conf_livefile $file) local sing for sing in $SINGULARITIES ; do if [ "$sing" = ${livefile%-$module} ] ; then return 0 fi done return 1 } # Takes a working copy file path and returns the associated module function conf_wfile_module { local file=$(${readlink_cmd} -m "$1") ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH") local module=${file#${ABS_WORK}/} module=${module%%/*} echo "$module" } # Takes a file as an argument and returns the path of the file relative # to WORK_PATH/ABS_WORK function conf_rel_path { local file="$1" local result if ! result=$(conf_rel_path_helper "$file") ; then echo "$file" does not appear to be located within $WORK_PATH >&2 return 1 fi echo ${result#/} return 0 } # Recursive helper for determining conf_rel_path function conf_rel_path_helper { local file="$1" local target=$(${readlink_cmd} -m "$file") local ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH") local dir rc sofar if [ "$target" = "$ABS_WORK" ] ; then return 0 elif [ "$file" = "/" ] ; then return 1 else dir=$(dirname "$file") case "$dir" in ".") dir=$(pwd);; "..") dir=$(cd ..; pwd);; esac sofar=$(conf_rel_path "$dir") || return $? printf "%s/%s" "$sofar" $(basename "$file") return 0 fi } function conf_install { conf_debug conf_install called with args "$@" ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH") local module="$1" local file="$2" shift 2 local livefile local failures=false # Recursive base case if [ -z "$file" ] ; then conf_debug "conf_install base case reached" return 0 fi file=$(conf_rel_path "$file") || return 1 livefile="/${file#*/}" file="${ABS_WORK}/${module}${livefile}" # See if it even exists if [ ! -e "$file" ] ; then conf_debug "$file" does not appear to exist conf_install $module "$@" return 1 fi # If we got here, figure it out local owner=`conf_get_prop ${file} owner` local group=`conf_get_prop ${file} group` local mode=`conf_get_prop ${file} mode` local symlink="`conf_get_prop ${file} symlink`" if [ -f "$file" ] ; then local opts="-o $owner -g $group -m $mode" else local opts="-d -o $owner -g $group -m $mode" fi if [ -n "$symlink" ] ; then local cmd="$SUDO ln -snf $symlink ${LIVE_ROOT}$livefile" else local cmd="$SUDO $install_cmd $opts $file ${LIVE_ROOT}$livefile" fi ${NFS_HACK:-false} && chmod o+rx ${ABS_WORK} echo $cmd $cmd || failures=true ${NFS_HACK:-false} && chmod o-rx ${ABS_WORK} if [ -d "$file" ] ; then conf_debug "conf_install encountered a directory. Recursing." conf_install $module $(find "$file" \ -mindepth 1 -maxdepth 1 -not -name '.svn') || failures=true fi conf_debug "conf_install recursing" conf_install $module "$@" || failures=true conf_debug "conf_install returning" if $failures ; then return 1 else return 0 fi } function conf_list { local file=$1 local module=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/.*:\1:'` local livefile=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/:/:'` if @SVN@ status $file | egrep -q "^\?"; then echo -e "Not in confman repository\t\t${livefile}" return fi local owner=`conf_get_prop ${file} owner` local group=`conf_get_prop ${file} group` local mode=`conf_get_prop ${file} mode` local comment=`conf_get_prop ${file} comment` local symlink=`conf_get_prop ${file} symlink` [ -n "$symlink" ] && symlink=" -> $symlink" echo -e "$mode\t$owner\t$group\t$comment\t\t${livefile}${symlink}" } # This function assembles the singularities: function conf_assemble_sing { local file=$1 local livefile="${LIVE_ROOT}${file}" local tmpfile=$(conf_tmp_file) || exit 1 local layers="$(conf_get_recipe)" local owner group mode flag livepart msg for layer in $layers ; do livepart="${LIVE_ROOT}${file}-${layer}" myfile="${WORK_PATH}/${layer}/${file}-${layer}" if [ -f $myfile ] && $SUDO [ -f $livepart ] ; then owner=`conf_get_prop ${myfile} owner` group=`conf_get_prop ${myfile} group` mode=`conf_get_prop ${myfile} mode` comment=`conf_get_prop ${myfile} comment` $SUDO cat $livepart >> $tmpfile $SUDO rm $livepart flag=1 fi done if [ ! -z $flag ] ; then local opts="-o $owner -g $group -m $mode" local cmd="$SUDO $install_cmd $opts $tmpfile $livefile" echo $cmd $cmd fi rm -f $tmpfile } # This function creates a symlink in your working copy and imports it # into confman. If the third argument is "true", then the link is # forced. Throws an erorr if link_name already exists and "force" # option is not used. # e.g. conf_ln TARGET ( LINK_NAME | DIRECTORY ) [ FORCE ] function conf_ln { local target=$1 local link=$2 local forced=$3 local opts isdir linkdir rtarget rlink if [ "$forced" == "true" ] ; then opts="-f" elif [ -e "$link" -a ! -d "$link" ] ; then echo "$link already exists. Did you mean to use \"-f\"?" return 1 fi if [ -d "$link" ] ; then link="${link}/$(basename "${target}")" fi # target needs to be transformed to be relative to the link. In order # to do this, we need to cannonicalize filenames relative to the # working copy root ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH") target=`${readlink_cmd} -m $target` rtarget=${target#${ABS_WORK}/} if [ "$target" = "$rtarget" ] ; then echo "target $target does not appear to be in your working copy." >&4 return 1 fi link=`${readlink_cmd} -m $link` rlink="${link#${ABS_WORK}/}" if [ "$link" = "$rlink" ] ; then echo "link $link does not appear to be in your working copy." >&4 return 1 fi linkdir="${rlink%/*}/" while [[ ${linkdir} =~ ^.+/ ]]; do rtarget="../$rtarget" linkdir=${linkdir#*/} done ln -s $opts "$rtarget" "${WORK_PATH}/$rlink" @SVN@ add "${WORK_PATH}/$rlink" } # This function generates a checkpoint called NAME for the given MODULE. # eg: conf_new_checkpoint MODULE NAME function conf_new_checkpoint { local module=$1 local checkpoint=$2 local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}" local revision=`conf_revision` echo $revision > $chkpath @SVN@ add $chkpath local msg="Created a checkpoint, ${checkpoint} for ${module} --`whoami`" @SVN@ commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg" } # This function will remove the checkpoint NAME from MODULE. # eg: conf_rm_checkpoint MODULE NAME function conf_rm_checkpoint { local module=$1 local checkpoint=$2 local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}" @SVN@ rm ${chkpath} local msg="Removed the checkpoint ${checkpoint} from ${module} --`whoami`" @SVN@ commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg" } # This function will restore the state of your module's working copy to the # named checkpoint. eg: conf_rollback MODULE NAME function conf_rollback { local module=$1 local checkpoint=$2 local clock=$3 local modpath="${WORK_PATH}/${module}" local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}" local revision local date=`echo $checkpoint | ${sed_cmd} -e 's:(....)(..)(..):\1-\2-\3:'` # Named checkpoint if [ -f "${chkpath}" ] ; then revision=`cat $chkpath` elif [ -z $clock ] ; then # Time checkpoint revision="{${date}}" else clock=`echo $clock | ${sed_cmd} -e 's#(..)(..)#\1:\2#'` revision="{${date}T${clock}}" fi symlinks="$symlinks `find ${module} -type l \ -exec ${readlink_cmd} -m {} \;`" #svn update --revision $revision $modpath @SVN@ update --revision $revision $modpath $symlinks } # This function will print the value of the specified property to STDOUT function conf_get_prop { local file=$1 local prop=$2 local result=`@SVN@ propget "confman:${prop}" ${file}` if [ -z "$result" ] ; then file=`$readlink_cmd -m ${file}` @SVN@ propget "confman:${prop}" ${file} else echo $result fi } # This function will set the value of the specified property. function conf_set_prop { local file=$1 local prop=$2 local value=$3 @SVN@ propset "confman:${prop}" "${value}" ${file} } function conf_gen_file { local module=$1 local file=$2 local owner=$3 local group=$4 local mode=$5 local comment="$6" local usefile=$7 local symlink="$8" local myfile="${WORK_PATH}/${module}${file}" local warning="${comment} ${CONF_WARNING}" local morewarn="${comment} Managed under ${module} module." local revision="${comment} \$Id\$" echo $file if [ -z $usefile ] ; then echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile elif head -n 1 ${usefile} | grep '^#!' >/dev/null ; then awk "{if (NR==2) print \"\\n${warning}\\n${morewarn}\\n${revision}\\n\"\$0; else print \$0 }" ${usefile} > $myfile else echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile cat ${usefile} >> $myfile fi @SVN@ add $myfile @SVN@ ps svn:keywords "Id" $myfile conf_set_prop $myfile owner $owner conf_set_prop $myfile group $group conf_set_prop $myfile mode $mode conf_set_prop $myfile comment "$comment" conf_set_prop $myfile symlink "$symlink" } function conf_rm_file { @SVN@ rm "$@" } function conf_mkdir { local directory=$1 local owner=$2 local group=$3 local mode=$4 local workdir="" local directories=`echo "$directory" | ${sed_cmd} -e 's:/: :g'` local dir for dir in $directories ; do workdir="${workdir}/${dir}" if [ ! -d ${workdir} ] ; then echo @SVN@ mkdir ${workdir} @SVN@ mkdir ${workdir} conf_set_prop $workdir owner $owner conf_set_prop $workdir group $group conf_set_prop $workdir mode $mode conf_set_prop $workdir comment "dir" fi done } function conf_remove_modules { local module local rc=0 for module in "$@"; do conf_rmmod "$module" || rc=1 done return $rc } function conf_rmmod { local module="$1" local dir local rc=0 for dir in ${WORK_PATH}/{,${REPO_CHECKPTS}/}${module}; do if ! [ -d "$dir" ]; then echo "No such directory: $dir" >&2 rc=1 else @SVN@ rm $dir || rc=1 fi done return $rc } function conf_rename { local oldmod=$1 local newmod=$2 local file @SVN@ mv ${WORK_PATH}/${oldmod} ${WORK_PATH}/${newmod} || return 1 @SVN@ mv ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \ ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} || return 1 for file in ${SINGULARITIES} ; do if [ -f ${WORK_PATH}/${newmod}${file}-${oldmod} ]; then @SVN@ mv ${WORK_PATH}/${newmod}${file}-${oldmod} \ ${WORK_PATH}/${newmod}${file}-${newmod} || return 1 fi done for file in $(find ${WORK_PATH}/${newmod} -type f -not -path '*/.svn/*') do sed_i_cmd \ "s/(Managed under )${oldmod}( module\.)$/\1${newmod}\2/" "$file" \ || return 1 done for file in $(find $(conf_recipe_dir) -maxdepth 1 -type f); do sed_i_cmd "s/^([ \t]*)${oldmod}([ \t]*)$/\1${newmod}\2/" "$file" || return 1 done @SVN@ commit -m "Renaming ${oldmod} to ${newmod}" \ ${WORK_PATH}/${oldmod} ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \ ${WORK_PATH}/${newmod} ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} \ $(conf_recipe_dir) || return 1 return 0 } function conf_mv { local oldname=$1 local newname=$2 @SVN@ mv $oldname $newname } function conf_cp { local oldname=$1 local newname=$2 @SVN@ cp $oldname $newname } function conf_diff { @SVN@ diff $* } function conf_log { @SVN@ log $* } # Accepts log messages on stdin until EOF function conf_logger { logger -t "$MYNAME[$$]: $USER" 2>&4 } function conf_chattr { local attr="confman:$1" shift local value="$1" shift @SVN@ propset "$attr" "$value" "$@" } function conf_lsattr { local file="$1" shift echo "$file" for prop in $(@SVN@ proplist $file | grep 'confman:' | sort) ; do echo -e "\t${prop#confman:}\t" $(@SVN@ propget $prop $file) done if [ -n "$1" ] ; then conf_lsattr "$@" fi } function conf_rmattr { local attr="confman:$1" shift @SVN@ propdel "$attr" "$@" } function conf_syncheck { local cmd local toReturn=true for file in "$@" ; do cmd=$(@SVN@ propget "confman:syncheck" "$file") if [ -z "$cmd" ] ; then continue fi if ! $cmd "$file" ; then echo "Syntax verification failed for $file" >&2 toReturn=false fi done $toReturn } function conf_markclean { $SUDO sh -c "echo clean > $REPO_SYNC_STATE" } function conf_markdirty { $SUDO sh -c "echo dirty > $REPO_SYNC_STATE" } function conf_isclean { local state=`$SUDO cat $REPO_SYNC_STATE 2>/dev/null` case $state in clean) return 0 ;; dirty) return 1 ;; *) echo "Invalid state. Reporting dirty." >&2 ; return 1 ;; esac } # Returns the current revision number of the repository on stdout function conf_revision { @SVN@ info $(conf_meta_dir) | awk '/^Revision:/ {print $2}' } # Returns the revision of the last commit or install operation on stdout function conf_sysrev { if [ -f "$REPO_ACTION" ]; then cut -d ':' -f 2 $REPO_ACTION else echo 0 fi } # Returns the last action of the repository on stdout function conf_lastact { if [ -f "$REPO_ACTION" ]; then cut -d ':' -f 1 $REPO_ACTION else echo none fi } # Records the specified action function conf_recordAction { local action="$1" local revision=`conf_revision` $SUDO sh -c "echo '${action}:${revision}' > $REPO_ACTION" } # Spits the recipe, modules separated by whitespace, out on stdout function conf_get_recipe { local module recipe_file="${1}" if [ -z "$recipe_file" ] ; then recipe_file="$(conf_recipe_path)" fi if ! [ -f "$recipe_file" ] ; then echo "Couldn't read the recipe!" >&2 die fi for module in `cat $recipe_file | grep "^[^#]"` ; do printf "%s " $module done } # Prints the name of this host's recipe on stdout or returns non-zero function conf_get_recipe_name { local recipe=$(cat ${RECIPE_FILE} 2>/dev/null) if [ -z "$recipe" ] ; then return 1 fi echo $recipe } # Set this host's recipe function conf_set_recipe { local recipe="$1" ${SUDO} sh -c "echo $recipe > ${RECIPE_FILE}" } function conf_recipe_create { local recipe="$1" local recipe_file="$(conf_recipe_dir)/${recipe}" if [ -f "${recipe_file}" ] ; then echo "Recipe $recipe already exists!" >&2 return 1 fi ${sed_cmd} -e "s:__NAME__:${recipe}:" $RECIPE_TEMPLATE > "$recipe_file" @SVN@ add "$recipe_file" } function conf_recipe_verify { local recipe_file="$1" local module for module in $(conf_get_recipe ${recipe_file}) ; do if ! [ -d "${WORK_PATH}/${module}" ] ; then echo "Error: recipe references non-existent module $module" >&2 return 1 fi done return 0 } function conf_commit_recipes { local recipe recipes local logfile="$1" shift for recipe in "$@" ; do recipes="${recipes:+${recipes} }$(conf_recipe_dir)/${recipe}" done @SVN@ commit -F "$logfile" $recipes } function conf_remove_recipes { local recipe recipe_file local oldrecipes local rc=0 for recipe in "$@"; do if conf_remove_recipe "$recipe"; then recipe_file="$(conf_recipe_dir)/${recipe}" oldrecipes="${oldrecipes:+${oldrecipes} }${recipe_file}" else rc=1 fi done return $rc } function conf_remove_recipe { local recipe="$1" local recipe_file="$(conf_recipe_dir)/${recipe}" if ! [ -f "${recipe_file}" ] ; then echo "Recipe $recipe doesn't exist!" >&2 return 1 fi @SVN@ rm "${recipe_file}" || return 1 return 0 } # Implements logic for selecting logfile based on -m/-F or interactive mode # Returns 0 if the log is "complete", 1 if the log needs editing # Prints filename containing the log on stdout function conf_log_message { local logfile if [ -n "$LOG_FILE" ] ; then echo "$LOG_FILE" return 0 fi logfile=$(conf_tmp_file) echo "$logfile" if $LOG_MESSAGE_SET ; then echo "$LOG_MESSAGE" > "$logfile" else cat "$LOG_TEMPLATE" > "$logfile" return 1 fi return 0 } # Because an exit doesn't help much when things are in subshells, we # provide a nice, easy way to halt execution. The result is the clean # exit trap gets called. We also call exit to halt execution of this # subshell (otherwise some commands will still be run before the parent # handles the signal and kills us off). function die { kill -INT $MY_PID exit 1 } function conf_interrupt_trap { # Stderr going to logger automatically seems to be broken upon # interrupt. This redirects stderr back to the initial stderr (usually # the terminal), as it existed before confman munged file descriptors. exec 2>&4 echo "Received a signal. Exiting cleanly." | conf_logger echo "Please wait until I finish cleaning up after you." >&4 conf_cleanExit 1 } function conf_cleanExit { # And in case we got an interrupt during a rollout, we still want the # permissions here to be in a consistent state. ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH} conf_debug "Removing leftover temp files..." >&4 rm -rf $TMPDIR conf_debug "Removing any system or working copy locks..." >&4 conf_wcopy_is_locked && conf_unlock_wcopy ORIGINAL conf_system_is_locked && conf_unlock_system ORIGINAL # And this clears any locks we may have created on our working copy: if [ -d "${WORK_PATH}" ] && $WCOPY_DIRTY ; then conf_debug "Undoing damage to your working copy..." >&4 @SVN@ cleanup ${WORK_PATH} fi conf_debug "All clean. Terminating." >&4 trap EXIT exit ${1:-1} } function conf_checkgroup { GROUP_FILE=$(conf_tmp_file) GROUP_STAT=$(chgrp staff $GROUP_FILE 2>&1) rm $GROUP_FILE if [ -n "$GROUP_STAT" ] ; then return 2 else return 0 fi } function conf_verify_option_set { local option="$1" local value eval value="\$${option}" if [ -z "$value" ] ; then echo "Error: ${option} must be defined in ${GCONF} or \n${UCONF}." >&2 exit 1 fi return 0 } function conf_require_recipe { if [ -z "$RECIPE_NAME" ] ; then echo 'Error: this action requires the recipe to be configured.' >&2 echo 'Please see `confman help recipe` for more information.' >&2 exit 1 fi } function conf_require_wcopy { if ! [ -d "${WORK_PATH}" ] ; then echo "Error: this action requires that your working copy exist." >&2 echo 'Please see `confman help setup` for more information.' >&2 die fi } function conf_tmp_file { local file=$(${mktemp_file}) || return 1 echo $file return 0 } function conf_tmp_dir { local dir=$(${mktemp_dir}) || return 1 echo $dir return 0 } function conf_debug { $DEBUG && echo "$@" >&4 } function conf_warn { echo "$@" >&2 } # vim:ts=4