Subject: Re: Locking file in cygwin
From: "Ehud Karni" <[email-masked]>
To: [email-masked]
Cc: [email-masked]
Date: Tue, 26 Apr 2005 23:11:11 +0300

On Tue, 26 Apr 2005 09:21:40 -0400, Vladislav Grinchenko wrote:
>
> In short, this happens when I try to handle PID file locking to preclude
> multiple instances of the same program simultaneously running on a
> host.
>
> If there is a more preferable way of handling this task in cygwin/win32,
> I wouldn't mind implementing it.

You can use "file creation test & lock" instead of UNIX file area
locking. Attached source for "crtst-tmout" command is below.

To check if another copy is in memory you can check the /proc file
system. You can use the attached source below as an example.
Instead of /proc/<pid>/stat you may use /proc/<pid>/exename (this
does not exist on UNIX, and /proc/<pid>/exe needs root permission).

Ehud.

------------------------- proc fs searching -------------------------

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <signal.h>                        /* for kill */

char *get_pname ( pid_t pid ) ;            /* find program name from pid
                                              returns program name or ""
                                              uses /proc/<pid>/stat        */

pid_t next_pid ( int cont ) ;              /* get next pid from /proc
                                              cont should be 0 on 1st call,
                                                          != 0 on continuation
                                              returns: pid (>0)
                                                       0 - no more pids
                                                       <0 - error          */

void kill_old ( void )                             /* kill other instances */
{
pid_t my_pid , pid = 0 ;                           /* my pid , pid (temp) to check */
char my_name [ 256 ] , *pname ;                    /* my name, temp program name */

   my_pid = getpid ( ) ;                           /* get my pid */
   pname = get_pname ( my_pid ) ;
   if ( *pname == 0 )                              /* empty name - Error */
       return ;

   strcpy ( my_name ,pname ) ;                     /* save my name */

   while ( ( pid = next_pid ( pid ) ) > 0 )        /* next pid */
   {
       if ( pid == my_pid )                        /* ignore myself */
           continue ;;
       pname = get_pname ( pid ) ;

       if ( strcmp ( my_name , pname ) == 0 )
           kill ( pid , SIGKILL ) ;                /* send the kill signal */
   }

   if ( pid < 0 )                                  /* no process found - ERROR */
       return ;                                    /* (must have found myself) */
}
/*============================================================================*/

char *get_pname ( pid_t pid )                      /* find program name from pid */
{                                                  /* use /proc/<pid>/stat */
FILE *stt ;                                        /* stat virtual file Handel */
static char pname [256] , *none = "" ;             /* program name is < 256 chars */
char *nbeg, *nend ;                                /* temp pointer */

/* structure of 1st and only line of /proc/<pid>/stat
pid (name) stt ..... name is the exact disk name (upper case under DOS)
1300 (bash) S 1 1300 1300 1280
1988 (SLeeP-TsT) S 1 1988 1988 1280        */

   sprintf ( pname , "/proc/%d/stat" , pid ) ;     /* stat file name */
   stt = fopen ( pname , "rt" ) ;                  /* try to open */
   if ( stt == NULL )
   {
       return ( none ) ;                           /* no name (error signal) */
   }

   fgets ( pname , 256 , stt ) ;                   /* read 1st (only) line from stat */
   fclose ( stt ) ;                                /* close "stat", no check */

   if ( ( nbeg = strchr ( pname , '(' ) ) == NULL )/* search "(" before name */
   {
       return ( none ) ;                           /* no name (error signal) */
   }

   if ( ( nend = strchr ( nbeg , ')' ) ) == NULL ) /* search ")" after name */
   {
       return ( none ) ;                           /* no name (error signal) */
   }

   *nend = 0 ;                                     /* cap it */
   return ( ++ nbeg ) ;                            /* 1st char of program name */
}
/*=========================================================================*/

pid_t next_pid ( int cont )                        /* get next pid */
{                                                  /* cont = 0 - start new search */
static DIR *hdir = NULL ;                          /* handle for open directory */
struct dirent *dent ;                              /* directory entry pointer */
#define FPID ( dent->d_name )                      /* PID as file name pointer */

   if ( cont == 0 )                                /* new search */
   {
       if ( hdir != NULL )                         /* a search in progress ? */
           closedir ( hdir ) ;                     /* close directory (no check) */
       if ( ( hdir = opendir ( "/proc" ) ) == NULL )   /* open directory "/proc" */
       {
           fprintf ( stderr , "Could not open \"/proc\" virtual directory.\n" ) ;
           exit ( 1 ) ;                            /* error return ? */
       }
   }

   if ( hdir == NULL )                             /* no search started / already ended */
       return ( -1 ) ;                             /* no next pid */

   while ( ( dent = readdir ( hdir ) ) != NULL )   /* next entry */
   {
       if ( ( FPID [ 0 ] < '1' ) ||                /* pid number must start */
            ( FPID [ 0 ] > '9' ) )                 /* with 1 to 9 digit */
           continue ;
       return ( atoi ( FPID ) ) ;                  /* return pid as number */
   }
/* no more pids */
   closedir ( hdir ) ;                             /* close directory (no check) */
   hdir = NULL ;                                   /* search ended ! */
   return ( 0 ) ;                                  /* no more pids */
}
/*=========================================================================*/

