2011-12-21 67 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