root/trunk/confmanlib.sh.in @ 510

Revision 510, 32.1 KB (checked in by ccowart, 9 months ago)

Fixing the die handler to exit the subshell instead of allowing some
additional commands to be executed before the parents kills it off.

Fixes #113

  • Property rc:mode set to 0444
  • Property svn:keywords set to Id
Line 
1# Copyright (c) 2008, Christopher Cowart and contributors
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7# * Redistributions of source code must retain the above copyright
8#   notice, this list of conditions and the following disclaimer.
9# * Redistributions in binary form must reproduce the above copyright
10#   notice, this list of conditions and the following disclaimer in the
11#   documentation and/or other materials provided with the distribution.
12#
13# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
19# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24#
25# $Id$
26#
27# This file provides shell libraries to confman
28
29REPO_ACTION="${REPO_DB}/last_action"
30REPO_SYNC_STATE="${REPO_DB}/state"
31RECIPE_NAME="$(cat "$RECIPE_FILE" 2>/dev/null)"
32WCOPY_DIRTY="false"
33CONF_EXPORT="false"
34LOG_MESSAGE_SET="false"
35
36VERSION='@VERSION@'
37REPO_VERSION="2"
38
39# Prints the repository URI
40function conf_repo_uri {
41        local proto="${REPO_PROTOCOL%:/*}"
42        local uri user
43
44    # When we're running in export mode and the user has configured
45    # a separate REPO location for it
46    if ${CONF_EXPORT} && [ -n "$EXPORT_REPO_PATH" ] ; then
47        # Determine which options must be set
48        if [ "x${EXPORT_REPO_PROTOCOL}" = "xfile://" ] ; then
49            MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_PATH"
50        else
51            MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_HOSTNAME
52                EXPORT_REPO_PATH"
53        fi
54        REPO_PROTOCOL="$EXPORT_REPO_PROTOCOL"
55        REPO_HOSTNAME="$EXPORT_REPO_HOSTNAME"
56        REPO_PATH="$EXPORT_REPO_PATH"
57    else
58        # Determine which options must be set
59        if [ "x${REPO_PROTOCOL}" = "xfile://" ] ; then
60            MUST_SET="REPO_PROTOCOL REPO_PATH"
61        else
62            MUST_SET="REPO_PROTOCOL REPO_HOSTNAME REPO_PATH"
63        fi
64    fi
65   
66    # Verify that required options have been set
67    for option in $MUST_SET ; do
68        conf_verify_option_set $option
69    done
70
71    user=${REPO_REMOTE_USER:+${REPO_REMOTE_USER}@}
72
73        case $proto in
74                file)
75                        uri="${REPO_PROTOCOL}${REPO_PATH}"
76                        ;;
77                *)
78                        uri="${REPO_PROTOCOL}${user}${REPO_HOSTNAME}${REPO_PATH}"
79                        ;;
80        esac
81
82        echo $uri
83}
84
85function conf_meta_dir {
86    echo "${WORK_PATH}/meta"
87}
88
89function conf_recipe_dir {
90    echo "$(conf_meta_dir)/recipes"
91}
92
93function conf_recipe_path {
94    echo "$(conf_recipe_dir)/${RECIPE_NAME}"
95}
96
97function conf_abswork {
98    ${readlink_cmd} -m "$WORK_PATH"
99}
100
101# Checks out the conf tree if we don't already have it. Updates it if we do.
102function conf_checkout_tree {
103        if [ ! -d ${WORK_PATH}/.svn ] ; then
104        local path=`echo ${WORK_PATH} | ${sed_cmd} -e 's:/[^/]+$::'`
105        mkdir -p ${WORK_PATH}
106       
107        # This makes sure nobody can enter your working copy. We relax the
108        # permissions during the rollout, and restrict them again at
109        # the end.
110        chmod 700 ${WORK_PATH}
111       
112        @SVN@ checkout `conf_repo_uri`/trunk ${WORK_PATH}
113        else
114        @SVN@ update ${WORK_PATH}
115        fi
116}
117
118# I update the working copy of a given module to the repository's copy.
119function conf_update_module {
120        local module="$1"
121        @SVN@ update ${WORK_PATH}/${module}
122}
123
124# Updates the whole source tree
125function conf_update_tree {
126    if [ -z "$*" ] ; then
127        @SVN@ update ${WORK_PATH}
128    else
129        @SVN@ update "$@"
130    fi
131}
132
133# Functions for locking and unlocking both the system and the working copy
134function conf_wcopy_lockfile {
135    echo "${WORK_PATH}/confman.lock"
136}
137
138function conf_lock_wcopy {
139    local locktype
140    conf_require_wcopy
141
142    # If this process already has a lock on the wcopy
143    if conf_wcopy_is_locked; then
144        locktype="RECURSIVE"
145    else
146        locktype="ORIGINAL"
147    fi
148
149    ${LOCK} lock $$ $(conf_wcopy_lockfile) || die
150    WCOPY_DIRTY="true"
151    echo $locktype
152}
153
154function conf_unlock_wcopy {
155    local locktype="$1"
156
157    if [ "$locktype" = "RECURSIVE" ] ; then
158        return 0
159    elif [ "$locktype" = "ORIGINAL" ] ; then
160        ${LOCK} unlock $$ $(conf_wcopy_lockfile)
161        return $?
162    else
163        echo "Invalid lock type in conf_unlock_wcopy" >&4
164        die
165    fi
166}
167
168function conf_wcopy_is_locked {
169    ${LOCK} haslock $$ $(conf_wcopy_lockfile)
170}
171
172function conf_lock_system {
173    local locktype
174
175    # If this process already has a lock on the wcopy
176    if conf_system_is_locked; then
177        locktype="RECURSIVE"
178    else
179        locktype="ORIGINAL"
180    fi
181
182    ${SUDO} ${LOCK} lock $$ "$LOCKFILE" || die
183    echo $locktype
184}
185
186function conf_unlock_system {
187    local locktype="$1"
188
189    if [ "$locktype" = "RECURSIVE" ] ; then
190        return 0
191    elif [ "$locktype" = "ORIGINAL" ] ; then
192        ${SUDO} ${LOCK} unlock $$ "$LOCKFILE"
193        return $?
194    else
195        echo "Invalid lock type in conf_unlock_system" >&2
196        die
197    fi
198}
199
200function conf_system_is_locked {
201    ${LOCK} haslock $$ "$LOCKFILE"
202}
203
204function conf_lock_is_recursive {
205    local locktype="$1"
206    case "$locktype" in
207        ORIGINAL)   return 1;;
208        RECURSIVE)  return 0;;
209        *)          echo "Invalid lock type in conf_lock_is_recursive" >&2
210                    die
211                    ;;
212    esac
213}
214
215# Revert a working file
216function conf_revert {
217        @SVN@ revert $*
218}
219
220function conf_create_modules {
221    local module
222    local newmodules
223    local rc=0
224
225    for module in "$@"; do
226        if conf_create_module "$module"; then
227            # Keep track of the modules that were successfully created
228            # avoid inadvertently committing outstanding changes to
229            # pre-existing modules
230            if [ -z "$newmodules" ]; then
231                newmodules="$module"
232            else
233                newmodules="$newmodules,$module"
234            fi
235        else
236            rc=1
237        fi
238    done
239
240    # $newmodules is in the form foo,bar,baz. This eval trick forces
241    # curly brace expansion.
242    if echo "$newmodules" | grep -q ','; then
243        @SVN@ commit $(eval echo ${WORK_PATH}/{$newmodules}) \
244            $(eval echo ${WORK_PATH}/${REPO_CHECKPTS}/{$newmodules}) -m \
245            "Created directory structure for $newmodules"
246    elif [ -n "$newmodules" ]; then
247        # We test -n for this case because $newmodules might
248        # be empty, i.e. no new modules were created
249        @SVN@ commit ${WORK_PATH}/$newmodules \
250            ${WORK_PATH}/${REPO_CHECKPTS}/$newmodules -m \
251            "Created directory structure for $newmodules"
252    fi
253
254    return $rc
255}
256
257# Assumes that we already have core setup in our work path.
258function conf_create_module {
259    local module=$1
260
261    if [ -d ${WORK_PATH}/$module -a -d ${WORK_PATH}/${REPO_CHECKPTS}/$module ]
262    then
263        echo "Module already exists: $module" >&2
264        return 1
265    fi
266
267    @SVN@ mkdir ${WORK_PATH}/$module
268    @SVN@ mkdir ${WORK_PATH}/${REPO_CHECKPTS}/$module
269
270    return 0
271}
272
273function conf_commit_file {
274    local msg="$1"
275    shift
276    local files
277    for file in "$@"; do
278        if [ -L "$file" ]; then
279            files="$files `${readlink_cmd} -m $file`"
280        fi
281        files="$files $file"
282    done
283
284    @SVN@ commit -F "$msg" $files
285}
286
287function conf_fetch {
288    local tmpfile
289
290    case ${CONF_EXPORT_STYLE} in
291        repository)
292            tmpfile=$(conf_tmp_file)
293            conf_fetch_file $tmpfile || return 1
294            ;;
295        recipe)
296            tmpfile=$(conf_tmp_file)
297            conf_fetch_file $tmpfile $(conf_get_recipe_name) || return 1
298            ;;
299        module)
300            tmpfile=$(conf_tmp_dir)
301            conf_fetch_file $tmpfile meta || return 1
302            tar -xzf $tmpfile/meta.tgz -C ${WORK_PATH}
303            for layer in $(conf_get_recipe); do
304                conf_fetch_file $tmpfile $layer || return 1
305            done
306            ;;
307        *)
308            echo "Unsupported export style in conf_fetch" >&2
309            return 1
310            ;;
311    esac
312
313    echo $tmpfile
314    return 0
315}
316
317function conf_fetch_file {
318    local proto="${CONF_EXPORT_URI%:/*}"
319    local tmpfile=$1
320    local file=$2
321    local remote
322
323    # If file is defined, munge it appropriately
324    if [ -n "$file" ]; then
325        file="/$file.tgz"
326    fi
327
328    case $proto in
329        sftp|scp)   
330            remote="${CONF_EXPORT_URI#*://}"
331            remote="${remote/\//:/}"
332
333            # XXX: Remove for confman-3.0
334            if [ -z "$CONF_FETCH_SSH_KEY" ] && [ -n "$CONF_SSH_KEY" ] ; then
335                CONF_FETCH_SSH_KEY="$CONF_SSH_KEY"
336                conf_warn "CONF_SSH_KEY is deprecated. Please define " \
337                    "CONF_FETCH_SSH_KEY instead."
338            fi
339
340            scp -p -i ${CONF_FETCH_SSH_KEY} ${CONF_FETCH_SSH_FLAGS} \
341                 "$remote$file" $tmpfile
342            ;;
343        http|https|ftp)
344            $fetch_cmd "$tmpfile$file" "${CONF_EXPORT_URI}$file"
345            ;;
346        file)
347            remote="${CONF_EXPORT_URI#*://}"
348            cp -p "$remote$file" $tmpfile
349            ;;
350        *)
351            echo "Unsupported Protocol in conf_fetch_file" >&2
352            return 1
353            ;;
354    esac
355
356    return 0
357}
358
359# A way to utilize the svn status feature.
360function conf_status {
361        @SVN@ status $*
362}
363
364# This exports a working copy of the repository
365function conf_export {
366    local exportpath="$1"
367    local modules
368
369    if [ -z "${CONF_EXPORT_WORK_PATH}" ]; then
370        WORK_PATH=$(conf_tmp_dir)
371    else
372        WORK_PATH="${CONF_EXPORT_WORK_PATH}"
373    fi
374
375    conf_checkout_tree
376
377    case ${CONF_EXPORT_STYLE} in
378        repository)
379            tar -czf "$exportpath" -C "${WORK_PATH}" .
380            ;;
381        module)
382            modules=$(ls -1 ${WORK_PATH} | egrep -v '^(\.svn|checkpoints)$') # until ticket $40 is resolved
383            for module in $modules; do
384                tar -hczf "$exportpath/$module.tgz" -C "${WORK_PATH}" $module
385            done
386            ;;
387        recipe)
388            for recipefile in $(conf_recipe_dir)/*; do
389                modules="meta $(conf_get_recipe $recipefile)"
390                tar -hczf "$exportpath/${recipefile#$(conf_recipe_dir)/}.tgz" -C "${WORK_PATH}" $modules
391            done
392            ;;
393        *)
394            echo "Unsupported export style in conf_export" >&2
395            return 1
396            ;;
397    esac
398
399    if [ -z "${CONF_EXPORT_WORK_PATH}" ]; then
400        rm -rf ${WORK_PATH}
401    fi
402   
403    return 0
404}
405
406# Roll out the specified module, optionally at the specified checkpoint
407# eg:   conf_rollout MODULE [checkpoint]
408function conf_rollout {
409        local module="$1"
410        if [ -z $2 ] ; then
411                local moduledir="${WORK_PATH}/$module"
412        else
413                local moduledir="${WORK_PATH}/checkpoints/${module}/${2}"
414        fi
415        # This is a hack to work around NFS home dirs, for now:
416        ${NFS_HACK:-false} && chmod o+rx ${WORK_PATH}
417
418    # Test to see if the SVN working copy is out of date
419    if ! @SVN@ info $moduledir >/dev/null ; then
420        echo "Error: $moduledir isn't usable by svn." >&2
421        exit 1
422    fi
423
424        for directory in `find -L $moduledir -mindepth 1 -type d | grep -v "\.svn"`;
425    do
426                local livedir=`echo $directory | ${sed_cmd} -e "s:$moduledir::"`
427                livedir="${LIVE_ROOT}${livedir}"
428                local owner=`conf_get_prop ${directory} owner`
429                local group=`conf_get_prop ${directory} group`
430                local mode=`conf_get_prop ${directory} mode`
431                local opts="-d -o $owner -g $group -m $mode"
432                local cmd="$SUDO $install_cmd $opts $livedir"
433                echo $cmd
434                $cmd
435        done
436        for file in `find -L $moduledir -type f | grep -v "\.svn"` ; do
437                local livefile=`echo "$file" | ${sed_cmd} -e "s:$moduledir::"`
438                local owner=`conf_get_prop ${file} owner`
439                local group=`conf_get_prop ${file} group`
440                local mode=`conf_get_prop ${file} mode`
441        local symlink="`conf_get_prop ${file} symlink`"
442        file=`$readlink_cmd -m $file`
443        if [ -n "$symlink" ]; then
444            local cmd="$SUDO ln -snf $symlink ${LIVE_ROOT}$livefile"
445            echo $cmd
446            $cmd
447        else
448                    local opts="-o $owner -g $group -m $mode"
449                    local cmd="$SUDO $install_cmd $opts $file ${LIVE_ROOT}$livefile"
450                    echo $cmd
451                    $cmd
452        fi
453        done
454        # This is a hack to work around NFS home dirs, for now:
455        ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH}
456    return 0
457}
458
459# Takes a working copy file path and returns the corresponding live file path
460function conf_livefile {
461    local file="$1"
462    local path=$(conf_rel_path "$file") || return
463    echo /${path#*/}
464}
465
466# Takes a working copy file path and returns whether it is a singularity
467function conf_wfile_is_singularity {
468    local file=$(${readlink_cmd} -m "$1")
469    local module=$(conf_wfile_module $file)
470    local livefile=$(conf_livefile $file)
471    local sing
472    for sing in $SINGULARITIES ; do
473        if [ "$sing" = ${livefile%-$module} ] ; then
474            return 0
475        fi
476    done
477    return 1
478}
479
480# Takes a working copy file path and returns the associated module
481function conf_wfile_module {
482    local file=$(${readlink_cmd} -m "$1")
483    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
484    local module=${file#${ABS_WORK}/}
485    module=${module%%/*}
486    echo "$module"
487}
488
489# Takes a file as an argument and returns the path of the file relative
490# to WORK_PATH/ABS_WORK
491function conf_rel_path {
492    local file="$1"
493    local result
494
495    if ! result=$(conf_rel_path_helper "$file") ; then
496        echo "$file" does not appear to be located within $WORK_PATH >&2
497        return 1
498    fi
499
500    echo ${result#/}
501    return 0
502}
503
504# Recursive helper for determining conf_rel_path
505function conf_rel_path_helper {
506    local file="$1"
507    local target=$(${readlink_cmd} -m "$file")
508    local ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
509    local dir rc sofar
510
511    if [ "$target" = "$ABS_WORK" ] ; then
512        return 0
513    elif [ "$file" = "/" ] ; then
514        return 1
515    else
516        dir=$(dirname "$file")
517        case "$dir" in
518            ".")    dir=$(pwd);;
519            "..")   dir=$(cd ..; pwd);;
520        esac
521        sofar=$(conf_rel_path "$dir") || return $?
522        printf "%s/%s" "$sofar" $(basename "$file")
523        return 0
524    fi
525}
526
527function conf_install {
528    conf_debug conf_install called with args "$@"
529    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
530        local module="$1"
531    local file="$2"
532    shift 2
533    local livefile
534    local failures=false
535
536    # Recursive base case
537    if [ -z "$file" ] ; then
538        conf_debug "conf_install base case reached"
539        return 0
540    fi
541
542    file=$(conf_rel_path "$file") || return 1
543    livefile="/${file#*/}"
544    file="${ABS_WORK}/${module}${livefile}"
545
546        # See if it even exists
547    if [ ! -e "$file" ] ; then
548        conf_debug "$file" does not appear to exist
549        conf_install $module "$@"
550        return 1
551    fi
552
553        # If we got here, figure it out
554        local owner=`conf_get_prop ${file} owner`
555        local group=`conf_get_prop ${file} group`
556        local mode=`conf_get_prop ${file} mode`
557    local symlink="`conf_get_prop ${file} symlink`"
558
559        if [ -f "$file" ] ; then
560                local opts="-o $owner -g $group -m $mode"
561        else
562                local opts="-d -o $owner -g $group -m $mode"
563        fi
564   
565    if [ -n "$symlink" ] ; then
566        local cmd="$SUDO ln -snf $symlink ${LIVE_ROOT}$livefile"
567    else
568            local cmd="$SUDO $install_cmd $opts $file ${LIVE_ROOT}$livefile"
569    fi
570
571        ${NFS_HACK:-false} && chmod o+rx ${ABS_WORK}
572        echo $cmd
573        $cmd || failures=true
574        ${NFS_HACK:-false} && chmod o-rx ${ABS_WORK}
575
576    if [ -d "$file" ] ; then
577        conf_debug "conf_install encountered a directory. Recursing."
578        conf_install $module $(find "$file" \
579            -mindepth 1 -maxdepth 1 -not -name '.svn') || failures=true
580    fi
581    conf_debug "conf_install recursing"
582        conf_install $module "$@" || failures=true
583    conf_debug "conf_install returning"
584    if $failures ; then
585        return 1
586    else
587        return 0
588    fi
589}
590
591function conf_list {
592        local file=$1
593        local module=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/.*:\1:'`
594        local livefile=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/:/:'`
595    if @SVN@ status $file | egrep -q "^\?"; then
596        echo -e "Not in confman repository\t\t${livefile}"
597        return
598    fi
599        local owner=`conf_get_prop ${file} owner`
600        local group=`conf_get_prop ${file} group`
601        local mode=`conf_get_prop ${file} mode`
602        local comment=`conf_get_prop ${file} comment`
603    local symlink=`conf_get_prop ${file} symlink`
604    [ -n "$symlink" ] && symlink=" -> $symlink"
605        echo -e "$mode\t$owner\t$group\t$comment\t\t${livefile}${symlink}"
606}
607       
608
609
610# This function assembles the singularities:
611function conf_assemble_sing {
612        local file=$1
613        local livefile="${LIVE_ROOT}${file}"
614        local tmpfile=$(conf_tmp_file) || exit 1
615    local layers="$(conf_get_recipe)"
616        local owner group mode flag livepart msg
617        for layer in $layers ; do
618                livepart="${LIVE_ROOT}${file}-${layer}"
619                myfile="${WORK_PATH}/${layer}/${file}-${layer}"
620                if [ -f $myfile ] && $SUDO [ -f $livepart ] ; then
621                                owner=`conf_get_prop ${myfile} owner`
622                                group=`conf_get_prop ${myfile} group`
623                                mode=`conf_get_prop     ${myfile} mode`
624                                comment=`conf_get_prop  ${myfile} comment`
625                        $SUDO cat $livepart >> $tmpfile
626                        $SUDO rm $livepart
627                        flag=1
628                fi
629        done
630        if [ ! -z $flag ] ; then
631                local opts="-o $owner -g $group -m $mode"
632                local cmd="$SUDO $install_cmd $opts $tmpfile $livefile"
633                echo $cmd
634                $cmd
635        fi
636        rm -f $tmpfile
637}
638
639# This function creates a symlink in your working copy and imports it
640# into confman.  If the third argument is "true", then the link is
641# forced.  Throws an erorr if link_name already exists and "force"
642# option is not used.
643# e.g. conf_ln TARGET ( LINK_NAME | DIRECTORY ) [ FORCE ]
644function conf_ln {
645    local target=$1
646    local link=$2
647    local forced=$3
648    local opts isdir linkdir rtarget rlink
649
650    if [ "$forced" == "true" ] ; then
651        opts="-f"
652    elif [ -e "$link" -a ! -d "$link" ] ; then
653        echo "$link already exists.  Did you mean to use \"-f\"?"
654        return 1
655    fi
656
657    if [ -d "$link" ] ; then
658        link="${link}/$(basename "${target}")"
659    fi
660
661    # target needs to be transformed to be relative to the link. In order
662    # to do this, we need to cannonicalize filenames relative to the
663    # working copy root
664
665    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
666    target=`${readlink_cmd} -m $target`
667    rtarget=${target#${ABS_WORK}/}
668
669    if [ "$target" = "$rtarget" ] ; then
670        echo "target $target does not appear to be in your working copy." >&4
671        return 1
672    fi
673
674    link=`${readlink_cmd} -m $link`
675    rlink="${link#${ABS_WORK}/}"
676
677    if [ "$link" = "$rlink" ] ; then
678        echo "link $link does not appear to be in your working copy." >&4
679        return 1
680    fi
681
682    linkdir="${rlink%/*}/"
683
684    while [[ ${linkdir} =~ ^.+/ ]]; do
685        rtarget="../$rtarget"
686        linkdir=${linkdir#*/}
687    done
688
689    ln -s $opts "$rtarget" "${WORK_PATH}/$rlink"
690    @SVN@ add "${WORK_PATH}/$rlink"
691}
692
693# This function generates a checkpoint called NAME for the given MODULE.
694# eg: conf_new_checkpoint MODULE NAME
695function conf_new_checkpoint {
696        local module=$1
697        local checkpoint=$2
698        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
699        local revision=`conf_revision`
700        echo $revision > $chkpath
701        @SVN@ add $chkpath
702        local msg="Created a checkpoint, ${checkpoint} for ${module} --`whoami`"
703        @SVN@ commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
704}
705
706# This function will remove the checkpoint NAME from MODULE.
707# eg: conf_rm_checkpoint MODULE NAME
708function conf_rm_checkpoint {
709        local module=$1
710        local checkpoint=$2
711        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
712        @SVN@ rm ${chkpath}
713        local msg="Removed the checkpoint ${checkpoint} from ${module} --`whoami`"
714        @SVN@ commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
715}
716
717# This function will restore the state of your module's working copy to the
718# named checkpoint. eg: conf_rollback MODULE NAME
719function conf_rollback {
720        local module=$1
721        local checkpoint=$2
722        local clock=$3
723        local modpath="${WORK_PATH}/${module}"
724        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
725        local revision
726        local date=`echo $checkpoint | ${sed_cmd} -e 's:(....)(..)(..):\1-\2-\3:'`
727
728        # Named checkpoint
729        if [ -f "${chkpath}" ] ; then
730        revision=`cat $chkpath`
731        elif [ -z $clock ] ; then               # Time checkpoint
732        revision="{${date}}"
733        else
734        clock=`echo $clock | ${sed_cmd} -e 's#(..)(..)#\1:\2#'`
735        revision="{${date}T${clock}}"
736        fi
737
738    symlinks="$symlinks `find ${module} -type l \
739        -exec ${readlink_cmd} -m {} \;`"
740
741        #svn update --revision $revision $modpath
742        @SVN@ update --revision $revision $modpath $symlinks
743}
744
745# This function will print the value of the specified property to STDOUT
746function conf_get_prop {
747        local file=$1
748        local prop=$2
749        local result=`@SVN@ propget "confman:${prop}" ${file}`
750    if [ -z "$result" ] ; then
751        file=`$readlink_cmd -m ${file}`
752        @SVN@ propget "confman:${prop}" ${file}
753    else
754        echo $result
755    fi
756}
757
758# This function will set the value of the specified property.
759function conf_set_prop {
760        local file=$1
761        local prop=$2
762        local value=$3
763        @SVN@ propset "confman:${prop}" "${value}" ${file}
764}
765
766function conf_gen_file {
767        local module=$1
768        local file=$2
769        local owner=$3
770        local group=$4
771        local mode=$5
772        local comment="$6"
773        local usefile=$7
774    local symlink="$8"
775        local myfile="${WORK_PATH}/${module}${file}"
776        local warning="${comment} ${CONF_WARNING}"
777        local morewarn="${comment} Managed under ${module} module."
778        local revision="${comment} \$Id\$"
779        echo $file
780
781        if [ -z $usefile ] ; then
782                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
783        elif head -n 1 ${usefile} | grep '^#!' >/dev/null ; then
784                                        awk "{if (NR==2)
785                                                        print \"\\n${warning}\\n${morewarn}\\n${revision}\\n\"\$0;
786                                                                else print \$0 }" ${usefile} > $myfile
787        else
788                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
789                                        cat ${usefile} >> $myfile
790        fi
791
792        @SVN@ add $myfile
793        @SVN@ ps svn:keywords "Id" $myfile
794        conf_set_prop $myfile owner $owner
795        conf_set_prop $myfile group $group
796        conf_set_prop $myfile mode      $mode
797        conf_set_prop $myfile comment "$comment"
798    conf_set_prop $myfile symlink "$symlink"
799}
800
801function conf_rm_file {
802        @SVN@ rm "$@"
803}
804
805function conf_mkdir {
806        local directory=$1
807        local owner=$2
808        local group=$3
809        local mode=$4
810        local workdir=""
811        local directories=`echo "$directory" | ${sed_cmd} -e 's:/: :g'`
812        local dir
813
814        for dir in $directories ; do
815                workdir="${workdir}/${dir}"
816                if [ ! -d ${workdir} ] ; then
817                        echo @SVN@ mkdir ${workdir}
818                        @SVN@ mkdir ${workdir}
819                        conf_set_prop $workdir owner $owner
820                        conf_set_prop $workdir group $group
821                        conf_set_prop $workdir mode $mode
822                        conf_set_prop $workdir comment "dir"
823                fi
824        done
825}
826
827function conf_remove_modules {
828    local module
829    local rc=0
830
831    for module in "$@"; do
832        conf_rmmod "$module" || rc=1
833    done
834
835    return $rc
836}
837
838function conf_rmmod {
839        local module="$1"
840    local dir
841    local rc=0
842
843    for dir in ${WORK_PATH}/{,${REPO_CHECKPTS}/}${module}; do
844        if ! [ -d "$dir" ]; then
845            echo "No such directory: $dir" >&2
846            rc=1
847        else
848            @SVN@ rm $dir || rc=1
849        fi
850    done
851
852    return $rc
853}
854
855function conf_rename {
856    local oldmod=$1
857    local newmod=$2
858    local file
859
860    @SVN@ mv ${WORK_PATH}/${oldmod} ${WORK_PATH}/${newmod} || return 1
861    @SVN@ mv ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
862        ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} || return 1
863
864    for file in ${SINGULARITIES} ; do
865        if [ -f ${WORK_PATH}/${newmod}${file}-${oldmod} ]; then
866            @SVN@ mv ${WORK_PATH}/${newmod}${file}-${oldmod} \
867                ${WORK_PATH}/${newmod}${file}-${newmod} || return 1
868        fi
869    done
870
871    for file in $(find ${WORK_PATH}/${newmod} -type f -not -path '*/.svn/*')
872    do
873        sed_i_cmd \
874            "s/(Managed under )${oldmod}( module\.)$/\1${newmod}\2/" "$file" \
875            || return 1
876    done
877
878    for file in $(find $(conf_recipe_dir) -maxdepth 1 -type f); do
879        sed_i_cmd "s/^([ \t]*)${oldmod}([ \t]*)$/\1${newmod}\2/" "$file" ||
880        return 1
881    done
882
883    @SVN@ commit -m "Renaming ${oldmod} to ${newmod}" \
884        ${WORK_PATH}/${oldmod} ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
885        ${WORK_PATH}/${newmod} ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} \
886        $(conf_recipe_dir) || return 1
887
888    return 0
889}
890
891function conf_mv {
892        local oldname=$1
893        local newname=$2
894        @SVN@ mv $oldname $newname
895}
896
897function conf_cp {
898        local oldname=$1
899        local newname=$2
900        @SVN@ cp $oldname $newname
901}
902
903function conf_diff {
904        @SVN@ diff $*
905}
906
907function conf_log {
908        @SVN@ log $*
909}
910
911# Accepts log messages on stdin until EOF
912function conf_logger {
913   logger -t "$MYNAME[$$]: $USER" 2>&4
914}
915
916function conf_chattr {
917    local attr="confman:$1"
918    shift
919    local value="$1"
920    shift
921    @SVN@ propset "$attr" "$value" "$@"
922}
923
924function conf_lsattr {
925    local file="$1"
926    shift
927    echo "$file"
928    for prop in $(@SVN@ proplist $file | grep 'confman:' | sort) ; do
929        echo -e "\t${prop#confman:}\t" $(@SVN@ propget $prop $file)
930    done
931    if [ -n "$1" ] ; then
932        conf_lsattr "$@"
933    fi
934}
935
936function conf_rmattr {
937    local attr="confman:$1"
938    shift
939    @SVN@ propdel "$attr" "$@"
940}
941
942function conf_syncheck {
943    local cmd
944    local toReturn=true
945    for file in "$@" ; do
946        cmd=$(@SVN@ propget "confman:syncheck" "$file")
947        if [ -z "$cmd" ] ; then
948            continue
949        fi
950        if ! $cmd "$file" ; then
951            echo "Syntax verification failed for $file" >&2
952            toReturn=false
953        fi
954    done
955    $toReturn
956}
957
958function conf_markclean {
959    $SUDO sh -c "echo clean > $REPO_SYNC_STATE"
960}
961
962function conf_markdirty {
963    $SUDO sh -c "echo dirty > $REPO_SYNC_STATE"
964}
965
966function conf_isclean {
967    local state=`$SUDO cat $REPO_SYNC_STATE 2>/dev/null`
968    case $state in
969        clean)  return 0 ;;
970        dirty)  return 1 ;;
971        *)      echo "Invalid state. Reporting dirty." >&2 ; return 1 ;;
972    esac
973}
974
975# Returns the current revision number of the repository on stdout
976function conf_revision {
977    @SVN@ info $(conf_meta_dir) | awk '/^Revision:/ {print $2}'
978}
979
980# Returns the revision of the last commit or install operation on stdout
981function conf_sysrev {
982    if [ -f "$REPO_ACTION" ]; then
983        cut -d ':' -f 2 $REPO_ACTION
984    else
985        echo 0
986    fi
987}
988
989# Returns the last action of the repository on stdout
990function conf_lastact {
991    if [ -f "$REPO_ACTION" ]; then
992        cut -d ':' -f 1 $REPO_ACTION
993    else
994        echo none
995    fi
996}
997
998# Records the specified action
999function conf_recordAction {
1000    local action="$1"
1001    local revision=`conf_revision`
1002    $SUDO sh -c "echo '${action}:${revision}' > $REPO_ACTION"
1003}
1004
1005# Spits the recipe, modules separated by whitespace, out on stdout
1006function conf_get_recipe {
1007    local module
1008    recipe_file="${1}"
1009
1010    if [ -z "$recipe_file" ] ; then
1011        recipe_file="$(conf_recipe_path)"
1012    fi
1013
1014    if ! [ -f "$recipe_file" ] ; then
1015            echo "Couldn't read the recipe!" >&2
1016        die
1017    fi
1018
1019    for module in `cat $recipe_file | grep "^[^#]"` ; do
1020        printf "%s " $module
1021    done
1022}
1023
1024# Prints the name of this host's recipe on stdout or returns non-zero
1025function conf_get_recipe_name {
1026    local recipe=$(cat ${RECIPE_FILE} 2>/dev/null)
1027    if [ -z "$recipe" ] ; then
1028        return 1
1029    fi
1030    echo $recipe
1031}
1032
1033# Set this host's recipe
1034function conf_set_recipe {
1035    local recipe="$1"
1036    ${SUDO} sh -c "echo $recipe > ${RECIPE_FILE}"
1037}
1038
1039function conf_recipe_create {
1040    local recipe="$1"
1041    local recipe_file="$(conf_recipe_dir)/${recipe}"
1042    if [ -f "${recipe_file}" ] ; then
1043        echo "Recipe $recipe already exists!" >&2
1044        return 1
1045    fi
1046    ${sed_cmd} -e "s:__NAME__:${recipe}:" $RECIPE_TEMPLATE > "$recipe_file"
1047    @SVN@ add "$recipe_file"
1048}
1049
1050function conf_recipe_verify {
1051    local recipe_file="$1"
1052    local module
1053    for module in $(conf_get_recipe ${recipe_file}) ; do
1054        if ! [ -d "${WORK_PATH}/${module}" ] ; then
1055            echo "Error: recipe references non-existent module $module" >&2
1056            return 1
1057        fi
1058    done
1059    return 0
1060}
1061
1062function conf_commit_recipes {
1063    local recipe recipes
1064
1065    local logfile="$1"
1066    shift
1067
1068    for recipe in "$@" ; do
1069        recipes="${recipes:+${recipes} }$(conf_recipe_dir)/${recipe}"
1070    done
1071
1072    @SVN@ commit -F "$logfile" $recipes
1073}
1074
1075function conf_remove_recipes {
1076    local recipe recipe_file
1077    local oldrecipes
1078    local rc=0
1079
1080    for recipe in "$@"; do
1081        if conf_remove_recipe "$recipe"; then
1082            recipe_file="$(conf_recipe_dir)/${recipe}"
1083            oldrecipes="${oldrecipes:+${oldrecipes} }${recipe_file}"
1084        else
1085            rc=1
1086        fi
1087    done
1088
1089    return $rc
1090}
1091
1092function conf_remove_recipe {
1093    local recipe="$1"
1094    local recipe_file="$(conf_recipe_dir)/${recipe}"
1095    if ! [ -f "${recipe_file}" ] ; then
1096        echo "Recipe $recipe doesn't exist!" >&2
1097        return 1
1098    fi
1099    @SVN@ rm "${recipe_file}" || return 1
1100    return 0
1101}
1102
1103# Implements logic for selecting logfile based on -m/-F or interactive mode
1104#   Returns 0 if the log is "complete", 1 if the log needs editing
1105#   Prints filename containing the log on stdout
1106function conf_log_message {
1107    local logfile
1108
1109    if [ -n "$LOG_FILE" ] ; then
1110        echo "$LOG_FILE"
1111        return 0
1112    fi
1113   
1114    logfile=$(conf_tmp_file)
1115    echo "$logfile"
1116
1117    if $LOG_MESSAGE_SET ; then
1118        echo "$LOG_MESSAGE" > "$logfile"
1119    else
1120        cat "$LOG_TEMPLATE" > "$logfile"
1121        return 1
1122    fi
1123    return 0
1124}
1125
1126# Because an exit doesn't help much when things are in subshells, we
1127# provide a nice, easy way to halt execution. The result is the clean
1128# exit trap gets called. We also call exit to halt execution of this
1129# subshell (otherwise some commands will still be run before the parent
1130# handles the signal and kills us off).
1131function die {
1132    kill -INT $MY_PID
1133    exit 1
1134}
1135
1136function conf_interrupt_trap {
1137    # Stderr going to logger automatically seems to be broken upon
1138    # interrupt. This redirects stderr back to the initial stderr (usually
1139    # the terminal), as it existed before confman munged file descriptors.
1140    exec 2>&4
1141    echo "Received a signal. Exiting cleanly." | conf_logger
1142        echo "Please wait until I finish cleaning up after you." >&4
1143    conf_cleanExit 1
1144}
1145
1146function conf_cleanExit {
1147        # And in case we got an interrupt during a rollout, we still want the
1148        # permissions here to be in a consistent state.
1149        ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH}
1150
1151        conf_debug "Removing leftover temp files..." >&4
1152    rm -rf $TMPDIR
1153
1154    conf_debug "Removing any system or working copy locks..." >&4
1155    conf_wcopy_is_locked && conf_unlock_wcopy ORIGINAL
1156    conf_system_is_locked && conf_unlock_system ORIGINAL
1157       
1158        # And this clears any locks we may have created on our working copy:
1159    if [ -d "${WORK_PATH}" ] && $WCOPY_DIRTY ; then
1160            conf_debug "Undoing damage to your working copy..." >&4
1161        @SVN@ cleanup ${WORK_PATH}
1162    fi
1163
1164    conf_debug "All clean. Terminating." >&4
1165
1166    trap EXIT
1167        exit ${1:-1}
1168}       
1169
1170function conf_checkgroup {
1171        GROUP_FILE=$(conf_tmp_file)
1172    GROUP_STAT=$(chgrp staff $GROUP_FILE 2>&1)
1173        rm $GROUP_FILE
1174        if [ -n "$GROUP_STAT" ] ; then
1175       return 2
1176    else
1177       return 0
1178    fi
1179}
1180
1181function conf_verify_option_set {
1182    local option="$1"
1183    local value
1184    eval value="\$${option}"
1185    if [ -z "$value" ] ; then
1186        echo "Error: ${option} must be defined in ${GCONF} or \n${UCONF}." >&2
1187        exit 1
1188    fi
1189    return 0
1190}
1191
1192function conf_require_recipe {
1193    if [ -z "$RECIPE_NAME" ] ; then
1194        echo 'Error: this action requires the recipe to be configured.' >&2
1195        echo 'Please see `confman help recipe` for more information.' >&2
1196        exit 1
1197    fi
1198}
1199
1200function conf_require_wcopy {
1201    if ! [ -d "${WORK_PATH}" ] ; then
1202        echo "Error: this action requires that your working copy exist." >&2
1203        echo 'Please see `confman help setup` for more information.' >&2
1204        die
1205    fi
1206}
1207
1208function conf_tmp_file {
1209    local file=$(${mktemp_file}) || return 1
1210    echo $file
1211    return 0
1212}
1213
1214function conf_tmp_dir {
1215    local dir=$(${mktemp_dir}) || return 1
1216    echo $dir
1217    return 0
1218}
1219
1220function conf_debug {
1221    $DEBUG && echo "$@" >&4
1222}
1223
1224function conf_warn {
1225    echo "$@" >&2
1226}
1227
1228# vim:ts=4
1229
Note: See TracBrowser for help on using the browser.