LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-28-2003, 09:57 AM   #1
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Rep: Reputation: 30
Keeping a lock across execve()?


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.

Language: C
OS: *nix

----------------------------------
Gentoo w/ kernel 2.6-test9
 
Old 10-28-2003, 11:41 AM   #2
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
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;
}

Last edited by Hko; 10-28-2003 at 11:46 AM.
 
Old 10-28-2003, 12:30 PM   #3
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Original Poster
Rep: Reputation: 30
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.
 
Old 10-28-2003, 04:30 PM   #4
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Original Poster
Rep: Reputation: 30
If anyone cared to see what I came up with here it is:

Code:
	/* Lock down */
	pidlck.l_type = F_WRLCK;
	pidlck.l_whence = SEEK_SET;
	pidlck.l_start = 0;
	pidlck.l_len = 0;
	pidlck.l_pid = -1;
	try_set_lck:
	if (fcntl(pid_fd, F_SETLK, &pidlck) < 0) {
		if (errno == EINTR) {
			/* We got interrupted in the middle; try again */
			goto try_set_lck;
		}
		/* Lock is held. Do I have it? */
		else if (((errno == EACCES) || (errno == EAGAIN))
			&& (fcntl(pid_fd, F_GETLK, &pidlck) < 0)
			&& (pidlck.l_pid != getpid())) {
				/* No someone else does, exit gracefully */
				DBGMSG(MSG_NFO, "Another process has lock.\n");
				exit(EXIT_SUCCESS);
		}
		else {
			/* Some odd error */
			DBGMSG(MSG_ERR | MSG_LOG,
				"Error attempting to aquire lock.\n");
			exit(EXIT_FAILURE);
		}
	}

Last edited by LogicG8; 10-28-2003 at 04:52 PM.
 
Old 10-29-2003, 10:31 AM   #5
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
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.
Good point.
Thanks.
 
Old 10-29-2003, 10:32 AM   #6
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Originally posted by LogicG8
If anyone cared to see what I came up with here it is:
Well yes, I care of course.
I can fix a possible race condition in my program using this idea.

Last edited by Hko; 10-29-2003 at 10:33 AM.
 
Old 10-29-2003, 02:20 PM   #7
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Original Poster
Rep: Reputation: 30
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.

Thanks again Hko
 
Old 10-29-2003, 04:26 PM   #8
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
This is why I love open source.
I agree.

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:
Code:
/* .... */
if (errno == EACCES || errno == EAGAIN) {
/* .... */

Last edited by Hko; 10-29-2003 at 04:28 PM.
 
Old 10-29-2003, 06:03 PM   #9
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Original Poster
Rep: Reputation: 30
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.
 
Old 10-30-2003, 11:37 AM   #10
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
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.
 
Old 10-30-2003, 12:19 PM   #11
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Original Poster
Rep: Reputation: 30
That will teach me to check the dates on my sources of information.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
E: Could not get lock /var/lib/dpkg/lock - open (11 Resource temporarily unavailable) phreakshew Linux - Newbie 24 04-12-2019 10:42 AM
Caps Lock & Scroll Lock Flashing thedug Linux - General 6 06-07-2008 09:43 PM
Caps Lock and Num Lock leds dont work! npc Linux - Hardware 2 11-08-2005 10:40 AM
Num, Scroll, Caps/Lock all Lock up Slackware Zamochit Linux - Newbie 0 10-18-2004 07:47 PM
RedHat 9.0 freezes with blinking Caps lock and Scroll lock queen-bee Linux - Software 0 07-30-2004 10:40 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:13 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration