Skip to content

Commit 6d084ee

Browse files
authored
Merge pull request #7214 from McdonaldSeanp/PUP-9292-2
(PUP-9292) Change windows service query functions to take blocks
2 parents 72f898a + 3cdf2ed commit 6d084ee

File tree

1 file changed

+105
-98
lines changed

1 file changed

+105
-98
lines changed

lib/puppet/util/windows/service.rb

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,14 @@ def resume(service_name)
360360
# @param [:string] service_name name of the service to query
361361
# @return [string] the status of the service
362362
def service_state(service_name)
363-
status = nil
363+
state = nil
364364
open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |service|
365-
status = query_status(service)
365+
query_status(service) do |status|
366+
state = SERVICE_STATES[status[:dwCurrentState]]
367+
end
366368
end
367-
state = SERVICE_STATES[status[:dwCurrentState]]
368369
if state.nil?
369-
raise Puppet::Error.new(_("Unknown Service state '%{current_state}' for '%{service_name}'") % { current_state: status[:dwCurrentState].to_s, service_name: service_name})
370+
raise Puppet::Error.new(_("Unknown Service state '%{current_state}' for '%{service_name}'") % { current_state: state.to_s, service_name: service_name})
370371
end
371372
state
372373
end
@@ -377,13 +378,14 @@ def service_state(service_name)
377378
# @param [:string] service_name name of the service to query
378379
# @return [QUERY_SERVICE_CONFIGW.struct] the configuration of the service
379380
def service_start_type(service_name)
380-
config = nil
381+
start_type = nil
381382
open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|
382-
config = query_config(service)
383+
query_config(service) do |config|
384+
start_type = SERVICE_START_TYPES[config[:dwStartType]]
385+
end
383386
end
384-
start_type = SERVICE_START_TYPES[config[:dwStartType]]
385387
if start_type.nil?
386-
raise Puppet::Error.new(_("Unknown start type '%{start_type}' for '%{service_name}'") % { start_type: config[:dwStartType].to_s, service_name: service_name})
388+
raise Puppet::Error.new(_("Unknown start type '%{start_type}' for '%{service_name}'") % { start_type: start_type.to_s, service_name: service_name})
387389
end
388390
start_type
389391
end
@@ -562,62 +564,62 @@ def open_scm(scm_access, &block)
562564
def transition_service_state(service_name, valid_initial_states, final_state, &block)
563565
service_access = SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS
564566
open_service(service_name, SC_MANAGER_CONNECT, service_access) do |service|
565-
status = query_status(service)
566-
initial_state = status[:dwCurrentState]
567-
568-
# If the service is already in the final_state, then
569-
# no further work needs to be done
570-
if initial_state == final_state
571-
Puppet.debug _("The service is already in the %{final_state} state. No further work needs to be done.") % { final_state: SERVICE_STATES[final_state] }
572-
573-
next
574-
end
567+
query_status(service) do |status|
568+
initial_state = status[:dwCurrentState]
569+
# If the service is already in the final_state, then
570+
# no further work needs to be done
571+
if initial_state == final_state
572+
Puppet.debug _("The service is already in the %{final_state} state. No further work needs to be done.") % { final_state: SERVICE_STATES[final_state] }
573+
574+
next
575+
end
575576

576-
# Check that initial_state corresponds to a valid
577-
# initial state
578-
unless valid_initial_states.include?(initial_state)
579-
valid_initial_states_str = valid_initial_states.map do |state|
580-
SERVICE_STATES[state]
581-
end.join(", ")
577+
# Check that initial_state corresponds to a valid
578+
# initial state
579+
unless valid_initial_states.include?(initial_state)
580+
valid_initial_states_str = valid_initial_states.map do |state|
581+
SERVICE_STATES[state]
582+
end.join(", ")
582583

583-
raise Puppet::Error, _("The service must be in one of the %{valid_initial_states} states to perform this transition. It is currently in the %{current_state} state.") % { valid_initial_states: valid_initial_states_str, current_state: SERVICE_STATES[initial_state] }
584-
end
584+
raise Puppet::Error, _("The service must be in one of the %{valid_initial_states} states to perform this transition. It is currently in the %{current_state} state.") % { valid_initial_states: valid_initial_states_str, current_state: SERVICE_STATES[initial_state] }
585+
end
585586

