source: branches/confman-1.9/confmanlib.sh.in @ 596

Revision 596, 45.0 KB checked in by blee, 5 months ago (diff)

Merge fix for #156 into confman-1.9.

Closes #156

  • 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"
30EXPORT_REV_FILE="${REPO_DB}/last_export"
31REPO_SYNC_STATE="${REPO_DB}/state"
32RECIPE_NAME="$(cat "$RECIPE_FILE" 2>/dev/null)"
33WCOPY_DIRTY="false"
34CONF_EXPORT="false"
35LOG_MESSAGE_SET="false"
36CONF_GET_PROP_USE_FDB="false" # XXX: Until ticket #151 is resolved
37
38VERSION='@VERSION@'
39REPO_VERSION="2"
40
41# Prints the repository URI
42function conf_repo_uri {
43        local proto="${REPO_PROTOCOL%:/*}"
44        local uri user
45
46    # When we're running in export mode and the user has configured
47    # a separate REPO location for it
48    if ${CONF_EXPORT} && [ -n "$EXPORT_REPO_PATH" ] ; then
49        # Determine which options must be set
50        if [ "x${EXPORT_REPO_PROTOCOL}" = "xfile://" ] ; then
51            MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_PATH"
52        else
53            MUST_SET="EXPORT_REPO_PROTOCOL EXPORT_REPO_HOSTNAME
54                EXPORT_REPO_PATH"
55        fi
56        REPO_PROTOCOL="$EXPORT_REPO_PROTOCOL"
57        REPO_HOSTNAME="$EXPORT_REPO_HOSTNAME"
58        REPO_PATH="$EXPORT_REPO_PATH"
59    else
60        # Determine which options must be set
61        if [ "x${REPO_PROTOCOL}" = "xfile://" ] ; then
62            MUST_SET="REPO_PROTOCOL REPO_PATH"
63        else
64            MUST_SET="REPO_PROTOCOL REPO_HOSTNAME REPO_PATH"
65        fi
66    fi
67   
68    # Verify that required options have been set
69    for option in $MUST_SET ; do
70        conf_verify_option_set $option
71    done
72
73    user=${REPO_REMOTE_USER:+${REPO_REMOTE_USER}@}
74
75        case $proto in
76                file)
77                        uri="${REPO_PROTOCOL}${REPO_PATH}"
78                        ;;
79                *)
80                        uri="${REPO_PROTOCOL}${user}${REPO_HOSTNAME}${REPO_PATH}"
81                        ;;
82        esac
83
84        echo $uri
85}
86
87function conf_meta_dir {
88    echo "${WORK_PATH}/meta"
89}
90
91function conf_recipe_dir {
92    echo "$(conf_meta_dir)/recipes"
93}
94
95function conf_recipe_path {
96    echo "$(conf_recipe_dir)/${RECIPE_NAME}"
97}
98
99function conf_abswork {
100    ${readlink_cmd} -m "$WORK_PATH"
101}
102
103function conf_svncommit {
104    @SVN@ commit --with-revprop "confman:version=$VERSION" "$@"
105}
106
107# Checks out the conf tree if we don't already have it. Updates it if we do.
108function conf_checkout_tree {
109        if [ ! -d ${WORK_PATH}/.svn ] ; then
110        local path=`echo ${WORK_PATH} | ${sed_cmd} -e 's:/[^/]+$::'`
111        mkdir -p ${WORK_PATH}
112       
113        # This makes sure nobody can enter your working copy. We relax the
114        # permissions during the rollout, and restrict them again at
115        # the end.
116        chmod 700 ${WORK_PATH}
117       
118        @SVN@ checkout `conf_repo_uri`/trunk ${WORK_PATH}
119        else
120        @SVN@ update ${WORK_PATH}
121        fi
122}
123
124# I update the working copy of a given module to the repository's copy.
125function conf_update_module {
126        local module="$1"
127        @SVN@ update ${WORK_PATH}/${module}
128}
129
130# Updates the whole source tree
131function conf_update_tree {
132    if [ -z "$*" ] ; then
133        @SVN@ update ${WORK_PATH}
134    else
135        @SVN@ update "$@"
136    fi
137}
138
139# Updates the relevant portions of the source tree
140function conf_update_relevant_tree {
141    local module symlink
142    local relevant_files="" recipe args
143    recipe="$1"
144    shift
145    args="$@"
146    [ -r $(conf_recipe_dir)/${recipe} ] || exit 1
147    @SVN@ update $args $(conf_recipe_dir)/${recipe}
148    if ! [[ $args =~ .*-r.* ]]; then
149        # make sure that svn is pulling the same revision everywhere
150        args="${args} -r $(@SVN@ info $(conf_recipe_dir)/${recipe} | \
151            awk '/^Revision:/ { print $2 }')"
152    fi
153    for module in $(conf_get_recipe $(conf_recipe_dir)/${recipe}); do
154        relevant_files="$relevant_files ${WORK_PATH}/${module}"
155        for symlink in $(find ${WORK_PATH}/${module} -type l); do
156            relevant_files="$relevant_files $(@GREADLINK@ -f $symlink)"
157        done
158    done
159    [ "$relevant_files" = "" ] || @SVN@ update $args $relevant_files | grep \
160        -v -e 'revision'
161}
162
163# Functions for locking and unlocking both the system and the working copy
164function conf_wcopy_lockfile {
165    echo "${WORK_PATH}/confman.lock"
166}
167
168function conf_lock_wcopy {
169    local locktype
170    conf_require_wcopy
171
172    # If this process already has a lock on the wcopy
173    if conf_wcopy_is_locked; then
174        locktype="RECURSIVE"
175    else
176        locktype="ORIGINAL"
177    fi
178
179    ${LOCK} lock $$ $(conf_wcopy_lockfile) || die
180    WCOPY_DIRTY="true"
181    echo $locktype
182}
183
184function conf_unlock_wcopy {
185    local locktype="$1"
186
187    if [ "$locktype" = "RECURSIVE" ] ; then
188        return 0
189    elif [ "$locktype" = "ORIGINAL" ] ; then
190        ${LOCK} unlock $$ $(conf_wcopy_lockfile)
191        return $?
192    else
193        echo "Invalid lock type in conf_unlock_wcopy" >&4
194        die
195    fi
196}
197
198function conf_wcopy_is_locked {
199    ${LOCK} haslock $$ $(conf_wcopy_lockfile)
200}
201
202function conf_lock_system {
203    local locktype
204
205    # If this process already has a lock on the wcopy
206    if conf_system_is_locked; then
207        locktype="RECURSIVE"
208    else
209        locktype="ORIGINAL"
210    fi
211
212    ${SUDO} ${LOCK} lock $$ "$LOCKFILE" || die
213    echo $locktype
214}
215
216function conf_unlock_system {
217    local locktype="$1"
218
219    if [ "$locktype" = "RECURSIVE" ] ; then
220        return 0
221    elif [ "$locktype" = "ORIGINAL" ] ; then
222        ${SUDO} ${LOCK} unlock $$ "$LOCKFILE"
223        return $?
224    else
225        echo "Invalid lock type in conf_unlock_system" >&2
226        die
227    fi
228}
229
230function conf_system_is_locked {
231    ${LOCK} haslock $$ "$LOCKFILE"
232}
233
234function conf_lock_is_recursive {
235    local locktype="$1"
236    case "$locktype" in
237        ORIGINAL)   return 1;;
238        RECURSIVE)  return 0;;
239        *)          echo "Invalid lock type in conf_lock_is_recursive" >&2
240                    die
241                    ;;
242    esac
243}
244
245function confexport_lock_system {
246    "${LOCK}" lock $$ "${CONFEXPORT_LOCKFILE}" || die
247}
248
249function confexport_unlock_system {
250    "${LOCK}" unlock $$ "${CONFEXPORT_LOCKFILE}"
251}
252
253function confexport_system_is_locked {
254    "${LOCK}" haslock $$ "${CONFEXPORT_LOCKFILE}"
255}
256
257# Revert a working file
258function conf_revert {
259        @SVN@ revert $*
260}
261
262function conf_create_modules {
263    local module
264    local newmodules
265    local rc=0
266
267    for module in "$@"; do
268        if conf_create_module "$module"; then
269            # Keep track of the modules that were successfully created
270            # avoid inadvertently committing outstanding changes to
271            # pre-existing modules
272            if [ -z "$newmodules" ]; then
273                newmodules="$module"
274            else
275                newmodules="$newmodules,$module"
276            fi
277        else
278            rc=1
279        fi
280    done
281
282    # $newmodules is in the form foo,bar,baz. This eval trick forces
283    # curly brace expansion.
284    if echo "$newmodules" | grep -q ','; then
285        conf_svncommit $(eval echo ${WORK_PATH}/{$newmodules}) \
286            $(eval echo ${WORK_PATH}/${REPO_CHECKPTS}/{$newmodules}) -m \
287            "Created directory structure for $newmodules"
288    elif [ -n "$newmodules" ]; then
289        # We test -n for this case because $newmodules might
290        # be empty, i.e. no new modules were created
291        conf_svncommit ${WORK_PATH}/$newmodules \
292            ${WORK_PATH}/${REPO_CHECKPTS}/$newmodules -m \
293            "Created directory structure for $newmodules"
294    fi
295
296    return $rc
297}
298
299# Assumes that we already have core setup in our work path.
300function conf_create_module {
301    local module=$1
302
303    if [ -d ${WORK_PATH}/$module -a -d ${WORK_PATH}/${REPO_CHECKPTS}/$module ]
304    then
305        echo "Module already exists: $module" >&2
306        return 1
307    fi
308
309    @SVN@ mkdir ${WORK_PATH}/$module
310    @SVN@ mkdir ${WORK_PATH}/${REPO_CHECKPTS}/$module
311
312    return 0
313}
314
315function conf_commit_file {
316    local msg="$1"
317    shift
318    local files
319    for file in "$@"; do
320        if [ -L "$file" ]; then
321            files="$files `${readlink_cmd} -m $file`"
322        fi
323        files="$files $file"
324    done
325
326    conf_svncommit -F "$msg" $files
327}
328
329function conf_fetch {
330    local tmpfile
331
332    case ${CONF_EXPORT_STYLE} in
333        repository)
334            tmpfile=$(conf_tmp_file)
335            conf_fetch_file $tmpfile || return 1
336            ;;
337        recipe)
338            tmpfile=$(conf_tmp_file)
339            conf_fetch_file $tmpfile $(conf_get_recipe_name) || return 1
340            ;;
341        module)
342            tmpfile=$(conf_tmp_dir)
343            conf_fetch_file $tmpfile meta || return 1
344            tar -xzf $tmpfile/meta.tgz -C ${WORK_PATH}
345            for layer in $(conf_get_recipe); do
346                conf_fetch_file $tmpfile $layer || return 1
347            done
348            ;;
349        *)
350            echo "Unsupported export style in conf_fetch" >&2
351            return 1
352            ;;
353    esac
354
355    echo $tmpfile
356    return 0
357}
358
359function conf_fetch_file {
360    local proto="${CONF_EXPORT_URI%:/*}"
361    local tmpfile=$1
362    local file=$2
363    local remote
364
365    # If file is defined, munge it appropriately
366    if [ -n "$file" ]; then
367        file="/$file.tgz"
368    fi
369
370    case $proto in
371        sftp|scp)   
372            remote="${CONF_EXPORT_URI#*://}"
373            remote="${remote/\//:/}"
374
375            # XXX: Remove for confman-3.0
376            if [ -z "$CONF_FETCH_SSH_KEY" ] && [ -n "$CONF_SSH_KEY" ] ; then
377                CONF_FETCH_SSH_KEY="$CONF_SSH_KEY"
378                conf_warn "CONF_SSH_KEY is deprecated. Please define " \
379                    "CONF_FETCH_SSH_KEY instead."
380            fi
381
382            scp -p -i ${CONF_FETCH_SSH_KEY} ${CONF_FETCH_SSH_FLAGS} \
383                 "$remote$file" $tmpfile
384            ;;
385        http|https|ftp)
386            $fetch_cmd "$tmpfile" "${CONF_EXPORT_URI}$file"
387            ;;
388        file)
389            remote="${CONF_EXPORT_URI#*://}"
390            cp -p "$remote$file" $tmpfile
391            ;;
392        *)
393            echo "Unsupported Protocol in conf_fetch_file" >&2
394            return 1
395            ;;
396    esac
397
398    return 0
399}
400
401# A way to utilize the svn status feature.
402function conf_status {
403        @SVN@ status $*
404}
405
406# XXX: Until we completely change metadata formats (ticket #151), provide
407# a way to convert Subversion properties into an fdb
408function conf_convert_metadata {
409    local module="$1"
410
411    local moduledir
412    declare -A metadata
413
414    metadata["module"]="${module}"
415
416    if [ "${module}" = "meta" ]; then
417        metadata["revision"]=`conf_revision_svn`
418        filedb_write "metadata" "`conf_meta_dir`/${module}.fdb"
419        unset metadata
420        return 0
421    fi
422
423    moduledir="${WORK_PATH}/${module}"
424
425    for directory in `find -L "${moduledir}" -mindepth 1 -type d -not -name '.svn' -not -path '*/.svn/*'`; do
426        owner=`conf_get_prop ${directory} owner`
427        group=`conf_get_prop ${directory} group`
428        mode=`conf_get_prop ${directory} mode`
429
430        directory="${directory#${moduledir}}"
431        metadata["${directory}:owner"]="${owner}"
432        metadata["${directory}:group"]="${group}"
433        metadata["${directory}:mode"]="${mode}"
434    done
435
436    for file in `find -L "${moduledir}" -mindepth 1 -type f -not -path '*/.svn/*'`; do
437        owner=`conf_get_prop ${file} owner`
438        group=`conf_get_prop ${file} group`
439        mode=`conf_get_prop ${file} mode`
440        symlink="`conf_get_prop ${file} symlink`"
441
442        file="${file#${moduledir}}"
443        metadata["${file}:owner"]="${owner}"
444        metadata["${file}:group"]="${group}"
445        metadata["${file}:mode"]="${mode}"
446        metadata["${file}:symlink"]="${symlink}"
447    done
448
449    filedb_write "metadata" "`conf_meta_dir`/${module}.fdb"
450    unset metadata
451}
452
453# This exports a working copy of the repository
454function conf_export {
455    local exportpath="$1"
456    local sys_revision="$2"
457    local export_revision="$3"
458    local modules
459    local module
460    local changed_recipes
461    local changed_modules
462    local updated_recipes
463    local recipe
464    local changed_module
465    local changed_recipe
466    local recipes
467   
468    case ${CONF_EXPORT_STYLE} in
469        repository)
470            tar -czf "$exportpath" -C "${WORK_PATH}" . || return 1
471            ;;
472        module)
473            if ! ${FORCE} && ${CONF_EXPORT_INCREMENTAL}; then
474                # Get the list of changed modules
475                # TODO: Catch in-repository symlinks, ticket #144
476                cd "${WORK_PATH}"
477                changed_modules=$(@SVN@ diff -r ${sys_revision}:${export_revision} --summarize | egrep -v '^D[^/]*$' | sed 's/^[^ ][ ]*//' | cut -d '/' -f 1 | sort | uniq | egrep -v '^(meta|checkpoints)$')
478                cd - >/dev/null
479
480                # Preserve old module exports where possible
481                for module in $(ls -1 ${WORK_PATH} | egrep -v '^(\.svn|meta|checkpoints)$'); do
482                    # If this module has changed, move on to the next one
483                    for changed_module in ${changed_modules}; do
484                        if [ "${module}" = "${changed_module}" ]; then
485                            continue 2
486                        fi
487                    done
488
489                    # This module was not changed, so preserve its old export
490                    echo "Preserving old export for module: ${module}"
491                    if ${CONF_EXPORT_INCREMENTAL_LN}; then
492                        ln "${CONF_EXPORT_FILE}/${module}.tgz" "${exportpath}" || return 1
493                    else
494                        cp -p "${CONF_EXPORT_FILE}/${module}.tgz" "${exportpath}" || return 1
495                    fi
496                done
497
498                modules="${changed_modules}"
499
500                # XXX: Metadata needs to be regenerated unconditionally for now
501                modules="meta ${modules}"
502            else
503                # XXX: Until ticket #40 is resolved
504                modules=$(ls -1 ${WORK_PATH} | egrep -v '^(\.svn|checkpoints)$')
505            fi
506
507            if ${CONF_EXPORT_FILEDB}; then
508                for module in ${modules}; do
509                    echo "Converting metadata for module: ${module}"
510                    conf_convert_metadata "${module}"
511
512                    echo "Generating new export for module: ${module}"
513                    if ${CONF_EXPORT_FILEDB_PLUS_SVN}; then
514                        tar -hczf "${exportpath}/${module}.tgz" -C "${WORK_PATH}" ${module} "meta/${module}.fdb" || return 1
515                    else
516                        tar -hczf "${exportpath}/${module}.tgz" -C "${WORK_PATH}" --exclude='*.svn*' ${module} "meta/${module}.fdb" || return 1
517                    fi
518
519                    # XXX: The fdbs aren't versioned yet (ticket #151), so
520                    # it's not necessary to keep them around because they
521                    # will be regenerated anyway when the module changes
522                    rm -f "$(conf_meta_dir)/${module}.fdb" || return 1
523                done
524            else
525                for module in ${modules}; do
526                    echo "Generating new export for module: ${module}"
527                    tar -hczf "${exportpath}/${module}.tgz" -C "${WORK_PATH}" ${module} || return 1
528                done
529            fi
530            ;;
531        recipe)
532            if ! ${FORCE} && ${CONF_EXPORT_INCREMENTAL}; then
533                # Get the list of changed recipes
534                cd "$(conf_recipe_dir)"
535                changed_recipes=$(@SVN@ diff -r ${sys_revision}:${export_revision} --summarize | grep -v '^D' | sed 's/^[^ ]*[ ]*//' | cut -d '/' -f 1)
536                cd - >/dev/null
537                conf_debug "changed_recipes: ${changed_recipes}"
538
539                # Get the list of changed modules
540                # TODO: Catch in-repository symlinks, ticket #144
541                cd "${WORK_PATH}"
542                changed_modules=$(@SVN@ diff -r ${sys_revision}:${export_revision} --summarize | sed 's/^[^ ]*[ ]*//' | cut -d '/' -f 1 | sort | uniq | egrep -v '^(meta|checkpoints)$')
543                cd - >/dev/null
544                conf_debug "changed_modules: ${changed_modules}"
545
546                # Check all recipes for changes
547                for recipe in $(ls -1 "$(conf_recipe_dir)" | egrep -v '^\.svn$'); do
548                    # If this recipe is in the list of changed recipes,
549                    # there's no need to check it any further since it will
550                    # be regenerated anyway
551                    for changed_recipe in ${changed_recipes}; do
552                        if [ "${recipe}" = "${changed_recipe}" ]; then
553                            continue 2
554                        fi
555                    done
556
557                    # If an unchanged recipe references a changed module,
558                    # its export needs to be regenerated
559                    modules=$(conf_get_recipe $(conf_recipe_dir)/${recipe})
560                    for module in ${modules}; do
561                        for changed_module in ${changed_modules}; do
562                            if [ "${module}" = "${changed_module}" ]; then
563                                updated_recipes="${updated_recipes} ${recipe}"
564                                continue 3
565                            fi
566                        done
567                    done
568
569                    # This recipe has neither changed nor references a
570                    # changed module, so preserve its old export
571                    echo "Preserving old export for recipe: ${recipe}"
572                    if ${CONF_EXPORT_INCREMENTAL_LN}; then
573                        ln "${CONF_EXPORT_FILE}/${recipe}.tgz" "${exportpath}" || return 1
574                    else
575                        cp -p "${CONF_EXPORT_FILE}/${recipe}.tgz" "${exportpath}" || return 1
576                    fi
577                done
578                conf_debug "updated_recipes: ${updated_recipes}"
579
580                modules="${changed_modules}"
581                recipes="${changed_recipes} ${updated_recipes}"
582
583                # XXX: Metadata needs to be regenerated unconditionally for now
584                modules="meta ${modules}"
585            else
586                # XXX: Until ticket #40 is resolved
587                modules=`ls -1 ${WORK_PATH} | egrep -v '^(\.svn|checkpoints)$'`
588                recipes=$(ls -1 "$(conf_recipe_dir)"| egrep -v '^\.svn$')
589            fi
590
591            if ${CONF_EXPORT_FILEDB}; then
592                for module in ${modules}; do
593                    echo "Regenerating metadata for module: ${module}"
594                    conf_convert_metadata "${module}"
595                done
596
597                for recipe in ${recipes}; do
598                    echo "Generating new export for recipe: ${recipe}"
599                    modules=$(conf_get_recipe $(conf_recipe_dir)/${recipe})
600                    if ${CONF_EXPORT_FILEDB_PLUS_SVN}; then
601                        tar -hczf "${exportpath}/${recipe}.tgz" -C "${WORK_PATH}" "meta/meta.fdb" "meta/.svn/entries" "meta/recipes/${recipe}" $(for module in ${modules}; do echo "meta/${module}.fdb"; done) ${modules} || return 1
602                    else
603                        tar -hczf "${exportpath}/${recipe}.tgz" -C "${WORK_PATH}" --exclude='*.svn*' "meta/meta.fdb" "meta/recipes/${recipe}" $(for module in ${modules}; do echo "meta/${module}.fdb"; done) ${modules} || return 1
604                    fi
605                done
606            else
607                for recipe in ${recipes}; do
608                    echo "Generating new export for recipe: ${recipe}"
609                    modules=$(conf_get_recipe $(conf_recipe_dir)/${recipe})
610                    tar -hczf "${exportpath}/${recipe}.tgz" -C "${WORK_PATH}" "meta/.svn/entries" "meta/recipes/${recipe}" ${modules} || return 1
611                done
612            fi
613            ;;
614        *)
615            echo "Unsupported export style in conf_export" >&2
616            return 1
617            ;;
618    esac
619
620    return 0
621}
622
623# Test to see if the pathname has been added to a statefile yet
624function conf_beenthere {
625    local statefile="$1"
626    local pathname="$2"
627    fgrep -xq "$pathname" "$statefile"
628}
629
630# Record this file in the given statefile
631function conf_gothere {
632    local statefile="$1"
633    local pathname="$2"
634    echo "$pathname" >> $statefile
635}
636
637# Roll out the specified module, optionally at the specified checkpoint
638# A statefile is a place to record the files that have been rolled out to
639# prevent a lower-priority module from installing a committed file.
640# eg:   conf_rollout MODULE STATEFILE [checkpoint]
641function conf_rollout {
642        local module="$1"
643    local statefile="$2"
644        if [ -z $3 ] ; then
645                local moduledir="${WORK_PATH}/$module"
646        else
647                local moduledir="${WORK_PATH}/checkpoints/${module}/${2}"
648        fi
649        # This is a hack to work around NFS home dirs, for now:
650        ${NFS_HACK:-false} && chmod o+rx ${WORK_PATH}
651
652    # XXX: Until ticket #151 is resolved, prefer to use the fdb metadata
653    # when it is available
654    if ! ${CONF_GET_PROP_USE_FDB}; then
655        # Test to see if the SVN working copy is out of date
656        if ! @SVN@ info $moduledir >/dev/null ; then
657            echo "Error: $moduledir isn't usable by svn." >&2
658            exit 1
659        fi
660    fi
661
662        for directory in `find -L $moduledir -mindepth 1 -type d | grep -v "\.svn"`;
663    do
664                local livedir=`echo $directory | ${sed_cmd} -e "s:$moduledir::"`
665                livedir="${LIVE_ROOT}${livedir}"
666        if ! conf_beenthere $statefile "$livedir" ; then
667                    local owner=`conf_get_prop ${directory} owner`
668                    local group=`conf_get_prop ${directory} group`
669                    local mode=`conf_get_prop ${directory} mode`
670                    local opts="-d -o $owner -g $group -m $mode"
671                    local cmd="$SUDO $install_cmd $opts $livedir"
672                    echo $cmd
673            conf_gothere $statefile "$livedir"
674                    $cmd
675        else
676            conf_debug "Ignoring directory ${livedir} in module ${module}"
677        fi
678        done   
679        for file in `find -L $moduledir -type f | grep -v "\.svn"` ; do
680                local livefile=`echo "$file" | ${sed_cmd} -e "s:$moduledir::"`
681        livefile="${LIVE_ROOT}${livefile}"
682        if ! conf_beenthere $statefile "$livefile" ; then
683            local owner=`conf_get_prop ${file} owner`
684            local group=`conf_get_prop ${file} group`
685            local mode=`conf_get_prop ${file} mode`
686            local symlink="`conf_get_prop ${file} symlink`"
687            file=`$readlink_cmd -m $file`
688            if [ -n "$symlink" ]; then
689                local cmd="$SUDO ln -snf $symlink $livefile"
690            else
691                        local opts="-o $owner -g $group -m $mode"
692                        local cmd="$SUDO $install_cmd $opts $file $livefile"
693            fi
694                    echo $cmd
695            conf_gothere $statefile "$livefile"
696                    $cmd
697        else
698            conf_debug "Ignoring file ${livefile} in module ${module}"
699        fi
700        done
701        # This is a hack to work around NFS home dirs, for now:
702        ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH}
703    return 0
704}
705
706# Takes a working copy file path and returns the corresponding live file path
707function conf_livefile {
708    local file="$1"
709    local path=$(conf_rel_path "$file") || return
710    echo /${path#*/}
711}
712
713# Takes a working copy file path and returns whether it is a singularity
714function conf_wfile_is_singularity {
715    local file=$(${readlink_cmd} -m "$1")
716    local module=$(conf_wfile_module $file)
717    local livefile=$(conf_livefile $file)
718    local sing
719    for sing in $SINGULARITIES ; do
720        if [ "$sing" = ${livefile%-$module} ] ; then
721            return 0
722        fi
723    done
724    return 1
725}
726
727# Takes a working copy file path and returns the associated module
728function conf_wfile_module {
729    local file=$(${readlink_cmd} -m "$1")
730    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
731    local module=${file#${ABS_WORK}/}
732    module=${module%%/*}
733    echo "$module"
734}
735
736# Takes a file as an argument and returns the path of the file relative
737# to WORK_PATH/ABS_WORK
738function conf_rel_path {
739    local file="$1"
740    local result
741
742    if ! result=$(conf_rel_path_helper "$file") ; then
743        echo "$file" does not appear to be located within $WORK_PATH >&2
744        return 1
745    fi
746
747    echo ${result#/}
748    return 0
749}
750
751# Recursive helper for determining conf_rel_path
752function conf_rel_path_helper {
753    local file="$1"
754    local target=$(${readlink_cmd} -m "$file")
755    local ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
756    local dir rc sofar
757
758    if [ "$target" = "$ABS_WORK" ] ; then
759        return 0
760    elif [ "$file" = "/" ] ; then
761        return 1
762    else
763        dir=$(dirname "$file")
764        case "$dir" in
765            ".")    dir=$(pwd);;
766            "..")   dir=$(cd ..; pwd);;
767        esac
768        sofar=$(conf_rel_path "$dir") || return $?
769        printf "%s/%s" "$sofar" $(basename "$file")
770        return 0
771    fi
772}
773
774function conf_install {
775    conf_debug conf_install called with args "$@"
776    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
777        local module="$1"
778    local statefile="$2"
779    local file="$3"
780    shift 3
781    local livefile
782    local failures=false
783
784    # Recursive base case
785    if [ -z "$file" ] ; then
786        conf_debug "conf_install base case reached"
787        return 0
788    fi
789
790    file=$(conf_rel_path "$file") || return 1
791    livefile="/${file#*/}"
792    file="${ABS_WORK}/${module}${livefile}"
793    livefile="${LIVE_ROOT}${livefile}"
794
795        # See if it even exists
796    if [ ! -e "$file" ] ; then
797        conf_debug "$file" does not appear to exist
798        conf_install $module $statefile "$@"
799        return 1
800    fi
801
802        # If we got here, figure it out
803        local owner=`conf_get_prop ${file} owner`
804        local group=`conf_get_prop ${file} group`
805        local mode=`conf_get_prop ${file} mode`
806    local symlink="`conf_get_prop ${file} symlink`"
807
808        if [ -f "$file" ] ; then
809                local opts="-o $owner -g $group -m $mode"
810        else
811                local opts="-d -o $owner -g $group -m $mode"
812        fi
813   
814    if [ -n "$symlink" ] ; then
815        local cmd="$SUDO ln -snf $symlink $livefile"
816    else
817            local cmd="$SUDO $install_cmd $opts $file $livefile"
818    fi
819
820    if ! conf_beenthere $statefile "$livefile" ; then
821            ${NFS_HACK:-false} && chmod o+rx ${ABS_WORK}
822            echo $cmd
823        conf_gothere $statefile "$livefile"
824            $cmd || failures=true
825            ${NFS_HACK:-false} && chmod o-rx ${ABS_WORK}
826    else
827        conf_debug "Ignoring file ${livefile} in module ${module}"
828    fi
829
830    if [ -d "$file" ] ; then
831        conf_debug "conf_install encountered a directory. Recursing."
832        conf_install $module $statefile $(find "$file" \
833            -mindepth 1 -maxdepth 1 -not -name '.svn') || failures=true
834    fi
835    conf_debug "conf_install recursing"
836        conf_install $module $statefile "$@" || failures=true
837    conf_debug "conf_install returning"
838    if $failures ; then
839        return 1
840    else
841        return 0
842    fi
843}
844
845function conf_list {
846        local file=$1
847        local module=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/.*:\1:'`
848        local livefile=`echo ${file#$WORK_PATH} | ${sed_cmd} -e 's:/([^/]+)/:/:'`
849    if @SVN@ status $file | egrep -q "^\?"; then
850        echo -e "Not in confman repository\t\t${livefile}"
851        return
852    fi
853        local owner=`conf_get_prop ${file} owner`
854        local group=`conf_get_prop ${file} group`
855        local mode=`conf_get_prop ${file} mode`
856        local comment=`conf_get_prop ${file} comment`
857    local symlink=`conf_get_prop ${file} symlink`
858    [ -n "$symlink" ] && symlink=" -> $symlink"
859        echo -e "$mode\t$owner\t$group\t$comment\t\t${livefile}${symlink}"
860}
861       
862
863
864# This function assembles the singularities:
865function conf_assemble_sing {
866        local file=$1
867        local livefile="${LIVE_ROOT}${file}"
868        local tmpfile=$(conf_tmp_file) || exit 1
869    local layers="$(conf_get_recipe)"
870        local owner group mode flag livepart msg
871        for layer in $layers ; do
872                livepart="${LIVE_ROOT}${file}-${layer}"
873                myfile="${WORK_PATH}/${layer}/${file}-${layer}"
874                if [ -f $myfile ] && $SUDO [ -f $livepart ] ; then
875            eval `$SUDO ${stat_cmd} "${stat_opts}" "${livepart}"`
876                        $SUDO cat $livepart >> $tmpfile
877                        $SUDO rm $livepart
878                        flag=1
879                fi
880        done
881        if [ ! -z $flag ] ; then
882                local opts="-o $owner -g $group -m $mode"
883                local cmd="$SUDO $install_cmd $opts $tmpfile $livefile"
884                echo $cmd
885                $cmd
886        fi
887        rm -f $tmpfile
888}
889
890# This function creates a symlink in your working copy and imports it
891# into confman.  If the third argument is "true", then the link is
892# forced.  Throws an erorr if link_name already exists and "force"
893# option is not used.
894# e.g. conf_ln TARGET ( LINK_NAME | DIRECTORY ) [ FORCE ]
895function conf_ln {
896    local target=$1
897    local link=$2
898    local forced=$3
899    local opts isdir linkdir rtarget rlink
900
901    if [ "$forced" == "true" ] ; then
902        opts="-f"
903    elif [ -e "$link" -a ! -d "$link" ] ; then
904        echo "$link already exists.  Did you mean to use \"-f\"?"
905        return 1
906    fi
907
908    if [ -d "$link" ] ; then
909        link="${link}/$(basename "${target}")"
910    fi
911
912    # target needs to be transformed to be relative to the link. In order
913    # to do this, we need to cannonicalize filenames relative to the
914    # working copy root
915
916    ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH")
917    target=`${readlink_cmd} -m $target`
918    rtarget=${target#${ABS_WORK}/}
919
920    if [ "$target" = "$rtarget" ] ; then
921        echo "target $target does not appear to be in your working copy." >&4
922        return 1
923    fi
924
925    link=`${readlink_cmd} -m $link`
926    rlink="${link#${ABS_WORK}/}"
927
928    if [ "$link" = "$rlink" ] ; then
929        echo "link $link does not appear to be in your working copy." >&4
930        return 1
931    fi
932
933    linkdir="${rlink%/*}/"
934
935    while [[ ${linkdir} =~ ^.+/ ]]; do
936        rtarget="../$rtarget"
937        linkdir=${linkdir#*/}
938    done
939
940    ln -s $opts "$rtarget" "${WORK_PATH}/$rlink"
941    @SVN@ add "${WORK_PATH}/$rlink"
942}
943
944# This function generates a checkpoint called NAME for the given MODULE.
945# eg: conf_new_checkpoint MODULE NAME
946function conf_new_checkpoint {
947        local module=$1
948        local checkpoint=$2
949        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
950        local revision=`conf_revision`
951        echo $revision > $chkpath
952        @SVN@ add $chkpath
953        local msg="Created a checkpoint, ${checkpoint} for ${module} --`whoami`"
954        conf_svncommit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
955}
956
957# This function will remove the checkpoint NAME from MODULE.
958# eg: conf_rm_checkpoint MODULE NAME
959function conf_rm_checkpoint {
960        local module=$1
961        local checkpoint=$2
962        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
963        @SVN@ rm ${chkpath}
964        local msg="Removed the checkpoint ${checkpoint} from ${module} --`whoami`"
965        conf_svncommit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
966}
967
968# This function will restore the state of your module's working copy to the
969# named checkpoint. eg: conf_rollback MODULE NAME
970function conf_rollback {
971        local module=$1
972        local checkpoint=$2
973        local clock=$3
974        local modpath="${WORK_PATH}/${module}"
975        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
976        local revision
977        local date=`echo $checkpoint | ${sed_cmd} -e 's:(....)(..)(..):\1-\2-\3:'`
978
979        # Named checkpoint
980        if [ -f "${chkpath}" ] ; then
981        revision=`cat $chkpath`
982        elif [ -z $clock ] ; then               # Time checkpoint
983        revision="{${date}}"
984        else
985        clock=`echo $clock | ${sed_cmd} -e 's#(..)(..)#\1:\2#'`
986        revision="{${date}T${clock}}"
987        fi
988
989    symlinks="$symlinks `find ${module} -type l \
990        -exec ${readlink_cmd} -m {} \;`"
991
992        #svn update --revision $revision $modpath
993        @SVN@ update --revision $revision $modpath $symlinks
994}
995
996# This function will print the value of the specified property to STDOUT
997function conf_get_prop {
998    local file="$1"
999    local prop="$2"
1000   
1001    local moduledir
1002    local result
1003   
1004    # XXX: Until ticket #151 is resolved, prefer to use the fdb metadata
1005    # when it is available
1006    if ${CONF_GET_PROP_USE_FDB}; then
1007        moduledir="${WORK_PATH}/${metadata[module]}"
1008        file="${file#${moduledir}}"
1009        result="${metadata[${file}:${prop}]}"
1010    else
1011        result="`@SVN@ propget "confman:${prop}" ${file}`"
1012        if [ -z "$result" ] ; then
1013            file=`$readlink_cmd -m ${file}`
1014            result="`@SVN@ propget "confman:${prop}" ${file}`"
1015        fi
1016    fi
1017   
1018    echo "${result}"
1019}
1020
1021# This function will set the value of the specified property.
1022function conf_set_prop {
1023        local file=$1
1024        local prop=$2
1025        local value=$3
1026        @SVN@ propset "confman:${prop}" "${value}" ${file}
1027}
1028
1029function conf_gen_file {
1030        local module=$1
1031        local file=$2
1032        local owner=$3
1033        local group=$4
1034        local mode=$5
1035        local comment="$6"
1036        local usefile=$7
1037    local symlink="$8"
1038        local myfile="${WORK_PATH}/${module}${file}"
1039        local warning="${comment} ${CONF_WARNING}"
1040        local morewarn="${comment} Managed under ${module} module."
1041        local revision="${comment} \$Id\$"
1042        echo $file
1043
1044        if [ -z "${usefile}" ] ; then
1045                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
1046        elif head -n 1 ${usefile} | grep '^#!' >/dev/null ; then
1047                                        awk "{if (NR==2)
1048                                                        print \"\\n${warning}\\n${morewarn}\\n${revision}\\n\"\$0;
1049                                                                else print \$0 }" ${usefile} > $myfile
1050        else
1051                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
1052                                        cat ${usefile} >> $myfile
1053        fi
1054
1055        @SVN@ add $myfile
1056        @SVN@ ps svn:keywords "Id" $myfile
1057        conf_set_prop $myfile owner $owner
1058        conf_set_prop $myfile group $group
1059        conf_set_prop $myfile mode      $mode
1060        conf_set_prop $myfile comment "$comment"
1061    conf_set_prop $myfile symlink "$symlink"
1062}
1063
1064function conf_rm_file {
1065        @SVN@ rm "$@"
1066}
1067
1068function conf_mkdir {
1069        local directory=$1
1070        local owner=$2
1071        local group=$3
1072        local mode=$4
1073        local workdir=""
1074        local directories=`echo "$directory" | ${sed_cmd} -e 's:/: :g'`
1075        local dir
1076
1077        for dir in $directories ; do
1078                workdir="${workdir}/${dir}"
1079                if [ ! -d ${workdir} ] ; then
1080                        echo @SVN@ mkdir ${workdir}
1081                        @SVN@ mkdir ${workdir}
1082                        conf_set_prop $workdir owner $owner
1083                        conf_set_prop $workdir group $group
1084                        conf_set_prop $workdir mode $mode
1085                        conf_set_prop $workdir comment "dir"
1086                fi
1087        done
1088}
1089
1090function conf_remove_modules {
1091    local module
1092    local rc=0
1093
1094    for module in "$@"; do
1095        conf_rmmod "$module" || rc=1
1096    done
1097
1098    return $rc
1099}
1100
1101function conf_rmmod {
1102        local module="$1"
1103    local dir
1104    local rc=0
1105
1106    for dir in ${WORK_PATH}/{,${REPO_CHECKPTS}/}${module}; do
1107        if ! [ -d "$dir" ]; then
1108            echo "No such directory: $dir" >&2
1109            rc=1
1110        else
1111            @SVN@ rm $dir || rc=1
1112        fi
1113    done
1114
1115    return $rc
1116}
1117
1118function conf_rename {
1119    local oldmod=$1
1120    local newmod=$2
1121    local file
1122
1123    @SVN@ mv ${WORK_PATH}/${oldmod} ${WORK_PATH}/${newmod} || return 1
1124    @SVN@ mv ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
1125        ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} || return 1
1126
1127    for file in ${SINGULARITIES} ; do
1128        if [ -f ${WORK_PATH}/${newmod}${file}-${oldmod} ]; then
1129            @SVN@ mv ${WORK_PATH}/${newmod}${file}-${oldmod} \
1130                ${WORK_PATH}/${newmod}${file}-${newmod} || return 1
1131        fi
1132    done
1133
1134    for file in $(find ${WORK_PATH}/${newmod} -type f -not -path '*/.svn/*')
1135    do
1136        sed_i_cmd \
1137            "s/(Managed under )${oldmod}( module\.)$/\1${newmod}\2/" "$file" \
1138            || return 1
1139    done
1140
1141    for file in $(find $(conf_recipe_dir) -maxdepth 1 -type f); do
1142        sed_i_cmd "s/^([ \t]*)${oldmod}([ \t]*)$/\1${newmod}\2/" "$file" ||
1143        return 1
1144    done
1145
1146    conf_svncommit -m "Renaming ${oldmod} to ${newmod}" \
1147        ${WORK_PATH}/${oldmod} ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
1148        ${WORK_PATH}/${newmod} ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} \
1149        $(conf_recipe_dir) || return 1
1150
1151    return 0
1152}
1153
1154function conf_mv {
1155        local oldname=$1
1156        local newname=$2
1157        @SVN@ mv $oldname $newname
1158}
1159
1160function conf_cp {
1161        local oldname=$1
1162        local newname=$2
1163        @SVN@ cp $oldname $newname
1164}
1165
1166function conf_diff {
1167        @SVN@ diff $*
1168}
1169
1170function conf_log {
1171        @SVN@ log $*
1172}
1173
1174# Accepts log messages on stdin until EOF
1175function conf_logger {
1176   logger -t "$MYNAME[$$]: $USER" 2>&4
1177}
1178
1179function conf_chattr {
1180    local attr="confman:$1"
1181    shift
1182    local value="$1"
1183    shift
1184    @SVN@ propset "$attr" "$value" "$@"
1185}
1186
1187function conf_lsattr {
1188    local file="$1"
1189    shift
1190    echo "$file"
1191    for prop in $(@SVN@ proplist $file | grep 'confman:' | sort) ; do
1192        echo -e "\t${prop#confman:}\t" "$(@SVN@ propget $prop $file)"
1193    done
1194    if [ -n "$1" ] ; then
1195        conf_lsattr "$@"
1196    fi
1197}
1198
1199function conf_rmattr {
1200    local attr="confman:$1"
1201    shift
1202    @SVN@ propdel "$attr" "$@"
1203}
1204
1205function conf_syncheck {
1206    local cmd
1207    local toReturn=true
1208    for file in "$@" ; do
1209        cmd=$(@SVN@ propget "confman:syncheck" "$file")
1210        if [ -z "$cmd" ] ; then
1211            continue
1212        fi
1213        if ! $cmd "$file" ; then
1214            echo "Syntax verification failed for $file" >&2
1215            toReturn=false
1216        fi
1217    done
1218    $toReturn
1219}
1220
1221function conf_markclean {
1222    $SUDO sh -c "echo clean > $REPO_SYNC_STATE"
1223}
1224
1225function conf_markdirty {
1226    $SUDO sh -c "echo dirty > $REPO_SYNC_STATE"
1227}
1228
1229function conf_isclean {
1230    local state=`$SUDO cat $REPO_SYNC_STATE 2>/dev/null`
1231    case $state in
1232        clean)  return 0 ;;
1233        dirty)  return 1 ;;
1234        *)      echo "Invalid state. Reporting dirty." >&2 ; return 1 ;;
1235    esac
1236}
1237
1238# Returns the current revision number of the repository on stdout
1239function conf_revision {
1240    # XXX: Until ticket #151 is resolved, prefer to use the fdb
1241    # when it is available
1242    if [ -r "$(conf_meta_dir)/meta.fdb" ]; then
1243        conf_revision_fdb "$@"
1244    else
1245        conf_revision_svn "$@"
1246    fi
1247}
1248
1249# Returns the current revision number of the repository using Subversion
1250function conf_revision_svn {
1251    @SVN@ info "$(conf_meta_dir)" | awk '/^Revision:/ {print $2}'
1252}
1253
1254# Returns the current revision number of the repository using filedb
1255function conf_revision_fdb {
1256    declare -A metadata
1257    filedb_load "$(conf_meta_dir)/meta.fdb" metadata
1258    echo "${metadata["revision"]}"
1259    unset metadata
1260}
1261
1262# Returns the revision of the last commit or install operation on stdout
1263function conf_sysrev {
1264    if [ -f "$REPO_ACTION" ]; then
1265        cut -d ':' -f 2 $REPO_ACTION
1266    else
1267        echo 0
1268    fi
1269}
1270
1271# Returns the revision of the last export operation on stdout
1272function conf_export_sysrev {
1273    local export_sysrev
1274    local rv
1275
1276    export_sysrev="none:0"
1277    rv=0
1278
1279    if [ -f "${EXPORT_REV_FILE}" ]; then
1280        if ! export_sysrev=$(cat "${EXPORT_REV_FILE}"); then
1281            echo "Could not read file: ${EXPORT_REV_FILE}" >&2
1282            export_sysrev="none:0"
1283            rv=1
1284        fi
1285    fi
1286
1287    echo "${export_sysrev}"
1288    return ${rv}
1289}
1290
1291# Returns the last action of the repository on stdout
1292function conf_lastact {
1293    if [ -f "$REPO_ACTION" ]; then
1294        cut -d ':' -f 1 $REPO_ACTION
1295    else
1296        echo none
1297    fi
1298}
1299
1300# Records the specified action
1301function conf_recordAction {
1302    local action="$1"
1303    local revision=`conf_revision`
1304    $SUDO sh -c "echo '${action}:${revision}' > $REPO_ACTION"
1305}
1306
1307# Records the export operation
1308function conf_record_export {
1309    local revision=$(conf_revision)
1310
1311    if ! echo "${CONF_EXPORT_STYLE}:${revision}" > "${EXPORT_REV_FILE}"; then
1312            echo "Could not record export to file: ${EXPORT_REV_FILE}" >&2
1313            return 1
1314    fi
1315}
1316
1317# Gets a reversed copy of the recipe
1318function conf_get_reverse_recipe {
1319    local fwd_recipe=$(conf_get_recipe "$@")
1320    local rev_recipe
1321    for module in $fwd_recipe ; do
1322        rev_recipe="$module $rev_recipe"
1323    done
1324    echo $rev_recipe
1325}
1326
1327# Spits the recipe, modules separated by whitespace, out on stdout
1328function conf_get_recipe {
1329    local module
1330    recipe_file="${1}"
1331
1332    if [ -z "$recipe_file" ] ; then
1333        recipe_file="$(conf_recipe_path)"
1334    fi
1335
1336    if ! [ -f "$recipe_file" ] ; then
1337            echo "Couldn't read the recipe!" >&2
1338        die
1339    fi
1340
1341    for module in `cat $recipe_file | grep "^[^#]"` ; do
1342        printf "%s " $module
1343    done
1344}
1345
1346# Prints the name of this host's recipe on stdout or returns non-zero
1347function conf_get_recipe_name {
1348    local recipe=$(cat ${RECIPE_FILE} 2>/dev/null)
1349    if [ -z "$recipe" ] ; then
1350        return 1
1351    fi
1352    echo $recipe
1353}
1354
1355# Set this host's recipe
1356function conf_set_recipe {
1357    local recipe="$1"
1358    ${SUDO} sh -c "echo $recipe > ${RECIPE_FILE}"
1359}
1360
1361function conf_recipe_create {
1362    local recipe="$1"
1363    local recipe_file="$(conf_recipe_dir)/${recipe}"
1364    if [ -f "${recipe_file}" ] ; then
1365        echo "Recipe $recipe already exists!" >&2
1366        return 1
1367    fi
1368    ${sed_cmd} -e "s:__NAME__:${recipe}:" $RECIPE_TEMPLATE > "$recipe_file"
1369    @SVN@ add "$recipe_file"
1370}
1371
1372function conf_recipe_verify {
1373    local recipe_file="$1"
1374    local module
1375    for module in $(conf_get_recipe ${recipe_file}) ; do
1376        if ! [ -d "${WORK_PATH}/${module}" ] ; then
1377            echo "Error: recipe references non-existent module $module" >&2
1378            return 1
1379        fi
1380    done
1381    return 0
1382}
1383
1384function conf_commit_recipes {
1385    local recipe recipes
1386
1387    local logfile="$1"
1388    shift
1389
1390    for recipe in "$@" ; do
1391        recipes="${recipes:+${recipes} }$(conf_recipe_dir)/${recipe}"
1392    done
1393
1394    conf_svncommit -F "$logfile" $recipes
1395}
1396
1397function conf_remove_recipes {
1398    local recipe recipe_file
1399    local oldrecipes
1400    local rc=0
1401
1402    for recipe in "$@"; do
1403        if conf_remove_recipe "$recipe"; then
1404            recipe_file="$(conf_recipe_dir)/${recipe}"
1405            oldrecipes="${oldrecipes:+${oldrecipes} }${recipe_file}"
1406        else
1407            rc=1
1408        fi
1409    done
1410
1411    return $rc
1412}
1413
1414function conf_remove_recipe {
1415    local recipe="$1"
1416    local recipe_file="$(conf_recipe_dir)/${recipe}"
1417    if ! [ -f "${recipe_file}" ] ; then
1418        echo "Recipe $recipe doesn't exist!" >&2
1419        return 1
1420    fi
1421    @SVN@ rm "${recipe_file}" || return 1
1422    return 0
1423}
1424
1425# Implements logic for selecting logfile based on -m/-F or interactive mode
1426#   Returns 0 if the log is "complete", 1 if the log needs editing
1427#   Prints filename containing the log on stdout
1428function conf_log_message {
1429    local logfile
1430
1431    if [ -n "$LOG_FILE" ] ; then
1432        echo "$LOG_FILE"
1433        return 0
1434    fi
1435   
1436    logfile=$(conf_tmp_file)
1437    echo "$logfile"
1438
1439    if $LOG_MESSAGE_SET ; then
1440        echo "$LOG_MESSAGE" > "$logfile"
1441    else
1442        cat "$LOG_TEMPLATE" > "$logfile"
1443        return 1
1444    fi
1445    return 0
1446}
1447
1448# Because an exit doesn't help much when things are in subshells, we
1449# provide a nice, easy way to halt execution. The result is the clean
1450# exit trap gets called. We also call exit to halt execution of this
1451# subshell (otherwise some commands will still be run before the parent
1452# handles the signal and kills us off).
1453function die {
1454    kill -INT $MY_PID
1455    exit 1
1456}
1457
1458function conf_interrupt_trap {
1459    # Stderr going to logger automatically seems to be broken upon
1460    # interrupt. This redirects stderr back to the initial stderr (usually
1461    # the terminal), as it existed before confman munged file descriptors.
1462    exec 2>&4
1463    echo "Received a signal. Exiting cleanly." | conf_logger
1464        echo "Please wait until I finish cleaning up after you." >&4
1465    conf_cleanExit 1
1466}
1467
1468function conf_cleanExit {
1469        # And in case we got an interrupt during a rollout, we still want the
1470        # permissions here to be in a consistent state.
1471        ${NFS_HACK:-false} && chmod o-rx ${WORK_PATH}
1472
1473        conf_debug "Removing leftover temp files..." >&4
1474    rm -rf $TMPDIR
1475
1476    conf_debug "Removing any system or working copy locks..." >&4
1477    if ${CONF_EXPORT}; then
1478        confexport_system_is_locked && confexport_unlock_system
1479    else
1480        conf_wcopy_is_locked && conf_unlock_wcopy ORIGINAL
1481        conf_system_is_locked && conf_unlock_system ORIGINAL
1482    fi
1483       
1484        # And this clears any locks we may have created on our working copy:
1485    if [ -d "${WORK_PATH}" ] && $WCOPY_DIRTY ; then
1486            conf_debug "Undoing damage to your working copy..." >&4
1487        @SVN@ cleanup ${WORK_PATH}
1488    fi
1489
1490    conf_debug "All clean. Terminating." >&4
1491
1492    trap EXIT
1493        exit ${1:-1}
1494}       
1495
1496function conf_checkgroup {
1497        GROUP_FILE=$(conf_tmp_file)
1498    GROUP_STAT=$(chgrp staff $GROUP_FILE 2>&1)
1499        rm $GROUP_FILE
1500        if [ -n "$GROUP_STAT" ] ; then
1501       return 2
1502    else
1503       return 0
1504    fi
1505}
1506
1507function conf_verify_option_set {
1508    local option="$1"
1509    local value
1510    eval value="\$${option}"
1511    if [ -z "$value" ] ; then
1512        echo "Error: ${option} must be defined in ${GCONF} or \n${UCONF}." >&2
1513        exit 1
1514    fi
1515    return 0
1516}
1517
1518function conf_require_recipe {
1519    if [ -z "$RECIPE_NAME" ] ; then
1520        echo 'Error: this action requires the recipe to be configured.' >&2
1521        echo 'Please see `confman help recipe` for more information.' >&2
1522        exit 1
1523    fi
1524}
1525
1526function conf_require_wcopy {
1527    if ! [ -d "${WORK_PATH}" ] ; then
1528        echo "Error: this action requires that your working copy exist." >&2
1529        echo 'Please see `confman help setup` for more information.' >&2
1530        die
1531    fi
1532}
1533
1534function conf_tmp_file {
1535    local file=$(${mktemp_file}) || return 1
1536    echo $file
1537    return 0
1538}
1539
1540function conf_tmp_dir {
1541    local dir=$(${mktemp_dir}) || return 1
1542    echo $dir
1543    return 0
1544}
1545
1546function conf_debug {
1547    $DEBUG && echo "$@" >&4
1548}
1549
1550function conf_warn {
1551    echo "$@" >&2
1552}
1553
1554# vim:ts=4
1555
Note: See TracBrowser for help on using the repository browser.