Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com Date: Tue, 26 Apr 2005 23:11:11 +0300 Message-Id: <200504262011.j3QKBBXO027057@beta.mvs.co.il> From: "Ehud Karni" To: vladislav DOT grinchenko AT comtechmobile DOT com Cc: cygwin AT cygwin DOT com Subject: Re: Locking file in cygwin In-reply-to: <6BD989BB73712F41B1EDD1B8A32EA4C385F3DC@EXCHANGEVS1.comtechtel.com> (message from Vladislav Grinchenko on Tue, 26 Apr 2005 09:21:40 -0400) Organization: Mivtach-Simon Insurance agencies Reply-to: ehud AT unix DOT mvs DOT co DOT il References: <6BD989BB73712F41B1EDD1B8A32EA4C385F3DC AT EXCHANGEVS1 DOT comtechtel DOT com> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-8-i X-IsSubscribed: yes Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from quoted-printable to 8bit by delorie.com id j3QKBk7G022375 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//stat you may use /proc//exename (this does not exist on UNIX, and /proc//exe needs root permission). Ehud. ------------------------- proc fs searching ------------------------- #include #include #include #include #include #include #include /* for kill */ char *get_pname ( pid_t pid ) ; /* find program name from pid returns program name or "" uses /proc//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//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//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 #include #include #include /* standard library */ #include #include #include /* for file 'stat' */ #include #include /* 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 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/