class FormulaLock
  LOCKDIR = HOMEBREW_CACHE_FORMULA

  def initialize(name)
    @name = name
    @path = LOCKDIR.join("#{@name}.brewing")
    @lockfile = nil
  end

  def lock
    LOCKDIR.mkpath
    @lockfile = get_or_create_lockfile
    unless @lockfile.flock(File::LOCK_EX | File::LOCK_NB)
      raise OperationInProgressError, @name
    end
  end

  def unlock
    unless @lockfile.nil? || @lockfile.closed?
      @lockfile.flock(File::LOCK_UN)
      @lockfile.close
    end
  end

  def with_lock
    lock
    yield
  ensure
    unlock
  end

  private

  def get_or_create_lockfile
    if @lockfile.nil? || @lockfile.closed?
      @path.open(File::RDWR | File::CREAT)
    else
      @lockfile
    end
  end
end

require 'fcntl'

class FormulaLock
  LOCKDIR = HOMEBREW_CACHE_FORMULA

  def initialize(name)
    @name = name
    @path = LOCKDIR.join("#{@name}.brewing")
    @lockfile = nil
  end

  def lock
    LOCKDIR.mkpath
    @lockfile = get_or_create_lockfile
    unless @lockfile.flock(File::LOCK_EX | File::LOCK_NB)
      raise OperationInProgressError, @name
    end
  end

  def unlock
    unless @lockfile.nil? || @lockfile.closed?
      @lockfile.flock(File::LOCK_UN)
      @lockfile.close
    end
  end

  def with_lock
    lock
    yield
  ensure
    unlock
  end

  private

  def get_or_create_lockfile
    if @lockfile.nil? || @lockfile.closed?
      @lockfile = @path.open(File::RDWR | File::CREAT)
      @lockfile.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
      @lockfile
    else
      @lockfile
    end
  end
end

Set close-on-exec on lock file descriptors
Set close-on-exec on lock file descriptors

The formula locks used by the installer and commands like link and
unlink are backed by open files and flock(). The open file descriptors
are thus leaked to any subprocesses. This can result in weird behavior
in programs spawned from formula that do not expect to inherit these
descriptors.

Fix this by setting close-on-exec on the lock file descriptors.

Fixes #21486.
