Present an API to Apache's DB files for mod_rewrite.
This is a bit complicated since there don't appear to be a DB file format common to ruby and Apache that does not have corruption issues. The only format they can agree on is text, which is slow for 10's of thousands of entries. Unfortunately, that means we have to go through a convoluted process to populate the final Apache DB.
This locks down to one thread for safety. You MUST ensure that close is called to release all locks. Close also syncs changes to Apache if data was modified.
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 859 def initialize(flags=nil) @closed = false if self.MAPNAME.nil? raise NotImplementedError.new("Must subclass with proper map name.") end @config = OpenShift::Config.new @basedir = @config.get("OPENSHIFT_HTTP_CONF_DIR") @mode = 0640 if flags.nil? @flags = READER else @flags = flags end @filename = File.join(@basedir, self.MAPNAME) @lockfile = @@LOCKFILEBASE + '.' + self.MAPNAME + self.SUFFIX + '.lock' super() # Each filename needs its own mutex and lockfile @@LOCKS[@lockfile].lock begin @lfd = File.new(@lockfile, Fcntl::O_RDWR | Fcntl::O_CREAT, 0640) if writable? @lfd.flock(File::LOCK_EX) else @lfd.flock(File::LOCK_SH) end if @flags != NEWDB reload end rescue begin if not @lfd.nil? @lfd.close() end ensure @@LOCKS[@lockfile].unlock end raise end end
Preferred method of access is to feed a block to open so we can guarantee the close.
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 1015 def self.open(flags=nil) inst = new(flags) if block_given? begin return yield(inst) rescue @flags = nil # Disable flush raise ensure if not inst.closed? inst.close end end end inst end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 943 def callout # Use Berkeley DB so that there's no race condition between # multiple file moves. The Berkeley DB implementation creates a # scratch working file under certain circumstances. Use a # scratch dir to protect it. Dir.mktmpdir([File.basename(@filename) + ".db-", ""], File.dirname(@filename)) do |wd| tmpdb = File.join(wd, 'new.db') httxt2dbm = ["/usr/bin","/usr/sbin","/bin","/sbin"].map {|d| File.join(d, "httxt2dbm")}.select {|p| File.exists?(p)}.pop if httxt2dbm.nil? logger.warn("WARNING: no httxt2dbm command found, relying on PATH") httxt2dbm="httxt2dbm" end cmd = %Q{#{httxt2dbm} -f DB -i #{@filename}#{self.SUFFIX} -o #{tmpdb}} out,err,rc = Utils::oo_spawn(cmd) if rc == 0 logger.debug("httxt2dbm: #{@filename}: #{rc}: stdout: #{out} stderr:#{err}") begin oldstat = File.stat(@filename + '.db') File.chown(oldstat.uid, oldstat.gid, tmpdb) File.chmod(oldstat.mode & 0777, tmpdb) rescue Errno::ENOENT end FileUtils.mv(tmpdb, @filename + '.db', :force=>true) else logger.error("ERROR: failure httxt2dbm #{@filename}: #{rc}: stdout: #{out} stderr:#{err}") unless rc == 0 end end end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 996 def close @closed=true begin begin self.flush ensure @lfd.close() unless @lfd.closed? end ensure @@LOCKS[@lockfile].unlock if @@LOCKS[@lockfile].locked? end end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 1009 def closed? @closed end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 912 def decode_contents(f) f.each do |l| path, dest = l.strip.split if (not path.nil?) and (not dest.nil?) self.store(path, dest) end end end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 921 def encode_contents(f) self.each do |k, v| f.write([k, v].join(' ') + "\n") end end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 974 def flush if writable? File.open(@filename + self.SUFFIX + '-', Fcntl::O_RDWR | Fcntl::O_CREAT | Fcntl::O_TRUNC, 0640) do |f| encode_contents(f) end # Ruby 1.9 Hash preserves order, compare files to see if anything changed if FileUtils.compare_file(@filename + self.SUFFIX + '-', @filename + self.SUFFIX) FileUtils.rm(@filename + self.SUFFIX + '-', :force=>true) else begin oldstat = File.stat(@filename + self.SUFFIX) FileUtils.chown(oldstat.uid, oldstat.gid, @filename + self.SUFFIX + '-') FileUtils.chmod(oldstat.mode & 0777, @filename + self.SUFFIX + '-') rescue Errno::ENOENT end FileUtils.mv(@filename + self.SUFFIX + '-', @filename + self.SUFFIX, :force=>true) callout end end end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 927 def reload begin File.open(@filename + self.SUFFIX, Fcntl::O_RDONLY) do |f| decode_contents(f) end rescue Errno::ENOENT if not [WRCREAT, NEWDB].include?(@flags) raise end end end
Public, update using a block The block is called for each key, value pair of the hash and uses the following parameters:
deletions Array of keys to delete updates Hash of key->value pairs to add/update k, v Key and value of this iteration
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 1038 def update_block deletions = [] updates = {} self.each do |k, v| yield(deletions, updates, k, v) end self.delete_if { |k, v| deletions.include?(k) } self.update(updates) end
# File lib/openshift-origin-node/model/frontend_httpd.rb, line 939 def writable? [WRITER, WRCREAT, NEWDB].include?(@flags) end