586-
# Check if there's a pending transition to the final_state. If so, then wait for
587-
# that transition to finish.
588-
possible_pending_states = FINAL_STATES.keys.select do |pending_state|
589-
# SERVICE_RUNNING has two pending states, SERVICE_START_PENDING and
590-
# SERVICE_CONTINUE_PENDING. That is why we need the #select here
591-
FINAL_STATES[pending_state] == final_state
592-
end
593-
if possible_pending_states.include?(initial_state)
594-
Puppet.debug _("There is already a pending transition to the %{final_state} state for the %{service_name} service.") % { final_state: SERVICE_STATES[final_state], service_name: service_name }
595-
wait_on_pending_state(service, initial_state)
587+
# Check if there's a pending transition to the final_state. If so, then wait for
588+
# that transition to finish.
589+
possible_pending_states = FINAL_STATES.keys.select do |pending_state|
590+
# SERVICE_RUNNING has two pending states, SERVICE_START_PENDING and
591+
# SERVICE_CONTINUE_PENDING. That is why we need the #select here
592+
FINAL_STATES[pending_state] == final_state
593+
end
594+
if possible_pending_states.include?(initial_state)
595+
Puppet.debug _("There is already a pending transition to the %{final_state} state for the %{service_name} service.") % { final_state: SERVICE_STATES[final_state], service_name: service_name }
596+
wait_on_pending_state(service, initial_state)
596597

597-
next
598-
end
598+
next
599+
end
599600

600-
# If we are in an unsafe pending state like SERVICE_START_PENDING
601-
# or SERVICE_STOP_PENDING, then we want to wait for that pending
602-
# transition to finish before transitioning the service state.
603-
# The reason we do this is because SERVICE_START_PENDING is when
604-
# the service thread is being created and initialized, while
605-
# SERVICE_STOP_PENDING is when the service thread is being cleaned
606-
# up and destroyed. Thus there is a chance that when the service is
607-
# in either of these states, its service thread may not yet be ready
608-
# to perform the state transition (it may not even exist).
609-
if UNSAFE_PENDING_STATES.include?(initial_state)
610-
Puppet.debug _("The service is in the %{pending_state} state, which is an unsafe pending state.") % { pending_state: SERVICE_STATES[initial_state] }
611-
wait_on_pending_state(service, initial_state)
612-
initial_state = FINAL_STATES[initial_state]
613-
end
601+
# If we are in an unsafe pending state like SERVICE_START_PENDING
602+
# or SERVICE_STOP_PENDING, then we want to wait for that pending
603+
# transition to finish before transitioning the service state.
604+
# The reason we do this is because SERVICE_START_PENDING is when
605+
# the service thread is being created and initialized, while
606+
# SERVICE_STOP_PENDING is when the service thread is being cleaned
607+
# up and destroyed. Thus there is a chance that when the service is
608+
# in either of these states, its service thread may not yet be ready
609+
# to perform the state transition (it may not even exist).
610+
if UNSAFE_PENDING_STATES.include?(initial_state)
611+
Puppet.debug _("The service is in the %{pending_state} state, which is an unsafe pending state.") % { pending_state: SERVICE_STATES[initial_state] }
612+
wait_on_pending_state(service, initial_state)
613+
initial_state = FINAL_STATES[initial_state]
614+
end
614615

615-
Puppet.debug _("Transitioning the %{service_name} service from %{initial_state} to %{final_state}") % { service_name: service_name, initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state] }
616+
Puppet.debug _("Transitioning the %{service_name} service from %{initial_state} to %{final_state}") % { service_name: service_name, initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state] }
616617

617-
yield service
618+
yield service
618619

619-
Puppet.debug _("Waiting for the transition to finish")
620-
wait_on_state_transition(service, initial_state, final_state)
620+
Puppet.debug _("Waiting for the transition to finish")
621+
wait_on_state_transition(service, initial_state, final_state)
622+
end
621623
end
622624
rescue => detail
623625
raise Puppet::Error, _("Failed to transition the %{service_name} service to the %{final_state} state. Detail: %{detail}") % { service_name: service_name, final_state: SERVICE_STATES[final_state], detail: detail }, detail.backtrace
@@ -661,9 +663,9 @@ def query_status(service)
661663
if success == FFI::WIN32_FALSE
662664
raise Puppet::Util::Windows::Error.new(_("Service query failed"))
663665
end
666+
yield status
664667
end
665668
end
666-
status
667669
end
668670
private :query_status
669671

@@ -673,7 +675,7 @@ def query_status(service)
673675
#
674676
# @param [:handle] service handle of the service to query
675677
# @return [QUERY_SERVICE_CONFIGW struct] the result of the query
676-
def query_config(service)
678+
def query_config(service, &block)
677679
config = nil
678680
size_required = nil
679681
# Fetch the bytes of memory required to be allocated
@@ -697,9 +699,9 @@ def query_config(service)
697699
if success == FFI::WIN32_FALSE
698700
raise Puppet::Util::Windows::Error.new(_("Service query failed"))
699701
end
702+
yield config
700703
end
701704
end
702-
config
703705
end
704706
private :query_config
705707

@@ -741,21 +743,25 @@ def wait_on_state_transition(service, initial_state, final_state)
741743
state = nil
742744
elapsed_time = 0
743745
while elapsed_time <= DEFAULT_TIMEOUT
744-
status = query_status(service)
745-
state = status[:dwCurrentState]
746-
747-
return if state == final_state
748-
if state == pending_state
749-
Puppet.debug _("The service transitioned to the %{pending_state} state.") % { pending_state: SERVICE_STATES[pending_state] }
750-
wait_on_pending_state(service, pending_state)
751-
return
752-
end
753746

