puppet 5.5.8 → 5.5.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +14 -14
  3. data/Rakefile +3 -1
  4. data/ext/solaris/smf/svc-puppetd +8 -1
  5. data/ext/solaris/smf/svc-puppetmasterd +8 -1
  6. data/lib/puppet/application/device.rb +45 -29
  7. data/lib/puppet/face/config.rb +1 -1
  8. data/lib/puppet/forge.rb +4 -1
  9. data/lib/puppet/pops/evaluator/collectors/abstract_collector.rb +3 -1
  10. data/lib/puppet/pops/types/p_timespan_type.rb +2 -2
  11. data/lib/puppet/pops/types/string_converter.rb +11 -2
  12. data/lib/puppet/provider/cron/crontab.rb +1 -1
  13. data/lib/puppet/provider/package/windows/exe_package.rb +13 -0
  14. data/lib/puppet/provider/package/windows/msi_package.rb +8 -0
  15. data/lib/puppet/provider/package/windows/package.rb +9 -1
  16. data/lib/puppet/provider/parsedfile.rb +25 -4
  17. data/lib/puppet/provider/service/smf.rb +54 -0
  18. data/lib/puppet/transaction.rb +4 -1
  19. data/lib/puppet/transaction/event_manager.rb +13 -1
  20. data/lib/puppet/transaction/resource_harness.rb +3 -1
  21. data/lib/puppet/util/command_line.rb +2 -3
  22. data/lib/puppet/util/filetype.rb +36 -4
  23. data/lib/puppet/util/selinux.rb +1 -1
  24. data/lib/puppet/util/windows/api_types.rb +1 -1
  25. data/lib/puppet/util/windows/registry.rb +29 -5
  26. data/lib/puppet/util/windows/service.rb +106 -99
  27. data/lib/puppet/version.rb +1 -1
  28. data/locales/puppet.pot +97 -69
  29. data/man/man5/puppet.conf.5 +2 -2
  30. data/man/man8/puppet-agent.8 +1 -1
  31. data/man/man8/puppet-apply.8 +1 -1
  32. data/man/man8/puppet-ca.8 +1 -1
  33. data/man/man8/puppet-catalog.8 +1 -1
  34. data/man/man8/puppet-cert.8 +1 -1
  35. data/man/man8/puppet-certificate.8 +1 -1
  36. data/man/man8/puppet-certificate_request.8 +1 -1
  37. data/man/man8/puppet-certificate_revocation_list.8 +1 -1
  38. data/man/man8/puppet-config.8 +2 -2
  39. data/man/man8/puppet-describe.8 +1 -1
  40. data/man/man8/puppet-device.8 +23 -19
  41. data/man/man8/puppet-doc.8 +1 -1
  42. data/man/man8/puppet-epp.8 +1 -1
  43. data/man/man8/puppet-facts.8 +1 -1
  44. data/man/man8/puppet-filebucket.8 +1 -1
  45. data/man/man8/puppet-generate.8 +1 -1
  46. data/man/man8/puppet-help.8 +1 -1
  47. data/man/man8/puppet-key.8 +1 -1
  48. data/man/man8/puppet-lookup.8 +1 -1
  49. data/man/man8/puppet-man.8 +1 -1
  50. data/man/man8/puppet-master.8 +1 -1
  51. data/man/man8/puppet-module.8 +1 -1
  52. data/man/man8/puppet-node.8 +1 -1
  53. data/man/man8/puppet-parser.8 +1 -1
  54. data/man/man8/puppet-plugin.8 +1 -1
  55. data/man/man8/puppet-report.8 +1 -1
  56. data/man/man8/puppet-resource.8 +1 -1
  57. data/man/man8/puppet-script.8 +1 -1
  58. data/man/man8/puppet-status.8 +1 -1
  59. data/man/man8/puppet.8 +2 -2
  60. data/spec/fixtures/unit/provider/service/smf/svcs_fmri.out +6 -0
  61. data/spec/fixtures/unit/provider/service/smf/svcs_multiple_fmris.out +13 -0
  62. data/spec/integration/ssl/key_spec.rb +0 -4
  63. data/spec/integration/transaction_spec.rb +1 -1
  64. data/spec/integration/util/windows/registry_spec.rb +39 -0
  65. data/spec/unit/application/device_spec.rb +4 -1
  66. data/spec/unit/application/lookup_spec.rb +10 -9
  67. data/spec/unit/forge/forge_spec.rb +4 -2
  68. data/spec/unit/indirector/yaml_spec.rb +1 -1
  69. data/spec/unit/pops/loaders/loader_spec.rb +6 -7
  70. data/spec/unit/pops/types/p_timespan_type_spec.rb +22 -0
  71. data/spec/unit/pops/types/p_timestamp_type_spec.rb +19 -0
  72. data/spec/unit/pops/types/string_converter_spec.rb +20 -0
  73. data/spec/unit/provider/cron/parsed_spec.rb +4 -3
  74. data/spec/unit/provider/group/ldap_spec.rb +22 -25
  75. data/spec/unit/provider/group/pw_spec.rb +7 -10
  76. data/spec/unit/provider/host/parsed_spec.rb +3 -17
  77. data/spec/unit/provider/nameservice/directoryservice_spec.rb +97 -103
  78. data/spec/unit/provider/package/aix_spec.rb +5 -8
  79. data/spec/unit/provider/package/apt_spec.rb +3 -6
  80. data/spec/unit/provider/package/dnf_spec.rb +29 -31
  81. data/spec/unit/provider/package/dpkg_spec.rb +18 -21
  82. data/spec/unit/provider/package/freebsd_spec.rb +4 -7
  83. data/spec/unit/provider/package/gem_spec.rb +41 -41
  84. data/spec/unit/provider/package/hpux_spec.rb +7 -10
  85. data/spec/unit/provider/package/macports_spec.rb +13 -15
  86. data/spec/unit/provider/package/nim_spec.rb +3 -10
  87. data/spec/unit/provider/package/openbsd_spec.rb +14 -17
  88. data/spec/unit/provider/package/pip3_spec.rb +3 -6
  89. data/spec/unit/provider/package/pip_spec.rb +44 -72
  90. data/spec/unit/provider/package/pkgin_spec.rb +13 -18
  91. data/spec/unit/provider/package/pkgng_spec.rb +21 -24
  92. data/spec/unit/provider/package/puppet_gem_spec.rb +6 -9
  93. data/spec/unit/provider/package/tdnf_spec.rb +9 -12
  94. data/spec/unit/provider/package/yum_spec.rb +29 -15
  95. data/spec/unit/provider/package/zypper_spec.rb +17 -19
  96. data/spec/unit/provider/service/bsd_spec.rb +8 -12
  97. data/spec/unit/provider/service/daemontools_spec.rb +12 -20
  98. data/spec/unit/provider/service/debian_spec.rb +8 -16
  99. data/spec/unit/provider/service/freebsd_spec.rb +2 -5
  100. data/spec/unit/provider/service/openbsd_spec.rb +12 -18
  101. data/spec/unit/provider/service/rcng_spec.rb +3 -7
  102. data/spec/unit/provider/service/redhat_spec.rb +21 -23
  103. data/spec/unit/provider/service/runit_spec.rb +9 -19
  104. data/spec/unit/provider/service/smf_spec.rb +82 -21
  105. data/spec/unit/provider/service/src_spec.rb +14 -23
  106. data/spec/unit/provider/user/hpux_spec.rb +2 -5
  107. data/spec/unit/provider/user/ldap_spec.rb +29 -32
  108. data/spec/unit/provider/user/pw_spec.rb +10 -13
  109. data/spec/unit/ssl/key_spec.rb +2 -4
  110. data/spec/unit/transaction/event_manager_spec.rb +12 -1
  111. data/spec/unit/transaction/resource_harness_spec.rb +18 -0
  112. data/spec/unit/transaction_spec.rb +25 -0
  113. data/spec/unit/util/filetype_spec.rb +13 -5
  114. data/spec/unit/util/logging_spec.rb +0 -41
  115. data/spec/unit/util/monkey_patches_spec.rb +18 -5
  116. data/spec/unit/util/selinux_spec.rb +4 -0
  117. metadata +6 -2
