Provided by: liblockfile-dev_1.16-1.1_amd64 bug

NAME

       lockfile_create, lockfile_remove, lockfile_touch, lockfile_check - manage lockfiles

SYNOPSIS

       #include <lockfile.h>

       cc [ flag ... ] file ... -llockfile [ library ]

       int lockfile_create( const char *lockfile, int retrycnt, int flags );
       int lockfile_remove( const char *lockfile );
       int lockfile_touch( const char *lockfile );
       int lockfile_check( const char *lockfile, int flags  );

DESCRIPTION

       The lockfile_create function creates a lockfile in an NFS safe way.

       If  flags  is  set  to  L_PID  or  L_PPID  then lockfile_create will not only check for an
       existing lockfile, but it will read the contents as well to see if it contains  a  process
       id in ASCII. If so, the lockfile is only valid if that process still exists.

       When  creating a lockfile, if L_PID is set in flags, then the current process' PID will be
       written to the lockfile. Sometimes it can be useful to use the parent's PID  instead  (for
       example,  the  dotlockfile  command  uses that). In such cases you can use the L_PPID flag
       instead.

       If the lockfile is on a shared filesystem, it might have been created by a  process  on  a
       remote host. Thus the process-id checking is useless and the L_PID flag should not be set.
       In this case, there is no good way to see  if  a  lockfile  is  stale.  Therefore  if  the
       lockfile  is  older  then  5  minutes,  it will be removed. That is why the lockfile_touch
       function is provided: while holding the lock, it needs to  be  refreshed  regulary  (every
       minute or so) by calling lockfile_touch ()  .

       The  lockfile_check  function checks if a valid lockfile is already present without trying
       to create a new lockfile.

       Finally the lockfile_remove function removes the lockfile.

RETURN VALUES

       lockfile_create returns one of the following status codes:

          #define L_SUCCESS   0    /* Lockfile created                      */
          #define L_NAMELEN   1    /* Recipient name too long (> 13 chars)  */
          #define L_TMPLOCK   2    /* Error creating tmp lockfile           */
          #define L_TMPWRITE  3    /* Can't write pid int tmp lockfile      */
          #define L_MAXTRYS   4    /* Failed after max. number of attempts  */
          #define L_ERROR     5    /* Unknown error; check errno            */
          #define L_ORPHANED  7    /* Called with L_PPID but parent is gone */

       lockfile_check returns 0 if a valid lockfile is  present.  If  no  lockfile  or  no  valid
       lockfile is present, -1 is returned.

       lockfile_touch  and  lockfile_remove  return  0  on success. On failure -1 is returned and
       errno is set appropriately. It  is  not  an  error  to  lockfile_remove()  a  non-existing
       lockfile.

ALGORITHM

       The  algorithm  that  is  used to create a lockfile in an atomic way, even over NFS, is as
       follows:

       1      A unique file is created. In printf format, the name of the  file  is  .lk%05d%x%s.
              The  first  argument  (%05d)  is  the  current process id. The second argument (%x)
              consists of the 4 minor bits of the value returned by time(2). The last argument is
              the system hostname.

       2      Then the lockfile is created using link(2). The return value of link is ignored.

       3      Now the lockfile is stat()ed. If the stat fails, we go to step 6.

       4      The stat value of the lockfile is compared with that of the temporary file. If they
              are the same, we have the lock. The temporary file is deleted  and  a  value  of  0
              (success) is returned to the caller.

       5      A  check is made to see if the existing lockfile is a valid one. If it isn't valid,
              the stale lockfile is deleted.

       6      Before retrying, we sleep for n seconds. n is initially 5 seconds, but after  every
              retry  5  extra  seconds  is  added  up  to a maximum of 60 seconds (an incremental
              backoff). Then we go to step 2 up to retries times.

REMOTE FILE SYSTEMS AND THE KERNEL ATTRIBUTE CACHE

       These functions do not lock a file - they generate a lockfile.  However in a lot of cases,
       such  as  Unix mailboxes, all concerned programs accessing the mailboxes agree on the fact
       that the presence of <filename>.lock means that <filename> is locked.

       If you are using lockfile_create to create a lock on a  file  that  resides  on  a  remote
       server,  and  you  already  have that file open, you need to flush the NFS attribute cache
       after locking. This is needed to prevent the following scenario:

       o  open /var/mail/USERNAME
       o  attributes, such as size, inode, etc are now cached in the kernel!
       o  meanwhile, another remote system appends data to /var/mail/USERNAME
       o  grab lock using lockfile_create()
       o  seek to end of file
       o  write data

       Now the end of the file really isn't  the  end  of  the  file  -  the  kernel  cached  the
       attributes  on  open, and st_size is not the end of the file anymore. So after locking the
       file, you need to tell the kernel to flush the NFS file attribute cache.

       The only portable way to do this is the POSIX fcntl() file locking primitives - locking  a
       file  using  fcntl()  has the fortunate side-effect of invalidating the NFS file attribute
       cache of the kernel.

       lockfile_create() cannot do this for you for two reasons. One, it just creates a lockfile-
       it  doesn't  know which file you are actually trying to lock! Two, even if it could deduce
       the file you're locking from the filename, by  just  opening  and  closing  it,  it  would
       invalidate  any  existing  POSIX  locks  the program might already have on that file (yes,
       POSIX locking semantics are insane!).

       So basically what you need to do is something like this:

         fd = open("/var/mail/USER");
         .. program code ..

         lockfile_create("/var/mail/USER.lock", x, y);

         /* Invalidate NFS attribute cache using POSIX locks */
         if (lockf(fd, F_TLOCK, 0) == 0) lockf(fd, F_ULOCK, 0);

       You have to be careful with this if you're putting this in an existing program that  might
       already be using fcntl(), flock() or lockf() locking- you might invalidate existing locks.

       There  is also a non-portable way. A lot of NFS operations return the updated attributes -
       and the Linux kernel actually uses these to update  the  attribute  cache.  One  of  these
       operations is chmod(2).

       So  stat()ing  a  file  and  then chmod()ing it to st.st_mode will not actually change the
       file, nor will it interfere with any locks  on  the  file,  but  it  will  invalidate  the
       attribute cache. The equivalent to use from a shell script would be

         chmod u=u /var/mail/USER

PERMISSIONS

       If  you are on a system that has a mail spool directory that is only writable by a special
       group (usually "mail") you cannot create a lockfile directly in  the  mailspool  directory
       without special permissions.

       Lockfile_create  and  lockfile_remove check if the lockfile ends in $USERNAME.lock, and if
       the directory the lockfile is writable by group "mail". If so, an  external  set  group-id
       mail executable (dotlockfile(1) ) is spawned to do the actual locking / unlocking.

FILES

       /usr/lib/liblockfile.so.1

AUTHOR

       Miquel van Smoorenburg <miquels@cistron.nl>

SEE ALSO

       dotlockfile(1), maillock(3), touchlock (3), mailunlock(3)