#!/usr/bin/rubyrequire 'fileutils'=begin This is a daemon that executes backup_script Goal:Backup once a day, but only do it when I'm asleep. Soloution: This script watches monitor_file, sleeps until it is 12 hours old. If mouse movement is detected, the script will sleep for an additional 3 hours until the mouse hasn't moved for 3 hours. Must be ran as root. BUG: Mouse won't move, and/or dissapears after script is killed/stopped=endclass Log @@fp = false @@filename = "" def initialize(msg, filename = "") if filename != "" @@filename = filename begin @@fp = File.new(filename,'a') rescue STDERR.write("Can't open file:#{filename}\n"); exit end end w(msg) end def w(msg) t = "[" + Time.now.strftime("%Y-%m-%d %H:%M:%S") + "] " nmsg = t + msg.to_s # puts "#{nmsg}<br />" if (@@fp) @@fp.write("#{nmsg}\n") @@fp.flush end end def close() @@fp.close(); endenddef format_time(sec) hr = (sec / (60 * 60)) sec = (sec - (hr * (60*60))); min = (sec / 60) sec = (sec % 60) return sprintf("%d:%02d:%02d", hr, min, sec)endmodule Daemon WorkingDirectory = File.expand_path(File.dirname(__FILE__)) class Base def self.pid_fn # File.join(WorkingDirectory, "#{name}.pid") return "/var/run/backup-when-idle.pid" end def self.daemonize Controller.daemonize(self) end end module PidFile def self.store(daemon, pid) File.open(daemon.pid_fn, 'w') {|f| f << pid} end def self.recall(daemon) IO.read(daemon.pid_fn).to_i rescue nil end end module Controller def self.daemonize(daemon) case !ARGV.empty? && ARGV[0] when 'start' start(daemon) when 'stop' stop(daemon) when 'restart' stop(daemon) start(daemon) else puts "Invalid command. Please specify start, stop or restart." exit end end def self.start(daemon) if (File.exists?(daemon.pid_fn)) fp = File.new(daemon.pid_fn) pid = fp.readpartial(1024) fp.close if (File.exists?("/proc/#{pid}/exe")) exit 1 end end fork do Process.setsid exit if fork PidFile.store(daemon, Process.pid) Dir.chdir WorkingDirectory File.umask 0000 STDIN.reopen "/dev/null" STDOUT.reopen "/dev/null", "a" STDERR.reopen STDOUT trap("TERM") {daemon.stop; exit} daemon.start end exit 0 end def self.stop(daemon) # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred if !File.file?(daemon.pid_fn) puts "Pid file not found. Is the daemon started?" exit 1 end pid = PidFile.recall(daemon) FileUtils.rm(daemon.pid_fn) pid && Process.kill("TERM", pid) exit 0 end endendclass MouseWatcher def initialize(filename, hours_of_idle_time, l) @filename = filename min = 60 hr = min * 60 @l = l @hours_of_idle_time = hours_of_idle_time @last_move = Time.now - (hr * hours_of_idle_time) + (10 * min) @mouse = File.new(filename,'r') @l.w("opened:#{filename}") @thread = Thread.new() { while !@mouse.closed? data = @mouse.readpartial(1024) # print data @last_move = Time.now # @l.w("#{filename} last_move:#{@last_move}") end } end def seconds_to_inactive() min = 60 hr = min * 60 inactive_at = (@last_move + (@hours_of_idle_time * hr)) seconds_to_inactive = inactive_at.to_i - Time.now.to_i return seconds_to_inactive end def close @mouse.close @thread.kill @l.w("closed:#{@filename}") endendclass Wait_for_idle < Daemon::Base def self.start @mice = [] min = 60 hr = min * 60 log_file = "/var/log/backup-when-idle.log" monitor_file = '/media/backup/last_ran_backup.txt' backup_script = '/home/erm/bin/backup.sh' hours_between_backups = 12 hours_of_idle_time = 1 backup_script = '/home/erm/bin/backup.sh' mount_command = "mount /dev/hdb1 /media/backup" # Set to 10 min to start with if the mouse hasn't moved by then # it should be ok to backup. @l = Log.new("Opened log", log_file) if (!File.exists?(monitor_file)) @l.w("missing:#{monitor_file}") `#{mount_command}` end if (!File.exists?(monitor_file)) # `mount /dev/hdb1 /media/backup` @l.w("Unable to mount using #{mount_command}") self.stop() exit end Dir['/dev/input/mouse*'].each { |m| @mice.push(MouseWatcher.new(m, hours_of_idle_time, @l)) } # check what time it is. loop do mtime = File.mtime(monitor_file) time_to_backup = (mtime + (hr * hours_between_backups)) seconds_to_backup_time = (time_to_backup - Time.now).to_i; seconds_to_inactive = 0 @mice.each { |m| # get the largest "seconds to inactive." tmp_seconds_to_inactive = m.seconds_to_inactive() if (tmp_seconds_to_inactive > seconds_to_inactive) seconds_to_inactive = tmp_seconds_to_inactive end } if seconds_to_backup_time > 0 @l.w("sleeping for #{seconds_to_backup_time} seconds " + format_time(seconds_to_backup_time) + " - it's not time to backup yet. Backup @#{time_to_backup} Last backup:#{mtime}") sleep seconds_to_backup_time elsif seconds_to_inactive > 0 @l.w("sleeping for #{seconds_to_inactive} seconds " + format_time(seconds_to_inactive) + " - mouse movement detected. Past time: "+ format_time(seconds_to_backup_time) + " Backup was scheduled @#{time_to_backup}") sleep seconds_to_inactive else @l.w("Time to backup started:" + Time.now.to_s) start_time = Time.now.to_i #`nice -n 19 #{backup_script} > /media/backup/backup.log` @l.w("Finished:" + Time.now.to_s + " = " + format_time((Time.now.to_i - start_time))) FileUtils.touch([monitor_file]) end end end def self.stop @mice.each { |m| m.close() } @l.w("Shutting down") @l.close() # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred endendWait_for_idle.daemonizeexit 0# Exit# 0 if daemon has been started# 1 if daemon was already running# 2 if daemon could not be started