'''Snapshot''' [CMcC]: The idea is to set up a user on a remote machine, and backup to it using rsync and ssh. The original I took this from was written in [PERL] and is called rsnapshot [http://www.rsnapshot.org/]. I wasn't happy with it because it doesn't back up to a remote machine. One nice feature is that it keeps complete snapshots of the directories which use hard links to save space - the total consumed for all of the snapshots should be proportional to the size of one copy plus the size of the changed files. Because it relies upon [ssh], and a dedicated user on a remote machine, there's a fair bit of setup required. It also depends on [remote ssh tcl], from this Wiki. It should be suitable for [cron] to run periodically, and you can expect all that nice [rsync] performance. It needs frills like --exclude lists, etc, to be really finished. Needless to say, it's an inherently risky business to be providing networked services as root, even via sudo, and it's incumbent upon you, the user, to understand what you're doing and assure yourself that it's safe. snapshot uses ssl, which assumes the existence of an account on the remote machine with the following qualities: the remote 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 #!/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 # 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] \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]