@@ -49,6 +49,52 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
49
49
  end
50
50
  end
51
51
 
52
+ # Returns the service's FMRI. We fail if multiple FMRIs correspond to
53
+ # @resource[:name].
54
+ #
55
+ # If the service does not exist or we fail to get any FMRIs from svcs,
56
+ # this method will raise a Puppet::Error
57
+ def service_fmri
58
+ return @fmri if @fmri
59
+
60
+ # `svcs -l` is better to use because we can detect service instances
61
+ # that have not yet been activated or enabled (i.e. it lets us detect
62
+ # services that svcadm has not yet touched). `svcs -H -o fmri` is a bit
63
+ # more limited.
64
+ lines = svcs("-l", @resource[:name]).chomp.lines.to_a
65
+ lines.select! { |line| line =~ /^fmri/ }
66
+ fmris = lines.map! { |line| line.split(' ')[-1].chomp }
67
+ unless fmris.length == 1
68
+ raise Puppet::Error, _("Failed to get the FMRI of the %{service} service: The pattern '%{service}' matches multiple FMRIs! These are the FMRIs it matches: %{all_fmris}") % { service: @resource[:name], all_fmris: fmris.join(', ') }
69
+ end
70
+
71
+ @fmri = fmris.first
72
+ end
73
+
74
+ # Returns true if the provider supports incomplete services.
75
+ def supports_incomplete_services?
76
+ Puppet::Util::Package.versioncmp(Facter.value(:operatingsystemrelease), '11.1') >= 0
77
+ end
78
+
79
+ # Returns true if the service is complete. A complete service is a service that
80
+ # has the general/complete property defined.
81
+ def complete_service?
82
+ unless supports_incomplete_services?
83
+ raise Puppet::Error, _("Cannot query if the %{service} service is complete: The concept of complete/incomplete services was introduced in Solaris 11.1. You are on a Solaris %{release} machine.") % { service: @resource[:name], release: Facter.value(:operatingsystemrelease) }
84
+ end
85
+
86
+ return @complete_service if @complete_service
87
+
88
+ # We need to use the service's FMRI when querying its config. because
89
+ # general/complete is an instance-specific property.
90
+ fmri = service_fmri
91
+
92
+ # Check if the general/complete property is defined. If it is undefined,
93
+ # then svccfg will not print anything to the console.
94
+ property_defn = svccfg("-s", fmri, "listprop", "general/complete").chomp
95
+ @complete_service = ! property_defn.empty?
96
+ end
97
+
52
98
  def enable
