#!/usr/bin/env ruby require 'ftools' require 'lsync' require 'lsync/version' require 'optparse' OPTIONS = { :ConfigFiles => [], :LogPath => "/var/log/lsync/backup.log" } ARGV.options do |o| script_name = File.basename($0) o.set_summary_indent(' ') o.banner = "Usage: #{script_name} [options] [directory | config]" o.define_head "This script is used to run lsync scripts and plans. If you specify a directory, files ending in .lsync-script and .lsync-plan will all be processed." o.separator "" o.separator "Help and Copyright information" o.on("-l log_path", String, "Set the directory for backup logs") do |log_path| OPTIONS[:LogPath] = log_path end o.on_tail("--copy", "Display copyright information") do puts "#{script_name} v#{LSync::VERSION::STRING}. Copyright (c) 2008-2010 Samuel Williams. Released under the GPLv3." puts "See http://www.oriontransfer.co.nz/ for more information." exit end o.on_tail("-h", "--help", "Show this help message.") { puts o; exit } end.parse! # ============================== Main Scripts / Plans ============================== class LogPathChecker def initialize(directory, filename) full_path = File.join(directory, filename) @directory_writable = File.directory?(directory) && File.writable?(directory) @file_not_writable = File.exist?(full_path) && !File.writable?(full_path) @accessable = @directory_writable && !@file_not_writable @full_path = full_path end attr :accessable attr :full_path end def setup_logging # Console log output $console_log = Logger.new($stdout) $console_log.formatter = MinimalLogFormat.new # File log output filename = File.basename(OPTIONS[:LogPath]) directory = File.dirname(OPTIONS[:LogPath]) if filename == nil || filename == "" filename = "backup.log" end check = LogPathChecker.new(directory, filename) unless check.accessable $console_log.warn "Could not write to #{check.full_path.dump} - changing log path to #{Dir.pwd.dump}" directory = Dir.pwd check = LogPathChecker.new(directory, filename) end if check.accessable $backup_log = Logger.new(check.full_path, 'weekly') else $console_log.warn "Could not write to #{check.full_path.dump} - not logging to disk" $backup_log = nil end $logger = TeeLogger.new($console_log, $backup_log) end def process_config_files ARGV.each do |p| unless File.exists? p $logger.error "Could not process #{p}" exit(20) end if File.directory? p OPTIONS[:ConfigFiles] += Dir[File.join(p, "*.{lsync-script,lsync-plan}")] else OPTIONS[:ConfigFiles] << p end end end def run_backups(config_files) if config_files.size == 0 $logger.warn "No configuration files specified!" else config_files.each do |c| config = LSync::load_from_file(c) config.logger = $logger config.run_backup end end end def dump_exception_backtrace ex ex.backtrace.each do |bt| $logger.error bt end end # Main Script Execution setup_logging process_config_files $logger.info " Backup beginning at #{Time.now.to_s} ".center(96, "=") begin run_backups(OPTIONS[:ConfigFiles]) rescue LSync::Error $logger.error "#{$!.class.name}: #{$!.to_s}. Dumping backtrace:" dump_exception_backtrace($!) exit(230) rescue Interrupt $logger.error "Backup process recevied interrupt (Ctrl-C). Dumping backtrace:" dump_exception_backtrace($!) exit(240) rescue $logger.error "Unknown exception #{$!.class.name} thrown: #{$!.to_s}. Dumping backtrace:" dump_exception_backtrace($!) exit(250) ensure $logger.info " Backup finishing at #{Time.now.to_s} ".center(96, "=") end