backup_demon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -0
- data/backup_demon.gemspec +24 -0
- data/bin/backup_demon +5 -0
- data/lib/backup_demon/cli.rb +68 -0
- data/lib/backup_demon/config.rb +39 -0
- data/lib/backup_demon/core_ext/hash.rb +12 -0
- data/lib/backup_demon/daemon.rb +42 -0
- data/lib/backup_demon/device.rb +61 -0
- data/lib/backup_demon/sync.rb +8 -0
- data/lib/backup_demon/version.rb +3 -0
- data/lib/backup_demon.rb +22 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2M0Nzk4YzkwOGI1OWVkZmVjNzFlMmQ1NDMwOGY3YTJlMmFkZTE4OA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDViNGE3NzcyN2MwMTE1MTcwZTgxYzU4MzFiZjA4NTVmODg5YmI1NA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Y2E0YTljNGFiYzMwZmU3MzNkYTU3OWQ1YmE5YWE2ZmM5MmVjMTYxZGUwMTlh
|
10
|
+
OWU3ZTIzOTBhNTBkYTk1Yzk2N2VmZjYzZDEwODU5NWU4ZGZjNThhZWE2ZDY1
|
11
|
+
ZDU1NDcxMDFhOGM4YmZiN2RkOGFlZDE2Njk5NDEzOTUyYmRhYWU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZGFlNTgzNzkzOGRlYjAxMjQ5NzAxMDdlZDdmMjRhYmMzMGQ4YWY1NjJiMmFi
|
14
|
+
YzdmNWFjODgxMjM5MjAwMWJiMTc2ZDRlYjdhMDk2ZjE2MGUwMjViNWNkZDdl
|
15
|
+
NWEyYzBhOGFlODU5ZGY2YzIxN2FiNTg2MGVkMjZmNWE2NTFkNWQ=
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift 'lib'
|
2
|
+
require 'backup_demon/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "backup_demon"
|
6
|
+
s.version = BackupDemon::Version
|
7
|
+
s.summary = "BackupDemon backs up to external drives"
|
8
|
+
s.homepage = "https://github.com/ryanfaerman"
|
9
|
+
s.email = ["ry@nwitty.com"]
|
10
|
+
s.authors = ["Ryan Faerman"]
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split($/).reject{ |f| f =~ /^examples/ }
|
13
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
# s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
# s.extra_rdoc_files = [ "LICENSE.txt", "CHANGELOG.md", "README.md", "CONTRIBUTING.md" ]
|
18
|
+
# s.rdoc_options = ["--charset=UTF-8"]
|
19
|
+
|
20
|
+
s.add_dependency "thor", "~> 0.18"
|
21
|
+
|
22
|
+
s.description = %s{
|
23
|
+
BackupDemon is a daemon for backing up directories to an external drive}
|
24
|
+
end
|
data/bin/backup_demon
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'backup_demon'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module BackupDemon
|
6
|
+
class CLI < Thor
|
7
|
+
|
8
|
+
class_option :config, :aliases => ["-c"], :type => :string
|
9
|
+
|
10
|
+
desc "start", "run the BackupDemon Daemon"
|
11
|
+
option :daemon, aliases: ["-d"], type: :boolean, default: false
|
12
|
+
option :interval, aliases: ["-i"], type: :numeric
|
13
|
+
option :pid, aliases: ["-p"], type: :string
|
14
|
+
option :device, aliases: ["-s"], type: :string
|
15
|
+
option :mount, aliases: ["-m"], type: :string
|
16
|
+
option :directories, type: :string
|
17
|
+
|
18
|
+
def start(directories = nil)
|
19
|
+
load_config
|
20
|
+
|
21
|
+
directories = directories.to_s.split(',').map(&:strip) unless directories.nil?
|
22
|
+
|
23
|
+
daemon = BackupDemon::Daemon.new(
|
24
|
+
directories || BackupDemon.config.directories,
|
25
|
+
BackupDemon.config.device,
|
26
|
+
BackupDemon.config.mount
|
27
|
+
)
|
28
|
+
|
29
|
+
if BackupDemon.config.daemon
|
30
|
+
Process.daemon(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
if BackupDemon.config.pid
|
34
|
+
File.open(BackupDemon.config.pid, 'w') { |f| f << daemon.pid }
|
35
|
+
end
|
36
|
+
|
37
|
+
daemon.run!(BackupDemon.config.interval)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "stop", "stop the BackupDemon Daemon"
|
41
|
+
option :pid, aliases: ["-p"], type: :string
|
42
|
+
def stop(pid = nil)
|
43
|
+
load_config
|
44
|
+
pidfile = BackupDemon.config.pid
|
45
|
+
|
46
|
+
if pidfile && File.exists?(pidfile)
|
47
|
+
File.foreach(pidfile) do |pid|
|
48
|
+
begin
|
49
|
+
Process.kill("KILL", pid.to_i)
|
50
|
+
rescue
|
51
|
+
puts "Nothing to stop"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
def load_config
|
59
|
+
opts = {}
|
60
|
+
if options[:config]
|
61
|
+
opts = YAML.load_file(File.expand_path(options[:config]))
|
62
|
+
end
|
63
|
+
|
64
|
+
BackupDemon.config = opts.merge!(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "backup_demon/core_ext/hash"
|
3
|
+
|
4
|
+
module BackupDemon
|
5
|
+
class Config
|
6
|
+
attr_accessor :options
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = {
|
10
|
+
daemon: false,
|
11
|
+
interval: 5,
|
12
|
+
pid: nil,
|
13
|
+
device: "/dev/sdb1",
|
14
|
+
mount: "/mnt/backuphdd",
|
15
|
+
directories: ""
|
16
|
+
}.merge!(options.symbolize_keys!)
|
17
|
+
end
|
18
|
+
|
19
|
+
def timeout
|
20
|
+
@options[:timeout].to_f
|
21
|
+
end
|
22
|
+
|
23
|
+
def interval
|
24
|
+
@options[:interval].to_f
|
25
|
+
end
|
26
|
+
|
27
|
+
def directories
|
28
|
+
@options[:directories]
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(name)
|
32
|
+
name = name.to_sym
|
33
|
+
if @options.has_key?(name)
|
34
|
+
@options[name]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module BackupDemon
|
2
|
+
class Daemon
|
3
|
+
def initialize(directories, device_path, mount_point = "/mnt/backuphdd")
|
4
|
+
@directories = [directories].flatten
|
5
|
+
@drive = Device.new(device_path)
|
6
|
+
@mount_point = mount_point
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(interval = 5.0)
|
10
|
+
interval = Float(interval)
|
11
|
+
procline 'waiting for drive...'
|
12
|
+
|
13
|
+
# puts "#{@drive.exists?} && #{@drive.different?} (#{@drive.exists? && @drive.different?})"
|
14
|
+
# puts "#{@drive.previous.inspect} != #{@drive.current.inspect} || #{@drive.current.nil?}"
|
15
|
+
if @drive.exists? && @drive.different?
|
16
|
+
procline 'Found Drive'
|
17
|
+
if @drive.mount(@mount_point)
|
18
|
+
@directories.each do |source|
|
19
|
+
procline "Backing up #{source}"
|
20
|
+
Sync.start(source, @drive.mount_point)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Kernel.sleep(interval)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run!(interval = 5.0)
|
29
|
+
loop do
|
30
|
+
run(interval)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def procline(string)
|
35
|
+
$0 = "backup-demon: #{string}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def pid
|
39
|
+
@pid ||= Process.pid
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module BackupDemon
|
4
|
+
class Device
|
5
|
+
attr_reader :device, :mounted, :mount_point
|
6
|
+
def initialize(device)
|
7
|
+
@device = device
|
8
|
+
@mounted = false
|
9
|
+
@previous = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def exists?
|
13
|
+
File.exists?(@device)
|
14
|
+
end
|
15
|
+
|
16
|
+
def different?
|
17
|
+
begin
|
18
|
+
@previous != File.stat(@device)
|
19
|
+
rescue
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.mounted
|
25
|
+
raw_mount_list = `mount`
|
26
|
+
mount_list = {}
|
27
|
+
raw_mount_list.split("\n").each do |line|
|
28
|
+
items = line.split(' ')
|
29
|
+
mount_list[items[0]] = items[2]
|
30
|
+
end
|
31
|
+
mount_list.select { |k, v| k =~ /^\/.*/} # only real devices
|
32
|
+
end
|
33
|
+
|
34
|
+
def mount(mount_point)
|
35
|
+
@mount_point = mount_point
|
36
|
+
FileUtils.mkdir_p(@mount_point) unless File.exists?(@mount_point)
|
37
|
+
puts "mount #{@device} #{@mount_point}"
|
38
|
+
|
39
|
+
if mounted?
|
40
|
+
@previous = File.stat(@device)
|
41
|
+
true
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def mounted?
|
48
|
+
Device.mounted.keys.include?(@device)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def unmount
|
53
|
+
puts "umount #{@device}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def unmounted?
|
57
|
+
!mounted?
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/backup_demon.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'backup_demon/sync'
|
2
|
+
require 'backup_demon/device'
|
3
|
+
require 'backup_demon/daemon'
|
4
|
+
require 'backup_demon/config'
|
5
|
+
|
6
|
+
module BackupDemon
|
7
|
+
|
8
|
+
def self.config=(options = {})
|
9
|
+
@config = Config.new(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.config
|
13
|
+
@config ||= Config.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield config
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# BackupDemon::Daemon.new(['tmp/banana', 'tmp/jones'], 'tmp/device', 'tmp/mount/point').run!
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backup_demon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Faerman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.18'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.18'
|
27
|
+
description: ! "\n BackupDemon is a daemon for backing up directories to an external
|
28
|
+
drive"
|
29
|
+
email:
|
30
|
+
- ry@nwitty.com
|
31
|
+
executables:
|
32
|
+
- backup_demon
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- Gemfile
|
37
|
+
- Gemfile.lock
|
38
|
+
- backup_demon.gemspec
|
39
|
+
- bin/backup_demon
|
40
|
+
- lib/backup_demon.rb
|
41
|
+
- lib/backup_demon/cli.rb
|
42
|
+
- lib/backup_demon/config.rb
|
43
|
+
- lib/backup_demon/core_ext/hash.rb
|
44
|
+
- lib/backup_demon/daemon.rb
|
45
|
+
- lib/backup_demon/device.rb
|
46
|
+
- lib/backup_demon/sync.rb
|
47
|
+
- lib/backup_demon/version.rb
|
48
|
+
homepage: https://github.com/ryanfaerman
|
49
|
+
licenses: []
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.0.3
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: BackupDemon backs up to external drives
|
71
|
+
test_files: []
|