53
99
  self.start
54
100
  end
@@ -131,6 +177,14 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
131
177
  end
132
178
 
133
179
  begin
180
+ if supports_incomplete_services?
181
+ unless complete_service?
182
+ debug _("The %{service} service is incomplete so its status will be reported as :stopped. See `svcs -xv %{fmri}` for more details.") % { service: @resource[:name], fmri: service_fmri }
183
+
184
+ return :stopped
185
+ end
186
+ end
187
+
134
188
  # get the current state and the next state, and if the next
135
189
  # state is set (i.e. not "-") use it for state comparison
136
190
  states = service_states
@@ -56,6 +56,8 @@ class Puppet::Transaction
56
56
  @prefetched_providers = Hash.new { |h,k| h[k] = {} }
57
57
 
58
58
  @prefetch_failed_providers = Hash.new { |h,k| h[k] = {} }
59
+
60
+ @failed_dependencies_already_notified = Set.new()
59
61
  end
60
62
 
61
63
  # Invoke the pre_run_check hook in every resource in the catalog.
@@ -291,8 +293,9 @@ class Puppet::Transaction
291
293
  if s && s.dependency_failed?
292
294
  # See above. --daniel 2011-06-06
293
295
  unless suppress_report then
294
- s.failed_dependencies.each do |dep|
296
+ s.failed_dependencies.find_all { |d| !(@failed_dependencies_already_notified.include?(d.ref)) }.each do |dep|
295
297
  resource.notice _("Dependency %{dep} has failures: %{status}") % { dep: dep, status: resource_status(dep).failed }
