6

I am writing a script to do some apt commands, but I am running into potential issues of the apt/dpkg databases being locked so my script bails. I want to check the lock files (i.e. /var/lib/dpkg/lock) before doing anything just like apt does when it runs its command, but I can't figure out how apt is performing the lock.

The lock file is always there, and apt-get is not doing a flock on the file. How else would it be checking to see if it is in use? From an strace I see that apt-get opens the file, but that is it. In my script, I can open the file while apt-get has it open as well.

3
  • Why can't you just catch the error and return an understandable error code before bailing? You didn't say what language you are using, but all script languages that I know of would let you do this, including ksh, Ruby, Perl and almost certainly Python as well.
    – Mei
    Jan 13, 2011 at 21:57
  • 1
    Let me add: the scripting languages (Perl, Ruby, Python) should also have function libraries to interface with APT: could those help?
    – Mei
    Jan 13, 2011 at 21:58
  • I'm using ruby, but come to find out ruby doesn't fully implement fcntl. I'd rather look for the lock the same way as apt instead of wasting time to let apt start and fail.
    – gondoi
    Jan 14, 2011 at 14:56

4 Answers 4

5

Well I thought there would be a simple answer here but I can't find anything. First, are you 100% sure that lock file is always there? Try running

lsof /var/lib/dpkg/lock

as root to see if any process has it open.

From what I've read, apt-get does an fcntl lock, but I haven't looked at the code to verify. I guess that wuld explain why the file is there all the time, apt just locks it as needed.

What about just doing a check of the process list when your script runs, and exiting if apt is running at the same time? Would that be sufficient for your use?

Looks like this person went down the same path as you did, without much success.

1
  • I thought of the lsof, but I wanted to use the same lock as apt. I did find that the fcntl is how it is locking from an strace.
    – gondoi
    Jan 14, 2011 at 14:56
5

I came here looking for a solution similar to what gondoi ended up using but written in Python instead of Ruby. The following seems to work well:

import fcntl

def is_dpkg_active():
    """
    Check whether ``apt-get`` or ``dpkg`` is currently active.

    This works by checking whether the lock file ``/var/lib/dpkg/lock`` is
    locked by an ``apt-get`` or ``dpkg`` process, which in turn is done by
    momentarily trying to acquire the lock. This means that the current process
    needs to have sufficient privileges.

    :returns: ``True`` when the lock is already taken (``apt-get`` or ``dpkg``
              is running), ``False`` otherwise.
    :raises: :py:exc:`exceptions.IOError` if the required privileges are not
             available.

    .. note:: ``apt-get`` doesn't acquire this lock until it needs it, for
              example an ``apt-get update`` run consists of two phases (first
              fetching updated package lists and then updating the local
              package index) and only the second phase claims the lock (because
              the second phase writes the local package index which is also
              read from and written to by ``dpkg``).
    """
    with open('/var/lib/dpkg/lock', 'w') as handle:
        try:
            fcntl.lockf(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
            return False
        except IOError:
            return True
3

From a shell script (see flock(1)):

flock --timeout 60 --exclusive --close /var/lib/dpkg/lock apt-get -y -o Dpkg::Options::="--force-confold" upgrade
if [ $? -ne 0 ]; then
  echo "Another process has f-locked /var/lib/dpkg/lock" 1>&2
  exit 1
fi
2

I found out that apt is using a fcntl. Since I am using Ruby for the scripting language, I had to create my own function to look for lock. The reason for this, is because Ruby does not implement the fcntl function entirely. It only provides the function call and constants. The ability to build flock structs and how to pass them is left out or not documented.

Here is the list I found talking about that.

Here is the function I ended up writing:

def flocked? &block
  flockstruct = [Fcntl::F_RDLCK, 0, 0, 0, 0].pack("ssqqi")
  fcntl Fcntl::F_GETLK, flockstruct
  status = flockstruct.unpack("ssqqi")[0]
  case status
    when Fcntl::F_UNLCK
      return false 
    when Fcntl::F_WRLCK|Fcntl::F_RDLCK
      return true
    else
      raise SystemCallError, status
  end
end
2
  • So to be clear, this method is intended to be monkey-patched onto the File class? It certainly doesn't work if you define it in the main context, as fcntl isn't a method there, and you haven't provided any mechanism for passing in the file to check. Also, what's the &block for? It's never called. Also, you left out the require 'fcntl'
    – Ajedi32
    Aug 5, 2016 at 20:13
  • Also, in my tests, this code only succeeds in testing for write locks. Fcntl::F_WRLCK|Fcntl::F_RDLCK is equivalent to Fcntl::F_WRLCK, since Fcntl::F_RDLCK is 0, and even aside from that, passing Fcntl::F_RDLCK in the flockstruct means that the system call will respond with Fcntl::F_UNLCK anyway unless the file is write-locked...
    – Ajedi32
    Aug 9, 2016 at 14:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .