source: tags/confman-1.5.6a/confmanlib.sh @ 180

Revision 180, 16.8 KB checked in by arjun, 5 years ago (diff)

Fixing some issues with locking

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