298
+ @failed_dependencies_already_notified.add(dep.ref)
296
299
  end
297
300
  end
298
301
  end
@@ -147,12 +147,18 @@ class Puppet::Transaction::EventManager
147
147
  resource.send(callback)
148
148
 
149
149
  if not resource.is_a?(Puppet::Type.type(:whit))
150
- resource.notice n_("Triggered '%{callback}' from %{count} event", "Triggered '%{callback}' from %{count} events", events.length) % { count: events.length, callback: callback }
150
+ message = n_("Triggered '%{callback}' from %{count} event", "Triggered '%{callback}' from %{count} events", events.length) % { count: events.length, callback: callback }
151
+ resource.notice message
152
+ add_callback_status_event(resource, callback, message, "success")
151
153
  end
154
+
152
155
  return true
153
156
  rescue => detail
154
157
  resource_error_message = _("Failed to call %{callback}: %{detail}") % { callback: callback, detail: detail }
155
158
  resource.err resource_error_message
159
+ if not resource.is_a?(Puppet::Type.type(:whit))
160
+ add_callback_status_event(resource, callback, resource_error_message, "failure")
161
+ end
156
162
 
157
163
  transaction.resource_status(resource).failed_to_restart = true
158
164
  transaction.resource_status(resource).fail_with_event(resource_error_message)
@@ -160,6 +166,12 @@ class Puppet::Transaction::EventManager
160
166
  return false
161
167
  end
162
168
 
169
+ def add_callback_status_event(resource, callback, message, status)
170
+ options = { message: message, status: status, name: callback.to_s }
171
+ event = resource.event options
172
+ transaction.resource_status(resource) << event if event
173
+ end
174
+
163
175
  def process_noop_events(resource, callback, events)
164
176
  resource.notice n_("Would have triggered '%{callback}' from %{count} event", "Would have triggered '%{callback}' from %{count} events", events.length) % { count: events.length, callback: callback }
165
177
 
@@ -158,7 +158,9 @@ class Puppet::Transaction::ResourceHarness
158
158
  raise
159
159
  ensure
160
160
  if event
161
- event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, param.name.to_s))
161
+ name = param.name.to_s
162
+ event.message ||= _("could not create change error message for %{name}") % { name: name }
163
+ event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, name))
162
164
  context.record(event)
163
165
  event.send_log
164
166
  context.synced_params << param.name
@@ -106,7 +106,7 @@ module Puppet
106
106
  def run
107
107
  # For most applications, we want to be able to load code from the modulepath,
108
108
  # such as apply, describe, resource, and faces.
109
- # For agent, we only want to load pluginsync'ed code from libdir.
109
+ # For agent and device in agent mode, we only want to load pluginsync'ed code from libdir.
110
110
  # For master, we shouldn't ever be loading per-environment code into the master's
111
111
  # ruby process, but that requires fixing (#17210, #12173, #8750). So for now
112
112
  # we try to restrict to only code that can be autoloaded from the node's
@@ -116,8 +116,7 @@ module Puppet
116
116
  # have an appropriate application-wide current_environment set.
117
117
  # If we cannot find the configured environment, which may not exist,
118
118
  # we do not attempt to add plugin directories to the load path.
119
- #
120
- if @subcommand_name != 'master' and @subcommand_name != 'agent'
119
+ unless @subcommand_name == 'master' || @subcommand_name == 'agent' || (@subcommand_name == 'device' && (['--apply', '--facts', '--resource'] - @command_line.args).empty?)
121
120
  if configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])
122
121
  configured_environment.each_plugin_directory do |dir|