754-
sleep(1)
755-
elapsed_time += 1
747+
query_status(service) do |status|
748+
state = status[:dwCurrentState]
749+
return if state == final_state
750+
if state == pending_state
751+
Puppet.debug _("The service transitioned to the %{pending_state} state.") % { pending_state: SERVICE_STATES[pending_state] }
752+
wait_on_pending_state(service, pending_state)
753+
return
754+
end
755+
sleep(1)
756+
elapsed_time += 1
757+
end
756758
end
757-
758759
# Timed out while waiting for the transition to finish. Raise an error
760+
# We can still use the state variable read from the FFI struct because
761+
# FFI creates new Integer objects during an assignment of an integer value
762+
# stored in an FFI struct. We verified that the '=' operater is safe
763+
# from the freed memory since the new ruby object created during the
764+
# assignment will remain in ruby memory and remain immutable and constant.
759765
raise Puppet::Error, _("Timed out while waiting for the service to transition from %{initial_state} to %{final_state} OR from %{initial_state} to %{pending_state} to %{final_state}. The service's current state is %{current_state}.") % { initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state], pending_state: SERVICE_STATES[pending_state], current_state: SERVICE_STATES[state] }
760766
end
761767
private :wait_on_state_transition
@@ -775,36 +781,37 @@ def wait_on_pending_state(service, pending_state)
775781
elapsed_time = 0
776782
last_checkpoint = -1
777783
loop do
778-
status = query_status(service)
779-
state = status[:dwCurrentState]
780-
781-
# Check if our service has finished transitioning to
782-
# the final_state OR if an unexpected transition
783-
# has occurred
784-
return if state == final_state
785-
unless state == pending_state
786-
raise Puppet::Error, _("Unexpected transition to the %{current_state} state while waiting for the pending transition from %{pending_state} to %{final_state} to finish.") % { current_state: SERVICE_STATES[state], pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state] }
787-
end
784+
query_status(service) do |status|
785+
state = status[:dwCurrentState]
786+
checkpoint = status[:dwCheckPoint]
787+
wait_hint = status[:dwWaitHint]
788+
# Check if our service has finished transitioning to
789+
# the final_state OR if an unexpected transition
790+
# has occurred
791+
return if state == final_state
792+
unless state == pending_state
793+
raise Puppet::Error, _("Unexpected transition to the %{current_state} state while waiting for the pending transition from %{pending_state} to %{final_state} to finish.") % { current_state: SERVICE_STATES[state], pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state] }
794+
end
788795

789-
# Check if any progress has been made since our last sleep
790-
# using the dwCheckPoint. If no progress has been made then
791-
# check if we've timed out, and raise an error if so
792-
if status[:dwCheckPoint] > last_checkpoint
793-
elapsed_time = 0
794-
last_checkpoint = status[:dwCheckPoint]
795-
else
796-
wait_hint = milliseconds_to_seconds(status[:dwWaitHint])
797-
timeout = wait_hint < DEFAULT_TIMEOUT ? DEFAULT_TIMEOUT : wait_hint
798-
799-
if elapsed_time >= timeout
800-
raise Puppet::Error, _("Timed out while waiting for the pending transition from %{pending_state} to %{final_state} to finish. The current state is %{current_state}.") % { pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state], current_state: SERVICE_STATES[state] }
796+
# Check if any progress has been made since our last sleep
797+
# using the dwCheckPoint. If no progress has been made then
798+
# check if we've timed out, and raise an error if so
799+
if checkpoint > last_checkpoint
800+
elapsed_time = 0
801+
last_checkpoint = checkpoint
802+
else
803+
wait_hint_in_seconds = milliseconds_to_seconds(wait_hint)
804+
timeout = wait_hint_in_seconds < DEFAULT_TIMEOUT ? DEFAULT_TIMEOUT : wait_hint_in_seconds
805+
806+
if elapsed_time >= timeout
807+
raise Puppet::Error, _("Timed out while waiting for the pending transition from %{pending_state} to %{final_state} to finish. The current state is %{current_state}.") % { pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state], current_state: SERVICE_STATES[state] }
808+
end
801809
end
810+
wait_time = wait_hint_to_wait_time(wait_hint)
811+
# Wait a bit before rechecking the service's state
812+
sleep(wait_time)
813+
elapsed_time += wait_time
802814
end
803-
804-
# Wait a bit before rechecking the service's state
805-
wait_time = wait_hint_to_wait_time(status[:dwWaitHint])
806-
sleep(wait_time)
807-
elapsed_time += wait_time
808815
end
809816
end
810817
private :wait_on_pending_state

0 commit comments

Comments
 (0)