Skip to content

Commit c5c5ac1

Browse files
committed
(PUP-1353) Start Posix based services with a 0 priority
Puppet's base service provider starts services as a child process of the Puppet process. On Posix systems the child process inherits the priority or nice value of the parent process. The result is that services which are started as child processes of Puppet have their priority set to Puppet's priority. This behavior is undesirable as services expect to be started with a 0 or normal priority. This commit ensures services are started with a normal priority by changing the priority of the child process when starting a service. Example of old & new behavior on Debian Wheezy: ; git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'. ; sudo /etc/init.d/ntp stop Stopping NTP server: ntpd. ; nice -n 13 sudo bundle exec puppet apply -e 'service { "ntp": ensure => "running" }' Notice: Compiled catalog for foo in environment production in 0.12 seconds Notice: /Stage[main]/Main/Service[ntp]/ensure: ensure changed 'stopped' to 'running' Notice: Applied catalog in 0.05 seconds ; ps -C ntpd -o pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm PID TID CLS RTPRIO NI PRI PSR %CPU STAT WCHAN COMMAND 30269 30269 TS - 13 6 6 0.0 SNs - ntpd ; git checkout service_priority Switched to branch 'service_priority' Your branch is up-to-date with 'origin/service_priority'. ; sudo /etc/init.d/ntp stop Stopping NTP server: ntpd. ; nice -n 13 sudo bundle exec puppet apply -e 'service { "ntp": ensure => "running" }' Notice: Compiled catalog for foo in environment production in 0.11 seconds Notice: /Stage[main]/Main/Service[ntp]/ensure: ensure changed 'stopped' to 'running' Notice: Applied catalog in 0.04 seconds ; ps -C ntpd -o pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm PID TID CLS RTPRIO NI PRI PSR %CPU STAT WCHAN COMMAND 30536 30536 TS - 0 19 10 0.0 Ss - ntpd
1 parent b3d12c5 commit c5c5ac1

File tree

11 files changed

+139
-81
lines changed

11 files changed

+139
-81
lines changed

lib/puppet/provider/service/service.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,23 @@ def restartcmd
2121

2222
# A simple wrapper so execution failures are a bit more informative.
2323
def texecute(type, command, fof = true, squelch = false, combine = true)
24+
# Set the process priority to 0 (or normal in Windows) so that services
25+
# which are started as children of puppet will start with normal priority,
26+
# rather than the priority of the puppet process itself.
27+
if Facter.value(:osfamily) == 'windows'
28+
priority = Process::NORMAL_PRIORITY_CLASS
29+
else
30+
priority = 0
31+
end
2432
begin
25-
execute(command, :failonfail => fof, :override_locale => false, :squelch => squelch, :combine => combine)
33+
opts = {
34+
:combine => combine,
35+
:failonfail => fof,
36+
:override_locale => false,
37+
:priority => priority,
38+
:squelch => squelch,
39+
}
40+
execute(command, opts)
2641
rescue Puppet::ExecutionFailure => detail
2742
@resource.fail Puppet::Error, "Could not #{type} #{@resource.ref}: #{detail}", detail
2843
end

lib/puppet/util/execution.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def self.execfail(command, exception)
133133
# Passing in a value of false for this option will allow the command to be executed using the user/system locale.
134134
# @option options [Hash<{String => String}>] :custom_environment ({}) a hash of key/value pairs to set as environment variables for the duration
135135
# of the command.
136+
# @option options [Integer] :priority (nil) The priority of child process, aka the nice value in Posix operatingsystems.
136137
# @return [Puppet::Util::Execution::ProcessOutput] output as specified by options
137138
# @raise [Puppet::ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is
138139
# `true`.
@@ -158,6 +159,7 @@ def self.execute(command, options = NoOptionsSpecified)
158159
:custom_environment => {},
159160
:sensitive => false,
160161
:suppress_window => false,
162+
:priority => nil,
161163
}
162164

163165
options = default_options.merge(options)
@@ -336,6 +338,11 @@ def self.execute_posix(command, options, stdin, stdout, stderr)
336338
# turning "/tmp/foo;\r\n /bin/echo" into ["/tmp/foo;\r\n", " /bin/echo"]
337339
command = [command].flatten
338340
Process.setsid
341+
if options[:priority]
342+
if Process.uid == 0
343+
Process.setpriority(Process::PRIO_PROCESS, 0, options[:priority])
344+
end
345+
end
339346
begin
340347
# We need to chdir to our cwd before changing privileges as there's a
341348
# chance that the user may not have permissions to access the cwd, which

spec/unit/provider/service/bsd_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@
9494

9595
it "should use the supplied start command if specified" do
9696
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))
97-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
97+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
9898
provider.start
9999
end
100100

101101
it "should start the serviced directly otherwise" do
102102
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
103-
expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
103+
expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestart], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
104104
expect(provider).to receive(:search).with('sshd').and_return('/etc/rc.d/sshd')
105105
provider.start
106106
end
@@ -113,13 +113,13 @@
113113

114114
it "should use the supplied stop command if specified" do
115115
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))
116-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
116+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
117117
provider.stop
118118
end
119119

