summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom May <thom@may.lt>2018-01-22 18:44:51 +0000
committerGitHub <noreply@github.com>2018-01-22 18:44:51 +0000
commit91c1ab1013eb3ca31c0a285d8395b2e5f03505a9 (patch)
tree10d8a1c8b89b69127d60639d50e9e7d13f86927c
parentee7cba011d45c23c7afcda1299749d0540210178 (diff)
parentb1b889406efaf65940726c5f6ee2316b785fa600 (diff)
downloadchef-91c1ab1013eb3ca31c0a285d8395b2e5f03505a9.tar.gz
Merge pull request #6595 from jasonwbarnett/feature/add-action_create-to-windows_service
add create and delete actions for windows_service
-rw-r--r--lib/chef/provider/service/windows.rb186
-rw-r--r--lib/chef/resource/windows_service.rb105
-rw-r--r--lib/chef/win32_service_constants.rb143
3 files changed, 390 insertions, 44 deletions
diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb
index 931e320695..8503bd684d 100644
--- a/lib/chef/provider/service/windows.rb
+++ b/lib/chef/provider/service/windows.rb
@@ -19,6 +19,7 @@
#
require "chef/provider/service/simple"
+require "chef/win32_service_constants"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
require "chef/win32/error"
require "win32/service"
@@ -30,6 +31,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
include Chef::Mixin::ShellOut
include Chef::ReservedNames::Win32::API::Error rescue LoadError
+ include Chef::Win32ServiceConstants
#Win32::Service.get_start_type
AUTO_START = "auto start"
@@ -50,18 +52,36 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
SERVICE_RIGHT = "SeServiceLogonRight"
def load_current_resource
- @current_resource = Chef::Resource::WindowsService.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}"
- case current_start_type
- when AUTO_START
- @current_resource.enabled(true)
- when DISABLED
- @current_resource.enabled(false)
+ @current_resource = Chef::Resource::WindowsService.new(new_resource.name)
+ current_resource.service_name(new_resource.service_name)
+
+ if Win32::Service.exists?(current_resource.service_name)
+ current_resource.running(current_state == RUNNING)
+ Chef::Log.debug "#{new_resource} running: #{current_resource.running}"
+ case current_start_type
+ when AUTO_START
+ current_resource.enabled(true)
+ when DISABLED
+ current_resource.enabled(false)
+ end
+ Chef::Log.debug "#{new_resource} enabled: #{current_resource.enabled}"
+
+ config_info = Win32::Service.config_info(current_resource.service_name)
+ current_resource.service_type(get_service_type(config_info[:service_type])) if config_info[:service_type]
+ current_resource.startup_type(get_start_type(config_info[:start_type])) if config_info[:start_type]
+ current_resource.error_control(get_error_control(config_info[:error_control])) if config_info[:error_control]
+ current_resource.binary_path_name(config_info[:binary_path_name]) if config_info[:binary_path_name]
+ current_resource.load_order_group(config_info[:load_order_group]) if config_info[:load_order_group]
+ current_resource.dependencies(config_info[:dependencies]) if config_info[:dependencies]
+ current_resource.run_as_user(config_info[:service_start_name]) if config_info[:service_start_name]
+ current_resource.display_name(config_info[:display_name]) if config_info[:display_name]
+
+ if delayed_start = current_delayed_start
+ current_resource.delayed_start(delayed_start)
+ end
end
- Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
- @current_resource
+
+ current_resource
end
def start_service
@@ -175,6 +195,44 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
end
+ action :create do
+ return if Win32::Service.exists?(new_resource.service_name)
+
+ converge_by("create service #{new_resource.service_name}") do
+ Win32::Service.new(windows_service_config)
+ end
+
+ converge_delayed_start
+ end
+
+ action :delete do
+ return unless Win32::Service.exists?(new_resource.service_name)
+
+ converge_by("delete service #{new_resource.service_name}") do
+ Win32::Service.delete(new_resource.service_name)
+ end
+ end
+
+ action :configure do
+ unless Win32::Service.exists?(new_resource.service_name)
+ Chef::Log.debug "#{new_resource} does not exist - nothing to do"
+ return
+ end
+
+ # Until #6300 is solved this is required
+ if new_resource.run_as_user == new_resource.class.properties[:run_as_user].default
+ new_resource.run_as_user = new_resource.class.properties[:run_as_user].default
+ end
+
+ converge_if_changed :service_type, :startup_type, :error_control,
+ :binary_path_name, :load_order_group, :dependencies,
+ :run_as_user, :display_name do
+ Win32::Service.configure(windows_service_config(:configure))
+ end
+
+ converge_delayed_start
+ end
+
def action_enable
if current_start_type != AUTO_START
converge_by("enable service #{@new_resource}") do
@@ -264,6 +322,14 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
Win32::Service.config_info(@new_resource.service_name).start_type
end
+ def current_delayed_start
+ if service = Win32::Service.services.find { |x| x.service_name == @new_resource.service_name }
+ service.delayed_start
+ else
+ nil
+ end
+ 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)
@@ -306,4 +372,102 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
)
@new_resource.updated_by_last_action(true)
end
+
+ def windows_service_config(action = :create)
+ config = {}
+
+ config[:service_name] = new_resource.service_name
+ config[:display_name] = new_resource.display_name if new_resource.display_name
+ config[:service_type] = new_resource.service_type if new_resource.service_type
+ config[:start_type] = new_resource.startup_type if new_resource.startup_type
+ config[:error_control] = new_resource.error_control if new_resource.error_control
+ config[:binary_path_name] = new_resource.binary_path_name if new_resource.binary_path_name
+ config[:load_order_group] = new_resource.load_order_group if new_resource.load_order_group
+ config[:dependencies] = new_resource.dependencies if new_resource.dependencies
+ config[:service_start_name] = new_resource.run_as_user unless new_resource.run_as_user.empty?
+ config[:password] = new_resource.run_as_password unless new_resource.run_as_user.empty? or new_resource.run_as_password.empty?
+ config[:description] = new_resource.description if new_resource.description
+
+ case action
+ when :create
+ config[:desired_access] = new_resource.desired_access if new_resource.desired_access
+ end
+
+ config
+ end
+
+ def converge_delayed_start
+ config = {}
+ config[:service_name] = new_resource.service_name
+ config[:delayed_start] = new_resource.delayed_start
+
+ # Until #6300 is solved this is required
+ if new_resource.delayed_start == new_resource.class.properties[:delayed_start].default
+ new_resource.delayed_start = new_resource.class.properties[:delayed_start].default
+ end
+
+ converge_if_changed :delayed_start do
+ Win32::Service.configure(config)
+ end
+ end
+
+ def get_service_type(service_type)
+ case service_type
+ when 'file system driver'
+ SERVICE_FILE_SYSTEM_DRIVER
+ when 'kernel driver'
+ SERVICE_KERNEL_DRIVER
+ when 'own process'
+ SERVICE_WIN32_OWN_PROCESS
+ when 'share proces'
+ SERVICE_WIN32_SHARE_PROCESS
+ when 'recognizer driver'
+ SERVICE_RECOGNIZER_DRIVER
+ when 'driver'
+ SERVICE_DRIVER
+ when 'win32'
+ SERVICE_WIN32
+ when 'all'
+ SERVICE_TYPE_ALL
+ when 'own process, interactive'
+ SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_OWN_PROCESS
+ when 'share process, interactive'
+ SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_SHARE_PROCESS
+ else
+ nil
+ end
+ end
+
+ def get_start_type(start_type)
+ case start_type
+ when 'auto start'
+ SERVICE_AUTO_START
+ when 'boot start'
+ SERVICE_BOOT_START
+ when 'demand start'
+ SERVICE_DEMAND_START
+ when 'disabled'
+ SERVICE_DISABLED
+ when 'system start'
+ SERVICE_SYSTEM_START
+ else
+ nil
+ end
+ end
+
+ def get_error_control(error_control)
+ case error_control
+ when 'critical'
+ SERVICE_ERROR_CRITICAL
+ when 'ignore'
+ SERVICE_ERROR_IGNORE
+ when 'normal'
+ SERVICE_ERROR_NORMAL
+ when 'severe'
+ SERVICE_ERROR_SEVERE
+ else
+ nil
+ end
+ end
+
end
diff --git a/lib/chef/resource/windows_service.rb b/lib/chef/resource/windows_service.rb
index 1c7c61320f..bcd0272b06 100644
--- a/lib/chef/resource/windows_service.rb
+++ b/lib/chef/resource/windows_service.rb
@@ -17,6 +17,7 @@
#
require "chef/resource/service"
+require "chef/win32_service_constants"
class Chef
class Resource
@@ -24,6 +25,13 @@ class Chef
#
# @since 12.0
class WindowsService < Chef::Resource::Service
+ include Chef::Win32ServiceConstants
+
+ ALLOWED_START_TYPES = {
+ :automatic => SERVICE_AUTO_START,
+ :manual => SERVICE_DEMAND_START,
+ :disabled => SERVICE_DISABLED,
+ }
# Until #1773 is resolved, you need to manually specify the windows_service resource
# to use action :configure_startup and attribute startup_type
@@ -31,44 +39,75 @@ class Chef
provides :windows_service, os: "windows"
provides :service, os: "windows"
- allowed_actions :configure_startup
+ allowed_actions :configure_startup, :create, :delete, :configure
identity_attr :service_name
state_attrs :enabled, :running
- def initialize(name, run_context = nil)
- super
- @startup_type = :automatic
- @run_as_user = ""
- @run_as_password = ""
- end
-
- def startup_type(arg = nil)
- # Set-Service arguments are automatic and manual
- # Win32::Service returns 'auto start' or 'demand start' respectively, which the provider currently uses
- set_or_return(
- :startup_type,
- arg,
- :equal_to => [ :automatic, :manual, :disabled ]
- )
- end
-
- def run_as_user(arg = nil)
- set_or_return(
- :run_as_user,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def run_as_password(arg = nil)
- set_or_return(
- :run_as_password,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :service_name, name_property: true
+
+ # The display name to be used by user interface programs to identify the
+ # service. This string has a maximum length of 256 characters.
+ property :display_name, String, regex: /^.{1,256}$/
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L19-L29
+ property :desired_access, Integer, default: SERVICE_ALL_ACCESS
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L31-L41
+ property :service_type, Integer, default: SERVICE_WIN32_OWN_PROCESS
+
+ # Valid options:
+ # - :automatic
+ # - :manual
+ # - :disabled
+ # Reference: https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L49-L54
+ property :startup_type, Integer, default: SERVICE_AUTO_START, coerce: proc { |x|
+ if x.is_a?(Integer)
+ x
+ elsif x.is_a?(String) or x.is_a?(Symbol)
+ x = x.to_sym
+ ALLOWED_START_TYPES.fetch(x) do
+ Chef::Log.warn("Unsupported startup_type #{x}, falling back to :automatic")
+ SERVICE_AUTO_START
+ end
+ end
+ }
+
+ # This only applies if startup_type is :automatic
+ property :delayed_start, [Integer], default: false, coerce: proc { |x|
+ if x.is_a?(TrueClass)
+ 1
+ elsif x.is_a?(FalseClass)
+ 0
+ elsif x.is_a?(Integer)
+ x.zero? ? 0 : 1
+ end
+ }
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L43-L47
+ property :error_control, Integer, default: SERVICE_ERROR_NORMAL
+
+ # The fully qualified path to the service binary file. The path can also
+ # include arguments for an auto-start service.
+ property :binary_path_name, String, required: true
+
+ # The names of the load ordering group of which this service is a member.
+ # Specify nil or an empty string if the service does not belong to a group.
+ property :load_order_group, String
+
+ # A pointer to a double null-terminated array of null-separated names of
+ # services or load ordering groups that the system must start before this
+ # service. Specify nil or an empty string if the service has no
+ # dependencies. Dependency on a group means that this service can run if
+ # at least one member of the group is running after an attempt to start
+ # all members of the group.
+ property :dependencies, [String, Array]
+
+ property :description, String
+
+ property :run_as_user, String, default: 'LocalSystem'
+ property :run_as_password, String, default: ''
end
end
end
diff --git a/lib/chef/win32_service_constants.rb b/lib/chef/win32_service_constants.rb
new file mode 100644
index 0000000000..4b5eb34327
--- /dev/null
+++ b/lib/chef/win32_service_constants.rb
@@ -0,0 +1,143 @@
+class Chef
+ module Win32ServiceConstants
+ SC_MANAGER_ALL_ACCESS = 0xF003F
+ SC_MANAGER_CREATE_SERVICE = 0x0002
+ SC_MANAGER_CONNECT = 0x0001
+ SC_MANAGER_ENUMERATE_SERVICE = 0x0004
+ SC_MANAGER_LOCK = 0x0008
+ SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020
+ SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
+ SC_STATUS_PROCESS_INFO = 0
+ SC_ENUM_PROCESS_INFO = 0
+
+ # Service control action types
+ SC_ACTION_NONE = 0
+ SC_ACTION_RESTART = 1
+ SC_ACTION_REBOOT = 2
+ SC_ACTION_RUN_COMMAND = 3
+
+ # Service access rights
+ SERVICE_ALL_ACCESS = 0xF01FF
+ SERVICE_CHANGE_CONFIG = 0x0002
+ SERVICE_ENUMERATE_DEPENDENTS = 0x0008
+ SERVICE_INTERROGATE = 0x0080
+ SERVICE_PAUSE_CONTINUE = 0x0040
+ SERVICE_QUERY_CONFIG = 0x0001
+ SERVICE_QUERY_STATUS = 0x0004
+ SERVICE_START = 0x0010
+ SERVICE_STOP = 0x0020
+ SERVICE_USER_DEFINED_CONTROL = 0x0100
+
+ # Service types
+ SERVICE_KERNEL_DRIVER = 0x00000001
+ SERVICE_FILE_SYSTEM_DRIVER = 0x00000002
+ SERVICE_ADAPTER = 0x00000004
+ SERVICE_RECOGNIZER_DRIVER = 0x00000008
+ SERVICE_WIN32_OWN_PROCESS = 0x00000010
+ SERVICE_WIN32_SHARE_PROCESS = 0x00000020
+ SERVICE_WIN32 = 0x00000030
+ SERVICE_INTERACTIVE_PROCESS = 0x00000100
+ SERVICE_DRIVER = 0x0000000B
+ SERVICE_TYPE_ALL = 0x0000013F
+
+ # Error control
+ SERVICE_ERROR_IGNORE = 0x00000000
+ SERVICE_ERROR_NORMAL = 0x00000001
+ SERVICE_ERROR_SEVERE = 0x00000002
+ SERVICE_ERROR_CRITICAL = 0x00000003
+
+ # Start types
+ SERVICE_BOOT_START = 0x00000000
+ SERVICE_SYSTEM_START = 0x00000001
+ SERVICE_AUTO_START = 0x00000002
+ SERVICE_DEMAND_START = 0x00000003
+ SERVICE_DISABLED = 0x00000004
+
+ # Service control
+
+ SERVICE_CONTROL_STOP = 0x00000001
+ SERVICE_CONTROL_PAUSE = 0x00000002
+ SERVICE_CONTROL_CONTINUE = 0x00000003
+ SERVICE_CONTROL_INTERROGATE = 0x00000004
+ SERVICE_CONTROL_SHUTDOWN = 0x00000005
+ SERVICE_CONTROL_PARAMCHANGE = 0x00000006
+ SERVICE_CONTROL_NETBINDADD = 0x00000007
+ SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
+ SERVICE_CONTROL_NETBINDENABLE = 0x00000009
+ SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
+ SERVICE_CONTROL_DEVICEEVENT = 0x0000000B
+ SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0x0000000C
+ SERVICE_CONTROL_POWEREVENT = 0x0000000D
+ SERVICE_CONTROL_SESSIONCHANGE = 0x0000000E
+ SERVICE_CONTROL_PRESHUTDOWN = 0x0000000F
+ SERVICE_CONTROL_TIMECHANGE = 0x00000010
+ SERVICE_CONTROL_TRIGGEREVENT = 0x00000020
+
+ # Service controls accepted
+
+ SERVICE_ACCEPT_STOP = 0x00000001
+ SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
+ SERVICE_ACCEPT_SHUTDOWN = 0x00000004
+ SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
+ SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
+ SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
+ SERVICE_ACCEPT_POWEREVENT = 0x00000040
+ SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
+ SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
+ SERVICE_ACCEPT_TIMECHANGE = 0x00000200
+ SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400
+
+ # Service states
+ SERVICE_ACTIVE = 0x00000001
+ SERVICE_INACTIVE = 0x00000002
+ SERVICE_STATE_ALL = 0x00000003
+
+ # Service current states
+ SERVICE_STOPPED = 0x00000001
+ SERVICE_START_PENDING = 0x00000002
+ SERVICE_STOP_PENDING = 0x00000003
+ SERVICE_RUNNING = 0x00000004
+ SERVICE_CONTINUE_PENDING = 0x00000005
+ SERVICE_PAUSE_PENDING = 0x00000006
+ SERVICE_PAUSED = 0x00000007
+
+ # Info levels
+ SERVICE_CONFIG_DESCRIPTION = 1
+ SERVICE_CONFIG_FAILURE_ACTIONS = 2
+ SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3
+ SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
+ SERVICE_CONFIG_SERVICE_SID_INFO = 5
+ SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6
+ SERVICE_CONFIG_PRESHUTDOWN_INFO = 7
+
+ # Configuration
+ SERVICE_NO_CHANGE = 0xffffffff
+
+ # Misc
+
+ WAIT_OBJECT_0 = 0
+ WAIT_TIMEOUT = 0x00000102
+ INFINITE = 0xFFFFFFFF
+
+ IDLE_CONTROL_CODE = 0
+
+ DELETE = 0x00010000
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
+ FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
+
+ NO_ERROR = 0
+
+ SE_PRIVILEGE_ENABLED = 0x00000002
+ TOKEN_ADJUST_PRIVILEGES = 0x0020
+ TOKEN_QUERY = 0x0008
+
+ # Errors
+
+ ERROR_INSUFFICIENT_BUFFER = 122
+ ERROR_MORE_DATA = 234
+ ERROR_FILE_NOT_FOUND = 2
+ ERROR_RESOURCE_TYPE_NOT_FOUND = 1813
+ ERROR_RESOURCE_NAME_NOT_FOUND = 1814
+ WAIT_FAILED = 0xFFFFFFFF
+ end
+end