summaryrefslogtreecommitdiffstats
path: root/lib/puppet/util
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-10-17 09:01:04 -0500
committerLuke Kanies <luke@madstop.com>2008-10-17 09:01:04 -0500
commit8aee40de69e6fe8d67ab58a2e223443b15820584 (patch)
tree89e230df3b43302a542f2cb6869f63e2fb93f6d8 /lib/puppet/util
parent1b517d2fb048603bd1743a662bde74e8ae4b13dc (diff)
parenta74ec60d33dee1c592ec858faeccc23d7a7b79f3 (diff)
Merge branch '0.24.x' Removed the 'after' blocks that call Type.clear,
since that method is deprecated. Conflicts: CHANGELOG bin/puppetca lib/puppet/file_serving/fileset.rb lib/puppet/network/xmlrpc/client.rb lib/puppet/type/file/selcontext.rb spec/unit/file_serving/metadata.rb spec/unit/type/file.rb
Diffstat (limited to 'lib/puppet/util')
-rwxr-xr-xlib/puppet/util/filetype.rb7
-rw-r--r--lib/puppet/util/log.rb49
-rw-r--r--lib/puppet/util/metric.rb32
-rwxr-xr-xlib/puppet/util/posix.rb22
-rw-r--r--lib/puppet/util/selinux.rb130
-rw-r--r--lib/puppet/util/settings.rb8
-rw-r--r--lib/puppet/util/user_attr.rb21
7 files changed, 216 insertions, 53 deletions
diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb
index ae078fff4..b10b50fb7 100755
--- a/lib/puppet/util/filetype.rb
+++ b/lib/puppet/util/filetype.rb
@@ -1,8 +1,13 @@
# Basic classes for reading, writing, and emptying files. Not much
# to see here.
+
+require 'puppet/util/selinux'
+
class Puppet::Util::FileType
attr_accessor :loaded, :path, :synced
+ include Puppet::Util::SELinux
+
class << self
attr_accessor :name
include Puppet::Util::ClassGen
@@ -114,6 +119,8 @@ class Puppet::Util::FileType
tf.print text; tf.flush
FileUtils.cp(tf.path, @path)
tf.close
+ # If SELinux is present, we need to ensure the file has its expected context
+ set_selinux_default_context(@path)
end
end
diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb
index 8824a8b50..0dfd36dfa 100644
--- a/lib/puppet/util/log.rb
+++ b/lib/puppet/util/log.rb
@@ -1,10 +1,12 @@
require 'syslog'
+require 'puppet/util/tagging'
# Pass feedback to the user. Log levels are modeled after syslog's, and it is
# expected that that will be the most common log destination. Supports
# multiple destinations, one of which is a remote server.
class Puppet::Util::Log
include Puppet::Util
+ include Puppet::Util::Tagging
@levels = [:debug,:info,:notice,:warning,:err,:alert,:emerg,:crit]
@loglevel = 2
@@ -470,12 +472,12 @@ class Puppet::Util::Log
@levels.include?(level)
end
- attr_accessor :level, :message, :time, :tags, :remote
+ attr_accessor :level, :message, :time, :remote
attr_reader :source
def initialize(args)
unless args.include?(:level) && args.include?(:message)
- raise Puppet::DevError, "Puppet::Util::Log called incorrectly"
+ raise ArgumentError, "Puppet::Util::Log called incorrectly"
end
if args[:level].class == String
@@ -483,35 +485,27 @@ class Puppet::Util::Log
elsif args[:level].class == Symbol
@level = args[:level]
else
- raise Puppet::DevError,
- "Level is not a string or symbol: #{args[:level].class}"
+ raise ArgumentError, "Level is not a string or symbol: #{args[:level].class}"
end
- # Just return unless we're actually at a level we should send
- #return unless self.class.sendlevel?(@level)
-
@message = args[:message].to_s
@time = Time.now
- # this should include the host name, and probly lots of other
- # stuff, at some point
- unless self.class.validlevel?(level)
- raise Puppet::DevError, "Invalid message level #{level}"
- end
- if args.include?(:tags)
- @tags = args[:tags]
- end
+ raise ArgumentError, "Invalid log level %s" % level unless self.class.validlevel?(level)
- if args.include?(:source)
- self.source = args[:source]
- else
- @source = "Puppet"
+ if tags = args[:tags]
+ tags.each { |t| self.tag(t) }
end
+ self.source = args[:source] || "Puppet"
+
+ # Tag myself with my log level
+ tag(level)
+
Log.newmessage(self)
end
- # Was the source of this log an object?
+ # Was the source of this log a Puppet resource or parameter?
def objectsource?
if defined? @objectsource and @objectsource
@objectsource
@@ -533,17 +527,11 @@ class Puppet::Util::Log
@objectsource = false
@source = source.to_s
end
- unless defined? @tags and @tags
- if source.respond_to?(:tags)
- @tags = source.tags
- end
+ if source.respond_to?(:tags)
+ source.tags.each { |t| tag(t) }
end
end
- def tagged?(tag)
- @tags.detect { |t| t.to_s == tag.to_s }
- end
-
def to_report
"%s %s (%s): %s" % [self.time, self.source, self.level, self.to_s]
end
@@ -552,5 +540,8 @@ class Puppet::Util::Log
return @message
end
end
-Puppet::Log = Puppet::Util::Log
+# This is for backward compatibility from when we changed the constant to Puppet::Util::Log
+# because the reports include the constant name. Apparently the alias was created in
+# March 2007, should could probably be removed soon.
+Puppet::Log = Puppet::Util::Log
diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb
index ca23aa87f..e6d7678aa 100644
--- a/lib/puppet/util/metric.rb
+++ b/lib/puppet/util/metric.rb
@@ -5,6 +5,8 @@ require 'puppet'
class Puppet::Util::Metric
# Load the library as a feature, so we can test its presence.
+ # It's only used by this class, so there's no reason to move it
+ # to the main feature list.
Puppet.features.add :rrd, :libs => 'RRDtool'
attr_accessor :type, :name, :value, :label
@@ -12,6 +14,15 @@ class Puppet::Util::Metric
attr_writer :basedir
+ # Return a specific value
+ def [](name)
+ if value = @values.find { |v| v[0] == name }
+ return value[2]
+ else
+ return nil
+ end
+ end
+
def basedir
if defined? @basedir
@basedir
@@ -93,11 +104,7 @@ class Puppet::Util::Metric
def initialize(name,label = nil)
@name = name.to_s
- if label
- @label = label
- else
- @label = name.to_s.capitalize.gsub("_", " ")
- end
+ @label = label || labelize(name)
@values = []
end
@@ -107,9 +114,7 @@ class Puppet::Util::Metric
end
def newvalue(name,value,label = nil)
- unless label
- label = name.to_s.capitalize.gsub("_", " ")
- end
+ label ||= labelize(name)
@values.push [name,label,value]
end
@@ -145,7 +150,16 @@ class Puppet::Util::Metric
def values
@values.sort { |a, b| a[1] <=> b[1] }
end
+
+ private
+
+ # Convert a name into a label.
+ def labelize(name)
+ name.to_s.capitalize.gsub("_", " ")
+ end
end
+# This is necessary because we changed the class path in early 2007,
+# and reports directly yaml-dump these metrics, so both client and server
+# have to agree on the class name.
Puppet::Metric = Puppet::Util::Metric
-
diff --git a/lib/puppet/util/posix.rb b/lib/puppet/util/posix.rb
index aff797485..3f6c1f6e3 100755
--- a/lib/puppet/util/posix.rb
+++ b/lib/puppet/util/posix.rb
@@ -7,10 +7,8 @@ module Puppet::Util::POSIX
# method search_posix_field in the gid and uid methods if a sanity check
# fails
def get_posix_field(space, field, id)
- unless id
- raise ArgumentError, "Did not get id"
- end
- prefix = "get" + space.to_s
+ raise Puppet::DevError, "Did not get id from caller" unless id
+
if id.is_a?(Integer)
if id > Puppet[:maximum_uid].to_i
Puppet.err "Tried to get %s field for silly id %s" % [field, id]
@@ -93,16 +91,16 @@ module Puppet::Util::POSIX
# Get the GID of a given group, provided either a GID or a name
def gid(group)
begin
- group = Integer(group)
+ group = Integer(group)
rescue ArgumentError
- # pass
+ # pass
end
if group.is_a?(Integer)
- name = get_posix_field(:group, :name, group)
+ return nil unless name = get_posix_field(:group, :name, group)
gid = get_posix_field(:group, :gid, name)
check_value = gid
else
- gid = get_posix_field(:group, :gid, group)
+ return nil unless gid = get_posix_field(:group, :gid, group)
name = get_posix_field(:group, :name, gid)
check_value = name
end
@@ -116,16 +114,16 @@ module Puppet::Util::POSIX
# Get the UID of a given user, whether a UID or name is provided
def uid(user)
begin
- user = Integer(user)
+ user = Integer(user)
rescue ArgumentError
- # pass
+ # pass
end
if user.is_a?(Integer)
- name = get_posix_field(:passwd, :name, user)
+ return nil unless name = get_posix_field(:passwd, :name, user)
uid = get_posix_field(:passwd, :uid, name)
check_value = uid
else
- uid = get_posix_field(:passwd, :uid, user)
+ return nil unless uid = get_posix_field(:passwd, :uid, user)
name = get_posix_field(:passwd, :name, uid)
check_value = name
end
diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb
new file mode 100644
index 000000000..0a4af3ca1
--- /dev/null
+++ b/lib/puppet/util/selinux.rb
@@ -0,0 +1,130 @@
+# Provides utility functions to help interfaces Puppet to SELinux.
+#
+# Currently this is implemented via the command line tools. At some
+# point support should be added to use the new SELinux ruby bindings
+# as that will be faster and more reliable then shelling out when they
+# are available. At this time (2008-09-26) these bindings aren't bundled on
+# any SELinux-using distribution I know of.
+
+module Puppet::Util::SELinux
+
+ def selinux_support?
+ FileTest.exists?("/selinux/enforce")
+ end
+
+ # Retrieve and return the full context of the file. If we don't have
+ # SELinux support or if the stat call fails then return nil.
+ def get_selinux_current_context(file)
+ unless selinux_support?
+ return nil
+ end
+ context = ""
+ begin
+ execpipe("/usr/bin/stat -c %C #{file}") do |out|
+ out.each do |line|
+ context << line
+ end
+ end
+ rescue Puppet::ExecutionFailure
+ return nil
+ end
+ context.chomp!
+ # Handle the case that the system seems to have SELinux support but
+ # stat finds unlabled files.
+ if context == "(null)"
+ return nil
+ end
+ return context
+ end
+
+ # Use the matchpathcon command, if present, to return the SELinux context
+ # which the SELinux policy on the system expects the file to have. We can
+ # use this to obtain a good default context. If the command does not
+ # exist or the call fails return nil.
+ #
+ # Note: For this command to work a full, non-relative, filesystem path
+ # should be given.
+ def get_selinux_default_context(file)
+ unless selinux_support?
+ return nil
+ end
+ unless FileTest.executable?("/usr/sbin/matchpathcon")
+ return nil
+ end
+ context = ""
+ begin
+ execpipe("/usr/sbin/matchpathcon #{file}") do |out|
+ out.each do |line|
+ context << line
+ end
+ end
+ rescue Puppet::ExecutionFailure
+ return nil
+ end
+ # For a successful match, matchpathcon returns two fields separated by
+ # a variable amount of whitespace. The second field is the full context.
+ context = context.split(/\s/)[1]
+ return context
+ end
+
+ # Take the full SELinux context returned from the tools and parse it
+ # out to the three (or four) component parts. Supports :seluser, :selrole,
+ # :seltype, and on systems with range support, :selrange.
+ def parse_selinux_context(component, context)
+ if context.nil? or context == "unlabeled"
+ return nil
+ end
+ unless context =~ /^([a-z0-9_]+):([a-z0-9_]+):([a-z0-9_]+)(?::([a-zA-Z0-9:,._-]+))?/
+ raise Puppet::Error, "Invalid context to parse: #{context}"
+ end
+ ret = {
+ :seluser => $1,
+ :selrole => $2,
+ :seltype => $3,
+ :selrange => $4,
+ }
+ return ret[component]
+ end
+
+ # This updates the actual SELinux label on the file. You can update
+ # only a single component or update the entire context. It is just a
+ # wrapper around the chcon command.
+ def set_selinux_context(file, value, component = false)
+ unless selinux_support?
+ return nil
+ end
+ case component
+ when :seluser
+ flag = "-u"
+ when :selrole
+ flag = "-r"
+ when :seltype
+ flag = "-t"
+ when :selrange
+ flag = "-l"
+ else
+ flag = ""
+ end
+
+ execute(["/usr/bin/chcon","-h",flag,value,file])
+ return true
+ end
+
+ # Since this call relies on get_selinux_default_context it also needs a
+ # full non-relative path to the file. Fortunately, that seems to be all
+ # Puppet uses. This will set the file's SELinux context to the policy's
+ # default context (if any) if it differs from the context currently on
+ # the file.
+ def set_selinux_default_context(file)
+ new_context = get_selinux_default_context(file)
+ unless new_context
+ return nil
+ end
+ cur_context = get_selinux_current_context(file)
+ if new_context != cur_context
+ set_selinux_context(file, new_context)
+ return new_context
+ end
+ return nil
+ end
+end
diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb
index 64bb40b52..d0c16ec92 100644
--- a/lib/puppet/util/settings.rb
+++ b/lib/puppet/util/settings.rb
@@ -115,12 +115,14 @@ class Puppet::Util::Settings
end
# Do variable interpolation on the value.
- def convert(value)
+ def convert(value, environment = nil)
return value unless value
return value unless value.is_a? String
newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value|
varname = $2 || $1
- if pval = self.value(varname)
+ if varname == "environment" and environment
+ environment
+ elsif pval = self.value(varname)
pval
else
raise Puppet::DevError, "Could not find value for %s" % value
@@ -692,7 +694,7 @@ Generated on #{Time.now}.
val = @config[param].default if val.nil?
# Convert it if necessary
- val = convert(val)
+ val = convert(val, environment)
# And cache it
@cache[environment||"none"][param] = val
diff --git a/lib/puppet/util/user_attr.rb b/lib/puppet/util/user_attr.rb
new file mode 100644
index 000000000..db8fb81b9
--- /dev/null
+++ b/lib/puppet/util/user_attr.rb
@@ -0,0 +1,21 @@
+class UserAttr
+ def self.get_attributes_by_name(name)
+ attributes = nil
+
+ File.readlines('/etc/user_attr').each do |line|
+ next if line =~ /^#/
+
+ token = line.split(':')
+
+ if token[0] == name
+ attributes = {:name => name}
+ token[4].split(';').each do |attr|
+ key_value = attr.split('=')
+ attributes[key_value[0].intern] = key_value[1].strip
+ end
+ break
+ end
+ end
+ return attributes
+ end
+end