Version 2 of backup utility using rsync

Updated 2004-05-28 09:36:22 by CMCc

#!/usr/bin/env tclsh

    # snapshot - use rsync to rsync a snapshot of a directory to a remote machine
    # via rsync and ssh.
    #
    # Usage: snapshot directory [interval]
    #        where interval is: one of hourly, daily, weekly, monthly
    #
    # snapshot uses ssl, which assumes the existence of an account on the remote machine
    # with the following qualities: the account's login shell is tclsh, account login is not permitted.
    # The user running snapshot must have keys sufficient to ssh-connect to the remote account
    # without password and without passphrase.

    # if you want to preserve times and owners you have to add the following to /etc/sudoers:
    # 'user ALL = /usr/bin/rsync'
    # and ensure user is in /etc/group under group sudo

    source ssh.tcl
    package require ssh

    # account@machine to run the remote
    set remote user@machine

    # location on remote to store snapshots
    set root /var/backup

    # schedule of snapshots - how many for each category
    array set schedule {
        hourly 24
        daily 7
        weekly 4
        monthly 3
    }

    if {[info exists argv0] && ($argv0 == [info script])} {
        if {[llength $argv] < 2} {
            puts stderr "Usage: [info script] <interval> <directory>\nwhere interval is: one of hourly, daily, weekly, monthly"
            exit 1
        }
        set interval [lindex $argv 0]

        connect $remote

        # send remote our globals
        remotes [subst {
            array set schedule [list [array get schedule]]
            set root [file normalize $root]
            array get schedule
        }]

        # rotate according to schedule
        remote {
            proc rotate {interval} {
                global schedule
                global root

                if {![file exists $root]} {
                    file mkdir $root
                }

                set stem [file join $root $interval]

                if {![file exists ${stem}.0]} {
                    # brand new
                    file mkdir ${stem}.0
                    return
                }

                # delete oldest snapshots
                if {[file exists $stem.$schedule($interval)]} {
                    file delete -force $stem.$schedule($interval)
                }

                # age snapshot names
                for {set i $schedule($interval)} {$i > 0} {incr i -1} {
                    if {[file exists $stem.$i]} {
                        file rename $stem.$i $stem.[expr {$i + 1}]
                    }
                }

                # age/link files from most recent snapshot to .1
                exec /bin/cp -al $stem.0/ $stem.1/
            }
        }

        remote "rotate $interval"        ;# first rotate this interval's snapshot
        remote exit                ;# clean up the rotation

        set stem [file join $root $interval]

        # rsync the local dirs to the appropriate snapshot
        foreach dir [lrange $argv 1 end] {
            set dir [file normalize $dir]
            set dest [file join ${stem}.0 [string map {/ @} $dir]]/
            exec sudo /usr/bin/rsync -a -P --delete --numeric-ids ${dir}/ ${remote}:${dest} >@stdout 2>@stderr
        }
    }

    # This goes into the remote user's home directory.  Note the sudo
    if {[info exists argv]} {
        if {[lindex $argv 0] == "-c"} {
            fconfigure stdin -buffering none -encoding binary -translation {binary binary}
            fconfigure stdout -buffering none -encoding binary -translation {binary binary}
            eval exec sudo [lindex $argv 1] >@stdout <@stdin 2>/tmp/snapshot.err
            exit
        }
    }

Category Application