Search code examples
cfcntl

fcntl how to know which process hold lock file?


I'm new with fcntl locking and following this example to create a sample lock in linux using c code: http://www.informit.com/articles/article.aspx?p=23618&seqNum=4

I wonder how can we can print out which process hold the lock file and which process is waiting for lock. I consider using l_pid to figure out the process id which is holding the lock but i'm not sure the right way to do it. What is the best way to print out which process is holding the lock?


Solution

  • As the man 2 fcntl page describes, you can use the F_GETLK to obtain the process ID that has the conflicting lock (if the conflicting lock is a process-associated one). So, for example,

    /* Return 0 if descriptor locked exclusively, positive PID if
       a known process holds a conflicting lock, or -1 if the
       descriptor cannot be locked (and errno has the reason).
    */
    static pid_t  lock_exclusively(const int fd)
    {
        struct flock  lock;
        int           err = 0;
    
        if (fd == -1) {
            errno = EINVAL;
            return -1;
        }
    
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        if (!fcntl(fd, F_SETLK, &lock))
            return 0;
    
        /* Remember the cause of the failure */
        err = errno;
    
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        lock.l_pid = 0;
        if (fcntl(fd, F_GETLK, &lock) == 0 && lock.l_pid > 0)
            return lock.l_pid;
    
        errno = err;
        return -1;
    }
    

    Do note that fd must be open for reading and writing. I recommend using open(path, O_RDWR | O_NOCTTY) or open(path, O_WRONLY | O_NOCTTY). Closing any file descriptor to the same file will release the lock.

    Some may say that re-setting the lock memebers before the second fcntl() call is unnecessary, but I'd rather err on the side of caution here.

    As to how to report it, I would simply use

    int    fd;
    pid_t  p;
    
    fd = open(path, O_RDWR | O_NOCTTY);
    if (fd == -1) {
        fprintf(stderr, "%s: Cannot open file: %s.\n",
                        path, strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    p = lock_exclusively(fd);
    if (p < 0) {
        fprintf(stderr, "%s: Cannot lock file: %s.\n",
                        path, strerror(errno));
        exit(EXIT_FAILURE);
    } else
    if (p > 0) {
        fprintf(stderr, "%s: File is already locked by process %ld.\n",
                        path, (long)p);
        exit(EXIT_FAILURE);
    }
    
    /* fd is now open and exclusive-locked. */
    

    The user can always run e.g. ps -o cmd= -p PID to see what command that is (or you can try reading /proc/PID/cmdline in Linux).