summaryrefslogtreecommitdiff
path: root/lib/chef/provider/service
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/provider/service')
-rw-r--r--lib/chef/provider/service/arch.rb113
-rw-r--r--lib/chef/provider/service/debian.rb152
-rw-r--r--lib/chef/provider/service/freebsd.rb175
-rw-r--r--lib/chef/provider/service/gentoo.rb67
-rw-r--r--lib/chef/provider/service/init.rb87
-rw-r--r--lib/chef/provider/service/insserv.rb52
-rw-r--r--lib/chef/provider/service/invokercd.rb35
-rw-r--r--lib/chef/provider/service/macosx.rb144
-rw-r--r--lib/chef/provider/service/redhat.rb77
-rw-r--r--lib/chef/provider/service/simple.rb172
-rw-r--r--lib/chef/provider/service/solaris.rb86
-rw-r--r--lib/chef/provider/service/systemd.rb115
-rw-r--r--lib/chef/provider/service/upstart.rb232
-rw-r--r--lib/chef/provider/service/windows.rb163
14 files changed, 1670 insertions, 0 deletions
diff --git a/lib/chef/provider/service/arch.rb b/lib/chef/provider/service/arch.rb
new file mode 100644
index 0000000000..8c8216c37f
--- /dev/null
+++ b/lib/chef/provider/service/arch.rb
@@ -0,0 +1,113 @@
+#
+# Author:: Jan Zimmek (<jan.zimmek@web.de>)
+# Copyright:: Copyright (c) 2010 Jan Zimmek
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service/init'
+require 'chef/mixin/command'
+
+class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
+
+ def initialize(new_resource, run_context)
+ super
+ @init_command = "/etc/rc.d/#{@new_resource.service_name}"
+ end
+
+ def load_current_resource
+ raise Chef::Exceptions::Service, "Could not find /etc/rc.conf" unless ::File.exists?("/etc/rc.conf")
+ raise Chef::Exceptions::Service, "No DAEMONS found in /etc/rc.conf" unless ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
+ super
+
+ @current_resource.enabled(daemons.include?(@current_resource.service_name))
+ @current_resource
+ end
+
+ # Get list of all daemons from the file '/etc/rc.conf'.
+ # Mutiple lines and background form are supported. Example:
+ # DAEMONS=(\
+ # foobar \
+ # @example \
+ # !net \
+ # )
+ def daemons
+ entries = []
+ if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
+ entries += $1.gsub(/\\?[\r\n]/, ' ').gsub(/# *[^ ]+/,' ').split(' ') if $1.length > 0
+ end
+
+ yield(entries) if block_given?
+
+ entries
+ end
+
+ # FIXME: Multiple entries of DAEMONS will cause very bad results :)
+ def update_daemons(entries)
+ content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(' ')})")
+ ::File.open("/etc/rc.conf", "w") do |f|
+ f.write(content)
+ end
+ end
+
+ def enable_service()
+ new_daemons = []
+ entries = daemons
+
+ if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
+ # exists and already enabled (or already enabled as a background service)
+ # new_daemons += entries
+ else
+ if entries.include?("!#{new_resource.service_name}")
+ # exists but disabled
+ entries.each do |daemon|
+ if daemon == "!#{new_resource.service_name}"
+ new_daemons << new_resource.service_name
+ else
+ new_daemons << daemon
+ end
+ end
+ else
+ # does not exist
+ new_daemons += entries
+ new_daemons << new_resource.service_name
+ end
+ update_daemons(new_daemons)
+ end
+ end
+
+ def disable_service()
+ new_daemons = []
+ entries = daemons
+
+ if entries.include?("!#{new_resource.service_name}")
+ # exists and disabled
+ # new_daemons += entries
+ else
+ if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
+ # exists but enabled (or enabled as a back-ground service)
+ # FIXME: Does arch support !@foobar ?
+ entries.each do |daemon|
+ if [new_resource.service_name, "@#{new_resource.service_name}"].include?(daemon)
+ new_daemons << "!#{new_resource.service_name}"
+ else
+ new_daemons << daemon
+ end
+ end
+ end
+ update_daemons(new_daemons)
+ end
+ end
+
+end
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
new file mode 100644
index 0000000000..e2a0f60d91
--- /dev/null
+++ b/lib/chef/provider/service/debian.rb
@@ -0,0 +1,152 @@
+#
+# Author:: AJ Christensen (<aj@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/init'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Debian < Chef::Provider::Service::Init
+ UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
+ UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
+
+ def load_current_resource
+ super
+ @priority_success = true
+ @rcd_status = nil
+ @current_resource.priority(get_priority)
+ @current_resource.enabled(service_currently_enabled?(@current_resource.priority))
+ @current_resource
+ end
+
+ def define_resource_requirements
+ # do not call super here, inherit only shared_requirements
+ shared_resource_requirements
+ requirements.assert(:all_actions) do |a|
+ update_rcd = "/usr/sbin/update-rc.d"
+ a.assertion { ::File.exists? update_rcd }
+ a.failure_message Chef::Exceptions::Service, "#{update_rcd} does not exist!"
+ # no whyrun recovery - this is a base system component of debian
+ # distros and must be present
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @priority_success }
+ a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} failed - #{@rcd_status.inspect}"
+ # This can happen if the service is not yet installed,so we'll fake it.
+ a.whyrun ["Unable to determine priority of service, assuming service would have been correctly installed earlier in the run.",
+ "Assigning temporary priorities to continue.",
+ "If this service is not properly installed prior to this point, this will fail."] do
+ temp_priorities = {"6"=>[:stop, "20"],
+ "0"=>[:stop, "20"],
+ "1"=>[:stop, "20"],
+ "2"=>[:start, "20"],
+ "3"=>[:start, "20"],
+ "4"=>[:start, "20"],
+ "5"=>[:start, "20"]}
+ @current_resource.priority(temp_priorities)
+ end
+ end
+ end
+
+ def get_priority
+ priority = {}
+
+ @rcd_status = popen4("/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} remove") do |pid, stdin, stdout, stderr|
+
+ [stdout, stderr].each do |iop|
+ iop.each_line do |line|
+ if UPDATE_RC_D_PRIORITIES =~ line
+ # priority[runlevel] = [ S|K, priority ]
+ # S = Start, K = Kill
+ # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot
+ priority[$1] = [($2 == "S" ? :start : :stop), $3]
+ end
+ if line =~ UPDATE_RC_D_ENABLED_MATCHES
+ enabled = true
+ end
+ end
+ end
+ end
+
+ unless @rcd_status.exitstatus == 0
+ @priority_success = false
+ end
+ priority
+ end
+
+ def service_currently_enabled?(priority)
+ enabled = false
+ priority.each { |runlevel, arguments|
+ Chef::Log.debug("#{@new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
+ # if we are in a update-rc.d default startup runlevel && we start in this runlevel
+ if (2..5).include?(runlevel.to_i) && arguments[0] == :start
+ enabled = true
+ end
+ }
+
+ enabled
+ end
+
+ def enable_service()
+ if @new_resource.priority.is_a? Integer
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
+ run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults #{@new_resource.priority} #{100 - @new_resource.priority}")
+ elsif @new_resource.priority.is_a? Hash
+ # we call the same command regardless of we're enabling or disabling
+ # users passing a Hash are responsible for setting their own start priorities
+ set_priority()
+ else # No priority, go with update-rc.d defaults
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
+ run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults")
+ end
+
+ end
+
+ def disable_service()
+ if @new_resource.priority.is_a? Integer
+ # Stop processes in reverse order of start using '100 - start_priority'
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop #{100 - @new_resource.priority} 2 3 4 5 .")
+ elsif @new_resource.priority.is_a? Hash
+ # we call the same command regardless of we're enabling or disabling
+ # users passing a Hash are responsible for setting their own stop priorities
+ set_priority()
+ else
+ # no priority, using '100 - 20 (update-rc.d default)' to stop in reverse order of start
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop 80 2 3 4 5 .")
+ end
+ end
+
+ def set_priority()
+ args = ""
+ @new_resource.priority.each do |level, o|
+ action = o[0]
+ priority = o[1]
+ args += "#{action} #{priority} #{level} . "
+ end
+ run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
+ run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} #{args}")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb
new file mode 100644
index 0000000000..b875838ec2
--- /dev/null
+++ b/lib/chef/provider/service/freebsd.rb
@@ -0,0 +1,175 @@
+#
+# Author:: Bryan McLellan (btm@loftninjas.org)
+# Copyright:: Copyright (c) 2009 Bryan McLellan
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/shell_out'
+require 'chef/provider/service'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Freebsd < Chef::Provider::Service::Init
+
+ include Chef::Mixin::ShellOut
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ @rcd_script_found = true
+ @enabled_state_found = false
+ # Determine if we're talking about /etc/rc.d or /usr/local/etc/rc.d
+ if ::File.exists?("/etc/rc.d/#{current_resource.service_name}")
+ @init_command = "/etc/rc.d/#{current_resource.service_name}"
+ elsif ::File.exists?("/usr/local/etc/rc.d/#{current_resource.service_name}")
+ @init_command = "/usr/local/etc/rc.d/#{current_resource.service_name}"
+ else
+ @rcd_script_found = false
+ return
+ end
+ Chef::Log.debug("#{@current_resource} found at #{@init_command}")
+ determine_current_status!
+ # Default to disabled if the service doesn't currently exist
+ # at all
+ var_name = service_enable_variable_name
+ if ::File.exists?("/etc/rc.conf") && var_name
+ read_rc_conf.each do |line|
+ case line
+ when /#{Regexp.escape(var_name)}="(\w+)"/
+ @enabled_state_found = true
+ if $1 =~ /[Yy][Ee][Ss]/
+ @current_resource.enabled true
+ elsif $1 =~ /[Nn][Oo][Nn]?[Oo]?[Nn]?[Ee]?/
+ @current_resource.enabled false
+ end
+ end
+ end
+ end
+ unless @current_resource.enabled
+ Chef::Log.debug("#{@new_resource.name} enable/disable state unknown")
+ @current_resource.enabled false
+ end
+
+ @current_resource
+ end
+
+ def define_resource_requirements
+ shared_resource_requirements
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { @rcd_script_found }
+ a.failure_message Chef::Exceptions::Service, "#{@new_resource}: unable to locate the rc.d script"
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @enabled_state_found }
+ # for consistentcy with original behavior, this will not fail in non-whyrun mode;
+ # rather it will silently set enabled state=>false
+ a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled."
+ end
+
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { @rcd_script_found && service_enable_variable_name != nil }
+ a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{@init_command} and rcvar"
+ # No recovery in whyrun mode - the init file is present but not correct.
+ end
+ end
+
+ def start_service
+ if @new_resource.start_command
+ super
+ else
+ shell_out!("#{@init_command} faststart")
+ end
+ end
+
+ def stop_service
+ if @new_resource.stop_command
+ super
+ else
+ shell_out!("#{@init_command} faststop")
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+
+ super
+ elsif @new_resource.supports[:restart]
+ shell_out!("#{@init_command} fastrestart")
+ else
+ stop_service
+ sleep 1
+ start_service
+ end
+ end
+
+ def read_rc_conf
+ ::File.open("/etc/rc.conf", 'r') { |file| file.readlines }
+ end
+
+ def write_rc_conf(lines)
+ ::File.open("/etc/rc.conf", 'w') do |file|
+ lines.each { |line| file.puts(line) }
+ end
+ end
+
+ # The variable name used in /etc/rc.conf for enabling this service
+ def service_enable_variable_name
+ # Look for name="foo" in the shell script @init_command. Use this for determining the variable name in /etc/rc.conf
+ # corresponding to this service
+ # For example: to enable the service mysql-server with the init command /usr/local/etc/rc.d/mysql-server, you need
+ # to set mysql_enable="YES" in /etc/rc.conf$
+ if @rcd_script_found
+ ::File.open(@init_command) do |rcscript|
+ rcscript.each_line do |line|
+ if line =~ /^name="?(\w+)"?/
+ return $1 + "_enable"
+ end
+ end
+ end
+ # some scripts support multiple instances through symlinks such as openvpn.
+ # We should get the service name from rcvar.
+ Chef::Log.debug("name=\"service\" not found at #{@init_command}. falling back to rcvar")
+ sn = shell_out!("#{@init_command} rcvar").stdout[/(\w+_enable)=/, 1]
+ return sn
+ end
+ # Fallback allows us to keep running in whyrun mode when
+ # the script does not exist.
+ @new_resource.service_name
+ end
+
+ def set_service_enable(value)
+ lines = read_rc_conf
+ # Remove line that set the old value
+ lines.delete_if { |line| line =~ /#{Regexp.escape(service_enable_variable_name)}/ }
+ # And append the line that sets the new value at the end
+ lines << "#{service_enable_variable_name}=\"#{value}\""
+ write_rc_conf(lines)
+ end
+
+ def enable_service()
+ set_service_enable("YES") unless @current_resource.enabled
+ end
+
+ def disable_service()
+ set_service_enable("NO") if @current_resource.enabled
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/gentoo.rb b/lib/chef/provider/service/gentoo.rb
new file mode 100644
index 0000000000..45b5a21f9b
--- /dev/null
+++ b/lib/chef/provider/service/gentoo.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Lee Jensen (<ljensen@engineyard.com>)
+# Author:: AJ Christensen (<aj@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/mixin/command'
+
+class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
+ def load_current_resource
+
+ @new_resource.supports[:status] = true
+ @new_resource.supports[:restart] = true
+ @found_script = false
+ super
+
+ @current_resource.enabled(
+ Dir.glob("/etc/runlevels/**/#{@current_resource.service_name}").any? do |file|
+ @found_script = true
+ exists = ::File.exists? file
+ readable = ::File.readable? file
+ Chef::Log.debug "#{@new_resource} exists: #{exists}, readable: #{readable}"
+ exists and readable
+ end
+ )
+ Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
+
+ @current_resource
+ end
+
+ def define_resource_requirements
+ requirements.assert(:all_actions) do |a|
+ a.assertion { ::File.exists?("/sbin/rc-update") }
+ a.failure_message Chef::Exceptions::Service, "/sbin/rc-update does not exist"
+ # no whyrun recovery -t his is a core component whose presence is
+ # unlikely to be affected by what we do in the course of a chef run
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @found_script }
+ # No failure, just informational output from whyrun
+ a.whyrun "Could not find service #{@new_resource.service_name} under any runlevel"
+ end
+ end
+
+ def enable_service()
+ run_command(:command => "/sbin/rc-update add #{@new_resource.service_name} default")
+ end
+
+ def disable_service()
+ run_command(:command => "/sbin/rc-update del #{@new_resource.service_name} default")
+ end
+end
diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb
new file mode 100644
index 0000000000..ab843d764d
--- /dev/null
+++ b/lib/chef/provider/service/init.rb
@@ -0,0 +1,87 @@
+#
+# Author:: AJ Christensen (<aj@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/shell_out'
+require 'chef/provider/service'
+require 'chef/provider/service/simple'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Init < Chef::Provider::Service::Simple
+
+ include Chef::Mixin::ShellOut
+
+ def initialize(new_resource, run_context)
+ super
+ @init_command = "/etc/init.d/#{@new_resource.service_name}"
+ end
+
+ def define_resource_requirements
+ # do not call super here, inherit only shared_requirements
+ shared_resource_requirements
+ requirements.assert(:start, :stop, :restart, :reload) do |a|
+ a.assertion { ::File.exist?(@init_command) }
+ a.failure_message(Chef::Exceptions::Service, "#{@init_command} does not exist!")
+ a.whyrun("Init script '#{@init_command}' doesn't exist, assuming a prior action would have created it.") do
+ # blindly assume that the service exists but is stopped in why run mode:
+ @status_load_success = false
+ end
+ end
+ end
+
+ def start_service
+ if @new_resource.start_command
+ super
+ else
+ shell_out!("#{@init_command} start")
+ end
+ end
+
+ def stop_service
+ if @new_resource.stop_command
+ super
+ else
+ shell_out!("#{@init_command} stop")
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ elsif @new_resource.supports[:restart]
+ shell_out!("#{@init_command} restart")
+ else
+ stop_service
+ sleep 1
+ start_service
+ end
+ end
+
+ def reload_service
+ if @new_resource.reload_command
+ super
+ elsif @new_resource.supports[:reload]
+ shell_out!("#{@init_command} reload")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
new file mode 100644
index 0000000000..32152376ee
--- /dev/null
+++ b/lib/chef/provider/service/insserv.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/init'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Insserv < Chef::Provider::Service::Init
+
+ def load_current_resource
+ super
+
+ # Look for a /etc/rc.*/SnnSERVICE link to signifiy that the service would be started in a runlevel
+ if Dir.glob("/etc/rc**/S*#{@current_resource.service_name}").empty?
+ @current_resource.enabled false
+ else
+ @current_resource.enabled true
+ end
+
+ @current_resource
+ end
+
+ def enable_service()
+ run_command(:command => "/sbin/insserv -r -f #{@new_resource.service_name}")
+ run_command(:command => "/sbin/insserv -d -f #{@new_resource.service_name}")
+ end
+
+ def disable_service()
+ run_command(:command => "/sbin/insserv -r -f #{@new_resource.service_name}")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb
new file mode 100644
index 0000000000..69a17bb4fb
--- /dev/null
+++ b/lib/chef/provider/service/invokercd.rb
@@ -0,0 +1,35 @@
+#
+# Author:: AJ Christensen (<aj@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/init'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Invokercd < Chef::Provider::Service::Init
+
+ def initialize(new_resource, run_context)
+ super
+ @init_command = "/usr/sbin/invoke-rc.d #{@new_resource.service_name}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
new file mode 100644
index 0000000000..72c02779c6
--- /dev/null
+++ b/lib/chef/provider/service/macosx.rb
@@ -0,0 +1,144 @@
+#
+# Author:: Igor Afonov <afonov@gmail.com>
+# Copyright:: Copyright (c) 2011 Igor Afonov
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+
+class Chef
+ class Provider
+ class Service
+ class Macosx < Chef::Provider::Service::Simple
+ include Chef::Mixin::ShellOut
+
+ PLIST_DIRS = %w{~/Library/LaunchAgents
+ /Library/LaunchAgents
+ /Library/LaunchDaemons
+ /System/Library/LaunchAgents
+ /System/Library/LaunchDaemons }
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ @plist_size = 0
+ @plist = find_service_plist
+ set_service_status
+
+ @current_resource
+ end
+
+ def define_resource_requirements
+ #super
+ requirements.assert(:enable) do |a|
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"
+ end
+
+ requirements.assert(:disable) do |a|
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"
+ end
+
+ requirements.assert(:reload) do |a|
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @plist_size < 2 }
+ a.failure_message Chef::Exceptions::Service, "Several plist files match service name. Please use full service name."
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @plist_size > 0 }
+ # No failrue here in original code - so we also will not
+ # fail. Instead warn that the service is potentially missing
+ a.whyrun "Assuming that the service would have been previously installed and is currently disabled." do
+ @current_resource.enabled(false)
+ @current_resource.running(false)
+ end
+ end
+
+ end
+
+ def start_service
+ if @current_resource.running
+ Chef::Log.debug("#{@new_resource} already running, not starting")
+ else
+ if @new_resource.start_command
+ super
+ else
+ shell_out!("launchctl load -w '#{@plist}'", :user => @owner_uid, :group => @owner_gid)
+ end
+ end
+ end
+
+ def stop_service
+ unless @current_resource.running
+ Chef::Log.debug("#{@new_resource} not running, not stopping")
+ else
+ if @new_resource.stop_command
+ super
+ else
+ shell_out!("launchctl unload '#{@plist}'", :user => @owner_uid, :group => @owner_gid)
+ end
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ else
+ stop_service
+ sleep 1
+ start_service
+ end
+ end
+
+
+ def set_service_status
+ return if @plist == nil
+
+ @current_resource.enabled(!@plist.nil?)
+
+ if @current_resource.enabled
+ @owner_uid = ::File.stat(@plist).uid
+ @owner_gid = ::File.stat(@plist).gid
+
+ shell_out!("launchctl list", :user => @owner_uid, :group => @owner_gid).stdout.each_line do |line|
+ case line
+ when /(\d+|-)\s+(?:\d+|-)\s+(.*\.?)#{@current_resource.service_name}/
+ pid = $1
+ @current_resource.running(!pid.to_i.zero?)
+ end
+ end
+ else
+ @current_resource.running(false)
+ end
+ end
+
+ private
+
+ def find_service_plist
+ plists = PLIST_DIRS.inject([]) do |results, dir|
+ entries = Dir.glob("#{::File.expand_path(dir)}/*#{@current_resource.service_name}*.plist")
+ entries.any? ? results << entries : results
+ end
+ plists.flatten!
+ @plist_size = plists.size
+ plists.first
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
new file mode 100644
index 0000000000..629e4ee0c3
--- /dev/null
+++ b/lib/chef/provider/service/redhat.rb
@@ -0,0 +1,77 @@
+#
+# Author:: AJ Christensen (<aj@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/init'
+require 'chef/mixin/shell_out'
+
+class Chef
+ class Provider
+ class Service
+ class Redhat < Chef::Provider::Service::Init
+ include Chef::Mixin::ShellOut
+
+ CHKCONFIG_ON = /\d:on/
+ CHKCONFIG_MISSING = /No such/
+
+ def initialize(new_resource, run_context)
+ super
+ @init_command = "/sbin/service #{@new_resource.service_name}"
+ @new_resource.supports[:status] = true
+ @service_missing = false
+ end
+
+ def define_resource_requirements
+ shared_resource_requirements
+
+ requirements.assert(:all_actions) do |a|
+ chkconfig_file = "/sbin/chkconfig"
+ a.assertion { ::File.exists? chkconfig_file }
+ a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} does not exist!"
+ end
+
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { !@service_missing }
+ a.failure_message Chef::Exceptions::Service, "#{@new_resource}: unable to locate the init.d script!"
+ a.whyrun "Assuming service would be disabled. The init script is not presently installed."
+ end
+ end
+
+ def load_current_resource
+ super
+
+ if ::File.exists?("/sbin/chkconfig")
+ chkconfig = shell_out!("/sbin/chkconfig --list #{@current_resource.service_name}", :returns => [0,1])
+ @current_resource.enabled(!!(chkconfig.stdout =~ CHKCONFIG_ON))
+ @service_missing = !!(chkconfig.stderr =~ CHKCONFIG_MISSING)
+ end
+
+ @current_resource
+ end
+
+ def enable_service()
+ shell_out! "/sbin/chkconfig #{@new_resource.service_name} on"
+ end
+
+ def disable_service()
+ shell_out! "/sbin/chkconfig #{@new_resource.service_name} off"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb
new file mode 100644
index 0000000000..670c62d480
--- /dev/null
+++ b/lib/chef/provider/service/simple.rb
@@ -0,0 +1,172 @@
+#
+# Author:: Mathieu Sauve-Frankel <msf@kisoku.net>
+# Copyright:: Copyright (c) 2009 Mathieu Sauve-Frankel
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/shell_out'
+require 'chef/provider/service'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Simple < Chef::Provider::Service
+
+ include Chef::Mixin::ShellOut
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+
+ @status_load_success = true
+ @ps_command_failed = false
+
+ determine_current_status!
+
+ @current_resource
+ end
+
+ def whyrun_supported?
+ true
+ end
+
+ def shared_resource_requirements
+ super
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @status_load_success }
+ a.whyrun ["Service status not available. Assuming a prior action would have installed the service.", "Assuming status of not running."]
+ end
+ end
+
+ def define_resource_requirements
+ # FIXME? need reload from service.rb
+ shared_resource_requirements
+ requirements.assert(:start) do |a|
+ a.assertion { @new_resource.start_command }
+ a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that start_command be set"
+ end
+ requirements.assert(:stop) do |a|
+ a.assertion { @new_resource.stop_command }
+ a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that stop_command be set"
+ end
+
+ requirements.assert(:restart) do |a|
+ a.assertion { @new_resource.restart_command || ( @new_resource.start_command && @new_resource.stop_command ) }
+ a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires a restart_command or both start_command and stop_command be set in order to perform a restart"
+ end
+
+ requirements.assert(:reload) do |a|
+ a.assertion { @new_resource.reload_command }
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} requires a reload_command be set in order to perform a reload"
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @new_resource.status_command or @new_resource.supports[:status] or
+ (!ps_cmd.nil? and !ps_cmd.empty?) }
+ a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute"
+ end
+ requirements.assert(:all_actions) do |a|
+ a.assertion { !@ps_command_failed }
+ a.failure_message Chef::Exceptions::Service, "Command #{ps_cmd} failed to execute, cannot determine service current status"
+ end
+ end
+
+ def start_service
+ shell_out!(@new_resource.start_command)
+ end
+
+ def stop_service
+ shell_out!(@new_resource.stop_command)
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ shell_out!(@new_resource.restart_command)
+ else
+ stop_service
+ sleep 1
+ start_service
+ end
+ end
+
+ def reload_service
+ shell_out!(@new_resource.reload_command)
+ end
+
+ protected
+ def determine_current_status!
+ if @new_resource.status_command
+ Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+
+ begin
+ if shell_out(@new_resource.status_command).exitstatus == 0
+ @current_resource.running true
+ Chef::Log.debug("#{@new_resource} is running")
+ end
+ rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError
+ # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
+ # Temporarily catching different types of exceptions here until we get Shellout fixed.
+ # TODO: Remove the line before one we get the ShellOut fix.
+ @status_load_success = false
+ @current_resource.running false
+ nil
+ end
+
+ elsif @new_resource.supports[:status]
+ Chef::Log.debug("#{@new_resource} supports status, running")
+ begin
+ if shell_out("#{@init_command} status").exitstatus == 0
+ @current_resource.running true
+ Chef::Log.debug("#{@new_resource} is running")
+ end
+ # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
+ # Temporarily catching different types of exceptions here until we get Shellout fixed.
+ # TODO: Remove the line before one we get the ShellOut fix.
+ rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError
+ @status_load_success = false
+ @current_resource.running false
+ nil
+ end
+ else
+ Chef::Log.debug "#{@new_resource} falling back to process table inspection"
+ r = Regexp.new(@new_resource.pattern)
+ Chef::Log.debug "#{@new_resource} attempting to match '#{@new_resource.pattern}' (#{r.inspect}) against process list"
+ begin
+ shell_out!(ps_cmd).stdout.each_line do |line|
+ if r.match(line)
+ @current_resource.running true
+ break
+ end
+ end
+
+ @current_resource.running false unless @current_resource.running
+ Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
+ # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
+ # Temporarily catching different types of exceptions here until we get Shellout fixed.
+ # TODO: Remove the line before one we get the ShellOut fix.
+ rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError
+ @ps_command_failed = true
+ end
+ end
+ end
+
+ def ps_cmd
+ @run_context.node[:command] && @run_context.node[:command][:ps]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
new file mode 100644
index 0000000000..8e131590e8
--- /dev/null
+++ b/lib/chef/provider/service/solaris.rb
@@ -0,0 +1,86 @@
+#
+# Author:: Toomas Pelberg (<toomasp@gmx.net>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/mixin/command'
+
+class Chef
+ class Provider
+ class Service
+ class Solaris < Chef::Provider::Service
+
+ def initialize(new_resource, run_context=nil)
+ super
+ @init_command = "/usr/sbin/svcadm"
+ @status_command = "/bin/svcs -l"
+ end
+
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ unless ::File.exists? "/bin/svcs"
+ raise Chef::Exceptions::Service, "/bin/svcs does not exist!"
+ end
+ @status = service_status.enabled
+ @current_resource
+ end
+
+ def enable_service
+ run_command(:command => "#{@init_command} enable #{@new_resource.service_name}")
+ return service_status.enabled
+ end
+
+ def disable_service
+ run_command(:command => "#{@init_command} disable #{@new_resource.service_name}")
+ return service_status.enabled
+ end
+
+ alias_method :stop_service, :disable_service
+ alias_method :start_service, :enable_service
+
+ def reload_service
+ run_command(:command => "#{@init_command} refresh #{@new_resource.service_name}")
+ end
+
+ def restart_service
+ disable_service
+ return enable_service
+ end
+
+ def service_status
+ status = popen4("#{@status_command} #{@current_resource.service_name}") do |pid, stdin, stdout, stderr|
+ stdout.each do |line|
+ case line
+ when /state\s+online/
+ @current_resource.enabled(true)
+ @current_resource.running(true)
+ end
+ end
+ end
+ unless @current_resource.enabled
+ @current_resource.enabled(false)
+ @current_resource.running(false)
+ end
+ @current_resource
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb
new file mode 100644
index 0000000000..59b4fe1564
--- /dev/null
+++ b/lib/chef/provider/service/systemd.rb
@@ -0,0 +1,115 @@
+#
+# Author:: Stephen Haynes (<sh@nomitor.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/simple'
+require 'chef/mixin/command'
+
+class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ @status_check_success = true
+
+ if @new_resource.status_command
+ Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+
+ begin
+ if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
+ @current_resource.running(true)
+ end
+ rescue Chef::Exceptions::Exec
+ @status_check_success = false
+ @current_resource.running(false)
+ @current_resource.enabled(false)
+ nil
+ end
+ else
+ @current_resource.running(is_active?)
+ end
+
+ @current_resource.enabled(is_enabled?)
+ @current_resource
+ end
+
+ def define_resource_requirements
+ shared_resource_requirements
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @status_check_success }
+ # We won't stop in any case, but in whyrun warn and tell what we're doing.
+ a.whyrun ["Failed to determine status of #{@new_resource}, using command #{@new_resource.status_command}.",
+ "Assuming service would have been installed and is disabled"]
+ end
+ end
+
+ def start_service
+ if @current_resource.running
+ Chef::Log.debug("#{@new_resource} already running, not starting")
+ else
+ if @new_resource.start_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl start #{@new_resource.service_name}")
+ end
+ end
+ end
+
+ def stop_service
+ unless @current_resource.running
+ Chef::Log.debug("#{@new_resource} not running, not stopping")
+ else
+ if @new_resource.stop_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl stop #{@new_resource.service_name}")
+ end
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl restart #{@new_resource.service_name}")
+ end
+ end
+
+ def reload_service
+ if @new_resource.reload_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl reload #{@new_resource.service_name}")
+ end
+ end
+
+ def enable_service
+ run_command_with_systems_locale(:command => "/bin/systemctl enable #{@new_resource.service_name}")
+ end
+
+ def disable_service
+ run_command_with_systems_locale(:command => "/bin/systemctl disable #{@new_resource.service_name}")
+ end
+
+ def is_active?
+ run_command_with_systems_locale({:command => "/bin/systemctl is-active #{@new_resource.service_name}", :ignore_failure => true}) == 0
+ end
+
+ def is_enabled?
+ run_command_with_systems_locale({:command => "/bin/systemctl is-enabled #{@new_resource.service_name}", :ignore_failure => true}) == 0
+ end
+end
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
new file mode 100644
index 0000000000..763a2aa92b
--- /dev/null
+++ b/lib/chef/provider/service/upstart.rb
@@ -0,0 +1,232 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 2010 Bryan McLellan
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/simple'
+require 'chef/mixin/command'
+require 'chef/util/file_edit'
+
+class Chef
+ class Provider
+ class Service
+ class Upstart < Chef::Provider::Service::Simple
+ UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/
+
+ # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in.
+ # In chef, when we ask a service to start, we expect it to have started before performing the next step
+ # since we have top down dependencies. Which is to say we may follow witha resource next that requires
+ # that service to be running. According to [2] we can trust that sending a 'goal' such as start will not
+ # return until that 'goal' is reached, or some error has occured.
+ #
+ # [1] http://upstart.ubuntu.com/wiki/JobStates
+ # [2] http://www.netsplit.com/2008/04/27/upstart-05-events/
+
+ def initialize(new_resource, run_context)
+ # TODO: re-evaluate if this is needed after integrating cookbook fix
+ raise ArgumentError, "run_context cannot be nil" unless run_context
+ super
+
+ run_context.node
+
+ @job = @new_resource.service_name
+
+ if @new_resource.parameters
+ @new_resource.parameters.each do |key, value|
+ @job << " #{key}=#{value}"
+ end
+ end
+
+ platform, version = Chef::Platform.find_platform_and_version(run_context.node)
+ if platform == "ubuntu" && (8.04..9.04).include?(version.to_f)
+ @upstart_job_dir = "/etc/event.d"
+ @upstart_conf_suffix = ""
+ else
+ @upstart_job_dir = "/etc/init"
+ @upstart_conf_suffix = ".conf"
+ end
+
+ @command_success = true # new_resource.status_command= false, means upstart used
+ @config_file_found = true
+ @upstart_command_success = true
+ end
+
+ def define_resource_requirements
+ # Do not call super, only call shared requirements
+ shared_resource_requirements
+ requirements.assert(:all_actions) do |a|
+ if !@command_success
+ whyrun_msg = @new_resource.status_command ? "Provided status command #{@new_resource.status_command} failed." :
+ "Could not determine upstart state for service"
+ end
+ a.assertion { @command_success }
+ # no failure here, just document the assumptions made.
+ a.whyrun "#{whyrun_msg} Assuming service installed and not running."
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @config_file_found }
+ # no failure here, just document the assumptions made.
+ a.whyrun "Could not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}. Assuming service is disabled."
+ end
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+
+ # Get running/stopped state
+ # We do not support searching for a service via ps when using upstart since status is a native
+ # upstart function. We will however support status_command in case someone wants to do something special.
+ if @new_resource.status_command
+ Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+
+ begin
+ if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
+ @current_resource.running true
+ end
+ rescue Chef::Exceptions::Exec
+ @command_success = false
+ @current_resource.running false
+ nil
+ end
+ else
+ begin
+ if upstart_state == "running"
+ @current_resource.running true
+ else
+ @current_resource.running false
+ end
+ rescue Chef::Exceptions::Exec
+ @command_success = false
+ @current_resource.running false
+ nil
+ end
+ end
+ # Get enabled/disabled state by reading job configuration file
+ if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ Chef::Log.debug("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}",'r') do |file|
+ while line = file.gets
+ case line
+ when /^start on/
+ Chef::Log.debug("#{@new_resource} enabled: #{line.chomp}")
+ @current_resource.enabled true
+ break
+ when /^#start on/
+ Chef::Log.debug("#{@new_resource} disabled: #{line.chomp}")
+ @current_resource.enabled false
+ break
+ end
+ end
+ end
+ else
+ @config_file_found = false
+ Chef::Log.debug("#{@new_resource} did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ @current_resource.enabled false
+ end
+
+ @current_resource
+ end
+
+ def start_service
+ # Calling start on a service that is already started will return 1
+ # Our 'goal' when we call start is to ensure the service is started
+ if @current_resource.running
+ Chef::Log.debug("#{@new_resource} already running, not starting")
+ else
+ if @new_resource.start_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/sbin/start #{@job}")
+ end
+ end
+ end
+
+ def stop_service
+ # Calling stop on a service that is already stopped will return 1
+ # Our 'goal' when we call stop is to ensure the service is stopped
+ unless @current_resource.running
+ Chef::Log.debug("#{@new_resource} not running, not stopping")
+ else
+ if @new_resource.stop_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/sbin/stop #{@job}")
+ end
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ # Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start.
+ # Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883
+ else @new_resource.supports[:restart]
+ if @current_resource.running
+ run_command_with_systems_locale(:command => "/sbin/restart #{@job}")
+ else
+ start_service
+ end
+ end
+ end
+
+ def reload_service
+ if @new_resource.reload_command
+ super
+ else
+ # upstart >= 0.6.3-4 supports reload (HUP)
+ run_command_with_systems_locale(:command => "/sbin/reload #{@job}")
+ end
+ end
+
+ # https://bugs.launchpad.net/upstart/+bug/94065
+
+ def enable_service
+ Chef::Log.debug("#{@new_resource} upstart lacks inherent support for enabling services, editing job config file")
+ conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ conf.search_file_replace(/^#start on/, "start on")
+ conf.write_file
+ end
+
+ def disable_service
+ Chef::Log.debug("#{@new_resource} upstart lacks inherent support for disabling services, editing job config file")
+ conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ conf.search_file_replace(/^start on/, "#start on")
+ conf.write_file
+ end
+
+ def upstart_state
+ command = "/sbin/status #{@job}"
+ status = popen4(command) do |pid, stdin, stdout, stderr|
+ stdout.each_line do |line|
+ # rsyslog stop/waiting
+ # service goal/state
+ # OR
+ # rsyslog (stop) waiting
+ # service (goal) state
+ line =~ UPSTART_STATE_FORMAT
+ data = Regexp.last_match
+ return data[2]
+ end
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb
new file mode 100644
index 0000000000..ba51e53bed
--- /dev/null
+++ b/lib/chef/provider/service/windows.rb
@@ -0,0 +1,163 @@
+#
+# Author:: Nuo Yan <nuo@opscode.com>
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Author:: Seth Chisamore <schisamo@opscode.com>
+# Copyright:: Copyright (c) 2010-2011 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/shell_out'
+require 'chef/provider/service/simple'
+if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ require 'win32/service'
+end
+
+class Chef::Provider::Service::Windows < Chef::Provider::Service
+
+ include Chef::Mixin::ShellOut
+
+ RUNNING = 'running'
+ STOPPED = 'stopped'
+ AUTO_START = 'auto start'
+ DISABLED = 'disabled'
+
+ def whyrun_supported?
+ false
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ @current_resource.running(current_state == RUNNING)
+ Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
+ @current_resource.enabled(start_type == AUTO_START)
+ Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
+ @current_resource
+ end
+
+ def start_service
+ if Win32::Service.exists?(@new_resource.service_name)
+ if current_state == RUNNING
+ Chef::Log.debug "#{@new_resource} already started - nothing to do"
+ else
+ if @new_resource.start_command
+ Chef::Log.debug "#{@new_resource} starting service using the given start_command"
+ shell_out!(@new_resource.start_command)
+ else
+ spawn_command_thread do
+ Win32::Service.start(@new_resource.service_name)
+ wait_for_state(RUNNING)
+ end
+ end
+ @new_resource.updated_by_last_action(true)
+ end
+ else
+ Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ def stop_service
+ if Win32::Service.exists?(@new_resource.service_name)
+ if current_state == RUNNING
+ if @new_resource.stop_command
+ Chef::Log.debug "#{@new_resource} stopping service using the given stop_command"
+ shell_out!(@new_resource.stop_command)
+ else
+ spawn_command_thread do
+ Win32::Service.stop(@new_resource.service_name)
+ wait_for_state(STOPPED)
+ end
+ end
+ @new_resource.updated_by_last_action(true)
+ else
+ Chef::Log.debug "#{@new_resource} already stopped - nothing to do"
+ end
+ else
+ Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ def restart_service
+ if Win32::Service.exists?(@new_resource.service_name)
+ if @new_resource.restart_command
+ Chef::Log.debug "#{@new_resource} restarting service using the given restart_command"
+ shell_out!(@new_resource.restart_command)
+ else
+ stop_service
+ start_service
+ end
+ @new_resource.updated_by_last_action(true)
+ else
+ Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ def enable_service
+ if Win32::Service.exists?(@new_resource.service_name)
+ if start_type == AUTO_START
+ Chef::Log.debug "#{@new_resource} already enabled - nothing to do"
+ else
+ Win32::Service.configure(
+ :service_name => @new_resource.service_name,
+ :start_type => Win32::Service::AUTO_START
+ )
+ @new_resource.updated_by_last_action(true)
+ end
+ else
+ Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ def disable_service
+ if Win32::Service.exists?(@new_resource.service_name)
+ if start_type == AUTO_START
+ Win32::Service.configure(
+ :service_name => @new_resource.service_name,
+ :start_type => Win32::Service::DISABLED
+ )
+ @new_resource.updated_by_last_action(true)
+ else
+ Chef::Log.debug "#{@new_resource} already disabled - nothing to do"
+ end
+ else
+ Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ private
+ def current_state
+ Win32::Service.status(@new_resource.service_name).current_state
+ end
+
+ def start_type
+ Win32::Service.config_info(@new_resource.service_name).start_type
+ end
+
+ # Helper method that waits for a status to change its state since state
+ # changes aren't usually instantaneous.
+ def wait_for_state(desired_state)
+ sleep 1 until current_state == desired_state
+ end
+
+ # There ain't no party like a thread party...
+ def spawn_command_thread
+ worker = Thread.new do
+ yield
+ end
+ Timeout.timeout(60) do
+ worker.join
+ end
+ end
+end