------------------------- crtest-tmout -------------------------

/*  crtest-tmout: create file name (1st arg) with optional timeout (2nd arg)

    Exit code is 0 if the file is created successfully.

    If the file already exist the program sleeps for 7 seconds and trys again.

    Exit code of 1 is returned if the file can not be created because of
    any other error (invalid name, dir or system error).

    2nd argument is immidate/timeout value:
        Value of 0 cause the program to try only once and returns
        exit code 2 if the file exist.
        A positive value is Timoout (in seconds). If the lock age is
        greater than this, the lock is removed.
*/

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>                            /* standard library */
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>                          /* for file 'stat' */
#include <time.h>
#include <unistd.h>                            /* exit/sleep functions */

#define  SLEEP_TIME   7                        /*  time to sleep between trys  */

char *locknm ;                                 /* lock file name */
int tmout = -1 ;                               /* optional second arg - wait condition */

int chk_tmout ( void ) ;                       /* return 1 if lock has timed out */
void try_rplc ( void ) ;                       /* try to replace lock */

int main ( int argc, char *argv[] )
{
int  hndl = -1;                                /* open file handle */

   locknm = argv [ 1 ] ;                       /* lock file name */
   if ( argc > 2 )                             /* is there 2nd parameter ? */
       tmout = atoi ( argv [ 2 ] ) ;           /* Yes, get its numeric value */

   while (hndl < 0)
   {
       hndl = open ( locknm , O_RDWR | O_CREAT | O_EXCL , 0666 ) ;
       if ( hndl > 0 )
           exit ( 0 ) ;                        /* all ok */

       if (errno != EEXIST)
       {
           fprintf ( stderr , "File %s, open error: %d\n" , locknm , errno) ;

           exit (1) ;
       }

       if ( tmout == 0 )                       /* NO wait (2nd arg = 0) ? */
           exit ( 2 ) ;                        /* yes exit with error code 2 */

       if ( tmout > 0 )                        /* Timeout value (2nd arg > 0) ? */
           if ( chk_tmout () > 0 )             /* check if passed */
               try_rplc ( ) ;                  /* YES, try replace */

       sleep ( SLEEP_TIME ) ;                  /* no, wait for next time */
   }
   return ( 0 ) ;
}
/*===========================================================================*/

int chk_tmout ( void )                         /* return 1 if lock has timed out */
{
struct stat fs ;                               /* input file statistics */
time_t crrnt , ltime ;                         /* seconds from 1/1/1970 0.0.0 GMT */

   if ( stat ( locknm , & fs ) < 0 )           /* file statistics (times) */
       return ( -1 ) ;                         /* Error (whatever) */

   ltime = fs.st_mtime ;                       /* file time */
   crrnt = time ( NULL ) ;                     /* current time */

   if ( ( crrnt - ltime ) > tmout )            /* time out has passed */
       return ( 1 ) ;

   return ( -1 ) ;                             /* NOT timed out */
}
/*===========================================================================*/

void try_rplc ( void )                         /* try to replace lock */
{
char *tmp_nm = NULL ;
size_t nmlng ;
int hndl ;
   nmlng = strlen ( locknm ) + 15 ;            /* name of temp secondary lock */
   while ( ( tmp_nm = malloc ( nmlng ) ) == NULL )
       sleep ( 1 ) ;                           /* wait 1 sec */
   sprintf ( tmp_nm , "%s [*]   temp" , locknm ) ; /* temp name */

   hndl = open ( tmp_nm , O_RDWR | O_CREAT | O_EXCL , 0666 ) ;
   if ( hndl > 0 )
   {
       if ( chk_tmout () > 0 )                 /* Re check time out */
           if ( rename ( tmp_nm , locknm ) == 0 )  /* rename succeeded */
               exit ( 0 ) ;                    /* ALL OK ! ! ! */

       close ( hndl ) ;                        /* better close (free handle) */
       unlink ( tmp_nm ) ;                     /* MUST ! (otherwise will stop lock check) */
   }

   free ( tmp_nm ) ;                           /* free tmp_nm when failed */
}
/*===========================================================================*/

--
 Ehud Karni           Tel: +972-3-7966-561  /"\
 Mivtach - Simon      Fax: +972-3-7966-667  \ /  ASCII Ribbon Campaign
 Insurance agencies   (USA) voice mail and   X   Against   HTML   Mail
 http://www.mvs.co.il  FAX:  1-815-5509341  / \
 GnuPG: 98EA398D <http://www.keyserver.net/>    Better Safe Than Sorry

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/