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).