Class: OsCtld::EtcHosts
- Inherits:
-
Object
- Object
- OsCtld::EtcHosts
- Includes:
- OsCtl::Lib::Utils::File
- Defined in:
- lib/osctld/etc_hosts.rb
Overview
Manipulate localhost hostnames in ‘/etc/hosts` like files
Constant Summary collapse
- NOTICE_HEAD =
'### Start of osctld-generated notice'.freeze
- NOTICE_BODY =
<<~END.freeze # This file is updated by osctld from vpsAdminOS on every VPS start to configure # the hostname. If you would like to manage the hostname manually, # administrators can configure this by `osctl ct unset hostname` and users # in VPS details in vpsAdmin. In addition, osctld will not modify this file # if the write by user permission is removed: # # chmod u-w /etc/hosts END
- NOTICE_TAIL =
'### End of osctld-generated notice'.freeze
Instance Attribute Summary collapse
- #path ⇒ String readonly
Instance Method Summary collapse
-
#add_name(line, name, after: nil) ⇒ Object
protected
Add hostname to ‘line` from `/etc/hosts`.
-
#all_names(hostname) ⇒ Object
protected
Return all names in the order that should be set for a hostname.
- #clear_notice(dst, src) ⇒ Object protected
-
#do_edit(names) {|new, old| ... } ⇒ Object
protected
Edit the hosts file and let the caller transform it.
-
#each_line(io) {|line| ... } ⇒ Object
protected
Iterate over all lines except the osctld-generated notice.
-
#includes_name?(line, name) ⇒ Boolean
protected
Check if a line of string contains specific hostname.
-
#initialize(path) ⇒ EtcHosts
constructor
A new instance of EtcHosts.
- #replace(old_hostname, new_hostname) ⇒ Object
-
#replace_name(line, old_name, new_name) ⇒ Object
protected
Replace hostname in ‘line` read from `/etc/hosts`.
- #set(hostname) ⇒ Object
- #unmanage ⇒ Object
- #write_notice(dst) ⇒ Object protected
- #zip_all(a, b) ⇒ Object protected
Constructor Details
#initialize(path) ⇒ EtcHosts
Returns a new instance of EtcHosts.
24 25 26 |
# File 'lib/osctld/etc_hosts.rb', line 24 def initialize(path) @path = path end |
Instance Attribute Details
#path ⇒ String (readonly)
21 22 23 |
# File 'lib/osctld/etc_hosts.rb', line 21 def path @path end |
Instance Method Details
#add_name(line, name, after: nil) ⇒ Object (protected)
Add hostname to ‘line` from `/etc/hosts`
The hostname is put into the first position.
202 203 204 205 206 207 208 209 210 211 |
# File 'lib/osctld/etc_hosts.rb', line 202 def add_name(line, name, after: nil) if after replace_name(line, after, "#{after} #{name}") else return if line !~ /^([^\s]+)(\s+)/ i = $~.end(2) "#{::Regexp.last_match(1)}#{::Regexp.last_match(2)}#{name} #{line[i..]}" end end |
#all_names(hostname) ⇒ Object (protected)
Return all names in the order that should be set for a hostname
176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/osctld/etc_hosts.rb', line 176 def all_names(hostname) ret = [] if hostname.local == hostname.fqdn ret << hostname.fqdn else ret << hostname.fqdn << hostname.local end ret end |
#clear_notice(dst, src) ⇒ Object (protected)
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/osctld/etc_hosts.rb', line 152 def clear_notice(dst, src) inside = false src.each_line do |line| if line.start_with?(NOTICE_HEAD) inside = true next end if inside if !line.start_with?('#') # malformed notice inside = false elsif line.start_with?(NOTICE_TAIL) inside = false next end end dst.write(line) unless inside end end |
#do_edit(names) {|new, old| ... } ⇒ Object (protected)
Edit the hosts file and let the caller transform it
If the target file exists, IOs to both new and old files are yielded.
If the target file does not exist, it is created and populated with ‘names`. Nothing is yielded to the caller in that case.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/osctld/etc_hosts.rb', line 108 def do_edit(names) regenerate_file(path, 0o644) do |new, old| write_notice(new) if old yield(new, old) else new.puts("127.0.0.1 #{(names + %w[localhost]).join(' ')}") new.puts("::1 #{(names + %w[localhost ip6-localhost ip6-loopback]).join(' ')}") end end end |
#each_line(io) {|line| ... } ⇒ Object (protected)
Iterate over all lines except the osctld-generated notice
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/osctld/etc_hosts.rb', line 124 def each_line(io) within_notice = false io.each_line do |line| if line.start_with?(NOTICE_HEAD) within_notice = true next end if within_notice if !line.start_with?('#') # malformed notice within_notice = false elsif line.start_with?(NOTICE_TAIL) within_notice = false next end end yield(line) unless within_notice end end |
#includes_name?(line, name) ⇒ Boolean (protected)
Check if a line of string contains specific hostname
191 192 193 |
# File 'lib/osctld/etc_hosts.rb', line 191 def includes_name?(line, name) /\s#{Regexp.escape(name)}(\s|$)/ =~ line end |
#replace(old_hostname, new_hostname) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/osctld/etc_hosts.rb', line 56 def replace(old_hostname, new_hostname) old_names = all_names(old_hostname) new_names = all_names(new_hostname) do_edit(new_names) do |new, old| each_line(old) do |line| if /^127\.0\.0\.1\s/ !~ line && /^::1\s/ !~ line new.write(line) next end new_line = line last = nil zip_all(new_names, old_names).each do |new_name, old_name| if old_name && includes_name?(new_line, old_name) new_line = replace_name(new_line, old_name, new_name || '') elsif new_name new_line = add_name(new_line, new_name, after: last) end last = new_name end new.write(new_line) end end end |
#replace_name(line, old_name, new_name) ⇒ Object (protected)
Replace hostname in ‘line` read from `/etc/hosts`
218 219 220 221 222 223 |
# File 'lib/osctld/etc_hosts.rb', line 218 def replace_name(line, old_name, new_name) line.sub( /(\s)#{Regexp.escape(old_name)}(\s|$)/, "\\1#{new_name}\\2" ) end |
#set(hostname) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/osctld/etc_hosts.rb', line 29 def set(hostname) names = all_names(hostname) do_edit(names) do |new, old| each_line(old) do |line| if /^127\.0\.0\.1\s/ !~ line && /^::1\s/ !~ line new.write(line) next end new_line = line last = nil names.each do |name| next if includes_name?(new_line, name) new_line = add_name(new_line, name, after: last) last = name end new.write(new_line) end end end |
#unmanage ⇒ Object
85 86 87 88 89 90 91 92 93 |
# File 'lib/osctld/etc_hosts.rb', line 85 def unmanage return unless File.exist?(path) regenerate_file(path, 0o644) do |new, old| next if old.nil? clear_notice(new, old) end end |
#write_notice(dst) ⇒ Object (protected)
146 147 148 149 150 |
# File 'lib/osctld/etc_hosts.rb', line 146 def write_notice(dst) dst.puts(NOTICE_HEAD) dst.write(NOTICE_BODY) dst.puts(NOTICE_TAIL) end |
#zip_all(a, b) ⇒ Object (protected)
225 226 227 |
# File 'lib/osctld/etc_hosts.rb', line 225 def zip_all(a, b) [a.size, b.size].max.times.map { |i| [a[i], b[i]] } end |