123
122
  $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
@@ -191,13 +191,21 @@ class Puppet::Util::FileType
191
191
 
192
192
  # Read a specific @path's cron tab.
193
193
  def read
194
+ unless Puppet::Util.uid(@path)
195
+ Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
196
+
197
+ return ""
198
+ end
199
+
194
200
  Puppet::Util::Execution.execute("#{cmdbase} -l", failonfail: true, combine: true)
195
201
  rescue => detail
196
202
  case detail.to_s
197
203
  when /no crontab for/
198
204
  return ""
199
205
  when /are not allowed to/
200
- raise FileReadError, _("User %{path} not authorized to use cron") % { path: @path }, detail.backtrace
206
+ Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
207
+
208
+ return ""
201
209
  else
202
210
  raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
203
211
  end
@@ -215,7 +223,15 @@ class Puppet::Util::FileType
215
223
 
216
224
  # Overwrite a specific @path's cron tab; must be passed the @path name
217
225
  # and the text with which to create the cron tab.
226
+ #
227
+ # TODO: We should refactor this at some point to make it identical to the
228
+ # :aixtab and :suntab's write methods so that, at the very least, the pipe
229
+ # is not created and the crontab command's errors are not swallowed.
218
230
  def write(text)
231
+ unless Puppet::Util.uid(@path)
232
+ raise Puppet::Error, _("Cannot write the %{path} user's crontab: The user does not exist") % { path: @path }
233
+ end
234
+
219
235
  # this file is managed by the OS and should be using system encoding
