source: tags/confman-1.5.6a1/confmanlib.sh @ 173

Revision 173, 16.4 KB checked in by terrio, 4 years ago (diff)

Bug fix for singularities that aren't world-readable

  • Property rc:mode set to 0444
  • Property svn:keywords set to Id
Line 
1#! /bin/bash
2#
3# This file provides shell libraries to our configuration management system.
4# It will be sourced by the relevant Rescomp scripts.
5#
6# Author: Chris Cowart <ccowart@rescomp.berkeley.edu>
7# Date: 30 March 2006
8#
9# $Id$
10
11REPO_URI="${REPO_PROTOCOL}${REPO_HOSTNAME}${REPO_PATH}"
12REPO_ACTION="${REPO_DB}/last_action"
13REPO_SYNC_STATE="${REPO_DB}/state"
14
15# Checks out the conf tree if we don't already have it. Updates it if we do.
16function conf_checkout_tree {
17        if [ ! -d ${WORK_PATH}/.svn ] ; then
18                        local path=`echo ${WORK_PATH} | sed -E 's:/[^/]+$::'`
19                        mkdir -p ${WORK_PATH}
20
21                        # This makes sure nobody can enter your working copy. We relax the
22                        # permissions during the rollout, and restrict them again at
23                        # the end.
24                        chmod 700 ${WORK_PATH}
25
26                        svn checkout ${REPO_URI} ${WORK_PATH}
27        else
28                        svn update ${WORK_PATH}
29        fi
30}
31
32# I update the working copy of a given module to the repository's copy.
33function conf_update_module {
34        local module="$1"
35        svn update ${WORK_PATH}/${module}
36}
37
38# Updates the whole source tree
39function conf_update_tree {
40        svn update ${WORK_PATH}
41}
42
43function conf_setup_lock {
44    if [ -f ${WORK_PATH}/confman.lock ]
45    then
46        return 1
47    else
48        touch ${WORK_PATH}/confman.lock
49        echo $$ > "${WORK_PATH}/confman.lock" #Send PID to the lock to allow for recursive calls
50        return 0
51    fi
52}
53
54function conf_lock_wcopy {
55    if [ ! -d ${WORK_PATH}/.svn ]
56    then
57        echo "${WORK_PATH} is not a valid svn working copy or does not exist." >&4
58        exit 1
59    fi
60    if [ -f ${WORK_PATH}/confman.lock ]
61    then
62        return 1
63    else
64        touch ${WORK_PATH}/confman.lock
65        echo $$ > "${WORK_PATH}/confman.lock" #Send PID to the lock to allow for recursive calls
66        return 0
67    fi
68}
69
70function conf_unlock_wcopy {
71    if [ -f ${WORK_PATH}/confman.lock ]
72    then
73        rm ${WORK_PATH}/confman.lock
74    fi
75}
76
77function conf_lock_system {
78    if [ -f /var/run/confman.lock ]
79    then
80        return 1
81    else
82        sudo touch /var/run/confman.lock
83        sudo chmod 666 /var/run/confman.lock
84        sudo echo $$ > "/var/run/confman.lock" 
85        return 1
86    fi
87}
88
89function conf_unlock_system {
90    if [ -f /var/run/confman.lock ]
91    then
92        sudo rm /var/run/confman.lock
93    fi
94}
95
96function conf_wcopy_locked? {
97    if [ -f ${WORK_PATH}/confman.lock ]
98    then
99        return 0
100    else
101        return 1
102    fi
103}
104
105function conf_system_locked? {
106    if [ -f /var/run/confman.lock ]
107    then
108        return 0
109    else
110        return 1
111    fi
112}
113
114# Revert a working file
115function conf_revert {
116        svn revert $*
117}
118
119# Assumes that we already have core setup in our work path.
120function conf_create_module {
121        local module=$1
122        svn mkdir ${WORK_PATH}/$module
123        svn mkdir ${WORK_PATH}/${REPO_CHECKPTS}/$module
124        svn commit ${WORK_PATH}/${module} ${WORK_PATH}/${REPO_CHECKPTS}/$module -m \
125                        "Created directory structure for ${module} --`whoami`"
126}
127
128# Commits module $1 with message $2
129function conf_commit {
130        local modules=$(echo $1 | tr ' ' ',')
131        local msg="$2"
132
133    # $modules is in the form foo,bar,baz. This eval trick forces
134    # curly brace expansion.
135    if echo "$modules" | grep ',' > /dev/null ; then
136            svn commit -F "$msg" $(eval echo ${WORK_PATH}/{$modules})
137    else
138        svn commit -F "$msg" ${WORK_PATH}/$modules
139    fi
140}
141
142function conf_fetch {
143    local tmpfile=`mktemp -t confman`
144    local proto="${CONF_EXPORT_URI%:/*}"
145    local remote
146
147    case $proto in
148        sftp|scp)   
149            remote="${CONF_EXPORT_URI#*://}"
150            remote="${remote/\//:/}"
151            scp -p -i ${CONF_SSH_KEY} -o StrictHostKeyCHecking=no \
152                $remote $tmpfile 
153            ;;
154        http|https|ftp)
155            fetch -o $tmpfile $remote
156            ;;
157        file)
158            remote="${CONF_EXPORT_URI#*://}"
159            cp -p $remote $tmpfile
160            ;;
161        *)
162            echo "Unsupported Protocol in conf_fetch" >&2
163            return 1
164            ;;
165    esac
166    echo $tmpfile
167    return 0
168}
169
170# A way to utilize the svn status feature.
171function conf_status {
172        svn status $*
173}
174
175# This exports a working copy of the repository to a tarball
176function conf_export {
177    local tarball="$1"
178    conf_checkout_tree
179    tar -czf "$tarball" -C "${WORK_PATH}" .
180}
181
182# Roll out the specified module, optionally at the specified checkpoint
183# eg:   conf_rollout MODULE [checkpoint]
184function conf_rollout {
185        local module="$1"
186        if [ -z $2 ] ; then
187                local moduledir="${WORK_PATH}/$module"
188        else
189                local moduledir="${WORK_PATH}/checkpoints/${module}/${2}"
190        fi
191        # This is a hack to work around NFS home dirs, for now:
192        chmod o+rx ${WORK_PATH}
193
194        for directory in `find $moduledir -type d -mindepth 1| grep -v "\.svn"`; do
195                local livedir=`echo $directory | sed "s:$moduledir::"`
196                livedir="${LIVE_ROOT}${livedir}"
197                local owner=`conf_get_prop ${directory} owner`
198                local group=`conf_get_prop ${directory} group`
199                local mode=`conf_get_prop ${directory} mode`
200                local opts="-d -o $owner -g $group -m $mode"
201                local cmd="sudo install $opts $livedir"
202                echo $cmd
203                $cmd
204        done
205        for file in `find $moduledir -type f | grep -v "\.svn"` ; do
206                local livefile=`echo "$file" | sed "s:$moduledir::"`
207                local owner=`conf_get_prop ${file} owner`
208                local group=`conf_get_prop ${file} group`
209                local mode=`conf_get_prop ${file} mode`
210                local opts="-Sp -o $owner -g $group -m $mode"
211                local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
212                echo $cmd
213                $cmd
214        done
215        # This is a hack to work around NFS home dirs, for now:
216        chmod o-rx ${WORK_PATH}
217}
218
219function conf_install {
220        local module="$1"
221        shift
222        local file=`abspath $1`
223        shift
224        local moduledir="${WORK_PATH}/$module"
225        local livefile=`echo "$file" | sed -E "s:${WORK_PATH}/[^/]+::"`
226        local pathmodule=`echo "$file" | sed -E "s:${WORK_PATH}/([^/]+).*:\1:"`
227        local suffix
228        livefile=`echo "$livefile" | sed -E "s:-${pathmodule}$::"`
229        if [[ $SINGULARITIES =~ $livefile ]] && [ ! -d "$file" ] ; then
230                suffix="-${module}"
231        fi
232        livefile="${livefile}${suffix}"
233
234
235        # See if it even exists
236        file="${moduledir}${livefile}"
237        if [ ! -e "$file" ] && [ -z "$1" ] ; then
238                return 0
239        elif [ ! -f "$file" ] && [ ! -d "$file" ] ; then
240                conf_install $module "$@"
241                return
242        fi
243
244        # If we got here, figure it out
245        local owner=`conf_get_prop ${file} owner`
246        local group=`conf_get_prop ${file} group`
247        local mode=`conf_get_prop ${file} mode`
248
249        if [ -f "$file" ] ; then
250                local opts="-Sp -o $owner -g $group -m $mode"
251            local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
252        else
253                local opts="-d -o $owner -g $group -m $mode"
254            local cmd="sudo install $opts ${LIVE_ROOT}$livefile"
255        fi
256
257        local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
258
259        chmod o+rx ${WORK_PATH}
260        echo $cmd
261        $cmd
262        chmod o-rx ${WORK_PATH}
263
264    if [ -d "$file" ] ; then
265        conf_install $module "$file"/*
266    fi
267        if [ ! -z $1 ] ; then
268                conf_install $module "$@"
269        fi
270}
271
272
273function conf_list {
274        local file=$1
275        local module=`echo ${file#$WORK_PATH} | sed -E 's:/([^/]+)/.*:\1:'`
276        local livefile=`echo ${file#$WORK_PATH} | sed -E 's:/([^/]+)/:/:'`
277        local owner=`conf_get_prop ${file} owner`
278        local group=`conf_get_prop ${file} group`
279        local mode=`conf_get_prop ${file} mode`
280        local comment=`conf_get_prop ${file} comment`
281        echo -e "$mode\t$owner\t$group\t$comment\t\t$livefile"
282}
283       
284
285
286# This function assembles the singularities:
287function conf_assemble_sing {
288        local file=$1
289        local livefile="${LIVE_ROOT}${file}"
290        local tmpfile=`mktemp -t confman` || exit 1
291        local owner group mode flag livepart msg
292        for layer in $LAYERS ; do
293                livepart="${LIVE_ROOT}${file}-${layer}"
294                myfile="${WORK_PATH}/${layer}/${file}-${layer}"
295                if sudo [ -f $myfile -a -f $livepart ] ; then
296                                owner=`conf_get_prop ${myfile} owner`
297                                group=`conf_get_prop ${myfile} group`
298                                mode=`conf_get_prop     ${myfile} mode`
299                                comment=`conf_get_prop  ${myfile} comment`
300                        sudo cat $livepart >> $tmpfile
301                        sudo rm $livepart
302                        flag=1
303                fi
304        done
305        if [ ! -z $flag ] ; then
306                local opts="-Sp -o $owner -g $group -m $mode"
307                local cmd="sudo install $opts $tmpfile $livefile"
308                echo $cmd
309                $cmd
310        fi
311        rm -f $tmpfile
312}
313
314# This function generates a checkpoint called NAME for the given MODULE.
315# eg: conf_new_checkpoint MODULE NAME
316function conf_new_checkpoint {
317        local module=$1
318        local checkpoint=$2
319        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
320        local revision=`conf_revision`
321        echo $revision > $chkpath
322        svn add $chkpath
323        local msg="Created a checkpoint, ${checkpoint} for ${module} --`whoami`"
324        svn commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
325}
326
327# This function will remove the checkpoint NAME from MODULE.
328# eg: conf_rm_checkpoint MODULE NAME
329function conf_rm_checkpoint {
330        local module=$1
331        local checkpoint=$2
332        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
333        svn rm ${chkpath}
334        local msg="Removed the checkpoint ${checkpoint} from ${module} --`whoami`"
335        svn commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
336}
337
338# This function will restore the state of your module's working copy to the
339# named checkpoint. eg: conf_rollback MODULE NAME
340function conf_rollback {
341        local module=$1
342        local checkpoint=$2
343        local clock=$3
344        local modpath="${WORK_PATH}/${module}"
345        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
346        local revision
347        local date=`echo $checkpoint | sed -E 's:(....)(..)(..):\1-\2-\3:'`
348
349        # Named checkpoint
350        if [ -f "${chkpath}" ] ; then
351        revision=`cat $chkpath`
352        elif [ -z $clock ] ; then               # Time checkpoint
353        revision="{${date}}"
354        else
355        clock=`echo $clock | sed -E 's#(..)(..)#\1:\2#'`
356        revision="{${date}T${clock}}"
357        fi
358
359        svn update --revision $revision $modpath
360}
361
362# This function will print the value of the specified property to STDOUT
363function conf_get_prop {
364        local file=$1
365        local prop=$2
366        svn propget "confman:${prop}" ${file}
367}
368
369# This function will set the value of the specified property.
370function conf_set_prop {
371        local file=$1
372        local prop=$2
373        local value=$3
374        svn propset "confman:${prop}" "${value}" ${file}
375}
376
377function conf_gen_file {
378        local module=$1
379        local file=$2
380        local owner=$3
381        local group=$4
382        local mode=$5
383        local comment="$6"
384        local usefile=$7
385        local myfile="${WORK_PATH}/${module}${file}"
386        local warning="${comment} ${CONF_WARNING}"
387        local morewarn="${comment} Managed under ${module} module."
388        local revision="${comment} \$Id\$"
389        echo $file
390
391        if [ -z $usefile ] ; then
392                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
393        elif head -n 1 ${usefile} | grep '^#!' >/dev/null ; then
394                                        awk "{if (NR==2)
395                                                        print \"\\n${warning}\\n${morewarn}\\n${revision}\\n\"\$0;
396                                                                else print \$0 }" ${usefile} > $myfile
397        else
398                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
399                                        cat ${usefile} >> $myfile
400        fi
401
402        svn add $myfile
403        svn ps svn:keywords "Id" $myfile
404        conf_set_prop $myfile owner $owner
405        conf_set_prop $myfile group $group
406        conf_set_prop $myfile mode      $mode
407        conf_set_prop $myfile comment "$comment"
408}
409
410function conf_rm_file {
411        svn rm $*
412}
413
414function conf_mkdir {
415        local directory=$1
416        local owner=$2
417        local group=$3
418        local mode=$4
419        local workdir="/"
420        local directories=`echo "$directory" | sed 's:/: :g'`
421        local dir
422
423        for dir in $directories ; do
424                workdir="${workdir}/${dir}"
425                if [ ! -d ${workdir} ] ; then
426                        echo svn mkdir ${workdir}
427                        svn mkdir ${workdir}
428                        conf_set_prop $workdir owner $owner
429                        conf_set_prop $workdir group $group
430                        conf_set_prop $workdir mode $mode
431                        conf_set_prop $workdir comment "dir"
432                fi
433        done
434}
435
436function conf_rmmod {
437        local module=$1
438        svn rm ${WORK_PATH}/${module}
439        svn rm ${WORK_PATH}/${REPO_CHECKPTS}/${module}
440}
441
442function conf_rename {
443        local oldmod=$1
444        local newmod=$2
445        local file
446
447        # First, we perform the easy operations
448        svn mv ${WORK_PATH}/${oldmod} ${WORK_PATH}/${newmod} || \
449                {       echo "There was a problem renaming the modules." >&4 ; \
450                        conf_cleanExit 1
451                }
452        svn mv ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
453                        ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} || \
454                {       echo "There was a problem renaming the checkpts." >&4 ; \
455                        conf_cleanExit 1
456                }
457
458        svn update ${WORK_PATH} || \
459                {       echo "Your working copy didn't update correctly." >&4 ; \
460                        conf_cleanExit 1
461                }
462        svn commit -m "Renaming ${oldmod} to ${newmod}, phase 1." \
463                ${WORK_PATH}/{,${REPO_CHECKPTS}}/{${oldmod},${newmod}} || \
464                {       echo "Committing the changes failed." >&4 ; conf_cleanExit 1
465                }
466
467        # Next, we have to rename any singularities
468        for file in ${SINGULARITIES} ; do
469                if [ -f "${WORK_PATH}/${newmod}${file}-${oldmod}" ] ; then
470                        svn mv ${WORK_PATH}/${newmod}${file}-${oldmod} \
471                                        ${WORK_PATH}/${newmod}${file}-${newmod}
472                fi
473        done
474
475        svn update ${WORK_PATH} || \
476                {       echo "Your working copy didn't update correctly." >&4 ; \
477                        conf_cleanExit 1
478                }
479        svn commit -m "Renaming ${oldmod} to ${newmod}, phase 2." \
480                ${WORK_PATH}/${newmod} || \
481                {       echo "Committing the changes failed." >&4 ; conf_cleanExit 1
482                }
483
484        # Now, we have to go fix all the confman headers in all the files...
485        find ${WORK_PATH}/${newmod} -type f -not -path '*/.svn/*' -exec \
486                gsed -i -r "s/^(# Managed under )${oldmod}( module)/\1${newmod}\2/" \
487                {} \;
488       
489        svn update ${WORK_PATH} || \
490                {       echo "Your working copy didn't update correctly." >&4 ; \
491                        conf_cleanExit 1
492                }
493        svn commit -m "Renaming ${oldmod} to ${newmod}, complete." \
494                ${WORK_PATH}/${newmod} || \
495                {       echo "Committing the changes failed." >&4 ; conf_cleanExit 1
496                }
497}
498
499function conf_mv {
500        local oldname=$1
501        local newname=$2
502        svn mv $oldname $newname
503}
504
505function conf_cp {
506        local oldname=$1
507        local newname=$2
508        svn cp $oldname $newname
509}
510
511function conf_diff {
512        svn diff $*
513}
514
515function conf_log {
516        svn log $*
517}
518
519# Accepts log messages on stdin until EOF
520function conf_logger {
521   logger -t "$MYNAME[$$]: $USER" 2>&4
522}
523
524function conf_chattr {
525    local attr="confman:$1"
526    shift
527    local value="$1"
528    shift
529    svn propset "$attr" "$value" "$@"
530}
531
532function conf_lsattr {
533    local file="$1"
534    shift
535    echo "$file"
536    for prop in $(svn proplist $file | grep 'confman:' | sort) ; do
537        echo -e "\t${prop#confman:}\t" $(svn propget $prop $file)
538    done
539    if [ -n "$1" ] ; then
540        conf_lsattr "$@"
541    fi
542}
543
544function conf_rmattr {
545    local attr="confman:$1"
546    shift
547    svn propdel "$attr" "$@"
548}
549
550function conf_syncheck {
551    local cmd
552    local toReturn=true
553    for file in "$@" ; do
554        cmd=$(svn propget "confman:syncheck" "$file")
555        if [ -z "$cmd" ] ; then
556            continue
557        fi
558        if ! $cmd "$file" ; then
559            echo "Syntax verification failed for $file" >&2
560            toReturn=false
561        fi
562    done
563    $toReturn
564}
565
566function conf_markclean {
567    sudo sh -c "echo clean > $REPO_SYNC_STATE"
568}
569
570function conf_markdirty {
571    sudo sh -c "echo dirty > $REPO_SYNC_STATE"
572}
573
574function conf_isclean {
575    local state=`cat $REPO_SYNC_STATE 2>/dev/null`
576    case $state in
577        clean)  return 0 ;;
578        dirty)  return 1 ;;
579        *)      echo "Invalid state. Reporting dirty." >&2 ; return 1 ;;
580    esac
581}
582
583# Returns the current revision number of the repository on stdout
584function conf_revision {
585    svn info ${WORK_PATH} | awk '/Last Changed Rev:/ {print $4}'
586}
587
588# Returns the revision of the last commit or install operation on stdout
589function conf_sysrev {
590    cut -d ':' -f 2 $REPO_ACTION
591}
592
593# Returns the last action of the repository on stdout
594function conf_lastact {
595    cut -d ':' -f 1 $REPO_ACTION
596}
597
598# Records the specified action
599function conf_recordAction {
600    local action="$1"
601    local revision=`conf_revision`
602    sudo sh -c "echo '${action}:${revision}' > $REPO_ACTION"
603}
604
605# WARNING: If you refer to fd 2 in this code, this function will silently
606# fail when called by trap. DO NOT ATTEMPT TO LOG TO fd 2 IN THIS FUNCTION!
607# The SIGNAL seems to kill the logger process spawned by the parent
608# process, which is receiving its input from our fd 2.
609function conf_cleanExit {
610    echo "Received a signal. Exiting cleanly." | conf_logger
611        echo "Abort, Abort! Patience is a virtue." >&4
612        echo "Please wait until I finish cleaning up after you." >&4
613
614        # And in case we got an interrupt during a rollout, we still want the
615        # permissions here to be in a consistent state.
616        chmod 700 ${WORK_PATH}
617        echo "Removing leftover temp files..." >&4
618        find /tmp/confman* -maxdepth 0 -user `whoami` -exec rm -rf {} \;
619
620        # And this clears any locks we may have created on our working copy:
621        echo "Undoing damage to your working copy..." >&4
622        [ -d "${WORK_PATH}" ] && svn cleanup ${WORK_PATH}
623
624    echo "Removing any system or working copy locks..." >&4
625    conf_unlock_wcopy
626    conf_unlock_system
627       
628    echo "All clean. Terminating." >&4
629
630        exit ${1:-1}
631}       
632
633function conf_checkgroup {
634        GROUP_FILE=$(mktemp -t confman)
635    GROUP_STAT=$(chgrp staff $GROUP_FILE 2>&1)
636        rm $GROUP_FILE
637        if [ -n "$GROUP_STAT" ] ; then
638       return 2
639    else
640       return 0
641    fi
642}
643
644# vim:ts=4
645
Note: See TracBrowser for help on using the repository browser.