mirror of
https://github.com/mistergibson/gxg-server.git
synced 2026-08-15 10:24:29 -07:00
1463 lines
55 KiB
Ruby
Executable file
1463 lines
55 KiB
Ruby
Executable file
#!/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")
|
|
# User-Agent code goes in '/Applications'. Rename to '/Agents' ??
|
|
::GxG::VFS.mount(::GxG::Storage::Volume.new({:directory => ::GxG::SYSTEM_PATHS[:applications]}), "/Applications")
|
|
::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()
|
|
"<Service #{@provides.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 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
|
|
#
|
|
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
|