ghost 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/ghost.rb CHANGED
@@ -1,8 +1,22 @@
1
1
  $: << File.dirname(__FILE__)
2
2
 
3
- case RUBY_PLATFORM
3
+ module Ghost
4
+ class RecordExists < StandardError
5
+ end
6
+ end
7
+
8
+ require 'rbconfig'
9
+
10
+ case RbConfig::CONFIG['host_os']
4
11
  when /darwin/
5
- require 'ghost/mac-host'
12
+ productVersion = `/usr/bin/sw_vers -productVersion`.strip
13
+ if productVersion =~ /^10\.7\.[2-9]{1}$/
14
+ require 'ghost/linux-host'
15
+ else
16
+ require 'ghost/mac-host'
17
+ end
6
18
  when /linux/
7
19
  require 'ghost/linux-host'
8
20
  end
21
+
22
+ require 'ghost/ssh_config'
@@ -1,92 +1,158 @@
1
1
  require 'socket'
2
2
 
3
- class Host
4
- attr_reader :host, :ip
3
+ module Ghost
4
+ class Host
5
5
 
6
- def initialize(host, ip)
7
- @host = host
8
- @ip = ip
9
- end
10
-
11
- def ==(other)
12
- @host == other.host && @ip = other.ip
13
- end
14
-
15
- alias :to_s :host
16
- alias :name :host
17
- alias :hostname :host
18
-
19
- @@hosts_file = '/etc/hosts'
20
- @@permanent_hosts = [Host.new("localhost", "127.0.0.1"),
21
- Host.new(`hostname`.chomp, "127.0.0.1")]
22
- class << self
23
- protected :new
24
-
25
- def list
26
- entries = []
27
- File.open(@@hosts_file).each do |line|
28
- next if line =~ /^#/
29
- if line =~ /^(\d+\.\d+\.\d+\.\d+)\s+(.*)$/
30
- ip = $1
31
- hosts = $2.split(/\s+/)
32
- hosts.each { |host| entries << Host.new(host, ip) }
6
+ attr_reader :host, :ip
7
+
8
+ def initialize(host, ip)
9
+ @host = host
10
+ @ip = ip
11
+ end
12
+
13
+ def ==(other)
14
+ @host == other.host && @ip = other.ip
15
+ end
16
+
17
+ alias :to_s :host
18
+ alias :name :host
19
+ alias :hostname :host
20
+
21
+ @@hosts_file = '/etc/hosts'
22
+ @@start_token, @@end_token = '# ghost start', '# ghost end'
23
+
24
+ class << self
25
+ protected :new
26
+
27
+ def list
28
+ with_exclusive_file_access(true) do |file|
29
+ entries = []
30
+ in_ghost_area = false
31
+ file.pos = 0
32
+
33
+ file.each do |line|
34
+ if !in_ghost_area and line =~ /^#{@@start_token}/
35
+ in_ghost_area = true
36
+ elsif in_ghost_area
37
+ if line =~ /^#{@@end_token}/o
38
+ in_ghost_area = false
39
+ elsif line =~ /^(\d+\.\d+\.\d+\.\d+)\s+(.*)$/
40
+ ip = $1
41
+ hosts = $2.split(/\s+/)
42
+ hosts.each { |host| entries << Host.new(host, ip) }
43
+ end
44
+ end
45
+ end
46
+
47
+ entries
33
48
  end
34
49
  end
35
- entries.delete_if { |host| @@permanent_hosts.include? host }
36
- entries
37
- end
38
50
 
39
- def add(host, ip = "127.0.0.1", force = false)
40
- if find_by_host(host).nil? || force
41
- delete(host)
42
-
43
- unless ip[/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/]
44
- ip = Socket.gethostbyname(ip)[3].bytes.to_a.join('.')
51
+ def add(host, ip = "127.0.0.1", force = false)
52
+ with_exclusive_file_access do
53
+ if find_by_host(host).nil? || force
54
+ hosts = list
55
+ hosts = hosts.delete_if { |h| h.name == host }
56
+
57
+ unless ip[/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/]
58
+ ip = Socket.gethostbyname(ip)[3].bytes.to_a.join('.')
59
+ end
60
+
61
+ new_host = Host.new(host, ip)
62
+
63
+ hosts << new_host
64
+ write_out!(hosts)
65
+
66
+ new_host
67
+ else
68
+ raise Ghost::RecordExists, "Can not overwrite existing record"
69
+ end
45
70
  end
