131 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Crystal
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Crystal
		
	
	
	
	
	
# Duration to wait before taken modification into account (in minutes).
 | 
						|
duration = 5
 | 
						|
 | 
						|
if ARGV.size != 2
 | 
						|
	puts "usage: #{PROGRAM_NAME} dnsmanagerd-bind9-dir powerdns-bind9-dir"
 | 
						|
	exit 0
 | 
						|
end
 | 
						|
 | 
						|
class BrainlessLog
 | 
						|
	class_property last_text_size : Int32 = 0
 | 
						|
 | 
						|
	# In case the lastly `unimportant` written string is to be kept on-screen.
 | 
						|
	def self.important(str : String)
 | 
						|
		unimportant "" # Clear the line and last_text_size = 0
 | 
						|
		STDOUT.puts str
 | 
						|
	end
 | 
						|
 | 
						|
	# Log something that isn't important, meaning text that will be
 | 
						|
	# rewritten in-place since we don't really want to read it.
 | 
						|
	# WARNING: user may want to keep the lastly written string, `keep` it.
 | 
						|
	def self.unimportant(str : String)
 | 
						|
		STDOUT.write ("\r" + (" " * self.last_text_size)).to_slice # Clear the line.
 | 
						|
		self.last_text_size = str.size
 | 
						|
		STDOUT.write ("\r" + str).to_slice
 | 
						|
	end
 | 
						|
 | 
						|
	# In case the last `unimportant` string is to be kept on-screen.
 | 
						|
	def self.keep
 | 
						|
		STDOUT.puts "" unless self.last_text_size == 0
 | 
						|
	end
 | 
						|
 | 
						|
	# In case the last `unimportant` string should be removed.
 | 
						|
	def self.flush
 | 
						|
		unimportant "" if self.last_text_size > 0
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
class Context
 | 
						|
	class_property dnsmanagerd_dir : String = ""
 | 
						|
	class_property powerdns_dir    : String = ""
 | 
						|
end
 | 
						|
 | 
						|
def copy_domain_files(domain : String) : Nil
 | 
						|
	src  = "#{Context.dnsmanagerd_dir}/#{domain}"
 | 
						|
	dest = "#{Context.powerdns_dir}/#{domain}"
 | 
						|
	BrainlessLog.important "copying #{src} -> #{dest}"
 | 
						|
	i = File.info src
 | 
						|
	File.copy src, dest
 | 
						|
rescue e : File::AccessDeniedError
 | 
						|
	BrainlessLog.important "You don't have enough rights: #{e}"
 | 
						|
rescue e : File::Error
 | 
						|
	BrainlessLog.important "File error: #{e}"
 | 
						|
end
 | 
						|
 | 
						|
def run_process(cmd : String, params : Array(String), env : Hash(String, String)) : Nil
 | 
						|
	unless Process.run(cmd, params, env,
 | 
						|
			true # clear environment
 | 
						|
			# input:   Process::Redirect::Inherit,
 | 
						|
			# output:  Process::Redirect::Inherit,
 | 
						|
			# error:   Process::Redirect::Inherit
 | 
						|
			).success?
 | 
						|
		puts "cannot run #{cmd} #{params.join(' ')}"
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
def pdns_reload(domain : String) : Nil
 | 
						|
	BrainlessLog.important "reloading a domain: pdns_control bind-reload-now #{domain}"
 | 
						|
	run_process("pdns_control", [ "bind-reload-now", domain ], { "HOME" => "/" })
 | 
						|
end
 | 
						|
 | 
						|
def update_domain(domain : String) : Nil
 | 
						|
	BrainlessLog.important "domain to reload: #{domain}"
 | 
						|
	copy_domain_files domain
 | 
						|
	pdns_reload domain
 | 
						|
end
 | 
						|
 | 
						|
def pdns_add(domain : String) : Nil
 | 
						|
	BrainlessLog.important "adding a new domain: pdns_control bind-add-zone #{Context.powerdns_dir}/#{domain}"
 | 
						|
	run_process("pdns_control",
 | 
						|
			[ "bind-add-zone", domain, "#{Context.powerdns_dir}/#{domain}" ],
 | 
						|
			{ "HOME" => "/" })
 | 
						|
end
 | 
						|
 | 
						|
def add_domain(domain : String) : Nil
 | 
						|
	BrainlessLog.important "domain to add: #{domain}"
 | 
						|
	copy_domain_files domain
 | 
						|
	pdns_add domain
 | 
						|
end
 | 
						|
 | 
						|
def delete_file(path : String)
 | 
						|
	File.delete path
 | 
						|
rescue e : File::AccessDeniedError
 | 
						|
	BrainlessLog.important "You don't have enough rights: #{e}"
 | 
						|
end
 | 
						|
 | 
						|
def del_domain(domain : String) : Nil
 | 
						|
	BrainlessLog.important "domain to delete: #{domain}"
 | 
						|
	delete_file "#{Context.powerdns_dir}/#{domain}"
 | 
						|
	# TODO: pdns_control ???
 | 
						|
end
 | 
						|
 | 
						|
Context.dnsmanagerd_dir = ARGV[0]
 | 
						|
Context.powerdns_dir    = ARGV[1]
 | 
						|
 | 
						|
dnsmanagerd_dir_content = Dir.children(Context.dnsmanagerd_dir).select { |d| ! d.ends_with? ".wip" }
 | 
						|
powerdns_dir_content    = Dir.children(Context.powerdns_dir)
 | 
						|
 | 
						|
both = dnsmanagerd_dir_content & powerdns_dir_content
 | 
						|
both.each do |d|
 | 
						|
	i1 = File.info "#{Context.dnsmanagerd_dir}/#{d}"
 | 
						|
	i2 = File.info "#{Context.powerdns_dir}/#{d}"
 | 
						|
 | 
						|
	if i1.modification_time > i2.modification_time
 | 
						|
		# Wait for a few minutes before changing anything, to avoid useless reloads.
 | 
						|
		if Time.local > i1.modification_time.shift minutes: duration
 | 
						|
			BrainlessLog.important "#{d}: updating"
 | 
						|
			update_domain d
 | 
						|
		else
 | 
						|
			BrainlessLog.unimportant "#{d}: has been modified less than #{duration} minute ago, do not update yet"
 | 
						|
		end
 | 
						|
	else
 | 
						|
		BrainlessLog.unimportant "#{d}: hasn't been modified"
 | 
						|
	end
 | 
						|
end
 | 
						|
BrainlessLog.flush
 | 
						|
 | 
						|
to_add = dnsmanagerd_dir_content - powerdns_dir_content
 | 
						|
to_add.each { |d| add_domain d }
 | 
						|
 | 
						|
to_delete = powerdns_dir_content - dnsmanagerd_dir_content
 | 
						|
to_delete.each { |d| del_domain d }
 |