2011-12-21 39 views
0

我有一個腳本,我用它檢查存儲在我的hosts.allow文件中的IP地址,以反映什麼IP映射到我的dyndns主機名,這樣我就可以登錄到我的服務器,一旦我同步我的當前IP到那主機名。出於某種原因,儘管腳本似乎會造成真正的間歇性問題。替換丟棄字符

我的hosts.allow文件中我有這樣一段:

#SOme.gotdns.com 
sshd : 192.168.0.1 
#EOme.gotdns.com 

#SOme2.gotdns.com 
sshd : 192.168.0.2 
#EOme2.gotdns.com 

我有一個cron運行的腳本(每分鐘),看起來像這樣:

#!/usr/bin/php 
<?php 
$hosts = array('me.gotdns.com','me2.gotdns.com'); 
foreach($hosts as $host) 
{ 
     $ip = gethostbyname($host); 
     $replaceWith = "#SO".$host."\nsshd : ".$ip."\n#EO".$host; 
     $filename = '/etc/hosts.allow'; 
     $handle = fopen($filename,'r'); 
     $contents = fread($handle, filesize($filename)); 
     fclose($handle); 
     if (preg_match('/#SO'.$host.'(.*?)#EO'.$host.'/si', $contents, $regs)) 
     { 
       $result = $regs[0]; 
     } 
     if($result != $replaceWith) 
     { 
       $newcontents = str_replace($result,$replaceWith,$contents); 
       $handle = fopen($filename,'w'); 
       if (fwrite($handle, $newcontents) === FALSE) { 
       } 
       fclose($handle); 
     } 
} 
?> 

問題我的是間歇性字符正在被丟棄(我假設替換過程中),導致未來的更新失敗,因爲它插入類似的東西:

#SOme.gotdns.com 
sshd : 192.168.0.1 
#EOme.gotdn 

注意失蹤「s.com」

當然,這意味着我失去了對服務器的訪問,任何想法,爲什麼這會發生?

謝謝。

+1

你確定兩個腳本不會在同一時間運行? – RageZ 2011-12-21 10:33:05

+0

你的意思是在第一個實例完成之前再次運行相同的腳本嗎? – robjmills 2011-12-21 10:42:52

+0

是的,因爲腳本使用相同的文件進行讀寫,所以同時運行的兩個進程可能會產生一些有趣的結果。 – RageZ 2011-12-21 10:44:41

回答

1

我會說,爲了安全地做到這一點,你應該在腳本開始處的文件上,將它全部讀入內存一次,在內存中修改它,然後在最後寫回文件。就磁盤I/O而言,這也會更有效率。

您還應該更改cron作業以減少運行頻率。很可能您目前遇到此問題的原因是兩個進程同時運行 - 通過鎖定文件,如果是這種情況,則可能會導致進程堆疊等待獲取鎖定。每5分鐘設置一次應該足夠好 - 你的IP不應該經常改變

所以做這個(FIXED):

#!/usr/bin/php 
<?php 

    // Settings 
    $hosts = array(
    'me.gotdns.com', 
    'me2.gotdns.com' 
); 
    $filename = '/etc/hosts.allow'; 

    // No time limit (shouldn't be necessary with CLI, but just in case) 
    set_time_limit(0); 

    // Open the file in read/write mode and lock it 
    // flock() should block until it gets a lock 
    if ((!$handle = fopen($filename, 'r+')) || !flock($handle, LOCK_EX)) exit(1); 

    // Read the file 
    if (($contents = fread($handle, filesize($filename)) === FALSE) exit(1); 

    // Will be set to true if we actually make any changes to the file 
    $changed = FALSE; 

    // Loop hosts list 
    foreach ($hosts as $host) { 

    // Get current IP address of host 
    if (($ip = gethostbyname($host)) == $host) continue; 

    // Find the entry in the file 
    $replaceWith = "#SO{$host}\nsshd : {$ip}\n#EO{$host}"; 
    if (preg_match("/#SO{$host}(.*?)#EO{$host}/si", $contents, $regs)) { 
     // Only do this if there was a match - otherise risk overwriting previous 
     // entries because you didn't reset the value of $result 
     if ($regs[0] != $replaceWith) { 
     $changed = TRUE; 
     $contents = str_replace($regs[0], $replaceWith, $contents); 
     } 
    } 

    } 

    // We'll only change the contents of the file if the data changed 
    if ($changed) { 
    ftruncate($handle, 0); // Zero the length of the file 
    rewind($handle); // start writing from the beginning 
    fwrite($handle, $contents); // write the new data 
    } 

    flock($handle, LOCK_UN); // Unlock 
    fclose($handle); // close 
+0

對我來說有一些新功能。會嘗試這個謝謝 – robjmills 2011-12-21 11:14:32

2

這可能是由於腳本執行時間過短 - 或者1分鐘間隔太短。當cron正在完成這項工作時,腳本的另一個進程開始並可能影響第一個腳本。

+0

'這可能是由於腳本執行時間造成的 - 這個*不應該是CLI的問題,時間限制應該只適用於web請求。你應該真的鎖定文件以對抗多個實例的可能性。 – DaveRandom 2011-12-21 11:08:39

2

這幾乎肯定是因爲在通過cron重新啓動之前,腳本在一分鐘內沒有完成執行。您需要實施某種鎖定,或者使用只允許運行一次腳本實例的工具。有幾種工具可以做到這一點,例如lockrun