220
236
  IO.popen("#{cmdbase()} -", "w", :encoding => Encoding.default_external) { |p|
221
237
  p.print text
@@ -240,13 +256,21 @@ class Puppet::Util::FileType
240
256
  newfiletype(:suntab) do
241
257
  # Read a specific @path's cron tab.
242
258
  def read
259
+ unless Puppet::Util.uid(@path)
260
+ Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
261
+
262
+ return ""
263
+ end
264
+
243
265
  Puppet::Util::Execution.execute(%w{crontab -l}, cronargs)
244
266
  rescue => detail
245
267
  case detail.to_s
246
268
  when /can't open your crontab/
247
269
  return ""
248
270
  when /you are not authorized to use cron/
249
- raise FileReadError, _("User %{path} not authorized to use cron") % { path: @path }, detail.backtrace
271
+ Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
272
+
273
+ return ""
250
274
  else
251
275
  raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
252
276
  end
@@ -283,13 +307,21 @@ class Puppet::Util::FileType
283
307
  newfiletype(:aixtab) do
284
308
  # Read a specific @path's cron tab.
285
309
  def read
310
+ unless Puppet::Util.uid(@path)
311
+ Puppet.debug _("The %{path} user does not exist. Treating their crontab file as empty in case Puppet creates them in the middle of the run.") % { path: @path }
312
+
313
+ return ""
314
+ end
315
+
286
316
  Puppet::Util::Execution.execute(%w{crontab -l}, cronargs)
287
317
  rescue => detail
288
318
  case detail.to_s
289
319
  when /open.*in.*directory/
290
320
  return ""
291
- when /You are not authorized to use the cron command/
292
- raise FileReadError, _("User %{path} not authorized to use cron") % { path: @path }, detail.backtrace
321
+ when /not.*authorized.*cron/
322
+ Puppet.debug _("The %{path} user is not authorized to use cron. Their crontab file is treated as empty in case Puppet authorizes them in the middle of the run (by, for example, modifying the cron.deny or cron.allow files).") % { path: @path }
323
+
324
+ return ""
293
325
  else
294
326
  raise FileReadError, _("Could not read crontab for %{path}: %{detail}") % { path: @path, detail: detail }, detail.backtrace
295
327
  end
@@ -190,7 +190,7 @@ module Puppet::Util::SELinux
190
190
  def selinux_label_support?(file)
191
191
  fstype = find_fs(file)
192
192
  return false if fstype.nil?
193
- filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs']
193
+ filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs', 'tmpfs']
194
194
  filesystems.include?(fstype)
195
195
  end
196
196
 
@@ -58,7 +58,7 @@ module Puppet::Util::Windows::APITypes
58
58
  str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')
59
59
  str.encode(dst_encoding, str.encoding, encode_options)
60
60
  rescue Exception => e
61
- Puppet.debug "Unable to convert value #{str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
61
+ Puppet.debug "Unable to convert value #{str.nil? ? 'nil' : str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
62
62
  raise
63
63
  end
64
64
 
@@ -65,6 +65,28 @@ module Puppet::Util::Windows
65
65
  vals
66
66
  end
67
67
 
68
+ # Retrieve a set of values from a registry key given their names
69
+ # Value names listed but not found in the registry will not be added to the
70
+ # resultant Hashtable
71
+ #
72
+ # @param key [RegistryKey] An open handle to a Registry Key
73
+ # @param names [String[]] An array of names of registry values to return if they exist
74
+ # @return [Hashtable<String, Object>] A hashtable of all of the found values in the registry key
75
+ def values_by_name(key, names)
76
+ vals = {}
77
+ names.each do |name|
78
+ FFI::Pointer.from_string_to_wide_string(name) do |subkeyname_ptr|
79
+ begin
80
+ _, vals[name] = read(key, subkeyname_ptr)
81
+ rescue Puppet::Util::Windows::Error => e
82
+ # ignore missing names, but raise other errors
83
+ raise e unless e.code == Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND
84
+ end
85
+ end
86
+ end
87
+ vals
88
+ end
89
+
68
90
  def each_value(key, &block)
69
91
  index = 0
70
92
  subkey = nil
@@ -104,7 +126,7 @@ module Puppet::Util::Windows
104
126
 
105
127
  if result != FFI::ERROR_SUCCESS
106
128
  msg = _("Failed to enumerate %{key} registry keys at index %{index}") % { key: key.keyname, index: index }
107
- raise Puppet::Util::Windows::Error.new(msg)
129
+ raise Puppet::Util::Windows::Error.new(msg, result)
108
130
  end
109
131
 
110
132
  filetime = FFI::WIN32::FILETIME.new(filetime_ptr)
@@ -135,7 +157,7 @@ module Puppet::Util::Windows
135
157
 
136
158
  if result != FFI::ERROR_SUCCESS
137
159
  msg = _("Failed to enumerate %{key} registry values at index %{index}") % { key: key.keyname, index: index }
138
- raise Puppet::Util::Windows::Error.new(msg)
160
+ raise Puppet::Util::Windows::Error.new(msg, result)
139
161
  end
140
162
 
141
163
  subkey_length = subkey_length_ptr.read_dword
@@ -165,7 +187,7 @@ module Puppet::Util::Windows
165
187
 
166
188
  if status != FFI::ERROR_SUCCESS
167
189
  msg = _("Failed to query registry %{key} for sizes") % { key: key.keyname }
168
- raise Puppet::Util::Windows::Error.new(msg)
190
+ raise Puppet::Util::Windows::Error.new(msg, status)
169
191
  end
170
192
 
171
193
  result = [
@@ -247,8 +269,10 @@ module Puppet::Util::Windows
247
269
  buffer_ptr, length_ptr)
248
270
 
249
271
  if result != FFI::ERROR_SUCCESS
250
- msg = _("Failed to read registry value %{value} at %{key}") % { value: name_ptr.read_wide_string, key: key.keyname }
251
- raise Puppet::Util::Windows::Error.new(msg)
272
+ # buffer is raw bytes, *not* chars - less a NULL terminator
273
+ name_length = (name_ptr.size / FFI.type_size(:wchar)) - 1 if name_ptr.size > 0
274
+ msg = _("Failed to read registry value %{value} at %{key}") % { value: name_ptr.read_wide_string(name_length), key: key.keyname }
275
+ raise Puppet::Util::Windows::Error.new(msg, result)
252
276
  end
253
277
 
254
278
  # allows caller to use FFI MemoryPointer helpers to read / shape
@@ -360,13 +360,14 @@ module Puppet::Util::Windows
360
360
  # @param [:string] service_name name of the service to query
361
361
  # @return [string] the status of the service
362
362
  def service_state(service_name)
363
- status = nil
363
+ state = nil
364
364
  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
366
368
  end
367
- state = SERVICE_STATES[status[:dwCurrentState]]
368
369
  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})
