summaryrefslogtreecommitdiffstats
path: root/lib/blink/objects
diff options
context:
space:
mode:
Diffstat (limited to 'lib/blink/objects')
-rwxr-xr-xlib/blink/objects/file.rb214
-rw-r--r--lib/blink/objects/package.rb197
-rwxr-xr-xlib/blink/objects/process.rb86
-rwxr-xr-xlib/blink/objects/service.rb136
-rw-r--r--lib/blink/objects/symlink.rb107
5 files changed, 740 insertions, 0 deletions
diff --git a/lib/blink/objects/file.rb b/lib/blink/objects/file.rb
new file mode 100755
index 000000000..3f2a8dcfc
--- /dev/null
+++ b/lib/blink/objects/file.rb
@@ -0,0 +1,214 @@
+#!/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
+ ]
+
+ @objects = Hash.new
+ @actions = Hash.new
+ @namevar = :path
+ end # Blink::Objects::File
+ end # Blink::Objects
+
+end
diff --git a/lib/blink/objects/package.rb b/lib/blink/objects/package.rb
new file mode 100644
index 000000000..f35368022
--- /dev/null
+++ b/lib/blink/objects/package.rb
@@ -0,0 +1,197 @@
+#!/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
+ ]
+
+ @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
+ }
+ }
+ end # Blink::Objects
+end
diff --git a/lib/blink/objects/process.rb b/lib/blink/objects/process.rb
new file mode 100755
index 000000000..182667d7c
--- /dev/null
+++ b/lib/blink/objects/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 Objects
+ class BProcess < Objects
+ attr_reader :stat, :path
+ @params = [:start, :stop, :user, :pattern, :binary, :arguments] # class instance variable
+
+ @objects = Hash.new
+ @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
+
+ 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
+end
diff --git a/lib/blink/objects/service.rb b/lib/blink/objects/service.rb
new file mode 100755
index 000000000..31fd68234
--- /dev/null
+++ b/lib/blink/objects/service.rb
@@ -0,0 +1,136 @@
+#!/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
+ ]
+
+ @objects = Hash.new
+ @actions = Hash.new
+ @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...
+ def initcmd(cmd)
+ script = self.initscript
+
+ Blink.debug "Running '%s %s' as initcmd for '%s'" %
+ [script,cmd,self]
+
+ rvalue = Kernel.system("%s status" %
+ [self.initscript])
+
+ rvalue = Kernel.system("%s %s" %
+ [self.initscript,cmd])
+
+ 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
new file mode 100644
index 000000000..11187e2cf
--- /dev/null
+++ b/lib/blink/objects/symlink.rb
@@ -0,0 +1,107 @@
+#!/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
+ ]
+
+ @objects = Hash.new
+ @actions = Hash.new
+ @namevar = :path
+ end # Blink::Objects::File
+ end # Blink::Objects
+
+end