From afd434921c9c71afd62b361970410644559e028f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 14 Apr 2005 04:58:12 +0000 Subject: updates git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@152 980ebf18-57e1-0310-9a29-db15c13687c0 --- lib/blink/objects.rb | 514 ------------------------------------------- lib/blink/objects/file.rb | 213 ------------------ lib/blink/objects/package.rb | 242 -------------------- lib/blink/objects/process.rb | 86 -------- lib/blink/objects/service.rb | 138 ------------ lib/blink/objects/symlink.rb | 106 --------- lib/blink/types.rb | 514 +++++++++++++++++++++++++++++++++++++++++++ lib/blink/types/file.rb | 213 ++++++++++++++++++ lib/blink/types/package.rb | 242 ++++++++++++++++++++ lib/blink/types/process.rb | 86 ++++++++ lib/blink/types/service.rb | 138 ++++++++++++ lib/blink/types/symlink.rb | 106 +++++++++ 12 files changed, 1299 insertions(+), 1299 deletions(-) delete mode 100644 lib/blink/objects.rb delete mode 100644 lib/blink/objects/file.rb delete mode 100644 lib/blink/objects/package.rb delete mode 100644 lib/blink/objects/process.rb delete mode 100644 lib/blink/objects/service.rb delete mode 100644 lib/blink/objects/symlink.rb create mode 100644 lib/blink/types.rb create mode 100644 lib/blink/types/file.rb create mode 100644 lib/blink/types/package.rb create mode 100644 lib/blink/types/process.rb create mode 100644 lib/blink/types/service.rb create mode 100644 lib/blink/types/symlink.rb diff --git a/lib/blink/objects.rb b/lib/blink/objects.rb deleted file mode 100644 index 4e5f9b268..000000000 --- a/lib/blink/objects.rb +++ /dev/null @@ -1,514 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink/attribute' -require 'blink/interface' - -#--------------------------------------------------------------- -# This class is the abstract base class for the mechanism for organizing -# work. No work is actually done by this class or its subclasses; rather, -# the subclasses include attributes which do the actual work. -# See attribute.rb for how work is actually done. - -module Blink - class Objects - include Enumerable - @objects = Hash.new - @@allobjects = Array.new # and then an array for all objects - - - @@typeary = [] - @@typehash = Hash.new { |hash,key| - raise "Object type %s not found" % key - } - - #--------------------------------------------------------------- - # the class methods - - #--------------------------------------------------------------- - # retrieve a named object - def Objects.[](name) - if @objects.has_key?(name) - return @objects[name] - else - raise "Object '#{name}' does not exist" - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is special, because it can be equivalent to running new - # this allows cool syntax like Blink::File["/etc/inetd.conf"] = ... - def Objects.[]=(name,object) - newobj = nil - if object.is_a?(Blink::Objects) - newobj = object - else - raise "must pass a Blink::Objects object" - end - - if @objects.has_key?(newobj.name) - puts @objects - raise "'#{newobj.name}' already exists in " + - "class '#{newobj.class}': #{@objects[newobj.name]}" - else - Blink.debug("adding %s of type %s to class list" % - [object.name,object.class]) - @objects[newobj.name] = newobj - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Objects.has_key?(name) - return @objects.has_key?(name) - end - #--------------------------------------------------------------- - - #----------------------------------- - # all objects total - def Objects.push(object) - @@allobjects.push object - Blink.debug("adding %s of type %s to master list" % - [object.name,object.class]) - end - #----------------------------------- - - #----------------------------------- - # this is meant to be run multiple times, e.g., when a new - # type is defined at run-time - def Objects.buildtypehash - @@typeary.each { |otype| - if @@typehash.include?(otype.name) - if @@typehash[otype.name] != otype - Blink.warning("Object type %s is already defined" % otype.name) - end - else - @@typehash[otype.name] = otype - end - } - end - #----------------------------------- - - #----------------------------------- - # this should make it so our subclasses don't have to worry about - # defining these class instance variables - def Objects.inherited(sub) - sub.module_eval %q{ - @objects = Hash.new - @actions = Hash.new - } - - # add it to the master list - # unfortunately we can't yet call sub.name, because the #inherited - # method gets called before any commands in the class definition - # get executed, which, um, sucks - @@typeary.push(sub) - end - #----------------------------------- - - #----------------------------------- - # this is used for mapping object types (e.g., Blink::Objects::File) - # to names (e.g., "file") - def Objects.name - return @name - end - #----------------------------------- - - #----------------------------------- - # some simple stuff to make it easier to get a name from everyone - def Objects.namevar - return @namevar - end - #----------------------------------- - - #----------------------------------- - # accessor for the list of acceptable params - def Objects.classparams - return @params - end - #----------------------------------- - - #----------------------------------- - # our param list is by class, so we need to convert it to names - # (see blink/objects/file.rb for an example of how params are defined) - def Objects.classparambyname - unless defined? @paramsbyname - @paramsbyname = Hash.new { |hash,key| - fail TypeError.new( - "Parameter %s is invalid for class %s" % - [key.to_s,self.class.to_s] - ) - } - @params.each { |param| - if param.is_a? Symbol - # store the Symbol class, not the symbol itself - symbolattr = Blink::Attribute::Symbol.new(param) - - @paramsbyname[param] = symbolattr - elsif param.respond_to?(:name) - # these are already classes - @paramsbyname[param.name] = param - else - fail TypeError.new( - "Parameter %s is invalid; it must be a class or symbol" % - param.to_s - ) - end - } - end - return @paramsbyname - end - #----------------------------------- - - #--------------------------------------------------------------- - # the instance methods - - #----------------------------------- - # parameter access and stuff - def [](param) - if @attributes.has_key?(param) - return @attributes[param].should - else - raise "Undefined parameter '#{param}' in #{self}" - end - end - #----------------------------------- - - #----------------------------------- - # because all object parameters are actually attributes, we - # have to do some shenanigans to make it look from the outside - # like @attributes is just a simple hash - # the Symbol stuff is especially a bit hackish - def []=(param,value) - if @attributes.has_key?(param) - @attributes[param].should = value - return - end - - attrclass = self.class.classparambyname[param] - - Blink.debug("creating attribute of type '%s'" % attrclass) - # any given object can normally only have one of any given attribute - # type, but it might have many Symbol attributes - # - # so, we need to make sure that the @attributes hash behaves - # the same whether it has a unique attribute or a bunch of Symbol - # attributes - if attrclass.is_a?(Blink::Attribute::Symbol) - attrclass.should = value - @attributes[param] = attrclass - else - attr = attrclass.new(value) - attr.object = self - if attr.is_a?(Array) - attr.each { |xattr| - @attributes[xattr.name] = attr - } - else - Blink.debug "Creating attr %s in %s" % [attr.name,self] - @attributes[attr.name] = attr - end - end - end - #----------------------------------- - - #----------------------------------- - # return action array - # these are actions to use for responding to events - # no, this probably isn't the best way, because we're providing - # access to the actual hash, which is silly - def action - if not defined? @actions - puts "defining action hash" - @actions = Hash.new - end - @actions - end - #----------------------------------- - - #----------------------------------- - # removing attributes - def delete(attr) - if @attributes.has_key?(attr) - @attributes.delete(attr) - else - raise "Undefined attribute '#{attr}' in #{self}" - end - end - #----------------------------------- - - #----------------------------------- - # XXX this won't work -- too simplistic - # a given object can be in multiple components - # which means... what? that i have to delete things from components? - # that doesn't seem right, somehow... - # do i really ever need to delete things? - #def delete - # self.class.delete[self.name] - #end - #----------------------------------- - - #----------------------------------- - # this can only be used with blocks that are - # valid on operations and objects, as it iterates over both of - # them - # essentially, the interface defined by Blink::Interface is used here - def each - ret = false - nodepth = 0 - unless block_given? - raise "'Each' was not given a block" - end - @attributes.each { |name,attr| - #Blink.debug "'%s' yielding '%s' of type '%s'" % [self,attr,attr.class] - yield(attr) - } - # DISABLED - # until we're clear on what 'enclosure' means, this is - # all disabled - - #if @encloses.length > 0 - # Blink.debug "#{self} encloses #{@encloses}" - ##end - #if defined? Blink['depthfirst'] - # self.eachobj { |enclosed| - # Blink.debug "yielding #{self} to object #{enclosed}" - # ret |= yield(enclosed) - # } - # nodepth = 1 - #end - #self.eachop { |op| - # Blink.debug "yielding #{self} to op #{op}" - # ret |= yield(op) - #} - #if ! defined? Blink['depthfirst'] and nodepth != 1 - # self.eachobj { |enclosed| - # Blink.debug "yielding #{self} to object #{enclosed}" - # ret |= yield(enclosed) - # } - #end - #return ret - end - #----------------------------------- - - #----------------------------------- - # this allows each object to act like both a node and - # a branch - # but each object contains two types of objects: operations and other - # objects.... - def eachobj - unless block_given? - raise "Eachobj was not given a block" - end - @encloses.each { |object| - yield(object) - } - end - #----------------------------------- - - #----------------------------------- - # store the object that immediately encloses us - def enclosedby(obj) - @enclosedby.push(obj) - end - #----------------------------------- - - #----------------------------------- - def enclosed? - defined? @enclosedby - end - #----------------------------------- - - #----------------------------------- - # store a enclosed object - def encloses(obj) - obj.enclosedby(self) - #obj.subscribe(self,'*') - @encloses.push(obj) - end - #----------------------------------- - - #----------------------------------- - # this is a wrapper, doing all of the work that should be done - # and none that shouldn't - def evaluate - raise "don't call evaluate; it's disabled" - end - #----------------------------------- - - #----------------------------------- - # call an event - # this is called on subscribers by the trigger method from the obj - # which sent the event - # event handling should probably be taking place in a central process, - # but.... - def event(event,obj) - Blink.debug "#{self} got event #{event} from #{obj}" - if @actions.key?(event) - Blink.debug "calling it" - @actions[event].call(self,obj,event) - else - p @actions - end - end - #----------------------------------- - - #----------------------------------- - # yay - def initialize(*args) - # params are for classes, attributes are for instances - # hokey but true - @attributes = Hash.new - @monitor = Array.new - - # default to always syncing - @performoperation = :sync - - begin - hash = Hash[*args] - rescue ArgumentError - fail TypeError.new("Incorrect number of arguments for %s" % - self.class.to_s) - end - - # if they passed in a list of attributes they're interested in, - # we mark them as "interesting" - # XXX maybe we should just consider params set to nil as 'interesting' - # - # this isn't used as much as it should be, but the idea is that - # the "interesting" attributes would be the ones retrieved during a - # 'retrieve' call - if hash.include?(:check) - @monitor = hash[:check].dup - hash.delete(:check) - end - - # we have to set the name of our object before anything else, - # because it might be used in creating the other attributes - if hash.has_key?(self.class.namevar) - self[self.class.namevar] = hash[self.class.namevar] - hash.delete(self.class.namevar) - else - raise TypeError.new("A name must be provided at initialization time") - end - - hash.each { |param,value| - @monitor.push(param) - Blink.debug("adding param '%s' with value '%s'" % - [param,value]) - self[param] = value - } - - # add this object to the specific class's list of objects - self.class[name] = self - - # and then add it to the master list - Blink::Objects.push(self) - - @notify = Hash.new - #@encloses = Array.new - #@enclosedby = Array.new - @actions = Hash.new - #@opsgenned = false - - # XXX i've no idea wtf is going on with enclosures - #if self.class == Blink::Objects::Root - # Blink.debug "not enclosing root (#{self.class}) in self" - #else - # Blink::Objects.root.encloses(self) - #end - end - # initialize - #----------------------------------- - - #----------------------------------- - def name - #namevar = self.class.namevar - #Blink.debug "namevar is '%s'" % namevar - #nameattr = @attributes[namevar] - #Blink.debug "nameattr is '%s'" % nameattr - #name = nameattr.value - #Blink.debug "returning %s from attr %s and namevar %s" % [name,nameattr,namevar] - #return name - return @attributes[self.class.namevar].value - end - #----------------------------------- - - #----------------------------------- - def newevent(args) - if args[:event].nil? - raise "newevent called wrong on #{self}" - end - - return Blink::Event.new( - :event => args[:event], - :object => self - ) - end - #----------------------------------- - - #----------------------------------- - # subscribe to an event or all events - # this entire event system is a hack job and needs to - # be replaced with a central event handler - def subscribe(args,&block) - obj = args[:object] - event = args[:event] || '*'.intern - if obj.nil? or event.nil? - raise "subscribe was called wrongly; #{obj} #{event}" - end - obj.action[event] = block - #events.each { |event| - unless @notify.key?(event) - @notify[event] = Array.new - end - unless @notify[event].include?(obj) - Blink.debug "pushing event '%s' for object '%s'" % [event,obj] - @notify[event].push(obj) - end - # } - #else - # @notify['*'.intern].push(obj) - end - #----------------------------------- - - #----------------------------------- - def to_s - self.name - end - #----------------------------------- - - #----------------------------------- - # initiate a response to an event - def trigger(event) - subscribers = Array.new - if @notify.include?('*') and @notify['*'].length > 0 - @notify['*'].each { |obj| subscribers.push(obj) } - end - if (@notify.include?(event) and (! @notify[event].empty?) ) - @notify[event].each { |obj| subscribers.push(obj) } - end - Blink.debug "triggering #{event}" - subscribers.each { |obj| - Blink.debug "calling #{event} on #{obj}" - obj.event(event,self) - } - end - #----------------------------------- - - #----------------------------------- - def validparam(param) - if (self.class.operparams.include?(param) or - self.class.staticparams.include?(param)) - return true - else - return false - end - end - #----------------------------------- - - #--------------------------------------------------------------- - end # Blink::Objects -end diff --git a/lib/blink/objects/file.rb b/lib/blink/objects/file.rb deleted file mode 100644 index 502a5a404..000000000 --- a/lib/blink/objects/file.rb +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'digest/md5' -require 'etc' -require 'blink/attribute' - -module Blink - # we first define all of the attribute that our file will use - # because the objects must be defined for us to use them in our - # definition of the file object - class Attribute - class FileUID < Blink::Attribute - require 'etc' - attr_accessor :file - @name = :owner - - def retrieve - stat = nil - - begin - stat = File.stat(self.object[:path]) - rescue - # this isn't correct, but what the hell - raise "File '%s' does not exist: #{$!}" % self.object[:path] - end - - self.value = stat.uid - unless self.should.is_a?(Integer) - begin - user = Etc.getpwnam(self.should) - if user.gid == "" - raise "Could not retrieve uid for %s" % self.object - end - Blink.debug "converting %s to integer %d" % - [self.should,user.uid] - self.should = user.uid - rescue - raise "Could not get any info on user %s" % self.should - end - end - Blink.debug "chown state is %d" % self.value - end - - #def <=>(other) - # if other.is_a?(Integer) - # begin - # other = Etc.getpwnam(other).uid - # rescue - # raise "Could not get uid for #{@params[:uid]}" - # end - # end -# -# self.value <=> other -# end - - def sync - begin - File.chown(value,-1,self.object[:path]) - rescue - raise "failed to sync #{@params[:file]}: #{$!}" - end - - self.object.newevent(:event => :inode_changed) - end - end - - # this attribute should actually somehow turn into many attributes, - # one for each bit in the mode - # I think MetaAttributes are the answer, but I'm not quite sure - class FileMode < Blink::Attribute - require 'etc' - - @name = :mode - - def retrieve - stat = nil - - begin - stat = File.stat(self.object[:path]) - rescue => error - raise "File %s could not be stat'ed: %s" % [self.object[:path],error] - end - - self.value = stat.mode & 007777 - Blink.debug "chmod state is %o" % self.value - end - - def sync - begin - File.chmod(self.should,self.object[:path]) - rescue - raise "failed to chmod #{self.object[:path]}: #{$!}" - end - self.object.newevent(:event => :inode_changed) - end - end - - # not used until I can figure out how to solve the problem with - # metaattributes - class FileSetUID < Blink::Attribute - require 'etc' - - @parent = Blink::Attribute::FileMode - - @name = :setuid - - def <=>(other) - self.value <=> @parent.value[11] - end - - # this just doesn't seem right... - def sync - tmp = 0 - if self.value == true - tmp = 1 - end - @parent.value[11] = tmp - end - end - - class FileGroup < Blink::Attribute - require 'etc' - - @name = :group - - def retrieve - stat = nil - - begin - stat = File.stat(self.object[:path]) - rescue - # this isn't correct, but what the hell - raise "File #{self.object[:path]} does not exist: #{$!}" - end - - self.value = stat.gid - - # we probably shouldn't actually modify the 'should' value - # but i don't see a good way around it right now - # mmmm, should - unless self.should.is_a?(Integer) - begin - group = Etc.getgrnam(self.should) - # yeah, don't ask me - # this is retarded - #p group - if group.gid == "" - raise "Could not retrieve gid for %s" % self.object - end - Blink.debug "converting %s to integer %d" % - [self.should,group.gid] - self.should = group.gid - rescue - raise "Could not get any info on group %s" % self.should - end - end - Blink.debug "chgrp state is %d" % self.value - end - -# def <=>(other) -# # unless we're numeric... -# if other.is_a?(Integer) -# begin -# group = Etc.getgrnam(other) -# # yeah, don't ask me -# # this is retarded -# #p group -# other = group.gid -# if other == "" -# raise "Could not retrieve gid for %s" % other -# end -# rescue -# raise "Could not get any info on group %s" % other -# end -# end -# -# #puts self.should -# self.value <=> other -# end - - def sync - Blink.debug "setting chgrp state to %d" % self.should - begin - # set owner to nil so it's ignored - File.chown(nil,self.should,self.object[:path]) - rescue - raise "failed to chgrp %s to %s: %s" % - [self.object[:path], self.should, $!] - end - self.object.newevent(:event => :inode_changed) - end - end - end - class Objects - class File < Objects - attr_reader :stat, :path, :params - # class instance variable - @params = [ - Blink::Attribute::FileUID, - Blink::Attribute::FileGroup, - Blink::Attribute::FileMode, - Blink::Attribute::FileSetUID, - :path - ] - - @name = :file - @namevar = :path - end # Blink::Objects::File - end # Blink::Objects - -end diff --git a/lib/blink/objects/package.rb b/lib/blink/objects/package.rb deleted file mode 100644 index 407b5871a..000000000 --- a/lib/blink/objects/package.rb +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -module Blink - class Objects - # packages are complicated because each package format has completely - # different commands. We need some way to convert specific packages - # into the general package object... - class Package < Objects - attr_reader :version, :format - # class instance variable - @params = [ - :install, - :format, - :version - ] - - @name = :package - @namevar = :name - - # this is already being done in objects.rb - #def Package.inhereted(sub) - # sub.module_eval %q{ - # @objects = Hash.new - # @actions = Hash.new - # } - #end - - def initialize(hash) - end - - def retrieve - end - - def insync? - end - - def sync - end - end # Blink::Objects::Package - - class PackagingType - attr_writer :list, :install, :remove, :check - - @@types = Hash.new(false) - - def PackagingType.[](name) - if @@types.include?(name) - return @@types[name] - else - raise "no such type %s" % name - end - end - - [:list, :install, :remove, :check].each { |method| - self.send(:define_method, method) { - # retrieve the variable - var = eval("@" + method.id2name) - if var.is_a?(Proc) - var.call() - else - raise "only blocks are supported right now" - end - } - } - - def initialize(name) - if block_given? - yield self - end - - @packages = Hash.new(false) - @@types[name] = self - end - - def retrieve - @packages.clear() - - @packages = self.list() - end - end - - PackagingType.new("dpkg") { |type| - type.list = proc { - packages = [] - - # dpkg only prints as many columns as you have available - # which means we don't get all of the info - # stupid stupid - oldcol = ENV["COLUMNS"] - ENV["COLUMNS"] = "500" - - # list out all of the packages - open("| dpkg -l") { |process| - # our regex for matching dpkg output - regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$} - fields = [:status, :name, :version, :description] - hash = {} - - 5.times { process.gets } # throw away the header - - # now turn each returned line into a package object - process.each { |line| - if match = regex.match(line) - hash.clear - - fields.zip(match.captures) { |field,value| - hash[field] = value - } - packages.push Blink::Objects::Package.new(hash) - else - raise "failed to match dpkg line %s" % line - end - } - } - ENV["COLUMNS"] = oldcol - - return packages - } - - # we need package retrieval mechanisms before we can have package - # installation mechanisms... - #type.install = proc { |pkg| - # raise "installation not implemented yet" - #} - - type.remove = proc { |pkg| - cmd = "dpkg -r %s" % pkg.name - output = %x{#{cmd}} - if $? != 0 - raise output - end - } - } - - PackagingType.new("sunpkg") { |type| - type.list = proc { - packages = [] - hash = {} - names = { - "PKGINST" => :name, - "NAME" => nil, - "CATEGORY" => :category, - "ARCH" => :platform, - "VERSION" => :version, - "BASEDIR" => :root, - "HOTLINE" => nil, - "EMAIL" => nil, - "VENDOR" => :vendor, - "DESC" => :description, - "PSTAMP" => nil, - "INSTDATE" => nil, - "STATUS" => nil, - "FILES" => nil - } - - # list out all of the packages - open("| pkginfo -l") { |process| - # we're using the long listing, so each line is a separate piece - # of information - process.each { |line| - case line - when /^$/ then - packages.push Blink::Objects::Package.new(hash) - hash.clear - when /\s*(\w+):\s+(.+)/ - name = $1 - value = $2 - if names.include?(name) - hash[names[name]] = value - else - raise "Could not find %s" % name - end - when /\s+\d+.+/ - # nothing; we're ignoring the FILES info - end - } - } - return packages - } - - # we need package retrieval mechanisms before we can have package - # installation mechanisms... - #type.install = proc { |pkg| - # raise "installation not implemented yet" - #} - - type.remove = proc { |pkg| - cmd = "pkgrm -n %s" % pkg.name - output = %x{#{cmd}} - if $? != 0 - raise output - end - } - } - - # this is how we retrieve packages - class PackageSource - attr_accessor :uri - attr_writer :retrieve - - @@sources = Hash.new(false) - - def PackageSource.get(file) - type = file.sub(%r{:.+},'') - source = nil - if source = @@sources[type] - return source.retrieve(file) - else - raise "Unknown package source: %s" % type - end - end - - def initialize(name) - if block_given? - yield self - end - - @@sources[name] = self - end - - def retrieve(path) - @retrieve.call(path) - end - - end - - PackageSource.new("file") { |obj| - obj.retrieve = proc { |path| - # this might not work for windows... - file = path.sub(%r{^file://},'') - - if FileTest.exists?(file) - return file - else - raise "File %s does not exist" % file - end - } - } - end # Blink::Objects -end diff --git a/lib/blink/objects/process.rb b/lib/blink/objects/process.rb deleted file mode 100644 index e2e5722fc..000000000 --- a/lib/blink/objects/process.rb +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/local/bin/ruby -w - -require 'blink/operation' -require 'blink/operation/processes' - -# DISABLED -# I'm only working on services, not processes, right now - -module Blink - class Attribute - class ProcessRunning < Attribute - def retrieve - running = 0 - regex = Regexp.new(@params[:pattern]) - begin - # this ps is only tested on Solaris - # XXX yeah, this definitely needs to be fixed... - %x{ps -ef -U #{@params[:user]}}.split("\n").each { |process| - if regex.match(process) - running += 1 - end - } - rescue - # this isn't correct, but what the hell - Blink::Message.new( - :level => :error, - :source => self.object, - :message => "Failed to run ps" - ) - end - - self.state = running - Blink.debug "there are #{running} #{self.object} processes for start" - end - - def <=>(other) - self.state < 1 - end - - def fix - require 'etc' - # ruby is really cool - uid = 0 - if @params[:user].is_a? Integer - uid = @params[:user] - else - uid = Etc.getpwnam(@params[:user]).uid - end - Kernel.fork { - Process.uid = uid - Process.euid = uid - string = @params[:binary] + (@params[:arguments] || "") - Blink::Message.new( - :level => :notice, - :source => self.object, - :message => "starting" - ) - Kernel.exec(string) - } - end - end - end - class Objects - class BProcess < Objects - attr_reader :stat, :path - @params = [:start, :stop, :user, :pattern, :binary, :arguments] - @name = :process - - @namevar = :pattern - - Blink::Relation.new(self, Blink::Operation::Start, { - :user => :user, - :pattern => :pattern, - :binary => :binary, - :arguments => :arguments - }) - - Blink::Relation.new(self, Blink::Operation::Stop, { - :user => :user, - :pattern => :pattern - }) - - end # Blink::Objects::BProcess - end # Blink::Objects - -end diff --git a/lib/blink/objects/service.rb b/lib/blink/objects/service.rb deleted file mode 100644 index 4e7f4b17e..000000000 --- a/lib/blink/objects/service.rb +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# this is our main way of managing processes right now -# -# a service is distinct from a process in that services -# can only be managed through the interface of an init script -# which is why they have a search path for initscripts and such - -module Blink - class Attribute - class ServiceRunning < Attribute - @name = :running - - def retrieve - self.value = self.running() - Blink.debug "Running value for '%s' is '%s'" % - [self.object.name,self.value] - end - - # should i cache this info? - def running - begin - status = self.object.initcmd("status") - Blink.debug "initcmd status for '%s' is '%s'" % - [self.object.name,status] - - if status # the command succeeded - return 1 - else - return 0 - end - rescue SystemCallError - raise "Could not execute %s" % initscript - end - - end - - def sync - if self.running > 0 - status = 1 - else - status = 0 - end - Blink.debug "'%s' status is '%s' and should be '%s'" % - [self,status,should] - if self.should > 0 - if status < 1 - Blink.debug "Starting '%s'" % self - unless self.object.initcmd("start") - raise "Failed to start %s" % self.name - end - else - Blink.debug "'%s' is already running, yo" % self - #Blink.debug "Starting '%s'" % self - #unless self.object.initcmd("start") - # raise "Failed to start %s" % self.name - #end - end - elsif status > 0 - Blink.debug "Stopping '%s'" % self - unless self.object.initcmd("stop") - raise "Failed to stop %s" % self.name - end - else - Blink.debug "Not running '%s' and shouldn't be running" % self - end - end - end - end - class Objects - class Service < Objects - attr_reader :stat - @params = [ - Blink::Attribute::ServiceRunning, - :name, - :pattern - ] - - @name = :service - @namevar = :name - - @searchpaths = Array.new - - def Service.addpath(path) - unless @searchpaths.include?(path) - # XXX should we check to see if the path exists? - @searchpaths.push(path) - end - end - - def Service.search(name) - @searchpaths.each { |path| - # must specify that we want the top-level File, not Blink::...::File - fqname = ::File.join(path,name) - begin - stat = ::File.stat(fqname) - rescue - # should probably rescue specific errors... - Blink.debug("Could not find %s in %s" % [name,path]) - next - end - - # if we've gotten this far, we found a valid script - return fqname - } - end - - # it'd be nice if i didn't throw the output away... - # this command returns true if the exit code is 0, and returns - # false otherwise - def initcmd(cmd) - script = self.initscript - - #Blink.debug "Executing '%s %s' as initcmd for '%s'" % - # [script,cmd,self] - - rvalue = Kernel.system("%s %s" % - [script,cmd]) - - #Blink.debug "'%s' ran with exit status '%s'" % - # [cmd,rvalue] - - - rvalue - end - - def initscript - if defined? @initscript - return @initscript - else - @initscript = Service.search(self.name) - end - end - end # Blink::Objects::BProcess - end # Blink::Objects -end diff --git a/lib/blink/objects/symlink.rb b/lib/blink/objects/symlink.rb deleted file mode 100644 index ca08ea952..000000000 --- a/lib/blink/objects/symlink.rb +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'etc' -require 'blink/attribute' -require 'blink/objects/file' - -module Blink - # okay, how do we deal with parameters that don't have operations - # associated with them? - class Attribute - class SymlinkTarget < Blink::Attribute - require 'etc' - attr_accessor :file - - @name = :target - - def create - begin - Blink.debug("Creating symlink '%s' to '%s'" % - [self.object[:path],self.should]) - unless File.symlink(self.should,self.object[:path]) - raise TypeError.new("Could not create symlink '%s'" % - self.object[:path]) - end - rescue => detail - raise TypeError.new("Cannot create symlink '%s': %s" % - [self.object[:path],detail]) - end - end - - def remove - if FileTest.symlink?(self.object[:path]) - Blink.debug("Removing symlink '%s'" % self.object[:path]) - begin - File.unlink(self.object[:path]) - rescue - raise TypeError.new("Failed to remove symlink '%s'" % - self.object[:path]) - end - elsif FileTest.exists?(self.object[:path]) - raise TypeError.new("Cannot remove normal file '%s'" % - self.object[:path]) - else - Blink.debug("Symlink '%s' does not exist" % - self.object[:path]) - end - end - - def retrieve - stat = nil - - if FileTest.symlink?(self.object[:path]) - self.value = File.readlink(self.object[:path]) - Blink.debug("link value is '%s'" % self.value) - return - else - self.value = nil - return - end - end - - # this is somewhat complicated, because it could exist and be - # a file - def sync - if self.should.nil? - self.remove() - else # it should exist and be a symlink - if FileTest.symlink?(self.object[:path]) - path = File.readlink(self.object[:path]) - if path != self.should - self.remove() - self.create() - end - elsif FileTest.exists?(self.object[:path]) - raise TypeError.new("Cannot replace normal file '%s'" % - self.object[:path]) - else - self.create() - end - end - - self.object.newevent(:event => :inode_changed) - end - end - end - - class Objects - class Symlink < Objects - attr_reader :stat, :path, :params - # class instance variable - @params = [ - Blink::Attribute::FileUID, - Blink::Attribute::FileGroup, - Blink::Attribute::FileMode, - Blink::Attribute::SymlinkTarget, - :path - ] - - @name = :symlink - @namevar = :path - end # Blink::Objects::File - end # Blink::Objects - -end diff --git a/lib/blink/types.rb b/lib/blink/types.rb new file mode 100644 index 000000000..4e5f9b268 --- /dev/null +++ b/lib/blink/types.rb @@ -0,0 +1,514 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +require 'blink/attribute' +require 'blink/interface' + +#--------------------------------------------------------------- +# This class is the abstract base class for the mechanism for organizing +# work. No work is actually done by this class or its subclasses; rather, +# the subclasses include attributes which do the actual work. +# See attribute.rb for how work is actually done. + +module Blink + class Objects + include Enumerable + @objects = Hash.new + @@allobjects = Array.new # and then an array for all objects + + + @@typeary = [] + @@typehash = Hash.new { |hash,key| + raise "Object type %s not found" % key + } + + #--------------------------------------------------------------- + # the class methods + + #--------------------------------------------------------------- + # retrieve a named object + def Objects.[](name) + if @objects.has_key?(name) + return @objects[name] + else + raise "Object '#{name}' does not exist" + end + end + #--------------------------------------------------------------- + + #--------------------------------------------------------------- + # this is special, because it can be equivalent to running new + # this allows cool syntax like Blink::File["/etc/inetd.conf"] = ... + def Objects.[]=(name,object) + newobj = nil + if object.is_a?(Blink::Objects) + newobj = object + else + raise "must pass a Blink::Objects object" + end + + if @objects.has_key?(newobj.name) + puts @objects + raise "'#{newobj.name}' already exists in " + + "class '#{newobj.class}': #{@objects[newobj.name]}" + else + Blink.debug("adding %s of type %s to class list" % + [object.name,object.class]) + @objects[newobj.name] = newobj + end + end + #--------------------------------------------------------------- + + #--------------------------------------------------------------- + def Objects.has_key?(name) + return @objects.has_key?(name) + end + #--------------------------------------------------------------- + + #----------------------------------- + # all objects total + def Objects.push(object) + @@allobjects.push object + Blink.debug("adding %s of type %s to master list" % + [object.name,object.class]) + end + #----------------------------------- + + #----------------------------------- + # this is meant to be run multiple times, e.g., when a new + # type is defined at run-time + def Objects.buildtypehash + @@typeary.each { |otype| + if @@typehash.include?(otype.name) + if @@typehash[otype.name] != otype + Blink.warning("Object type %s is already defined" % otype.name) + end + else + @@typehash[otype.name] = otype + end + } + end + #----------------------------------- + + #----------------------------------- + # this should make it so our subclasses don't have to worry about + # defining these class instance variables + def Objects.inherited(sub) + sub.module_eval %q{ + @objects = Hash.new + @actions = Hash.new + } + + # add it to the master list + # unfortunately we can't yet call sub.name, because the #inherited + # method gets called before any commands in the class definition + # get executed, which, um, sucks + @@typeary.push(sub) + end + #----------------------------------- + + #----------------------------------- + # this is used for mapping object types (e.g., Blink::Objects::File) + # to names (e.g., "file") + def Objects.name + return @name + end + #----------------------------------- + + #----------------------------------- + # some simple stuff to make it easier to get a name from everyone + def Objects.namevar + return @namevar + end + #----------------------------------- + + #----------------------------------- + # accessor for the list of acceptable params + def Objects.classparams + return @params + end + #----------------------------------- + + #----------------------------------- + # our param list is by class, so we need to convert it to names + # (see blink/objects/file.rb for an example of how params are defined) + def Objects.classparambyname + unless defined? @paramsbyname + @paramsbyname = Hash.new { |hash,key| + fail TypeError.new( + "Parameter %s is invalid for class %s" % + [key.to_s,self.class.to_s] + ) + } + @params.each { |param| + if param.is_a? Symbol + # store the Symbol class, not the symbol itself + symbolattr = Blink::Attribute::Symbol.new(param) + + @paramsbyname[param] = symbolattr + elsif param.respond_to?(:name) + # these are already classes + @paramsbyname[param.name] = param + else + fail TypeError.new( + "Parameter %s is invalid; it must be a class or symbol" % + param.to_s + ) + end + } + end + return @paramsbyname + end + #----------------------------------- + + #--------------------------------------------------------------- + # the instance methods + + #----------------------------------- + # parameter access and stuff + def [](param) + if @attributes.has_key?(param) + return @attributes[param].should + else + raise "Undefined parameter '#{param}' in #{self}" + end + end + #----------------------------------- + + #----------------------------------- + # because all object parameters are actually attributes, we + # have to do some shenanigans to make it look from the outside + # like @attributes is just a simple hash + # the Symbol stuff is especially a bit hackish + def []=(param,value) + if @attributes.has_key?(param) + @attributes[param].should = value + return + end + + attrclass = self.class.classparambyname[param] + + Blink.debug("creating attribute of type '%s'" % attrclass) + # any given object can normally only have one of any given attribute + # type, but it might have many Symbol attributes + # + # so, we need to make sure that the @attributes hash behaves + # the same whether it has a unique attribute or a bunch of Symbol + # attributes + if attrclass.is_a?(Blink::Attribute::Symbol) + attrclass.should = value + @attributes[param] = attrclass + else + attr = attrclass.new(value) + attr.object = self + if attr.is_a?(Array) + attr.each { |xattr| + @attributes[xattr.name] = attr + } + else + Blink.debug "Creating attr %s in %s" % [attr.name,self] + @attributes[attr.name] = attr + end + end + end + #----------------------------------- + + #----------------------------------- + # return action array + # these are actions to use for responding to events + # no, this probably isn't the best way, because we're providing + # access to the actual hash, which is silly + def action + if not defined? @actions + puts "defining action hash" + @actions = Hash.new + end + @actions + end + #----------------------------------- + + #----------------------------------- + # removing attributes + def delete(attr) + if @attributes.has_key?(attr) + @attributes.delete(attr) + else + raise "Undefined attribute '#{attr}' in #{self}" + end + end + #----------------------------------- + + #----------------------------------- + # XXX this won't work -- too simplistic + # a given object can be in multiple components + # which means... what? that i have to delete things from components? + # that doesn't seem right, somehow... + # do i really ever need to delete things? + #def delete + # self.class.delete[self.name] + #end + #----------------------------------- + + #----------------------------------- + # this can only be used with blocks that are + # valid on operations and objects, as it iterates over both of + # them + # essentially, the interface defined by Blink::Interface is used here + def each + ret = false + nodepth = 0 + unless block_given? + raise "'Each' was not given a block" + end + @attributes.each { |name,attr| + #Blink.debug "'%s' yielding '%s' of type '%s'" % [self,attr,attr.class] + yield(attr) + } + # DISABLED + # until we're clear on what 'enclosure' means, this is + # all disabled + + #if @encloses.length > 0 + # Blink.debug "#{self} encloses #{@encloses}" + ##end + #if defined? Blink['depthfirst'] + # self.eachobj { |enclosed| + # Blink.debug "yielding #{self} to object #{enclosed}" + # ret |= yield(enclosed) + # } + # nodepth = 1 + #end + #self.eachop { |op| + # Blink.debug "yielding #{self} to op #{op}" + # ret |= yield(op) + #} + #if ! defined? Blink['depthfirst'] and nodepth != 1 + # self.eachobj { |enclosed| + # Blink.debug "yielding #{self} to object #{enclosed}" + # ret |= yield(enclosed) + # } + #end + #return ret + end + #----------------------------------- + + #----------------------------------- + # this allows each object to act like both a node and + # a branch + # but each object contains two types of objects: operations and other + # objects.... + def eachobj + unless block_given? + raise "Eachobj was not given a block" + end + @encloses.each { |object| + yield(object) + } + end + #----------------------------------- + + #----------------------------------- + # store the object that immediately encloses us + def enclosedby(obj) + @enclosedby.push(obj) + end + #----------------------------------- + + #----------------------------------- + def enclosed? + defined? @enclosedby + end + #----------------------------------- + + #----------------------------------- + # store a enclosed object + def encloses(obj) + obj.enclosedby(self) + #obj.subscribe(self,'*') + @encloses.push(obj) + end + #----------------------------------- + + #----------------------------------- + # this is a wrapper, doing all of the work that should be done + # and none that shouldn't + def evaluate + raise "don't call evaluate; it's disabled" + end + #----------------------------------- + + #----------------------------------- + # call an event + # this is called on subscribers by the trigger method from the obj + # which sent the event + # event handling should probably be taking place in a central process, + # but.... + def event(event,obj) + Blink.debug "#{self} got event #{event} from #{obj}" + if @actions.key?(event) + Blink.debug "calling it" + @actions[event].call(self,obj,event) + else + p @actions + end + end + #----------------------------------- + + #----------------------------------- + # yay + def initialize(*args) + # params are for classes, attributes are for instances + # hokey but true + @attributes = Hash.new + @monitor = Array.new + + # default to always syncing + @performoperation = :sync + + begin + hash = Hash[*args] + rescue ArgumentError + fail TypeError.new("Incorrect number of arguments for %s" % + self.class.to_s) + end + + # if they passed in a list of attributes they're interested in, + # we mark them as "interesting" + # XXX maybe we should just consider params set to nil as 'interesting' + # + # this isn't used as much as it should be, but the idea is that + # the "interesting" attributes would be the ones retrieved during a + # 'retrieve' call + if hash.include?(:check) + @monitor = hash[:check].dup + hash.delete(:check) + end + + # we have to set the name of our object before anything else, + # because it might be used in creating the other attributes + if hash.has_key?(self.class.namevar) + self[self.class.namevar] = hash[self.class.namevar] + hash.delete(self.class.namevar) + else + raise TypeError.new("A name must be provided at initialization time") + end + + hash.each { |param,value| + @monitor.push(param) + Blink.debug("adding param '%s' with value '%s'" % + [param,value]) + self[param] = value + } + + # add this object to the specific class's list of objects + self.class[name] = self + + # and then add it to the master list + Blink::Objects.push(self) + + @notify = Hash.new + #@encloses = Array.new + #@enclosedby = Array.new + @actions = Hash.new + #@opsgenned = false + + # XXX i've no idea wtf is going on with enclosures + #if self.class == Blink::Objects::Root + # Blink.debug "not enclosing root (#{self.class}) in self" + #else + # Blink::Objects.root.encloses(self) + #end + end + # initialize + #----------------------------------- + + #----------------------------------- + def name + #namevar = self.class.namevar + #Blink.debug "namevar is '%s'" % namevar + #nameattr = @attributes[namevar] + #Blink.debug "nameattr is '%s'" % nameattr + #name = nameattr.value + #Blink.debug "returning %s from attr %s and namevar %s" % [name,nameattr,namevar] + #return name + return @attributes[self.class.namevar].value + end + #----------------------------------- + + #----------------------------------- + def newevent(args) + if args[:event].nil? + raise "newevent called wrong on #{self}" + end + + return Blink::Event.new( + :event => args[:event], + :object => self + ) + end + #----------------------------------- + + #----------------------------------- + # subscribe to an event or all events + # this entire event system is a hack job and needs to + # be replaced with a central event handler + def subscribe(args,&block) + obj = args[:object] + event = args[:event] || '*'.intern + if obj.nil? or event.nil? + raise "subscribe was called wrongly; #{obj} #{event}" + end + obj.action[event] = block + #events.each { |event| + unless @notify.key?(event) + @notify[event] = Array.new + end + unless @notify[event].include?(obj) + Blink.debug "pushing event '%s' for object '%s'" % [event,obj] + @notify[event].push(obj) + end + # } + #else + # @notify['*'.intern].push(obj) + end + #----------------------------------- + + #----------------------------------- + def to_s + self.name + end + #----------------------------------- + + #----------------------------------- + # initiate a response to an event + def trigger(event) + subscribers = Array.new + if @notify.include?('*') and @notify['*'].length > 0 + @notify['*'].each { |obj| subscribers.push(obj) } + end + if (@notify.include?(event) and (! @notify[event].empty?) ) + @notify[event].each { |obj| subscribers.push(obj) } + end + Blink.debug "triggering #{event}" + subscribers.each { |obj| + Blink.debug "calling #{event} on #{obj}" + obj.event(event,self) + } + end + #----------------------------------- + + #----------------------------------- + def validparam(param) + if (self.class.operparams.include?(param) or + self.class.staticparams.include?(param)) + return true + else + return false + end + end + #----------------------------------- + + #--------------------------------------------------------------- + end # Blink::Objects +end diff --git a/lib/blink/types/file.rb b/lib/blink/types/file.rb new file mode 100644 index 000000000..502a5a404 --- /dev/null +++ b/lib/blink/types/file.rb @@ -0,0 +1,213 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +require 'digest/md5' +require 'etc' +require 'blink/attribute' + +module Blink + # we first define all of the attribute that our file will use + # because the objects must be defined for us to use them in our + # definition of the file object + class Attribute + class FileUID < Blink::Attribute + require 'etc' + attr_accessor :file + @name = :owner + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue + # this isn't correct, but what the hell + raise "File '%s' does not exist: #{$!}" % self.object[:path] + end + + self.value = stat.uid + unless self.should.is_a?(Integer) + begin + user = Etc.getpwnam(self.should) + if user.gid == "" + raise "Could not retrieve uid for %s" % self.object + end + Blink.debug "converting %s to integer %d" % + [self.should,user.uid] + self.should = user.uid + rescue + raise "Could not get any info on user %s" % self.should + end + end + Blink.debug "chown state is %d" % self.value + end + + #def <=>(other) + # if other.is_a?(Integer) + # begin + # other = Etc.getpwnam(other).uid + # rescue + # raise "Could not get uid for #{@params[:uid]}" + # end + # end +# +# self.value <=> other +# end + + def sync + begin + File.chown(value,-1,self.object[:path]) + rescue + raise "failed to sync #{@params[:file]}: #{$!}" + end + + self.object.newevent(:event => :inode_changed) + end + end + + # this attribute should actually somehow turn into many attributes, + # one for each bit in the mode + # I think MetaAttributes are the answer, but I'm not quite sure + class FileMode < Blink::Attribute + require 'etc' + + @name = :mode + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue => error + raise "File %s could not be stat'ed: %s" % [self.object[:path],error] + end + + self.value = stat.mode & 007777 + Blink.debug "chmod state is %o" % self.value + end + + def sync + begin + File.chmod(self.should,self.object[:path]) + rescue + raise "failed to chmod #{self.object[:path]}: #{$!}" + end + self.object.newevent(:event => :inode_changed) + end + end + + # not used until I can figure out how to solve the problem with + # metaattributes + class FileSetUID < Blink::Attribute + require 'etc' + + @parent = Blink::Attribute::FileMode + + @name = :setuid + + def <=>(other) + self.value <=> @parent.value[11] + end + + # this just doesn't seem right... + def sync + tmp = 0 + if self.value == true + tmp = 1 + end + @parent.value[11] = tmp + end + end + + class FileGroup < Blink::Attribute + require 'etc' + + @name = :group + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue + # this isn't correct, but what the hell + raise "File #{self.object[:path]} does not exist: #{$!}" + end + + self.value = stat.gid + + # we probably shouldn't actually modify the 'should' value + # but i don't see a good way around it right now + # mmmm, should + unless self.should.is_a?(Integer) + begin + group = Etc.getgrnam(self.should) + # yeah, don't ask me + # this is retarded + #p group + if group.gid == "" + raise "Could not retrieve gid for %s" % self.object + end + Blink.debug "converting %s to integer %d" % + [self.should,group.gid] + self.should = group.gid + rescue + raise "Could not get any info on group %s" % self.should + end + end + Blink.debug "chgrp state is %d" % self.value + end + +# def <=>(other) +# # unless we're numeric... +# if other.is_a?(Integer) +# begin +# group = Etc.getgrnam(other) +# # yeah, don't ask me +# # this is retarded +# #p group +# other = group.gid +# if other == "" +# raise "Could not retrieve gid for %s" % other +# end +# rescue +# raise "Could not get any info on group %s" % other +# end +# end +# +# #puts self.should +# self.value <=> other +# end + + def sync + Blink.debug "setting chgrp state to %d" % self.should + begin + # set owner to nil so it's ignored + File.chown(nil,self.should,self.object[:path]) + rescue + raise "failed to chgrp %s to %s: %s" % + [self.object[:path], self.should, $!] + end + self.object.newevent(:event => :inode_changed) + end + end + end + class Objects + class File < Objects + attr_reader :stat, :path, :params + # class instance variable + @params = [ + Blink::Attribute::FileUID, + Blink::Attribute::FileGroup, + Blink::Attribute::FileMode, + Blink::Attribute::FileSetUID, + :path + ] + + @name = :file + @namevar = :path + end # Blink::Objects::File + end # Blink::Objects + +end diff --git a/lib/blink/types/package.rb b/lib/blink/types/package.rb new file mode 100644 index 000000000..407b5871a --- /dev/null +++ b/lib/blink/types/package.rb @@ -0,0 +1,242 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +module Blink + class Objects + # packages are complicated because each package format has completely + # different commands. We need some way to convert specific packages + # into the general package object... + class Package < Objects + attr_reader :version, :format + # class instance variable + @params = [ + :install, + :format, + :version + ] + + @name = :package + @namevar = :name + + # this is already being done in objects.rb + #def Package.inhereted(sub) + # sub.module_eval %q{ + # @objects = Hash.new + # @actions = Hash.new + # } + #end + + def initialize(hash) + end + + def retrieve + end + + def insync? + end + + def sync + end + end # Blink::Objects::Package + + class PackagingType + attr_writer :list, :install, :remove, :check + + @@types = Hash.new(false) + + def PackagingType.[](name) + if @@types.include?(name) + return @@types[name] + else + raise "no such type %s" % name + end + end + + [:list, :install, :remove, :check].each { |method| + self.send(:define_method, method) { + # retrieve the variable + var = eval("@" + method.id2name) + if var.is_a?(Proc) + var.call() + else + raise "only blocks are supported right now" + end + } + } + + def initialize(name) + if block_given? + yield self + end + + @packages = Hash.new(false) + @@types[name] = self + end + + def retrieve + @packages.clear() + + @packages = self.list() + end + end + + PackagingType.new("dpkg") { |type| + type.list = proc { + packages = [] + + # dpkg only prints as many columns as you have available + # which means we don't get all of the info + # stupid stupid + oldcol = ENV["COLUMNS"] + ENV["COLUMNS"] = "500" + + # list out all of the packages + open("| dpkg -l") { |process| + # our regex for matching dpkg output + regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$} + fields = [:status, :name, :version, :description] + hash = {} + + 5.times { process.gets } # throw away the header + + # now turn each returned line into a package object + process.each { |line| + if match = regex.match(line) + hash.clear + + fields.zip(match.captures) { |field,value| + hash[field] = value + } + packages.push Blink::Objects::Package.new(hash) + else + raise "failed to match dpkg line %s" % line + end + } + } + ENV["COLUMNS"] = oldcol + + return packages + } + + # we need package retrieval mechanisms before we can have package + # installation mechanisms... + #type.install = proc { |pkg| + # raise "installation not implemented yet" + #} + + type.remove = proc { |pkg| + cmd = "dpkg -r %s" % pkg.name + output = %x{#{cmd}} + if $? != 0 + raise output + end + } + } + + PackagingType.new("sunpkg") { |type| + type.list = proc { + packages = [] + hash = {} + names = { + "PKGINST" => :name, + "NAME" => nil, + "CATEGORY" => :category, + "ARCH" => :platform, + "VERSION" => :version, + "BASEDIR" => :root, + "HOTLINE" => nil, + "EMAIL" => nil, + "VENDOR" => :vendor, + "DESC" => :description, + "PSTAMP" => nil, + "INSTDATE" => nil, + "STATUS" => nil, + "FILES" => nil + } + + # list out all of the packages + open("| pkginfo -l") { |process| + # we're using the long listing, so each line is a separate piece + # of information + process.each { |line| + case line + when /^$/ then + packages.push Blink::Objects::Package.new(hash) + hash.clear + when /\s*(\w+):\s+(.+)/ + name = $1 + value = $2 + if names.include?(name) + hash[names[name]] = value + else + raise "Could not find %s" % name + end + when /\s+\d+.+/ + # nothing; we're ignoring the FILES info + end + } + } + return packages + } + + # we need package retrieval mechanisms before we can have package + # installation mechanisms... + #type.install = proc { |pkg| + # raise "installation not implemented yet" + #} + + type.remove = proc { |pkg| + cmd = "pkgrm -n %s" % pkg.name + output = %x{#{cmd}} + if $? != 0 + raise output + end + } + } + + # this is how we retrieve packages + class PackageSource + attr_accessor :uri + attr_writer :retrieve + + @@sources = Hash.new(false) + + def PackageSource.get(file) + type = file.sub(%r{:.+},'') + source = nil + if source = @@sources[type] + return source.retrieve(file) + else + raise "Unknown package source: %s" % type + end + end + + def initialize(name) + if block_given? + yield self + end + + @@sources[name] = self + end + + def retrieve(path) + @retrieve.call(path) + end + + end + + PackageSource.new("file") { |obj| + obj.retrieve = proc { |path| + # this might not work for windows... + file = path.sub(%r{^file://},'') + + if FileTest.exists?(file) + return file + else + raise "File %s does not exist" % file + end + } + } + end # Blink::Objects +end diff --git a/lib/blink/types/process.rb b/lib/blink/types/process.rb new file mode 100644 index 000000000..e2e5722fc --- /dev/null +++ b/lib/blink/types/process.rb @@ -0,0 +1,86 @@ +#!/usr/local/bin/ruby -w + +require 'blink/operation' +require 'blink/operation/processes' + +# DISABLED +# I'm only working on services, not processes, right now + +module Blink + class Attribute + class ProcessRunning < Attribute + def retrieve + running = 0 + regex = Regexp.new(@params[:pattern]) + begin + # this ps is only tested on Solaris + # XXX yeah, this definitely needs to be fixed... + %x{ps -ef -U #{@params[:user]}}.split("\n").each { |process| + if regex.match(process) + running += 1 + end + } + rescue + # this isn't correct, but what the hell + Blink::Message.new( + :level => :error, + :source => self.object, + :message => "Failed to run ps" + ) + end + + self.state = running + Blink.debug "there are #{running} #{self.object} processes for start" + end + + def <=>(other) + self.state < 1 + end + + def fix + require 'etc' + # ruby is really cool + uid = 0 + if @params[:user].is_a? Integer + uid = @params[:user] + else + uid = Etc.getpwnam(@params[:user]).uid + end + Kernel.fork { + Process.uid = uid + Process.euid = uid + string = @params[:binary] + (@params[:arguments] || "") + Blink::Message.new( + :level => :notice, + :source => self.object, + :message => "starting" + ) + Kernel.exec(string) + } + end + end + end + class Objects + class BProcess < Objects + attr_reader :stat, :path + @params = [:start, :stop, :user, :pattern, :binary, :arguments] + @name = :process + + @namevar = :pattern + + Blink::Relation.new(self, Blink::Operation::Start, { + :user => :user, + :pattern => :pattern, + :binary => :binary, + :arguments => :arguments + }) + + Blink::Relation.new(self, Blink::Operation::Stop, { + :user => :user, + :pattern => :pattern + }) + + end # Blink::Objects::BProcess + end # Blink::Objects + +end diff --git a/lib/blink/types/service.rb b/lib/blink/types/service.rb new file mode 100644 index 000000000..4e7f4b17e --- /dev/null +++ b/lib/blink/types/service.rb @@ -0,0 +1,138 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +# this is our main way of managing processes right now +# +# a service is distinct from a process in that services +# can only be managed through the interface of an init script +# which is why they have a search path for initscripts and such + +module Blink + class Attribute + class ServiceRunning < Attribute + @name = :running + + def retrieve + self.value = self.running() + Blink.debug "Running value for '%s' is '%s'" % + [self.object.name,self.value] + end + + # should i cache this info? + def running + begin + status = self.object.initcmd("status") + Blink.debug "initcmd status for '%s' is '%s'" % + [self.object.name,status] + + if status # the command succeeded + return 1 + else + return 0 + end + rescue SystemCallError + raise "Could not execute %s" % initscript + end + + end + + def sync + if self.running > 0 + status = 1 + else + status = 0 + end + Blink.debug "'%s' status is '%s' and should be '%s'" % + [self,status,should] + if self.should > 0 + if status < 1 + Blink.debug "Starting '%s'" % self + unless self.object.initcmd("start") + raise "Failed to start %s" % self.name + end + else + Blink.debug "'%s' is already running, yo" % self + #Blink.debug "Starting '%s'" % self + #unless self.object.initcmd("start") + # raise "Failed to start %s" % self.name + #end + end + elsif status > 0 + Blink.debug "Stopping '%s'" % self + unless self.object.initcmd("stop") + raise "Failed to stop %s" % self.name + end + else + Blink.debug "Not running '%s' and shouldn't be running" % self + end + end + end + end + class Objects + class Service < Objects + attr_reader :stat + @params = [ + Blink::Attribute::ServiceRunning, + :name, + :pattern + ] + + @name = :service + @namevar = :name + + @searchpaths = Array.new + + def Service.addpath(path) + unless @searchpaths.include?(path) + # XXX should we check to see if the path exists? + @searchpaths.push(path) + end + end + + def Service.search(name) + @searchpaths.each { |path| + # must specify that we want the top-level File, not Blink::...::File + fqname = ::File.join(path,name) + begin + stat = ::File.stat(fqname) + rescue + # should probably rescue specific errors... + Blink.debug("Could not find %s in %s" % [name,path]) + next + end + + # if we've gotten this far, we found a valid script + return fqname + } + end + + # it'd be nice if i didn't throw the output away... + # this command returns true if the exit code is 0, and returns + # false otherwise + def initcmd(cmd) + script = self.initscript + + #Blink.debug "Executing '%s %s' as initcmd for '%s'" % + # [script,cmd,self] + + rvalue = Kernel.system("%s %s" % + [script,cmd]) + + #Blink.debug "'%s' ran with exit status '%s'" % + # [cmd,rvalue] + + + rvalue + end + + def initscript + if defined? @initscript + return @initscript + else + @initscript = Service.search(self.name) + end + end + end # Blink::Objects::BProcess + end # Blink::Objects +end diff --git a/lib/blink/types/symlink.rb b/lib/blink/types/symlink.rb new file mode 100644 index 000000000..ca08ea952 --- /dev/null +++ b/lib/blink/types/symlink.rb @@ -0,0 +1,106 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +require 'etc' +require 'blink/attribute' +require 'blink/objects/file' + +module Blink + # okay, how do we deal with parameters that don't have operations + # associated with them? + class Attribute + class SymlinkTarget < Blink::Attribute + require 'etc' + attr_accessor :file + + @name = :target + + def create + begin + Blink.debug("Creating symlink '%s' to '%s'" % + [self.object[:path],self.should]) + unless File.symlink(self.should,self.object[:path]) + raise TypeError.new("Could not create symlink '%s'" % + self.object[:path]) + end + rescue => detail + raise TypeError.new("Cannot create symlink '%s': %s" % + [self.object[:path],detail]) + end + end + + def remove + if FileTest.symlink?(self.object[:path]) + Blink.debug("Removing symlink '%s'" % self.object[:path]) + begin + File.unlink(self.object[:path]) + rescue + raise TypeError.new("Failed to remove symlink '%s'" % + self.object[:path]) + end + elsif FileTest.exists?(self.object[:path]) + raise TypeError.new("Cannot remove normal file '%s'" % + self.object[:path]) + else + Blink.debug("Symlink '%s' does not exist" % + self.object[:path]) + end + end + + def retrieve + stat = nil + + if FileTest.symlink?(self.object[:path]) + self.value = File.readlink(self.object[:path]) + Blink.debug("link value is '%s'" % self.value) + return + else + self.value = nil + return + end + end + + # this is somewhat complicated, because it could exist and be + # a file + def sync + if self.should.nil? + self.remove() + else # it should exist and be a symlink + if FileTest.symlink?(self.object[:path]) + path = File.readlink(self.object[:path]) + if path != self.should + self.remove() + self.create() + end + elsif FileTest.exists?(self.object[:path]) + raise TypeError.new("Cannot replace normal file '%s'" % + self.object[:path]) + else + self.create() + end + end + + self.object.newevent(:event => :inode_changed) + end + end + end + + class Objects + class Symlink < Objects + attr_reader :stat, :path, :params + # class instance variable + @params = [ + Blink::Attribute::FileUID, + Blink::Attribute::FileGroup, + Blink::Attribute::FileMode, + Blink::Attribute::SymlinkTarget, + :path + ] + + @name = :symlink + @namevar = :path + end # Blink::Objects::File + end # Blink::Objects + +end -- cgit