370
371
  end
371
372
  state
372
373
  end
@@ -377,13 +378,14 @@ module Puppet::Util::Windows
377
378
  # @param [:string] service_name name of the service to query
378
379
  # @return [QUERY_SERVICE_CONFIGW.struct] the configuration of the service
379
380
  def service_start_type(service_name)
380
- config = nil
381
+ start_type = nil
381
382
  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
383
386
  end
384
- start_type = SERVICE_START_TYPES[config[:dwStartType]]
385
387
  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})
387
389
  end
388
390
  start_type
389
391
  end
@@ -562,62 +564,62 @@ module Puppet::Util::Windows
562
564
  def transition_service_state(service_name, valid_initial_states, final_state, &block)
563
565
  service_access = SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS
564
566
  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
575
576
 
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(", ")
582
583
 
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
585
586
 
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)
596
597
 
597
- next
598
- end
598
+ next
599
+ end
599
600
 
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
614
615
 
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] }
616
617
 
617
- yield service
618
+ yield service
618
619
 
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
621
623
  end
622
624
  rescue => detail
623
625
  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 @@ module Puppet::Util::Windows
661
663
  if success == FFI::WIN32_FALSE
662
664
  raise Puppet::Util::Windows::Error.new(_("Service query failed"))
663
665
  end
666
+ yield status
664
667
  end
665
668
  end
666
- status
667
669
  end
668
670
  private :query_status
669
671
 
@@ -673,7 +675,7 @@ module Puppet::Util::Windows
673
675
  #
674
676
  # @param [:handle] service handle of the service to query
675
677
  # @return [QUERY_SERVICE_CONFIGW struct] the result of the query
676
- def query_config(service)
678
+ def query_config(service, &block)
677
679
  config = nil
678
680
  size_required = nil
679
681
  # Fetch the bytes of memory required to be allocated
@@ -697,9 +699,9 @@ module Puppet::Util::Windows
697
699
  if success == FFI::WIN32_FALSE
698
700
  raise Puppet::Util::Windows::Error.new(_("Service query failed"))
699
701
  end
702
+ yield config
700
703
  end
701
704
  end
702
- config
703
705
  end
704
706
  private :query_config
705
707
 
@@ -712,7 +714,7 @@ module Puppet::Util::Windows
712
714
  FFI::MemoryPointer.new(SERVICE_STATUS.size) do |status_ptr|
713
715
  status = SERVICE_STATUS.new(status_ptr)
714
716
  if ControlService(service, signal, status) == FFI::WIN32_FALSE
715
- raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Failed with") % { control_signal: SERVICE_CONTROL_SIGNALS[signal], current_state: SERVICE_STATES[status[:dwCurrentState]] }
717
+ raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:") % { control_signal: SERVICE_CONTROL_SIGNALS[signal], current_state: SERVICE_STATES[status[:dwCurrentState]] }
716
718
  end
717
719
  end
718
720
  end
@@ -741,21 +743,25 @@ module Puppet::Util::Windows
741
743
  state = nil
742
744
  elapsed_time = 0
743
745
  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
753
746
 
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
756
758
  end
757
-
758
759
  # 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.
759
765
  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] }
760
766
  end
761
767
  private :wait_on_state_transition
@@ -775,36 +781,37 @@ module Puppet::Util::Windows
775
781
  elapsed_time = 0
776
782
  last_checkpoint = -1
777
783
  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
788
795
 
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
801
809
  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
802
814
  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
808
815
  end
809
816
  end
810
817
  private :wait_on_pending_state