46
-
47
- new_host = Host.new(host, ip)
48
-
49
- hosts = list
50
- hosts << new_host
51
- write_out!(hosts)
52
-
53
- new_host
54
- else
55
- raise "Can not overwrite existing record"
56
- end
57
- end
58
-
59
- def find_by_host(hostname)
60
- list.find{ |host| host.hostname == hostname }
61
- end
62
-
63
- def find_by_ip(ip)
64
- list.find_all{ |host| host.ip == ip }
65
- end
66
-
67
- def empty!
68
- write_out!([])
69
- end
70
-
71
- def delete(name)
72
- hosts = list
73
- hosts = hosts.delete_if { |host| host.name == name }
74
- write_out!(hosts)
75
- end
76
-
77
- def delete_matching(pattern)
78
- pattern = Regexp.escape(pattern)
79
- hosts = list.select { |host| host.name.match(/#{pattern}/) }
80
- hosts.each { |host| delete(host.name) }
81
- hosts
82
- end
71
+ end
83
72
 
84
- protected
73
+ def find_by_host(hostname)
74
+ list.find { |host| host.hostname == hostname }
75
+ end
85
76
 
86
- def write_out!(hosts)
87
- hosts += @@permanent_hosts
88
- output = hosts.inject("") {|s, h| s + "#{h.ip} #{h.hostname}\n" }
89
- File.open(@@hosts_file, 'w') {|f| f.print output }
77
+ def find_by_ip(ip)
78
+ list.find_all{ |host| host.ip == ip }
79
+ end
80
+
81
+ def empty!
82
+ write_out!([])
83
+ end
84
+
85
+ def delete(name)
86
+ with_exclusive_file_access do
87
+ hosts = list
88
+ hosts = hosts.delete_if { |host| host.name == name }
89
+ write_out!(hosts)
90
+ end
91
+ end
92
+
93
+ def delete_matching(pattern)
94
+ with_exclusive_file_access do
95
+ pattern = Regexp.escape(pattern)
96
+ hosts = list.select { |host| host.name.match(/#{pattern}/) }
97
+ hosts.each { |host| delete(host.name) }
98
+ hosts
99
+ end
100
+ end
101
+
102
+ protected
103
+
104
+ def with_exclusive_file_access(read_only = false)
105
+ return_val = nil
106
+ flag = read_only ? 'r' : 'r+'
107
+
108
+ if @_file
109
+ return_val = yield @_file
110
+ else
111
+ File.open(@@hosts_file, flag) do |f|
112
+ f.flock File::LOCK_EX
113
+ begin
114
+ @_file = f
115
+ return_val = yield f
116
+ ensure
117
+ @_file = nil
118
+ end
119
+ end
120
+ end
121
+
122
+ return_val
123
+ end
124
+
125
+ def write_out!(hosts)
126
+ with_exclusive_file_access do |f|
127
+ new_ghosts = hosts.inject("") {|s, h| s + "#{h.ip} #{h.hostname}\n" }
128
+
129
+ output = ""
130
+ in_ghost_area, seen_tokens = false,false
131
+ f.pos = 0
132
+
133
+ f.each do |line|
134
+ if line =~ /^#{@@start_token}/o
135
+ in_ghost_area, seen_tokens = true,true
136
+ output << line << new_ghosts
137
+ elsif line =~ /^#{@@end_token}/o
138
+ in_ghost_area = false
139
+ end
140
+ output << line unless in_ghost_area
141
+ end
142
+ if !seen_tokens
143
+ output << surround_with_tokens( new_ghosts )
144
+ end
145
+
146
+ f.pos = 0
147
+ f.print output
148
+ f.truncate(f.pos)
149
+ end
150
+ end
151
+
152
+
153
+ def surround_with_tokens(str)
154
+ "\n#{@@start_token}\n#{str}#{@@end_token}\n"
155
+ end
90
156
  end
91
157
  end
92
158
  end
@@ -1,114 +1,116 @@
1
1
  require 'socket'
2
2
 
3
- class Host
4
- ListCmd = "dscl localhost -list /Local/Default/Hosts 2>&1"
5
- ReadCmd = "dscl localhost -read /Local/Default/Hosts/%s 2>&1"
6
- CreateCmd = "sudo dscl localhost -create /Local/Default/Hosts/%s IPAddress %s 2>&1"
7
- DeleteCmd = "sudo dscl localhost -delete /Local/Default/Hosts/%s 2>&1"
8
-
9
- class << self
10
- protected :new
11
-
12
- def list
13
- list = `#{ListCmd}`
14
- list = list.split("\n")
15
- list.collect { |host| Host.new(host.chomp) }
16
- end
3
+ module Ghost
4
+ class Host
5
+ ListCmd = "dscl localhost -list /Local/Default/Hosts 2>&1"
6
+ ReadCmd = "dscl localhost -read /Local/Default/Hosts/%s 2>&1"
7
+ CreateCmd = "dscl localhost -create /Local/Default/Hosts/%s IPAddress %s 2>&1"
8
+ DeleteCmd = "dscl localhost -delete /Local/Default/Hosts/%s 2>&1"
9
+
10
+ class << self
11
+ protected :new
12
+
13
+ def list
14
+ list = `#{ListCmd}`
15
+ list = list.split("\n")
16
+ list.collect { |host| Host.new(host.chomp) }
17
+ end
18
+
19
+ def add(host, ip = "127.0.0.1", force = false)
20
+ if find_by_host(host).nil? || force
21
+ unless ip[/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/]
22
+ ip = Socket.gethostbyname(ip)[3].bytes.to_a.join('.')
23
+ end
24
+
25
+ `#{CreateCmd % [host, ip]}`
26
+ flush!
27
+ find_by_host(host)
28
+ else
29
+ raise Ghost::RecordExists, "Can not overwrite existing record"
30
+ end
31
+ end
32
+
33
+ def find_by_host(host)
34
+ @hosts ||= {}
35
+ @hosts[host] ||= begin
36
+ output = `#{ReadCmd % host}`
17
37
 
18
- def add(host, ip = "127.0.0.1", force = false)
19
- if find_by_host(host).nil? || force
20
- unless ip[/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/]
21
- ip = Socket.gethostbyname(ip)[3].bytes.to_a.join('.')
38
+ if output =~ /eDSRecordNotFound/
39
+ nil
40
+ else
41
+ host = parse_host(output)
42
+ ip = parse_ip(output)
43
+
44
+ Host.new(host, ip)
45
+ end
22
46
  end
23
-
24
- `#{CreateCmd % [host, ip]}`
47
+ end
48
+
49
+ def find_by_ip(ip)
50
+ nil
51
+ end
52
+
53
+ def empty!
54
+ list.each { |h| delete(h) }
55
+ nil
56
+ end
57
+
58
+ def delete(host)
59
+ `#{DeleteCmd % host.to_s}`
25
60
  flush!
26
- find_by_host(host)
27
- else
28
- raise "Can not overwrite existing record"
29
- end
30
- end
31
-
32
- def find_by_host(host)
33
- @hosts ||= {}
34
- @hosts[host] ||= begin
35
- output = `#{ReadCmd % host}`
36
-
37
- if output =~ /eDSRecordNotFound/
38
- nil
39
- else
40
- host = parse_host(output)
41
- ip = parse_ip(output)
42
-
43
- Host.new(host, ip)
61
+ end
62
+
63
+ def delete_matching(pattern)
64
+ pattern = Regexp.escape(pattern)
65
+ hosts = list.select { |h| h.to_s.match(/#{pattern}/) }
66
+ hosts.each do |h|
67
+ delete(h)
44
68
  end
69
+ flush! unless hosts.empty?
70
+ hosts
45
71
  end
46
- end
47
-
48
- def find_by_ip(ip)
49
- nil
50
- end
51
-
52
- def empty!
53
- list.each { |h| delete(h) }
54
- nil
55
- end
56
-
57
- def delete(host)
58
- `#{DeleteCmd % host.to_s}`
59
- flush!
60
- end
61
-
62
- def delete_matching(pattern)
63
- pattern = Regexp.escape(pattern)
64
- hosts = list.select { |h| h.to_s.match(/#{pattern}/) }
65
- hosts.each do |h|
66
- delete(h)
72
+
73
+ # Flushes the DNS Cache
74
+ def flush!
75
+ `dscacheutil -flushcache`
76
+ @hosts = {}
77
+ true
78
+ end
79
+
80
+ protected
81
+ def parse_host(output)
82
+ parse_value(output, 'RecordName')
83
+ end
84
+
85
+ def parse_ip(output)
86
+ parse_value(output, 'IPAddress')
87
+ end
88
+
89
+ def parse_value(output, key)
90
+ match = output.match(Regexp.new("^#{key}: (.*)$"))
91
+ match[1] unless match.nil?
67
92
  end
68
- flush! unless hosts.empty?
69
- hosts
70
93
  end
71
-
72
- # Flushes the DNS Cache
73
- def flush!
74
- `dscacheutil -flushcache`
75
- @hosts = {}
76
- true
94
+
95
+ def initialize(host, ip=nil)
96
+ @host = host
97
+ @ip = ip
77
98
  end
78
-
79
- protected
80
- def parse_host(output)
81
- parse_value(output, 'RecordName')
99
+
100
+ def hostname
101
+ @host
82
102
  end
83
-
84
- def parse_ip(output)
85
- parse_value(output, 'IPAddress')
103
+ alias :to_s :hostname
104
+ alias :host :hostname
105
+ alias :name :hostname
106
+
107
+ def ip
108
+ @ip ||= self.class.send(:parse_ip, dump)
86
109
  end
87
-
88
- def parse_value(output, key)
89
- match = output.match(Regexp.new("^#{key}: (.*)$"))
90
- match[1] unless match.nil?
110
+
111
+ private
112
+ def dump
113
+ @dump ||= `#{ReadCmd % hostname}`
91
114
  end
92
115
  end
93
-
94
- def initialize(host, ip=nil)
95
- @host = host
96
- @ip = ip
97
- end
98
-
99
- def hostname
100
- @host
101
- end
102
- alias :to_s :hostname
103
- alias :host :hostname
104
- alias :name :hostname
105
-
106
- def ip
107
- @ip ||= self.class.send(:parse_ip, dump)
108
- end
109
-
110
- private
111
- def dump
112
- @dump ||= `#{ReadCmd % hostname}`
113
- end
114
116
  end