diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb
index 16fc55b..770a7b1 100644
--- a/lib/puppet/provider/service/launchd.rb
+++ b/lib/puppet/provider/service/launchd.rb
@@ -37,6 +37,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
"
commands :launchctl => "/bin/launchctl"
+ commands :sw_vers => "/usr/bin/sw_vers"
defaultfor :operatingsystem => :darwin
confine :operatingsystem => :darwin
@@ -48,6 +49,8 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
"/System/Library/LaunchAgents",
"/System/Library/LaunchDaemons",]
+ Launchd_Overrides = "/var/db/launchd.db/com.apple.launchd/overrides.plist"
+
# returns a label => path map for either all jobs, or just a single
# job if the label is specified
@@ -86,6 +89,36 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
new(:name => job, :provider => :launchd, :path => jobs[job])
end
end
+
+
+ def self.get_macosx_version_major
+ if defined? @macosx_version_major
+ return @macosx_version_major
+ end
+ begin
+ # Make sure we've loaded all of the facts
+ Facter.loadfacts
+
+ if Facter.value(:macosx_productversion_major)
+ product_version_major = Facter.value(:macosx_productversion_major)
+ else
+ # TODO: remove this code chunk once we require Facter 1.5.5 or higher.
+ Puppet.warning("DEPRECATION WARNING: Future versions of the launchd provider will require Facter 1.5.5 or newer.")
+ product_version = Facter.value(:macosx_productversion)
+ if product_version.nil?
+ fail("Could not determine OS X version from Facter")
+ end
+ product_version_major = product_version.scan(/(\d+)\.(\d+)./).join(".")
+ end
+ if %w{10.0 10.1 10.2 10.3}.include?(product_version_major)
+ fail("%s is not supported by the launchd provider" % product_version_major)
+ end
+ @macosx_version_major = product_version_major
+ return @macosx_version_major
+ rescue Puppet::ExecutionFailure => detail
+ fail("Could not determine OS X version: %s" % detail)
+ end
+ end
# finds the path for a given label and returns the path and parsed plist
@@ -171,33 +204,72 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
# launchd jobs are enabled by default. They are only disabled if the key
# "Disabled" is set to true, but it can also be set to false to enable it.
+ # In 10.6, the Disabled key in the job plist is consulted, but only if there
+ # is no entry in the global overrides plist.
+ # We need to draw a distinction between undefined, true and false for both
+ # locations where the Disabled flag can be defined.
def enabled?
+ job_plist_disabled = nil
+ overrides_disabled = nil
+
job_path, job_plist = plist_from_label(resource[:name])
if job_plist.has_key?("Disabled")
- if job_plist["Disabled"] # inverse of disabled is enabled
- return :false
+ job_plist_disabled = job_plist["Disabled"]
+ end
+
+ if self.class.get_macosx_version_major == "10.6":
+ overrides = Plist::parse_xml(Launchd_Overrides)
+
+ unless overrides.nil?
+ if overrides.has_key?(resource[:name])
+ if overrides[resource[:name]].has_key?("Disabled")
+ overrides_disabled = overrides[resource[:name]]["Disabled"]
+ end
+ end
+ end
+ end
+
+ if overrides_disabled.nil?
+ if job_plist_disabled.nil? or job_plist_disabled == false
+ return :true
end
+ elsif overrides_disabled == false
+ return :true
end
- return :true
+ return :false
end
# enable and disable are a bit hacky. We write out the plist with the appropriate value
# rather than dealing with launchctl as it is unable to change the Disabled flag
# without actually loading/unloading the job.
+ # In 10.6 we need to write out a disabled key to the global overrides plist, in earlier
+ # versions this is stored in the job plist itself.
def enable
- job_path, job_plist = plist_from_label(resource[:name])
- if self.enabled? == :false
- job_plist.delete("Disabled")
- Plist::Emit.save_plist(job_plist, job_path)
+ if self.class.get_macosx_version_major == "10.6"
+ overrides = Plist::parse_xml(Launchd_Overrides)
+ overrides[resource[:name]] = { "Disabled" => false }
+ Plist::Emit.save_plist(overrides, Launchd_Overrides)
+ else
+ job_path, job_plist = plist_from_label(resource[:name])
+ if self.enabled? == :false
+ job_plist.delete("Disabled")
+ Plist::Emit.save_plist(job_plist, job_path)
+ end
end
end
def disable
- job_path, job_plist = plist_from_label(resource[:name])
- job_plist["Disabled"] = true
- Plist::Emit.save_plist(job_plist, job_path)
+ if self.class.get_macosx_version_major == "10.6"
+ overrides = Plist::parse_xml(Launchd_Overrides)
+ overrides[resource[:name]] = { "Disabled" => true }
+ Plist::Emit.save_plist(overrides, Launchd_Overrides)
+ else
+ job_path, job_plist = plist_from_label(resource[:name])
+ job_plist["Disabled"] = true
+ Plist::Emit.save_plist(job_plist, job_path)
+ end
end
diff --git a/spec/unit/provider/service/launchd.rb b/spec/unit/provider/service/launchd.rb
index b2c51a4..fa86a21 100755
--- a/spec/unit/provider/service/launchd.rb
+++ b/spec/unit/provider/service/launchd.rb
@@ -33,6 +33,10 @@ describe provider_class do
@provider.stubs(:plist_from_label).returns([@joblabel, @jobplist])
@provider.stubs(:execute).returns("")
@provider.stubs(:resource).returns @resource
+
+ # We stub this out for the normal case as 10.6 is "special".
+ provider_class.stubs(:get_macosx_version_major).returns("10.5")
+
end
it "should have a start method for #{@provider.object_id}" do
@@ -74,6 +78,42 @@ describe provider_class do
@provider.status.should == :running
end
end
+
+ describe "when checking whether the service is enabled" do
+ it "should return true if the job plist says disabled is false" do
+ @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}])
+ @provider.enabled?.should == :true
+ end
+ it "should return true if the job plist has no disabled key" do
+ @provider.stubs(:plist_from_label).returns(["foo", {}])
+ @provider.enabled?.should == :true
+ end
+ it "should return false if the job plist says disabled is true" do
+ @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}])
+ @provider.enabled?.should == :false
+ end
+ end
+
+ describe "when checking whether the service is enabled on OS X 10.6" do
+ it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do
+ provider_class.stubs(:get_macosx_version_major).returns("10.6")
+ @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}])
+ Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => false}})
+ @provider.enabled?.should == :true
+ end
+ it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do
+ provider_class.stubs(:get_macosx_version_major).returns("10.6")
+ @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}])
+ Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => true}})
+ @provider.enabled?.should == :false
+ end
+ it "should return true if the job plist and the global overrides have no disabled keys" do
+ provider_class.stubs(:get_macosx_version_major).returns("10.6")
+ @provider.stubs(:plist_from_label).returns(["foo", {}])
+ Plist.stubs(:parse_xml).returns({})
+ @provider.enabled?.should == :true
+ end
+ end
describe "when starting the service" do
it "should look for the relevant plist once" do
@@ -138,5 +178,23 @@ describe provider_class do
@provider.stop
end
end
+
+ describe "when enabling the service on OS X 10.6" do
+ it "should write to the global launchd overrides file once" do
+ provider_class.stubs(:get_macosx_version_major).returns("10.6")
+ Plist.stubs(:parse_xml).returns({})
+ Plist::Emit.expects(:save_plist).once
+ @provider.enable
+ end
+ end
+
+ describe "when disabling the service on OS X 10.6" do
+ it "should write to the global launchd overrides file once" do
+ provider_class.stubs(:get_macosx_version_major).returns("10.6")
+ Plist.stubs(:parse_xml).returns({})
+ Plist::Emit.expects(:save_plist).once
+ @provider.enable
+ end
+ end
end
--
1.6.4