class Chef::RunLock
Chef::RunLock
¶ ↑
Provides an interface for acquiring and releasing a system-wide exclusive lock.
Used by Chef::Client
to ensure only one instance of chef-client (or solo) is modifying the system at a time.
Attributes
Public Class Methods
Source
Public Instance Methods
Source
# File lib/chef/run_lock.rb, line 64 def acquire if timeout_given? begin Timeout.timeout(time_to_wait) do unless test if time_to_wait > 0.0 wait else exit_from_timeout end end end rescue Timeout::Error exit_from_timeout end else wait unless test end end
Acquire the system-wide lock. Will block indefinitely if another process already has the lock and Chef::Config is not set. Otherwise will block for Chef::Config seconds and exit if the lock is not acquired.
Each call to acquire should have a corresponding call to release
.
The implementation is based on File#flock (see also: flock(2)).
Either acquire() or test() methods should be called in order to get the ownership of run_lock.
Source
# File lib/chef/run_lock.rb, line 140 def acquire_lock if ChefUtils.windows? acquire_win32_mutex else # If we support FD_CLOEXEC, then use it. # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not # ruby-1.8.7/1.9.3 if Fcntl.const_defined?(:F_SETFD) && Fcntl.const_defined?(:FD_CLOEXEC) runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC) end # Flock will return 0 if it can acquire the lock otherwise it # will return false if runlock.flock(File::LOCK_NB | File::LOCK_EX) == 0 true else false end end end
@api private solely for race condition tests
Source
# File lib/chef/run_lock.rb, line 133 def create_lock # ensure the runlock_file path exists create_path(File.dirname(runlock_file)) @runlock = File.open(runlock_file, "a+") end
@api private solely for race condition tests
Source
# File lib/chef/run_lock.rb, line 117 def release if runlock if ChefUtils.windows? mutex.release else runlock.flock(File::LOCK_UN) end runlock.close # Don't unlink the pid file, if another chef-client was waiting, it # won't be recreated. Better to leave a "dead" pid file than not have # it available if you need to break the lock. reset end end
Release the system-wide lock.
Source
# File lib/chef/run_lock.rb, line 107 def save_pid runlock.truncate(0) runlock.rewind # truncate doesn't reset position to 0. runlock.write(Process.pid.to_s) # flush the file fsync flushes the system buffers # in addition to ruby buffers runlock.fsync end
Source
# File lib/chef/run_lock.rb, line 90 def test create_lock acquire_lock end
Tests and if successful acquires the system-wide lock. Returns true if the lock is acquired, false otherwise.
Either acquire() or test() methods should be called in order to get the ownership of run_lock.
Source
# File lib/chef/run_lock.rb, line 98 def wait Chef::Log.warn("#{ChefUtils::Dist::Infra::PRODUCT} #{runpid} is running, will wait for it to finish and then run.") if ChefUtils.windows? mutex.wait else runlock.flock(File::LOCK_EX) end end
Waits until acquiring the system-wide lock.
Private Instance Methods
Source
# File lib/chef/run_lock.rb, line 175 def acquire_win32_mutex @mutex = Chef::ReservedNames::Win32::Mutex.new("Global\\#{runlock_file.tr("\\", "/").downcase}") mutex.test end
Since flock mechanism doesn’t exist on windows we are using platform Mutex. We are creating a “Global” mutex here so that non-admin users can not DoS chef-client by creating the same named mutex we are using locally. Mutex name is case-sensitive contrary to other things in windows. “" is the only invalid character.
Source
# File lib/chef/run_lock.rb, line 192 def exit_from_timeout rp = runpid release # Just to be on the safe side... raise Chef::Exceptions::RunLockTimeout.new(time_to_wait, rp) end
Source
# File lib/chef/run_lock.rb, line 162 def reset @runlock = nil @mutex = nil @runpid = nil end
Source
# File lib/chef/run_lock.rb, line 180 def runpid @runpid ||= runlock.read.strip end
Source
# File lib/chef/run_lock.rb, line 188 def time_to_wait Chef::Config[:run_lock_timeout] end
Source
# File lib/chef/run_lock.rb, line 184 def timeout_given? !time_to_wait.nil? end