ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Problem:
How do I determine whether or not my process has
a lock on a file? calling flock() w/ LOCK_NB and checking
errno tells me that there is a lock but I want to know
if my process is the one that owns the lock.
Background:
I'm writing a daemon program that I only want to have
one instance running at a time. I create a file containing
just my programs pid and use a flock() on the file to make
sure my program only runs once. Now my problem is that
That my routines for parsing my config file use functions
that aren't async signal safe. (I use malloc() to build a
linked list). To get around that I wanted to keep my lock
leave the old process with execve() and upon starting
a new check for the existance of the lock and then see if
I am the process that owns it.
My "hack" solution:
If there is a lock on my pidfile I can read the file anyway
since flock() is only advisory locking. I can check in the
file to see if my pid equals the stored pid, but this
seems like a hack and I want to do the right thing (tm).
The "solution" creates a race where one instance of my
program could be writing its pid while another instance
of my program tries to read it.
At the moment I'm just finishing a program that needed the same thing. I did this by creating a temp-file in /tmp and locking it with fcntl(), because that allows to test if you can aquire a lock, and more important, it tells you at the same time which process-ID is holding a lock that would conflict with the lock you test for with fcntl().
I took the parts of my program that does this to demonstrate this. It has become quite complex thoug, for just ensuring one instance. If somebody knows a simpler way to do this, I would be very interested. Anyways, it work very well.
Code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <syslog.h>
#include <sys/types.h>
#define LOCKFILE "/tmp/daemon_lock_file.!"
static int lck;
void removelockfile(int sig)
{
/* In this simple case, the required parameter int sig (see
* man sigaction) not used here.
*/
syslog(LOG_INFO, "Exiting...");
if (close(lck) < 0) perror("removelockfile(): close()");
if (unlink(LOCKFILE) < 0) perror("removelockfile(): unlink()");
}
void myexit(void)
{
/* Wrapper because atexit() and sigaction require different
* function signatures: "void func(void)" and "void func(int)"
*/
removelockfile(0);
}
void getlock(void)
{
struct flock dlock;
struct sigaction sa;
dlock.l_type = F_WRLCK;
dlock.l_whence = SEEK_SET;
dlock.l_start = 0;
dlock.l_len = 0;
dlock.l_pid = -1;
if (fcntl(lck, F_SETLK, &dlock) < 0) {
perror("getlock(): F_SETLK");
exit(EXIT_FAILURE);
}
/* Ensure the tmp file is removed at exit */
atexit(myexit);
/* Also when QUIT signal arrives */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = removelockfile;
sigaction(SIGQUIT, &sa, NULL);
}
pid_t getlockpid(void)
{
struct flock serflock;
if ((lck = open(LOCKFILE, O_RDWR | O_CREAT, 0666)) == -1) {
perror("lockpid(): open(lockfile)");
exit(EXIT_FAILURE);
}
serflock.l_type = F_WRLCK;
serflock.l_whence = SEEK_SET;
serflock.l_start = 0;
serflock.l_len = 0;
serflock.l_pid = -1;
/* With F_GETLK specified, we are not actually trying to get
* a lock, but only retrieve info about possible exiting lock(s)
* that would prevent getting this lock.
* This info includes the PID of the process holding an existing lock that
* would prevent us from getting this lock (if we actually would try).
*/
if (fcntl(lck, F_GETLK, &serflock) < 0) {
perror("lockpid(): F_GETLK");
exit(EXIT_FAILURE);
}
/* If a lock is found (i.e. there is a instance running), we
* get the PID of the lock-holding process from the struct
* filled with data by fcntl().
*/
if (serflock.l_pid > 0) {
return serflock.l_pid;
}
return (pid_t) 0;
}
int main(int argc, char **argv)
{
pid_t dpid;
/* Get PID of running instance of this daemon.
* getlockpid() return 0 if there's no instance found.
*
* If no instance found, this will create the lock file,
* which we will later (after fork() or daemon() ) put a lock on.
*/
dpid = getlockpid();
/* If any argument on command line, kill
* the running instance of this program.
*/
if (argc >= 2) {
if (!dpid) {
fprintf(stderr, "No instance of %s found to kill.\n", argv[0]);
return 1;
}
if (kill(dpid, SIGQUIT) < 0) {
perror("Kill daemon");
fprintf(stderr, "Could not kill process %d.\n", dpid);
return 1;
}
printf("Killed daemon process with pid: %d.\n", dpid);
return 0;
}
/* Once here, the daemon is trying to start instead of killing
* a running instance.
*/
if (dpid) {
fprintf(stderr, "Daemon already running at pid %d.\n", dpid);
return 1;
}
/* Forking to become daemon here.
* This must be done *before* getting a lock, because locks
* created with fcntl() ("record locks") are NOT preserved
* across a fork(). This is different from locks created
* with flock().
*/
if(daemon(0,0) < 0) {
perror("daemon()");
fprintf(stderr, "ERROR: Could not fork to daemon mode.");
return 1;
}
syslog(LOG_INFO, "Forked!");
/* Create a lock on the temp file */
getlock();
/* This is a demo, so the daemon just sleeps for some time */
sleep(120); /* 2 minutes */
return 0;
}
Originally posted by LogicG8 Thanks, that's exactly what I need except I will try and test/grab the
lock atomically rather than test and then grab which is racy.
This is why I love open source. I got help I needed from someone
on a different continent and the solution to my problem helped them.
Having a global community is so convenient.
Not sure how much it really matters, but I think putting the label in front of the struct initialisation may be better, as fcntl() may have changed its contents.
Code:
try_set_lck:
pidlck.l_type = F_WRLCK;
pidlck.l_whence = SEEK_SET;
pidlck.l_start = 0;
pidlck.l_len = 0;
pidlck.l_pid = -1;
if (fcntl(pid_fd, F_SETLK, &pidlck) < 0) {
if (errno == EINTR) {
/* We got interrupted in the middle; try again */
goto try_set_lck;
Quote:
Code:
/* .... */
/* Lock is held. Do I have it? */
else if (((errno == EACCES) || (errno == EAGAIN))
&& (fcntl(pid_fd, F_GETLK, &pidlck) < 0)
&& (pidlck.l_pid != getpid())) {
/* .... */
Not all those checks are needed. If the current process itself already owns the lock, fcntl() will not fail. So just this is will do:
Good points, fcntl() could do anything with the pointer I pass
so I should re-init my variable. As for those checks you are
right, I can get rid of them. They are a holdover from using flock() which would block on trying to aquire the lock a second
time. Since flock() (at least on linux) is implemented with fcntl()
I assumed using fcntl() would fail rather than block but it does
not. flock() must use F_SETLKW. I'll test for curiousity's sake
later.
Originally posted by LogicG8 Since flock() (at least on linux) is implemented with fcntl() [..snip..]
On recent Linuces, it's not anymore like that. From the flock man page:
Quote:
flock(2) does not lock files over NFS. Use fcntl(2) instead: that does work over NFS, given a sufficiently recent version of Linux and a server which supports locking.
Since kernel 2.0, flock(2) is implemented as a system call in its own right rather than being emulated in the GNU C library as a call to fcntl(2). This yields true BSD semantics: there is no interaction between the types of lock placed by flock(2) and fcntl(2), and flock(2) does not detect deadlock.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.