class Rex::JSONHashFile

Attributes

path[RW]

Public Class Methods

new(path) click to toggle source
# File lib/rex/json_hash_file.rb, line 13
def initialize(path)
  self.path = path
  @lock = Mutex.new
  @hash = {}
  @last = 0
end

Public Instance Methods

[](k) click to toggle source
# File lib/rex/json_hash_file.rb, line 20
def [](k)
  synced_update
  @hash[k]
end
[]=(k,v) click to toggle source
# File lib/rex/json_hash_file.rb, line 25
def []=(k,v)
  synced_update do
    @hash[k] = v
  end
end
clear() click to toggle source
# File lib/rex/json_hash_file.rb, line 42
def clear
  synced_update do
    @hash.clear
  end
end
delete(k) click to toggle source
# File lib/rex/json_hash_file.rb, line 36
def delete(k)
  synced_update do
    @hash.delete(k)
  end
end
keys() click to toggle source
# File lib/rex/json_hash_file.rb, line 31
def keys
  synced_update
  @hash.keys
end

Private Instance Methods

parse_data(data) click to toggle source
# File lib/rex/json_hash_file.rb, line 82
def parse_data(data)
  return {} if data.to_s.strip.length == 0
  begin
    JSON.parse(data)
  rescue JSON::ParserError => e
    # elog("JSONHashFile @ #{path} was corrupt: #{e.class} #{e}"
    {}
  end
end
synced_update(&block) click to toggle source

Save the file, but prevent thread & process contention

# File lib/rex/json_hash_file.rb, line 51
def synced_update(&block)
  @lock.synchronize do
    ::FileUtils.mkdir_p(::File.dirname(path))
    ::File.open(path, ::File::RDWR|::File::CREAT) do |fd|
      fd.flock(::File::LOCK_EX)

      # Reload and merge if the file has changed recently
      if fd.stat.mtime.to_f > @last
        parse_data(fd.read).merge(@hash).each_pair do |k,v|
          @hash[k] = v
        end
      end

      res = nil

      # Update the file on disk if new data is written
      if block_given?
        res = block.call
        fd.rewind
        fd.write(JSON.pretty_generate(@hash))
        fd.sync
        fd.truncate(fd.pos)
      end

      @last = fd.stat.mtime.to_f

      res
    end
  end
end