120120
it "should stop the serviced directly otherwise" do
121121
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
122-
expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
122+
expect(provider).to receive(:execute).with(['/etc/rc.d/sshd', :onestop], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
123123
expect(provider).to receive(:search).with('sshd').and_return('/etc/rc.d/sshd')
124124
provider.stop
125125
end

spec/unit/provider/service/gentoo_spec.rb

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@
7979
describe "#start" do
8080
it "should use the supplied start command if specified" do
8181
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))
82-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
82+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
8383
provider.start
8484
end
8585

8686
it "should start the service with <initscript> start otherwise" do
8787
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
88-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
88+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
8989
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
9090
provider.start
9191
end
@@ -94,13 +94,13 @@
9494
describe "#stop" do
9595
it "should use the supplied stop command if specified" do
9696
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))
97-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
97+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
9898
provider.stop
9999
end
100100

101101
it "should stop the service with <initscript> stop otherwise" do
102102
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
103-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
103+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
104104
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
105105
provider.stop
106106
end
@@ -160,24 +160,24 @@
160160
describe "when a special status command is specified" do
161161
it "should use the status command from the resource" do
162162
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
163-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
164-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
163+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
164+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
165165
allow($CHILD_STATUS).to receive(:exitstatus).and_return(0)
166166
provider.status
167167
end
168168

169169
it "should return :stopped when the status command returns with a non-zero exitcode" do
170170
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
171-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
172-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
171+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
172+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
173173
allow($CHILD_STATUS).to receive(:exitstatus).and_return(3)
174174
expect(provider.status).to eq(:stopped)
175175
end
176176

177177
it "should return :running when the status command returns with a zero exitcode" do
178178
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
179-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
180-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
179+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
180+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
181181
allow($CHILD_STATUS).to receive(:exitstatus).and_return(0)
182182
expect(provider.status).to eq(:running)
183183
end
@@ -186,14 +186,14 @@
186186
describe "when hasstatus is false" do
187187
it "should return running if a pid can be found" do
188188
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))
189-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
189+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
190190
expect(provider).to receive(:getpid).and_return(1000)
191191
expect(provider.status).to eq(:running)
192192
end
193193

194194
it "should return stopped if no pid can be found" do
195195
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))
196-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
196+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
197197
expect(provider).to receive(:getpid).and_return(nil)
198198
expect(provider.status).to eq(:stopped)
199199
end
@@ -203,15 +203,15 @@
203203
it "should return running if <initscript> status exits with a zero exitcode" do
204204
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))
205205
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
206-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
206+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
207207
allow($CHILD_STATUS).to receive(:exitstatus).and_return(0)
208208
expect(provider.status).to eq(:running)
209209
end
210210

211211
it "should return stopped if <initscript> status exits with a non-zero exitcode" do
212212
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))
213213
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
214-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :override_locale => false, :squelch => false, :combine => true)
214+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:status], :combine => true, :failonfail => false, :override_locale => false, :priority => 0, :squelch => false)
215215
allow($CHILD_STATUS).to receive(:exitstatus).and_return(3)
216216
expect(provider.status).to eq(:stopped)
217217
end
@@ -221,24 +221,24 @@
221221
describe "#restart" do
222222
it "should use the supplied restart command if specified" do
223223
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))
224-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
225-
expect(provider).to receive(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
224+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
225+
expect(provider).to receive(:execute).with(['/bin/foo'], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
226226
provider.restart
227227
end
228228

229229
it "should restart the service with <initscript> restart if hasrestart is true" do
230230
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))
231231
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
232-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
232+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:restart], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
233233
provider.restart
234234
end
235235

236236
it "should restart the service with <initscript> stop/start if hasrestart is false" do
237237
provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))
238238
expect(provider).to receive(:search).with('sshd').and_return('/etc/init.d/sshd')
239-
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
240-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
241-
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :override_locale => false, :squelch => false, :combine => true)
239+
expect(provider).not_to receive(:execute).with(['/etc/init.d/sshd',:restart], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
240+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:stop], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
241+
expect(provider).to receive(:execute).with(['/etc/init.d/sshd',:start], :combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false)
242242
provider.restart
243243
end
244244
end

spec/unit/provider/service/init_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@
242242
describe "when starting a service on Solaris" do
243243
it "should use ctrun" do
244244
allow(Facter).to receive(:value).with(:osfamily).and_return('Solaris')
245-
expect(provider).to receive(:execute).with('/usr/bin/ctrun -l child /service/path/myservice start', {:failonfail => true, :override_locale => false, :squelch => false, :combine => true}).and_return("")
245+
expect(provider).to receive(:execute).with('/usr/bin/ctrun -l child /service/path/myservice start', {:combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false}).and_return("")
246246
allow($CHILD_STATUS).to receive(:exitstatus).and_return(0)
247247
provider.start
248248
end
@@ -251,7 +251,7 @@
251251
describe "when starting a service on RedHat" do
252252
it "should not use ctrun" do
253253
allow(Facter).to receive(:value).with(:osfamily).and_return('RedHat')
254-
expect(provider).to receive(:execute).with(['/service/path/myservice', :start], {:failonfail => true, :override_locale => false, :squelch => false, :combine => true}).and_return("")
254+
expect(provider).to receive(:execute).with(['/service/path/myservice', :start], {:combine => true, :failonfail => true, :override_locale => false, :priority => 0, :squelch => false}).and_return("")
255255
allow($CHILD_STATUS).to receive(:exitstatus).and_return(0)
256256
provider.start
257257
end

0 commit comments

Comments
 (0)