Hi people,
I’ve created a backup script for my own purposes.
What I’d like is incremental backups. I also want some logging and I created a script therefor.
It uses hardlinks and rsync to create a full backup, but is incremental underneath.
Maybe you will find this interesting!
I’ve placed an entry in my crontab to run this daily.
You can adjust the “count” variable to change the amount of backups.
I have made an entry in my /etc/fstab for mountpoint /mnt/backup (which is not automatically mounted).
voyager ~ # grep backup /etc/fstab
/dev/sdg1 /mnt/backup ext4 noauto 0 0
Change the “directories” array to tell the script which directories you would like to rsync.
I made it in about an hour, so the script might have some bugs, please check it carefully before letting it loose on your filesystem.
If you want to see the logging of your rsyncs, check the files in the log directory.
Output of the program:
voyager ~ # ~/bin/backup.rb
Mounting mountpoint /mnt/backup
Rsyncing directory '/data/tom/*' (25572MB) to /mnt/backup/backup/001/data/tom
Rsyncing directory '/data/linda/*' (401MB) to /mnt/backup/backup/001/data/linda
Rsyncing directory '/data/shared/Photo and Video/*' (8515MB) to /mnt/backup/backup/001/data/shared/Photo and Video
Rsyncing directory '/data/shared/Various/*' (17MB) to /mnt/backup/backup/001/data/shared/Various
Rsyncing directory '/data/shared/Music/*' (96721MB) to /mnt/backup/backup/001/data/shared/Music
Unmounting mountpoint /mnt/backup
#!/usr/bin/ruby
require 'fileutils'
mp = '/mnt/backup' # mountpoint which is defined in /etc/fstab
backupdir = "#{mp}/backup" # the directory where the backups are placed in
count = 365 # the amount of backups you would like to preserve
directories = ['/data/tom', '/data/linda', '/data/shared/Photo and Video', '/data/shared/Various', '/data/shared/Music']
def mounted? dir
lines = `mount`.rstrip.split("\n")
lines.each do |line|
tokens = line.split(/\s+/)
dev = tokens[0]
mountpoint = tokens[2]
return true if mountpoint == dir
end
return false
end
def mount args
mountpoint = args[:mountpoint] || nil
dev = args[:dev] || nil
options = args[:options] || nil
fs = args[:fs] || nil
cmd = Array.new
cmd << "mount"
cmd << "-t #{fs}" unless fs.nil?
cmd << "-o #{options}" unless options.nil?
cmd << dev unless dev.nil?
cmd << mountpoint unless mountpoint.nil?
`#{cmd.join(" ")}`
end
def umount mp
`umount #{mp}`
end
# mount if not mounted
if not mounted? mp
puts "Mounting mountpoint #{mp}"
mount :mountpoint => mp
else
puts "Mountpoint #{mp} already mounted"
end
if not mounted? mp
puts "A failure occured mounting mountpoint #{mp}"
exit 1
end
if not File.exist? backupdir
puts "Creating directory #{backupdir}"
FileUtils.mkdir_p backupdir
end
1.upto(count) do |x|
s = "%03d" % ([x])
FileUtils.mkdir_p "#{backupdir}/#{s}" if not File.exist? "#{backupdir}/#{s}"
end
# mv stuff
previous = nil
count.downto(1) do |x|
s = "%03d" % ([x])
if x == count
FileUtils.rm_rf "#{backupdir}/#{s}"
else
FileUtils.mv "#{backupdir}/#{s}", "#{backupdir}/#{previous}"
end
previous = s
end
`cp -al #{backupdir}/002 #{backupdir}/001`
FileUtils.mkdir_p "#{backupdir}/log" if not File.exist? "#{backupdir}/log"
directories.each do |d|
size = `du -sBM '#{d}'`.rstrip.split(/\s+/).shift
size += "B"
b = "#{backupdir}/001#{d}"
puts "Rsyncing directory '#{d}/*' (#{size}) to #{b}"
FileUtils.mkdir_p b if not File.exist? b
cmd = "rsync -aHuv --delete '#{d}/'* '#{b}'"
output = `#{cmd}`
output = "#{cmd}\n#{output}"
time = Time.now.strftime('%Y%m%d_%H%M%S')
log = "%s/%s/%s.%s.log" % ([backupdir,'log',d.gsub('/','+'),time])
File.open(log, 'w') { |f| f.write output }
end
# umount
puts "Unmounting mountpoint #{mp}"
umount mp
if mounted? mp
puts "A failure occured umounting mountpoint #{mp}"
exit 1
end