From b4ad2cfb9e61ee25e46ae040e50548d5caaff952 Mon Sep 17 00:00:00 2001 From: "G. Gibson" Date: Mon, 20 Mar 2023 11:38:18 -0700 Subject: [PATCH] Begin new setup --- new_setup.rb | 1816 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1816 insertions(+) create mode 100755 new_setup.rb diff --git a/new_setup.rb b/new_setup.rb new file mode 100755 index 0000000..bd8d399 --- /dev/null +++ b/new_setup.rb @@ -0,0 +1,1816 @@ +#!/usr/bin/env jruby +if ARGV.include?("--quiet") + STDIN.reopen("/dev/null", "r") + STDOUT.reopen("/dev/null", "w") + STDERR.reopen("/dev/null", "w") +end +require 'rubygems' +require 'gxg-framework' +# ### Define Directory Layout for Server +::GxG::SYSTEM.gxg_root = File.expand_path("../",File.dirname(__FILE__)) +module GxG + # Define Server directories, ensure directories are present + SYSTEM_PATHS = ::GxG::SYSTEM.server_paths() +end +::GxG::SYSTEM_PATHS.values.each do |a_path| + if a_path + unless Dir.exist?(a_path) + begin + FileUtils.mkpath(a_path) + rescue Exception => error + log_error({:error => error, :parameters => a_path}) + end + end + end +end +# ### Mount Databases by role +if File.exists?("#{GxG::SYSTEM_PATHS[:configuration]}/databases.json") + handle = File.open("#{GxG::SYSTEM_PATHS[:configuration]}/databases.json", "rb") + db_config = ::JSON::parse(handle.read(), {:symbolize_names => true}) + handle.close + # ### Set DB Roles, and other details + ::GxG::DB[:roles] = {} + if db_config[:databases].is_a?(::Array) + # + mounted = [] + db_config[:databases].each do |entry| + entry[:roles].each do |the_role| + if the_role.downcase == "users" + # + if ::URI::parse(entry[:url]).scheme.downcase == "ldap" + # TODO: ExternalAuthority + mounted << {:url => entry[:url].to_s, :db => nil, :roles => [:user]} + else + if ::URI::parse(entry[:url]).scheme.downcase == "sqlite" + if ::URI::parse(entry[:url]).hostname.to_s[0] == "/" || ::URI::parse(entry[:url]).path.to_s[0] == "/" + # absolute path + the_db_url = entry[:url] + else + # relative path + the_db_url = "sqlite://#{GxG::SYSTEM_PATHS[:databases]}/#{::URI::parse(entry[:url]).hostname}" + end + else + the_db_url = entry[:url] + end + the_db = ::GxG::Database::connect(the_db_url) + if the_db + the_db.clear_all_element_locks + the_db.empty_trash + ::GxG::DB[:authority] = the_db + ::GxG::DB[:roles][:users] = ::GxG::DB[:authority] + mounted << {:url => entry[:url].to_s, :db => the_db, :roles => [:users]} + break + end + end + # + end + end + if ::GxG::DB[:authority].is_any?(::GxG::Database::Database, ::GxG::Database::ExternalAuthority) + break + end + end + # + if ::GxG::DB[:authority].is_any?(::GxG::Database::Database, ::GxG::Database::ExternalAuthority) + db_config[:databases].each do |entry| + log_info "Mounting or creating a database ..." + if (mounted.map {|item| item[:url]}).include?(entry[:url].to_s) + # Flesh out other roles : {:url => entry[:url].to_s, :db => the_db, :roles => [:user]} + mounted.each do |the_mount| + # + if entry[:url] == the_mount[:url] + entry[:roles].each do |the_role| + unless the_mount[:roles].include?(the_role) + ::GxG::DB[:roles][(the_role.to_sym)] = the_mount[:db] + the_mount[:roles] << the_role.to_sym + end + end + end + # + end + else + # Mount fresh + if ::URI::parse(entry[:url]).scheme.downcase == "sqlite" + if ::URI::parse(entry[:url]).hostname.to_s[0] == "/" || ::URI::parse(entry[:url]).path.to_s[0] == "/" + # absolute path + the_db_url = entry[:url] + else + # relative path + the_db_url = "sqlite://#{GxG::SYSTEM_PATHS[:databases]}/#{::URI::parse(entry[:url]).hostname}" + end + else + the_db_url = entry[:url] + end + the_db = ::GxG::Database::connect(the_db_url, {:authority => ::GxG::DB[:authority]}) + if the_db + # house cleaning: + the_db.clear_all_element_locks + the_db.empty_trash + mounted_roles = [] + entry[:roles].each do |the_role| + unless ::GxG::DB[:roles][(the_role.to_sym)] + ::GxG::DB[:roles][(the_role.to_sym)] = the_db + mounted_roles << the_role.to_sym + end + end + mounted << {:url => entry[:url].to_s, :db => the_db, :roles => mounted_roles} + end + # + end + end + else + # Error - no authority db found, unable to proceed. + log_error "No Authority Database Found : Unable to proceed." + exit 1 + end + # + unless ::GxG::DB[:roles][:formats].is_a?(::GxG::Database::Database) + # Error - no format db found, unable to proceed. + log_error "No Format Database Found : Unable to proceed." + exit 1 + end + # + end + # Set Administrator/Members credential for system use. + ::GxG::DB[:administrator] = ::GxG::DB[:authority][:system_credentials][:administrator] + ::GxG::DB[:administrators] = ::GxG::DB[:authority][:system_credentials][:administrators] + ::GxG::DB[:developers] = ::GxG::DB[:authority][:system_credentials][:developers] + ::GxG::DB[:designers] = ::GxG::DB[:authority][:system_credentials][:designers] + ::GxG::DB[:users] = ::GxG::DB[:authority][:system_credentials][:users] +else + log_warn "No Database Configuration found: run setup.rb from the shell." +end +# ### Populate the Mandatory VFS with GxG::SYSTEM_PATHS +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:system]}), "/System") +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:services]}), "/Services") +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:installers]}), "/Installers") +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:temporary]}), "/Temporary") +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:logs]}), "/Logs") +::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:public]}), "/Public") +# Populate Optional VFS mount points +if GxG::valid_uuid?(GxG::DB[:administrator]) + if File.exists?("#{GxG::SYSTEM_PATHS[:configuration]}/mounts.json") + handle = File.open("#{GxG::SYSTEM_PATHS[:configuration]}/mounts.json", "rb") + mount_config = ::JSON::parse(handle.read(), {:symbolize_names => true}) + handle.close + # + volume = nil + mount_config[:mount_points].each do |entry| + if entry[:db_role] + the_db = ::GxG::DB[:roles][(entry[:db_role].to_sym)] + if the_db + volume = ::GxG::Storage::Volume.new({:database => the_db, :credential => GxG::DB[:administrator]}) + end + end + if entry[:file_system] + if ["./", ".."].include?(entry[:file_system][0..1]) + # Relative Path - to GXGROOT + fs_path = File.expand_path(entry[:file_system],::GxG::SYSTEM.gxg_root()) + else + # Absolute Path + fs_path = entry[:file_system] + end + unless Dir.exist?(fs_path) + FileUtils.mkdir_p(fs_path) + end + if entry[:path] == "/Users" + ::GxG::SYSTEM_PATHS[:users] = fs_path + unless Dir.exist?(fs_path + "/Shared") + FileUtils.mkdir_p((fs_path + "/Shared")) + end + else + ::GxG::SYSTEM_PATHS[(File.basename(entry[:path]).downcase.to_sym)] = fs_path + end + volume = ::GxG::Storage::Volume.new({:directory => fs_path}) + end + if volume + GxG::VFS.mount(volume, entry[:path]) + volume = nil + end + end + else + log_warn "No VFS Optional Configuration found: run setup.rb from the shell." + end +end +# ### Services Framework +module GxG + SERVICES = {} + module Services + # ### Service Management + def self.start_order() + # Calculate service startup order based upon interdepenedencies + # Review : Research a REAL dep tree calculator - See: https://stackoverflow.com/questions/21108109/how-do-i-find-out-all-the-dependencies-of-a-gem + deps = {} + ::GxG::SERVICES.each_pair do |moniker, service| + unless moniker == :core + unless deps[(moniker)] + deps[(moniker)] = 0 + end + service.required_services().each do |the_dep| + if deps[(the_dep.to_sym)] + deps[(the_dep.to_sym)] -= 1 + else + deps[(the_dep.to_sym)] = 0 + end + end + end + end + boot_queue = [] + (deps.sort_by {|key,value| value}).to_h.keys.each do |the_service_moniker| + ::GxG::SERVICES[(the_service_moniker)].required_services.each do |moniker| + unless boot_queue.include?(moniker) + boot_queue << moniker + end + end + unless boot_queue.include?(the_service_moniker) + boot_queue << the_service_moniker + end + end + # + boot_queue + end + # + def self.stop_order() + ::GxG::Services::start_order().reverse! + end + # + def self.start_service(moniker=nil) + if moniker.is_a?(::Symbol) + if ::GxG::SERVICES[(moniker)] + # ::GxG::SERVICES[(moniker)].start + ::GxG::SERVICES[(moniker)].call_event({:start => nil}) + end + end + end + # + def self.stop_service(moniker=nil) + if moniker.is_a?(::Symbol) + if ::GxG::SERVICES[(moniker)] + # ::GxG::SERVICES[(moniker)].stop + ::GxG::SERVICES[(moniker)].call_event({:stop => nil}) + end + end + end + # + def self.install_service(moniker=nil) + if moniker.is_a?(::Symbol) + configuration = ::GxG::SERVICES[:core].configuration + unless configuration[:available].is_a?(::Array) + configuration[:available] = [] + end + unless configuration[:available].include?(moniker.to_s) + configuration[:available] << moniker.to_s + end + ::GxG::SERVICES[:core].save_configuration + # + true + else + false + end + end + # + def self.service_available?(moniker=nil) + if moniker.is_a?(::Symbol) + configuration = ::GxG::SERVICES[:core].configuration + if configuration[:available].is_a?(::Array) + if configuration[:available].include?(moniker.to_s) + true + else + false + end + else + false + end + else + false + end + end + # + def self.enable_service(moniker=nil) + if moniker.is_a?(::Symbol) + configuration = ::GxG::SERVICES[:core].configuration + unless configuration[:disabled].is_a?(::Array) + configuration[:disabled] = [] + end + unless configuration[:enabled].is_a?(::Array) + configuration[:enabled] = [] + end + if configuration[:disabled].include?(moniker.to_s) + configuration[:disabled].delete_at(configuration[:enabled].find_index(moniker.to_s)) + end + unless configuration[:enabled].include?(moniker.to_s) + configuration[:enabled] << moniker.to_s + end + ::GxG::SERVICES[:core].save_configuration + # + true + else + false + end + end + # + def self.disable_service(moniker=nil) + if moniker.is_a?(::Symbol) + configuration = ::GxG::SERVICES[:core].configuration + unless configuration[:disabled].is_a?(::Array) + configuration[:disabled] = [] + end + unless configuration[:enabled].is_a?(::Array) + configuration[:enabled] = [] + end + if configuration[:enabled].include?(moniker.to_s) + configuration[:enabled].delete_at(configuration[:enabled].find_index(moniker.to_s)) + end + unless configuration[:disabled].include?(moniker.to_s) + configuration[:disabled] << moniker.to_s + end + ::GxG::SERVICES[:core].save_configuration + # + true + else + false + end + end + # + def self.register_service(moniker=:unspecified, service_harness=nil) + if moniker + unless moniker == :unspecified + if service_harness.is_a?(GxG::Services::Service) + GxG::SERVICES[(moniker)] = service_harness + end + end + end + end + # ### Service Class Definition + class Service + # Class-level toolbox: + # Instance toolbox: + def initialize(moniker=:unspecified, options={:event_interval => 0.333}) + if moniker.is_a?(::Symbol) + if moniker == :unspecified || (::GxG::SERVICES[:core] && moniker == :core) + raise Exception, "You MUST provide a unique, specific Symbol as your service moniker. This becomes the system-wide service name other services will need provided." + else + # Service Initialization code + @provides = moniker + @requirements = [:core] + @thread_safety = ::Mutex.new + @heap = {} + @status = :stopped + @state = nil + @configuration = {} + @dispatcher = ::GxG::Events::EventDispatcher.new((options[:event_interval] || 0.333)) + @interface = {} + # + self.load_configuration + # + self.on(:interface, {:description => "Available Commands", :usage => "{ :interface => nil }", :public => true}) do + self.interface() + end + # + ::GxG::Services::register_service(moniker, self) + # + end + else + raise Exception, "You MUST provide a unique service name as a Symbol for your service moniker." + end + self + end + # + def inspect() + "" + end + # ### FS & VFS + def vfs_root() + if @provides == :core + "/Services" + else + "/Services/#{@provides.to_s}" + end + end + # + def fs_root() + ::File.expand_path("#{::GxG::SYSTEM.gxg_root()}/#{self.vfs_root()}") + end + # ### API Support + def publish_api() + if ::GxG::Services::service_available?(:www) + ::GxG::SERVICES[:www].call_event({:publish_api => {:service => self, :path => "/#{@provides.to_s}"}}, ::GxG::DB[:administrator]) + end + end + def unpublish_api() + if ::GxG::Services::service_available?(:www) + ::GxG::SERVICES[:www].call_event({:unpublish_api => {:path => "/#{@provides.to_s}"}}, ::GxG::DB[:administrator]) + end + end + # ### Service Management + def provides() + @provides + end + # + def required_services() + @requirements.clone + end + # + def require_service(moniker=:unspecified) + if moniker.is_a?(::Symbol) + unless moniker == :unspecified + unless @requirements.include?(moniker) + @requirements << moniker + end + end + end + end + # ### Configuration Management + def configuration() + @configuration + end + # + def save_configuration(options={}) + result = false + config_path = ::File.expand_path(::GxG::SYSTEM_PATHS[:configuration] + "/" + "#{@provides.to_s}.json") + if File.exists?(config_path) + File.delete(config_path) + end + begin + handle = File.open(config_path, "w+b", 0664) + handle.write(::JSON::pretty_generate(@configuration)) + handle.close + result = true + rescue Exception => the_error + log_warn("Failed to save configuration for #{@provides.inspect} --> #{the_error.to_s}") + end + result + end + # + def load_configuration(options={}) + result = false + config_path = ::File.expand_path(::GxG::SYSTEM_PATHS[:configuration] + "/" + "#{@provides.to_s}.json") + if File.exists?(config_path) + handle = File.open(config_path, "rb", 0664) + begin + @configuration =::JSON::parse(handle.read(), {:symbolize_names => true}) + result = true + rescue Exception => the_error + log_warn("Failed to load configuration for #{@provides.inspect} --> #{the_error.to_s}") + end + handle.close + # + if result == true + if ::GxG::valid_uuid?(@configuration[:state_uuid]) + unless @state.is_a?(::GxG::Database::PersistedHash) + @state = ::GxG::DB[:roles][:data].retrieve_by_uuid(@configuration[:state_uuid].to_s.to_sym, ::GxG::DB[:administrator]) + if @state.is_a?(::GxG::Database::PersistedHash) + @state.get_reservation + else + # This means the db was blown out and this must be reconstructed. + @state = ::GxG::DB[:roles][:data].try_persist({}, ::GxG::DB[:administrator]) + @state.get_reservation + @state.set_title("#{@provides.to_s} State Data") + @state.save + @configuration[:state_uuid] = @state.uuid.to_s + end + end + else + orphaned_state_record = GxG::DB[:roles][:data].uuid_list(GxG::DB[:administrator],{:title => "#{@provides.to_s} State Data"}).first + if orphaned_state_record + @state = ::GxG::DB[:roles][:data].retrieve_by_uuid((orphaned_state_record[:uuid]),::GxG::DB[:administrator]) + end + unless @state.is_a?(::GxG::Database::PersistedHash) + @state = ::GxG::DB[:roles][:data].try_persist({}, ::GxG::DB[:administrator]) + @state.get_reservation + @state.set_title("#{@provides.to_s} State Data") + @state.save + end + @configuration[:state_uuid] = @state.uuid.to_s + end + end + else + if @state.is_a?(::GxG::Database::PersistedHash) + unless ::GxG::valid_uuid?(@configuration[:state_uuid]) + @configuration[:state_uuid] = @state.uuid.to_s + end + else + if ::GxG::valid_uuid?(@configuration[:state_uuid]) + @state = ::GxG::DB[:roles][:data].retrieve_by_uuid(@configuration[:state_uuid].to_s.to_sym, ::GxG::DB[:administrator]) + @state.get_reservation + else + orphaned_state_record = GxG::DB[:roles][:data].uuid_list(GxG::DB[:administrator],{:title => "#{@provides.to_s} State Data"}).first + if orphaned_state_record + @state = ::GxG::DB[:roles][:data].retrieve_by_uuid((orphaned_state_record[:uuid]),::GxG::DB[:administrator]) + end + unless @state.is_a?(::GxG::Database::PersistedHash) + @state = ::GxG::DB[:roles][:data].try_persist({}, ::GxG::DB[:administrator]) + @state.get_reservation + @state.set_title("#{@provides.to_s} State Data") + @state.save + end + @configuration[:state_uuid] = @state.uuid.to_s + end + end + self.save_configuration + result = true + end + result + end + # ### Heap & State Data + def keys() + @thread_safety.synchronize { @heap.keys } + end + # + def [](key=nil) + if key.is_any?(::String, ::Symbol) + @thread_safety.synchronize { @heap[(key)] } + else + nil + end + end + # + def []=(key=nil,value=nil) + if key.is_any?(::String, ::Symbol) + @thread_safety.synchronize { @heap[(key)] = value } + else + nil + end + end + # + def delete(key=nil) + if key.is_any?(::String, ::Symbol) + @thread_safety.synchronize { @heap.delete(key) } + else + nil + end + end + # + def state_keys() + @state.keys() + end + # + def state_get(state_key=nil) + result = nil + if state_key.is_a?(::Symbol) + if @state.is_a?(::GxG::Database::PersistedHash) + result = @state[(state_key)] + end + end + result + end + # + def state_set(state_key=nil, state_value=nil) + if state_key.is_a?(::Symbol) + if @state.is_a?(::GxG::Database::PersistedHash) + @state[(state_key)] = state_value + @state.save + end + end + end + # + def state_delete(key) + result = nil + if state_key.is_a?(::Symbol) + if @state.is_a?(::GxG::Database::PersistedHash) + result = @state.delete(state_key) + end + end + result + end + # ### Activity and Status Management + def is_running?() + @status == :running || @status == :paused + end + # + def status() + @status + end + # + def start(options={:timeout => 30000.0}) + # start entire service, load configuration + @status = :starting + if self.load_configuration() == true + timeout = Time.now.to_f + (options[:timeout] || 30000.0) + all_loaded = false + # + if @provides == :core + all_loaded = true + else + # wait until all requirements load until timeout + requirement_count = @requirements.size + while (Time.now.to_f < timeout) do + started_count = 0 + @requirements.each do |moniker| + if ::GxG::SERVICES[(moniker)].is_running? == true + started_count += 1 + end + end + if requirement_count == started_count + all_loaded = true + break + else + sleep 1.0 + end + end + unless all_loaded == true + log_warn("Aborted service startup for #{@provides.inspect} because other required services did not start in time.") + end + end + # + if all_loaded == true + if self.respond_to_event?(:before_start) + self.call_event({:before_start => self}, ::GxG::DB[:administrator]) + end + # start event dispatcher + @dispatcher.startup + @status = :running + if self.respond_to_event?(:at_start) + self.call_event({:at_start => self}, ::GxG::DB[:administrator]) + end + if self.respond_to_event?(:after_start) + self.call_event({:after_start => self}, ::GxG::DB[:administrator]) + end + true + else + @status = :stopped + false + end + # + else + @status = :stopped + false + end + # + end + # + def stop(options={}) + # stop entire service + if self.respond_to_event?(:before_stop) + self.call_event({:before_stop => self}, ::GxG::DB[:administrator]) + end + @status = :stopping + # + if self.respond_to_event?(:at_stop) + self.call_event({:at_stop => self}, ::GxG::DB[:administrator]) + end + @dispatcher.shutdown + # + @status = :stopped + if self.respond_to_event?(:after_stop) + self.call_event({:after_stop => self}, ::GxG::DB[:administrator]) + end + true + end + # + def restart(options={}) + # restart entire service, reloading configuration + if self.respond_to_event?(:before_restart) + self.call_event({:before_restart => self}, ::GxG::DB[:administrator]) + end + if self.stop(options) == true + self.start(options) + if self.respond_to_event?(:at_restart) + self.call_event({:at_restart => self}, ::GxG::DB[:administrator]) + end + if self.respond_to_event?(:after_restart) + self.call_event({:after_restart => self}, ::GxG::DB[:administrator]) + end + true + else + false + end + # + end + # + def pause(options={}) + # pause dispatcher + if self.respond_to_event?(:before_pause) + self.call_event({:before_pause => self}, ::GxG::DB[:administrator]) + end + @dispatcher.shutdown + @status = :paused + if self.respond_to_event?(:at_pause) + self.call_event({:at_pause => self}, ::GxG::DB[:administrator]) + end + if self.respond_to_event?(:after_pause) + self.call_event({:after_pause => self}, ::GxG::DB[:administrator]) + end + true + end + # + def resume(options={}) + # resume dispatcher + if self.respond_to_event?(:before_resume) + self.call_event({:before_resume => self}, ::GxG::DB[:administrator]) + end + @dispatcher.startup + @status = :running + if self.respond_to_event?(:at_resume) + self.call_event({:at_resume => self}, ::GxG::DB[:administrator]) + end + if self.respond_to_event?(:after_resume) + self.call_event({:after_resume => self}, ::GxG::DB[:administrator]) + end + true + end + # ### Command Interface: + def dispatcher() + @dispatcher + end + # + def on(the_event, options={}, &block) + unless the_event.is_a?(::Symbol) + raise ArgumentError, "You must specify an event listener with a unique Symbol." + end + unless block.respond_to?(:call) + raise ArgumentError, "You must provide an event code block to execute." + end + unless options[:description] + options[:description] = "Event : :#{the_event.to_s}" + end + unless options[:usage] + options[:usage] = "{ :#{the_event.to_s} => (your_data_payload / nil) }" + end + unless options[:public] == true + options[:public] = false + end + @interface[(the_event)] = {:description => options[:description], :usage => options[:usage], :public => (options[:public] || false), :users => (options[:users] || false), :administrators => true, :procedure => block} + true + end + # + def call_event(operation_envelope=nil, credential=(::GxG::DB[:administrator] || :"00000000-0000-4000-0000-000000000000")) + result = nil + if operation_envelope.is_a?(::Hash) + the_event = operation_envelope.keys[0] + if the_event + data = operation_envelope[(the_event)] + if @interface[(the_event)] + begin + can_call = false + if credential == :"00000000-0000-4000-0000-000000000000" && @interface[(the_event)][:public] == true + can_call = true + else + if ::GxG::DB[:authority].role_member?(GxG::DB[:authority][:system_credentials][:administrators].to_s.to_sym, credential) + if @interface[(the_event)][:administrators] == true + can_call = true + end + else + if ::GxG::valid_uuid?(credential) && @interface[(the_event)][:users] == true && credential != :"00000000-0000-4000-0000-000000000000" + can_call = true + end + end + end + # + if can_call + result = {:result => @interface[(the_event)][:procedure].call(data, credential)} + else + result = {:result => nil, :error => "You Do Not Have Sufficient Permissions for Command #{the_event.inspect}"} + end + rescue Exception => the_error + log_error({:error => the_error, :parameters => {:data => data, :credential => credential}}) + result = {:result => nil, :error => the_error.to_s} + end + else + result = {:result => nil, :error => "Command #{the_event.inspect} Not Found"} + end + end + end + result + end + # + def interface(credential=:"00000000-0000-4000-0000-000000000000") + result = {} + is_administrator = ::GxG::DB[:authority].role_member?(::GxG::DB[:authority][:system_credentials][:administrators].to_s.to_sym, credential) + @interface.each_pair do |the_event, the_record| + can_view = false + if credential == :"00000000-0000-4000-0000-000000000000" && the_record[:public] == true + can_view = true + else + if is_administrator == true + if the_record[:administrators] == true + can_view = true + end + else + if ::GxG::valid_uuid?(credential) && the_record[:users] == true && credential != :"00000000-0000-4000-0000-000000000000" + can_view = true + end + end + end + # + if can_view == true + result[(the_event)] = (the_record[:description] + ", Usage : " + the_record[:usage]) + end + end + result + end + # + def respond_to_event?(the_event=nil) + result = false + if the_event.is_a?(::Symbol) + if @interface[(the_event)] + result = true + end + end + result + end + # + end + # ### Universal Resource Access (URA) for Services + class UniversalResourcesAccess + # ??? + def initialize() + # VFS Lock format: {:path => "", :resource => File/DB_Object} + @thread_safety_read = ::Mutex.new + @read_locks = {} + @thread_safety_write = ::Mutex.new + @write_locks = {} + self + end + # + def busy?(the_path="") + result = false + # @thread_safety_read.synchronize { + # @read_locks.keys.each do |the_token| + # if @read_locks[(the_token)][:path] == the_path + # result = true + # break + # end + # end + # } + unless result == true + @thread_safety_write.synchronize { + @write_locks.keys.each do |the_token| + if @write_locks[(the_token)][:path] == the_path + result = true + break + end + end + } + end + result + end + # + def exist?(the_path="") + ::GxG::VFS.exist?(the_path) + end + # + def profile(the_path="", credential=:"00000000-0000-4000-0000-000000000000", options={}) + ::GxG::VFS.profile(the_path, options.merge({:with_credential => credential})) + end + # + def entries(the_path="", credential=:"00000000-0000-4000-0000-000000000000", options={}) + ::GxG::VFS.entries(the_path, credential) + end + # + def permissions(the_path="", credential=:"00000000-0000-4000-0000-000000000000") + the_profile = self.profile(the_path, credential) + if the_profile + the_profile[:permissions][:effective] + else + {:execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read=>false} + end + end + # + def get_permissions(the_path="", credential=:"00000000-0000-4000-0000-000000000000") + ::GxG::VFS.get_permissions(the_path, credential) + end + # + def set_permissions(the_path="", credential=:"00000000-0000-4000-0000-000000000000", permissions={}) + ::GxG::VFS.set_permissions(the_path, credential, permissions) + end + # + def set_permissions_recursive(the_path="", credential=:"00000000-0000-4000-0000-000000000000", permissions={}) + ::GxG::VFS.set_permissions_recursive(the_path, credential, permissions) + end + # + def revoke_permissions(the_path="", credential=:"00000000-0000-4000-0000-000000000000") + GxG::VFS.revoke_permissions(the_path, credential) + end + # + def rename(the_path="", credential=:"00000000-0000-4000-0000-000000000000", new_name="") + result = nil + if self.busy?(the_path) + result = {:result => false, :error => "Busy", :error_code => :busy} + end + unless result + if self.permissions(the_path, credential)[:rename] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + end + unless result + result = {:result => ::GxG::VFS.rename(the_path, new_name)} + end + result + end + # + def copy(the_path="", credential=:"00000000-0000-4000-0000-000000000000", new_path="") + result = nil + if self.busy?(the_path) + result = {:result => false, :error => "Busy", :error_code => :busy} + end + unless result + if self.permissions(File.dirname(new_path), credential)[:create] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + end + unless result + result = {:result => ::GxG::VFS.copy(the_path, new_path)} + end + result + end + # + def move(the_path="", credential=:"00000000-0000-4000-0000-000000000000", new_path="") + result = nil + if self.busy?(the_path) + result = {:result => false, :error => "Busy", :error_code => :busy} + end + unless result + if self.permissions(the_path, credential)[:move] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + end + unless result + result = {:result => ::GxG::VFS.move(the_path, new_path)} + end + result + end + # + def destroy(the_path="", credential=:"00000000-0000-4000-0000-000000000000") + result = nil + if self.busy?(the_path) + result = {:result => false, :error => "Busy", :error_code => :busy} + end + unless result + if self.permissions(the_path, credential)[:destroy] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + end + unless result + the_profile = self.profile(the_path, credential) + if [:virtual_directory, :directory, :persisted_array].include?(the_profile[:type]) + result = {:result => ::GxG::VFS.rmdir(the_path)} + else + result = {:result => ::GxG::VFS.rmfile(the_path)} + end + # xxx + end + result + end + # + def create(the_path="", credential=:"00000000-0000-4000-0000-000000000000", options={}) + result = nil + if self.busy?(the_path) + result = {:result => false, :error => "Busy", :error_code => :busy} + end + unless result + if self.permissions(File.dirname(the_path), credential)[:create] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + end + unless result + handle = ::GxG::VFS.mkfile(the_path, options) + if handle + if handle.is_a?(::GxG::Database::PersistedHash) + handle.set_permissions(credential, {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read=>true}) + handle.deactivate + result = {:result => true} + else + # FS + # Review : Create a extra layer of permission tracking (by path) of files and directories on the actual file system. This puts it at parity with DB Objects. + if handle.is_a?(::File) + handle.close + end + result = {:result => true} + end + else + result = {:result => false, :error => "Creation Error", :error_code => :create_fail} + end + end + result + end + # + def create_directory(the_path="", credential=:"00000000-0000-4000-0000-000000000000", options={}) + #result = {:result => false} + result = nil + if self.permissions(File.dirname(the_path), credential)[:create] == false + result = {:result => false, :error => "Permission Error", :error_code => :permission} + end + unless result + if ::GxG::VFS.mkdir(the_path) + the_profile = self.profile(the_path) + if the_profile[:uuid] + handle = ::GxG::DB[:roles][:data].retrieve_by_uuid(the_profile[:uuid], ::GxG::DB[:administrator]) + if handle.is_a?(::GxG::Database::PersistedArray) + handle.set_permissions(credential, {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read=>true}) + result = {:result => true} + else + result = {:result => false, :error => "Creation Error", :error_code => :create_fail} + end + else + # FS + # Review : Create a extra layer of permission tracking (by path) of files and directories on the actual file system. This puts it at parity with DB Objects. + result = {:result => true} + end + else + result = {:result => false, :error => "Creation Error", :error_code => :create_fail} + end + end + result + end + # + def open(the_path="", credential=:"00000000-0000-4000-0000-000000000000", flags=[:read, :binary]) + result = nil + if ::GxG::valid_uuid?(credential) && flags.is_a?(::Array) + if self.permissions(the_path, credential)[:read] == true + the_flags = [] + flags.each do |a_flag| + unless [:readwrite, :write].include?(a_flag) + the_flags << a_flag + end + end + supplimental_flags = [] + unless the_flags.include?(:read) + supplimental_flags << :read + end + unless the_flags.include?(:binary) + supplimental_flags << :binary + end + the_resource = ::GxG::VFS.open(the_path, {:flags => [(the_flags),(supplimental_flags)].flatten}) + if the_resource + the_token = ::GxG::uuid_generate.to_sym + @thread_safety_read.synchronize { + @read_locks[(the_token)] = {:path => the_path, :resource => the_resource} + } + result = {:token => the_token, :path => the_path, :resource => the_resource} + end + end + end + result + # + end + # + def open_writable(the_path="", credential=:"00000000-0000-4000-0000-000000000000", flags=[:readwrite, :binary]) + result = nil + if ::GxG::valid_uuid?(credential) && flags.is_a?(::Array) + if self.permissions(the_path, credential)[:write] == true + someone_else_locked = false + @thread_safety_write.synchronize { + @write_locks.keys.each do |the_write_token| + if @write_locks[(the_write_token)][:path] == the_path + someone_else_locked = true + break + end + end + } + # + unless someone_else_locked == true + supplimental_flags = [] + unless flags.include?(:readwrite) + supplimental_flags << :readwrite + end + unless flags.include?(:binary) + supplimental_flags << :binary + end + the_resource = ::GxG::VFS.open(the_path, {:flags => [(flags),(supplimental_flags)].flatten}) + if the_resource + the_token = ::GxG::uuid_generate.to_sym + @thread_safety_write.synchronize { + @write_locks[(the_token)] = {:path => the_path, :resource => the_resource} + } + result = {:token => the_token, :path => the_path, :resource => the_resource} + end + end + # + end + # + end + result + end + # + def mark_busy(the_path="") + the_token = ::GxG::uuid_generate.to_sym + @thread_safety_write.synchronize { + @write_locks[(the_token)] = {:path => the_path, :resource => true} + } + {:token => the_token, :path => the_path, :resource => true} + end + # + def reopen_writable(the_token="", credential=:"00000000-0000-4000-0000-000000000000", flags=[]) + result = nil + if ::GxG::valid_uuid?(the_token) + details = nil + someone_else_locked = false + @thread_safety_read.synchronize { + if @read_locks[(the_token.to_sym)] + details = @read_locks.delete(the_token.to_sym) + end + } + if details + # ONLY converts read-locks to write-locks. Be sure to use open first. + @thread_safety_write.synchronize { + @write_locks.keys.each do |the_write_token| + if @write_locks[(the_write_token)][:path] == details[:path] && the_token.to_sym != the_write_token + someone_else_locked = true + break + end + end + } + unless someone_else_locked == true + if details[:resource].is_any?(::GxG::Database::PersistedArray, ::GxG::Database::PersistedHash) + details[:resource].wait_for_reservation(5.0) + if details[:resource].write_reserved?() + @thread_safety_write.synchronize { + @write_locks[(the_token.to_sym)] = details + } + result = details + else + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + end + else + if details[:resource].is_a?(::Dir) + if self.permissions(details[:path], credential)[:write] == true + @thread_safety_write.synchronize { + @write_locks[(the_token.to_sym)] = details + } + result = details + else + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + end + else + if details[:resource].is_a?(::File) + if self.permissions(details[:path], credential)[:write] == true + details[:resource].close + details[:resource] = ::GxG::VFS.open(details[:path], {:flags => [:readwrite, :binary]}) + @thread_safety_read.synchronize { + @write_locks[(the_token.to_sym)] = details + } + result = details + else + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + end + end + end + end + end + end + end + result + end + # + def reopen_readonly(the_token="", credential=:"00000000-0000-4000-0000-000000000000", flags=[]) + result = nil + if ::GxG::valid_uuid?(the_token) && ::GxG::valid_uuid?(credential) && flags.is_a?(::Array) + the_flags = [] + flags.each do |a_flag| + unless [:readwrite, :write].include?(a_flag) + the_flags << a_flag + end + end + supplimental_flags = [] + unless the_flags.include?(:read) + supplimental_flags << :read + end + unless the_flags.include?(:binary) + supplimental_flags << :binary + end + details = nil + @thread_safety_write.synchronize { + if @write_locks[(the_token.to_sym)] + details = @write_locks.delete(the_token.to_sym) + end + } + if details + if self.permissions(details[:path], credential)[:read] == true + if details[:resource].is_any?(::GxG::Database::PersistedArray, ::GxG::Database::PersistedHash) + details[:resource].release_reservation() + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + result = details + else + if details[:resource].is_a?(::Dir) + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + result = details + else + if details[:resource].is_a?(::File) + details[:resource].close + details[:resource] = ::GxG::VFS.open(details[:path], {:flags => [:read, :binary]}) + @thread_safety_read.synchronize { + @read_locks[(the_token.to_sym)] = details + } + result = details + end + end + end + else + @thread_safety_write.synchronize { + @write_locks[(the_token.to_sym)] = details + } + end + end + end + result + end + # + def close(the_token="") + result = false + if ::GxG::valid_uuid?(the_token) + details = nil + @thread_safety_read.synchronize { + if @read_locks[(the_token.to_sym)] + details = @read_locks.delete(the_token.to_sym) + end + } + unless details + @thread_safety_write.synchronize { + if @write_locks[(the_token.to_sym)] + details = @write_locks.delete(the_token.to_sym) + end + } + end + if details + # VFS Lock format: {:path => "", :resource => File/DB_Object} + if details[:resource] + if details[:resource].is_a?(::Dir) + # Review : ?? + else + if details[:resource].is_a?(::File) + details[:resource].close + else + if details[:resource].is_any?(::GxG::Database::PersistedArray, ::GxG::Database::PersistedHash) + details[:resource].deactivate + else + # err + end + end + end + result = true + end + end + end + result + end + # ### User Home Directory Supports + def home_mounted?(credential=nil) + result = false + if GxG::valid_uuid?(credential) + if GxG::VFS.exist?(("/Users/" + credential.to_s)) + result = true + end + end + result + end + # + def home_mount(credential=nil) + result = false + if self.home_mounted?(credential) + unless GxG::VFS.mounted?("/User/#{credential.to_s}/Database") + volume_stub = GxG::DB[:roles][:vfs].search_database(credential, {:title => ("volume-" + credential.to_s)}).first + if volume_stub + volume_uuid = volume_stub[:uuid] + else + volume_persisted_array = GxG::DB[:roles][:vfs].try_persist([], credential) + volume_uuid = volume_persisted_array.uuid + volume_persisted_array.get_reservation() + volume_persisted_array.set_title("volume-" + credential.to_s) + volume_persisted_array.save + volume_persisted_array.deactivate + end + volume_handle = GxG::Storage::Volume.new({:database => GxG::DB[:roles][:vfs], :credential => credential, :root_uuid => volume_uuid}) + GxG::VFS.mount(volume_handle, "/User/#{credential.to_s}/Database") + end + result = true + else + home_path = ("/Users/" + credential.to_s) + manifest = [(home_path)] + ["Preferences", "Documents", "Pictures", "Music", "Videos", "Books", "Resources", "Volumes", "Database", "ApplicationData"].each do |subdir| + manifest << (home_path + "/" + subdir) + end + while manifest.size > 0 do + entry = manifest.shift + GxG::VFS.mkpath(entry) + if entry == home_path + GxG::VFS.set_permissions(home_path, GxG::DB[:authority][:system_credentials][:administrators], {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read=>true}) + GxG::VFS.set_permissions(home_path, credential, {:execute => false, :rename => false, :move => false, :destroy => false, :create => true, :write => true, :read=>true}) + else + # {:execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read=>false} + if ["Preferences","Volumes", "Database", "ApplicationData"].include?(File.basename(entry)) + # disallow delete + GxG::VFS.set_permissions(entry, GxG::DB[:authority][:system_credentials][:administrators], {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read=>true}) + GxG::VFS.set_permissions(entry, credential, {:execute => false, :rename => false, :move => false, :destroy => false, :create => true, :write => true, :read=>true}) + else + # allow delete ?? + GxG::VFS.set_permissions(entry, GxG::DB[:authority][:system_credentials][:administrators], {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read=>true}) + GxG::VFS.set_permissions(entry, credential, {:execute => false, :rename => false, :move => false, :destroy => false, :create => true, :write => true, :read=>true}) + end + end + end + # mount Objects DB root + volume_persisted_array = GxG::DB[:roles][:vfs].try_persist([], credential) + volume_uuid = volume_persisted_array.uuid + volume_persisted_array.get_reservation() + volume_persisted_array.set_title("volume-" + credential.to_s) + volume_persisted_array.save + volume_persisted_array.deactivate + volume_handle = GxG::Storage::Volume.new({:database => GxG::DB[:roles][:vfs], :credential => credential, :root_uuid => volume_uuid}) + GxG::VFS.mount(volume_handle, "/User/#{credential.to_s}/Database") + result = true + end + result + end + # + def home_path(credential=nil) + if credential == :"00000000-0000-4000-0000-000000000000" + result = "/Public/www" + else + # if GxG::DB[:authority].role_member?(GxG::DB[:authority][:system_credentials][:administrators], credential) || GxG::DB[:authority].role_member?(GxG::DB[:authority][:system_credentials][:developers], credential) || GxG::DB[:authority].role_member?(GxG::DB[:authority][:system_credentials][:designers], credential) + # result = "/" + # else + # self.home_mount(credential) + # result = ("/Users/" + credential.to_s) + # end + self.home_mount(credential) + result = ("/Users/" + credential.to_s) + end + result + end + # ### + # + end + # ### Installer Support + class SoftwareInstaller + # + def initialize(archive_name=nil) + # + unless archive_name.is_a?(::String) + raise "You MUST provide a valid installer archive name as a String." + end + unless ::GxG::SERVICES[:core][:resources].exist?("/Installers/#{archive_name}") + raise "Archive #{archive_name} does not exist." + end + if ::GxG::SERVICES[:core][:resources].busy?("/Installers/#{archive_name}") + raise "Archive #{archive_name} is in use currently." + end + # + @manifest = nil + @database = nil + @archive_token = ::GxG::SERVICES[:core][:resources].open("/Installers/#{archive_name}", GxG::DB[:administrator]) + if @archive_token + @database = ::GxG::Database::connect(::URI.parse("sqlite://#{::GxG::SYSTEM_PATHS[:installers]}/#{archive_name}"), {:read_only => true}) + unless @database.is_a?(::GxG::Database::Database) + ::GxG::SERVICES[:core][:resources].close(@archive_token[:token]) + raise "Unable to open #{archive_name}" + end + @installer_path = "/Installers/#{::GxG::uuid_generate()}" + @manifest = ::JSON::parse(@database[:installation_manifest].to_s.decode64, {:symbolize_names => true}) + ::GxG::VFS.mount(::GxG::Storage::Volume.new({:database => @database, :credential => GxG::DB[:administrator]}), @installer_path) + else + raise "Unable to secure a read-access token for: #{archive_name}" + end + # + self + end + # + def open?() + if @database.is_a?(::GxG::Database::Database) + true + else + false + end + end + # + def close() + if @archive_token.is_a?(::Hash) + ::GxG::SERVICES[:core][:resources].close(@archive_token[:token]) + ::GxG::VFS.unmount(@installer_path) + @archive_token = nil + if @database.is_a?(::GxG::Database::Database) + @database.close + @database = nil + end + end + true + end + # + def perform_install(options={}) + begin + if self.open?() && @manifest.is_a?(::Hash) + # flesh out manifest + # manifest format: {:package => "", :version => "0.0", :formats => {}, objects => {:"path/to/file" => [{:users => {:read => true}}]}} + # copy formats + ::GxG::DB[:roles][:formats].sync_import(GxG::DB[:administrator], {:formats => (@manifest[:formats] || {}), :records => []}) + # copy files into place, setting permissions + if @manifest[:objects].is_a?(::Hash) + @manifest[:objects].each_pair do |the_path, the_permissions| + if ::GxG::SERVICES[:core][:resources].exist?(the_path.to_s) + existing_permissions = ::GxG::VFS.get_permissions(the_path.to_s) + else + existing_permissions = nil + end + ::GxG::SERVICES[:core][:resources].copy("#{@installer_path}#{the_path.to_s}", GxG::DB[:administrator], the_path.to_s) + if the_permissions.is_a?(::Array) + ::GxG::SERVICES[:core][:resources].set_permissions(the_path.to_s, :"00000000-0000-4000-0000-000000000000", {:write => false, :read => false}) + ::GxG::SERVICES[:core][:resources].revoke_permissions(the_path.to_s, :"00000000-0000-4000-0000-000000000000") + # + the_permissions.each do |the_entry| + if the_entry.is_a?(::Hash) + # + the_credential = nil + case the_entry.keys[0] + when :public + the_credential = :"00000000-0000-4000-0000-000000000000" + when :users + the_credential = ::GxG::DB[:users] + when :designers + the_credential = ::GxG::DB[:designers] + when :developers + the_credential = ::GxG::DB[:developers] + when :administrators + the_credential = ::GxG::DB[:administrators] + end + if the_credential + ::GxG::SERVICES[:core][:resources].set_permissions(the_path.to_s, the_credential, (the_entry.values[0] || {:read => true})) + end + # + end + end + # + if existing_permissions.is_a?(::Array) + existing_permissions.each do |entry| + ::GxG::SERVICES[:core][:resources].set_permissions(the_path.to_s, entry[:credential] entry[:permissions]) + end + end + # + end + end + end + result = true + else + result = false + end + rescue Exception => the_error + log_error({:error => the_error}) + result = false + end + result + end + # + end + # + class SoftwareInstallerMaker + private + # + def portable_format(the_format_record=nil) + result = nil + if the_format_record.is_any?(::Hash, ::GxG::Database::DetachedHash, ::GxG::Database::PersistedHash) + if the_format_record.is_any?(::GxG::Database::DetachedHash, ::GxG::Database::PersistedHash) + the_format_record = the_format_record.unpersist + end + if (the_format_record[:uuid] && the_format_record[:type] && the_format_record[:ufs] && the_format_record[:version] && the_format_record[:mime_types]) + # + result = {:uuid => (the_format_record[:uuid]), :type => (the_format_record[:type]), :ufs => (the_format_record[:ufs]), :title => (the_format_record[:title].to_s), :version => (the_format_record[:version]), :mime_types => (the_format_record[:mime_types] || []), :content => the_format_record[:content].gxg_export} + # + else + raise "Malformed format record" + end + else + raise "Malformed format record" + end + result + end + # + def portable_permission(credential=nil, permissions=nil) + result = nil + if ::GxG::valid_uuid?(credential) && permissions.is_a?(::Hash) + if credential == :"00000000-0000-4000-0000-000000000000" + the_role = :public + else + the_role = nil + [:administrators, :developers, :designers, :users].each do |entry| + if credential == ::GxG::DB[(entry)] + the_role = entry + break + end + end + end + if the_role + result = {} + result[(the_role)] = permissions + end + end + result + end + # + public + # + def initialize() + @manifest = {:package => nil, :version => "0.0", :formats => {}, :objects => {}} + self + end + # + def package() + @manifest[:package] + end + # + def package=(package_name=nil) + if package_name + @manifest[:package] = package_name.to_s[0..127] + end + end + # + def version() + ::BigDecimal(@manifest[:version]) + end + # + def version=(the_version=nil) + if the_version.is_any?(::String, ::BigDecimal) + @manifest[:version] = the_version.to_s + else + raise "You MUST provide a String or BigDecimal as version number" + end + # + def add_format_from_uuid(the_uuid=nil) + the_format_record = ::GxG::DB[:roles][:formats].format_load({:uuid => the_uuid}) + unless the_format_record + the_format_record = ::GxG::DB[:formats][(the_uuid.to_s.to_sym)] + end + if the_format_record.is_a?(::Hash) + portable_record = portable_format(the_format_record) + if portable_record + @manifest[:formats][(the_format_record[:uuid].to_s.to_sym)] = portable_record + true + else + false + end + else + false + end + end + # + def add_format_from_ufs(the_ufs=nil) + the_format_record = ::GxG::DB[:roles][:formats].format_load({:ufs => the_ufs}) + unless the_format_record + ::GxG::DB[:formats].values.each do |entry| + if entry[:ufs] == the_ufs + the_format_record = entry + break + end + end + end + if the_format_record.is_a?(::Hash) + portable_record = portable_format(the_format_record) + if portable_record + @manifest[:formats][(the_format_record[:uuid].to_s.to_sym)] = portable_record + true + else + false + end + else + false + end + end + # + def add_format_from_record(the_format_record=nil) + if (the_format_record[:uuid] && the_format_record[:type] && the_format_record[:ufs] && the_format_record[:version] && the_format_record[:mime_types]) + portable_record = portable_format(the_format_record) + if portable_record + @manifest[:formats][(the_format_record[:uuid].to_s.to_sym)] = portable_record + true + else + false + end + else + raise "Malformed format record" + end + end + # + def add_path(the_path=nil) + if the_path.is_a?(::String) + the_profile = ::GxG::SERVICES[:core][:resources].profile(the_path, ::GxG::DB[:administrator]) + # + if [:virtual_directory, :directory, :persisted_array].include?(the_profile[:type]) + processing_db = [(the_path)] + while processing_db.size > 0 + item = processing_db.shift + if item + subitems = ::GxG::SERVICES[:core][:resources].entries(item, ::GxG::DB[:administrator]) + # + if subitems.size == 0 + @manifest[:objects][(item.to_s.to_sym)] = [] + ::GxG::VFS.get_permissions(item).each do |entry| + the_permission = portable_permission(entry[:credential], entry[:permissions]) + if the_permission + @manifest[:objects][(item.to_s.to_sym)] << the_permission + end + end + end + # + if subitems.size > 0 + subitems.each do |profile| + subpath = (item + "/" + profile[:title]) + if [:virtual_directory, :directory, :persisted_array].include?(profile[:type]) + processing_db << subpath + else + @manifest[:objects][(subpath.to_s.to_sym)] = [] + ::GxG::VFS.get_permissions(subpath).each do |entry| + the_permission = portable_permission(entry[:credential], entry[:permissions]) + if the_permission + @manifest[:objects][(subpath.to_s.to_sym)] << the_permission + end + end + end + end + end + # + end + end + else + @manifest[:objects][(the_path.to_s.to_sym)] = [] + ::GxG::VFS.get_permissions(the_path).each do |entry| + the_permission = portable_permission(entry[:credential], entry[:permissions]) + if the_permission + @manifest[:objects][(the_path.to_s.to_sym)] << the_permission + end + end + end + # + true + else + raise "Malformed path String" + end + end + # + def make_installer() + result = false + # + if @manifest[:package] + archive_name = (@manifest[:package].to_s.gsub(" ","-") + "_" + @manifest[:version].to_s.gsub(".","-") + ".gxg_installer") + installer_path = "/Installers/#{::GxG::uuid_generate()}" + database = ::GxG::Database::connect(::URI.parse("sqlite://#{::GxG::SYSTEM_PATHS[:installers]}/#{archive_name}")) + unless database.is_a?(::GxG::Database::Database) + raise "Unable to open #{archive_name}" + end + archive_token = ::GxG::SERVICES[:core][:resources].mark_busy("/Installers/#{archive_name}") + ::GxG::VFS.mount(::GxG::Storage::Volume.new({:database => database, :credential => GxG::DB[:administrator]}), installer_path) + begin + database[:installation_manifest] = @manifest.to_json.encode64 + @manifest[:objects].keys.each do |the_path| + ::GxG::SERVICES[:core][:resources].copy(the_path.to_s, GxG::DB[:administrator], "#{installer_path}#{the_path.to_s}") + ::GxG::SERVICES[:core][:resources].set_permissions("#{installer_path}#{the_path.to_s}", :"00000000-0000-4000-0000-000000000000", {:read => true}) + end + @manifest[:objects].keys.each do |the_path| + ::GxG::SERVICES[:core][:resources].revoke_permissions("#{installer_path}#{the_path.to_s}", ::GxG::DB[:users]) + ::GxG::SERVICES[:core][:resources].revoke_permissions("#{installer_path}#{the_path.to_s}", ::GxG::DB[:designers]) + ::GxG::SERVICES[:core][:resources].revoke_permissions("#{installer_path}#{the_path.to_s}", ::GxG::DB[:developers]) + ::GxG::SERVICES[:core][:resources].revoke_permissions("#{installer_path}#{the_path.to_s}", ::GxG::DB[:administrators]) + end + # + ::GxG::VFS.unmount(installer_path) + database.close + ::GxG::SERVICES[:core][:resources].close(archive_token[:token]) + result = true + rescue Exception => the_error + ::GxG::VFS.unmount(installer_path) + database.close + ::GxG::SERVICES[:core][:resources].close(archive_token[:token]) + ::GxG::SERVICES[:core][:resources].destroy(("/Installers/#{archive_name}"), ::GxG::DB[:administrator]) + log_error({:error => the_error}) + raise the_error + end + else + raise "You MUST first set the package name" + end + # + result + end + # + end + # + end + # + end +end +# ### Define The Core Service: +core_service = ::GxG::Services::Service.new(:core) +# ### Define Public Command Interface: +core_service.on(:start, {:description => "System Service Layer Start", :usage => "{ :start => nil }", :users => false}) do + ::GxG::SERVICES[:core].start +end +core_service.on(:stop, {:description => "System Service Layer Shutdown", :usage => "{ :stop => nil }", :users => false}) do + ::GxG::SERVICES[:core].stop +end +core_service.on(:restart, {:description => "System Service Layer Restart", :usage => "{ :restart => nil }", :users => false}) do + ::GxG::SERVICES[:core].restart +end +core_service.on(:pause, {:description => "System Service Pause", :usage => "{ :pause => nil }", :users => false}) do + ::GxG::SERVICES[:core].pause +end +core_service.on(:resume, {:description => "System Service Resume", :usage => "{ :resume => nil }", :users => false}) do + ::GxG::SERVICES[:core].resume +end +# ### Define Internal Service Control Events: +core_service.on(:at_start, {:description => "System Service Layer Startup", :usage => "{ :at_start => (service-object) }", :users => false}) do |service, credential| + # ### Set Engine Monitors + service.dispatcher.every("1 second") do + ::GxG::Engine::determine_event_allocations() + ::GxG::Engine::determine_loads() + end + # ### Load Services + if service.configuration()[:available] + service.configuration()[:available].each do |moniker| + unless service.configuration()[:disabled].include?(moniker) + service_file_path = ::File.expand_path("#{File.dirname(__FILE__)}/#{moniker}/#{moniker}.rb") + if ::File.exists?(service_file_path) + begin + require (service_file_path) + rescue Exception => the_error + log_error({:error => the_error, :parameters => {:service => moniker}}) + end + else + log_warn("WARNING: /Services/#{moniker.to_s}/#{moniker.to_s}.rb NOT FOUND -- skipping service") + end + end + end + # ### Start Services + ::GxG::Services::start_order().each do |the_service_moniker| + unless the_service_moniker == :core + if service.configuration()[:enabled].include?(the_service_moniker.to_s) + log_info("Starting Service: #{the_service_moniker.inspect} ...") + ::GxG::Services::start_service(the_service_moniker) + end + end + end + # + end + # ### Startup Scripts + Dir.entries("#{::GxG::SYSTEM_PATHS[:system]}/Startup").each do |entry| + if File.extname(entry) == ".rb" + begin + require "#{::GxG::SYSTEM_PATHS[:system]}/Startup/#{entry}" + rescue Exception => the_error + log_error({:error => the_error}) + end + end + end + # +end +# +core_service.on(:at_stop, {:description => "System Service Layer Shutdown", :usage => "{ :at_stop => (service-object) }", :public => false}) do |service| + # ### Stop Services + ::GxG::Services::stop_order().each do |the_service_moniker| + unless the_service_moniker == :core + log_info("Stopping Service: #{the_service_moniker.inspect} ...") + ::GxG::Services::stop_service(the_service_moniker) + end + end + # +end +# ### Provide Universal Resouce Access +core_service[:resources] = ::GxG::Services::UniversalResourcesAccess.new() +# +unless core_service.configuration()[:available] + core_service.configuration()[:available] = [] + core_service.configuration()[:enabled] = [] + core_service.configuration()[:disabled] = [] + core_service.save_configuration + ::GxG::VFS.entries("/Services").each do |the_profile| + if the_profile[:type] == :directory + ::GxG::Services::install_service(the_profile[:title].to_sym) + ::GxG::Services::enable_service(the_profile[:title].to_sym) + end + end +end +core_service.start +# ### Trap for exit signals +unless RUBY_ENGINE == "jruby" + Signal.trap("QUIT") do + ::GxG::Services::stop_service(:core) + exit(0) + end +end +Signal.trap("INT") do + ::GxG::Services::stop_service(:core) + exit(0) +end +Signal.trap("TERM") do + ::GxG::Services::stop_service(:core) + exit(0) +end +# ### Review : USR1 in use by JVM/JRuby and won't work. +# Signal.trap("USR1") do +# ::GxG::Services::stop_service(:core) +# end +Signal.trap("HUP") do + ::GxG::Services::stop_service(:core) + exit(0) +end +# +unless ($0 == "irb" || $0 == "jirb" || Module.constants.include?(:IRB)) + # ### Place-holder loop for running in the background + while true do + sleep 5.0 + end +end