file_name = $file_name; return true; } /** * @return null */ function getFileName() { return $this->file_name; } /** * @param $file_name * @return bool */ function setFileName( $file_name ) { if ( $file_name != '' ) { $this->file_name = $file_name; return true; } return false; } /** * @return bool|int */ function getCurrentPID() { if ( $this->use_pid == true && function_exists( 'getmypid' ) == true ) { $retval = getmypid(); //Debug::Text( 'Current PID: ' . $retval, __FILE__, __LINE__, __METHOD__, 10 ); return $retval; } return false; } /** * @param int|string $pid Process ID * @return bool|null */ function isPIDRunning( $pid ) { if ( $pid == '~STARTING' ) { //Used in create() Debug::Text( 'PID is ~STARTING, assume its running: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); return true; } else if ( $this->use_pid == true && (int)$pid > 0 && function_exists( 'posix_getpgid' ) == true ) { if ( posix_getpgid( $pid ) === false ) { Debug::Text( ' PID: '. $pid .' is NOT running!', __FILE__, __LINE__, __METHOD__, 10 ); return false; } else { Debug::Text( ' PID: '. $pid .' IS running!', __FILE__, __LINE__, __METHOD__, 10 ); return true; } } else { if ( trim( $pid ) == '' ) { Debug::Text( 'PID is blank, assume its NOT running: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); return false; } else { if ( OPERATING_SYSTEM == 'WIN' ) { //Debug::Text( 'Checking if PID is running on Windows: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); //Sometimes Windows can return: shell_exec(): Unable to execute 'tasklist.exe /FI "PID eq 13564" /FO CSV' // Not sure why, but silence the warning for now. $processes = array_map( 'str_getcsv', explode( "\n", @shell_exec( 'tasklist.exe /FI "PID eq ' . $pid . '" /FO CSV' ) ) ); //Filter tasklist to return just the PID we are looking for in CSV format. array_shift( $processes ); //Strip the first (header) off the array. if ( is_array( $processes ) ) { foreach ( $processes as $process ) { if ( isset( $process[1] ) && (int)$process[1] == (int)$pid ) { //PID Debug::Text( ' PID IS running: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); return true; } } Debug::Text( ' PID is NOT running: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); return false; } else { Debug::Text( 'Unable to get process list...', __FILE__, __LINE__, __METHOD__, 10 ); } } else { Debug::Text( ' ERROR: Unable to determine if PID is running... PID: ' . $pid, __FILE__, __LINE__, __METHOD__, 10 ); } } } return null; //Assuming the process is still running if the file exists and PID is invalid. } /** * @return bool|int */ function create( $initialize = false ) { //Attempt to create directory if it does not already exist. $file_name = $this->getFileName(); $dir = dirname( $file_name ); if ( file_exists( $dir ) == false ) { $mkdir_result = @mkdir( $dir, 0777, true ); //ugo+rwx if ( $mkdir_result == false ) { Debug::Text( 'ERROR: Unable to create lock file directory: ' . $dir, __FILE__, __LINE__, __METHOD__, 10 ); } else { Debug::Text( 'WARNING: Created lock file directory as it didnt exist: ' . $dir, __FILE__, __LINE__, __METHOD__, 10 ); } } $current_pid = $this->getCurrentPID(); //Write current PID to file, so we can check if its still running later on. $lock_file_pid = $this->readPIDFile( $file_name ); if ( ( $initialize == true && $lock_file_pid === false ) || ( $initialize == false && ( $lock_file_pid == '~STARTING' || $lock_file_pid === false ) ) ) { //Write file with locking, this prevents duplicate lock files with the same name from being created. $fp = @fopen( $file_name, 'wb'); if ( $fp ) { Debug::Text( ' Creating Lock File: ' . $file_name .' Initialize: '. (int)$initialize .' Existing Lock File PID: '. $lock_file_pid .' Current PID: '. $current_pid, __FILE__, __LINE__, __METHOD__, 10 ); @flock( $fp, LOCK_EX ); @chmod( $file_name, 0660 ); //ug+rw @fwrite( $fp, ( ( $initialize == true ) ? '~STARTING' : $current_pid ) ); // ~STARTING is used in isPIDRunning() @flock( $fp, LOCK_UN ); @fclose( $fp ); return true; } else { Debug::Text( ' ERROR: Unable to create Lock File: ' . $file_name .' Initialize: '. (int)$initialize, __FILE__, __LINE__, __METHOD__, 10 ); } } else { Debug::Text( ' ERROR: Unable to create Lock File: ' . $file_name .' already exists with PID: '. $lock_file_pid .'... Initialize: '. (int)$initialize, __FILE__, __LINE__, __METHOD__, 10 ); } return false; } /** * @return bool */ function delete( $check_own_pid = true ) { $current_pid = $this->getCurrentPID(); if ( file_exists( $this->getFileName() ) ) { if ( $check_own_pid == true ) { $lock_file_pid = $this->readPIDFile( $this->getFileName() ); if ( is_numeric( $lock_file_pid ) ) { if ( $current_pid != $lock_file_pid ) { Debug::Text( 'ERROR: Lock file is NOT our own, unable to delete... Lock File: ' . $this->getFileName() .' PID: '. $lock_file_pid .' Current PID: '. $current_pid, __FILE__, __LINE__, __METHOD__, 10 ); return false; } } else { Debug::Text( 'Lock file does not exist or is starting... Lock File: ' . $this->getFileName() .' PID: '. $lock_file_pid .' Current PID: '. $current_pid, __FILE__, __LINE__, __METHOD__, 10 ); } } Debug::Text( ' Deleting Lock File: ' . $this->getFileName() .' PID: '. $current_pid, __FILE__, __LINE__, __METHOD__, 10 ); return Misc::unlink( $this->getFileName() ); } else { Debug::text( ' WARNING: Failed to delete lock file, does not exist: ' . $this->getFileName() .' PID: '. $current_pid, __FILE__, __LINE__, __METHOD__, 10 ); } return false; } /** * @param $file_name * @return false|int */ function readPIDFile( $file_name ) { clearstatcache( true, $file_name ); if ( file_exists( $file_name ) ) { $lock_file_pid = @file_get_contents( $file_name ); if ( $lock_file_pid != '' ) { if ( $lock_file_pid != '~STARTING' ) { $lock_file_pid = (int)$lock_file_pid; } Debug::text( ' Lock file exists with PID: ' . $lock_file_pid . ' Lock File: ' . $file_name, __FILE__, __LINE__, __METHOD__, 10 ); return $lock_file_pid; } else { Debug::text( ' Lock file exists (or did) but does not contain a PID: ' . $lock_file_pid . ' Lock File: ' . $file_name, __FILE__, __LINE__, __METHOD__, 10 ); } } return false; } /** * @return bool|null */ function exists() { //Ignore lock files older than max_lock_file_age, so if the server crashes or is rebooted during an operation, it will start again the next day. clearstatcache(); $lock_file_pid = $this->readPIDFile( $this->getFileName() ); if ( $lock_file_pid !== false ) { //Check to see if PID is still running or not. $pid_running = $this->isPIDRunning( $lock_file_pid ); if ( $pid_running !== null ) { //PID result is reliable, use it. if ( $pid_running === false ) { Debug::text( ' Stale (PID not running) lock file exists with PID: ' . $lock_file_pid .' Removing Lock File: '. $this->getFileName(), __FILE__, __LINE__, __METHOD__, 10 ); Misc::unlink( $this->getFileName() ); } else if ( ( $pid_running == '~STARTING' && ( time() - @filemtime( $this->getFileName() ) ) > 300 ) ) { //If lock file is in "STARTING" state for more than 5 minutes, consider it stale. Debug::text( ' Stale lock file exists in STARTING mode... PID: ' . $lock_file_pid .' Removing Lock File: '. $this->getFileName(), __FILE__, __LINE__, __METHOD__, 10 ); Misc::unlink( $this->getFileName() ); } return $pid_running; } else if ( ( time() - @filemtime( $this->getFileName() ) ) > $this->max_lock_file_age ) { //PID result may not be reliable, fall back to using file time instead. return true; } } return false; } } ?>