替换掉落字符

时间:2011-12-21 10:27:15

标签: php replace cron preg-match fwrite

我使用的脚本检查存储在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

注意缺少的&#34; s.com&#34;

这当然意味着我无法访问服务器,任何想法为什么会发生这种情况?

感谢。

3 个答案:

答案 0 :(得分:2)

可能是由于脚本执行时间 - 可能太短 - 或者1分钟间隔太短。当cron正在完成这项工作时,另一个脚本程序启动,它可能影响第一个脚本。

答案 1 :(得分:2)

这几乎可以肯定是因为脚本在通过cron再次启动之前的一分钟时间内没有完成执行。您需要实现某种锁定,或使用仅允许运行一次脚本实例的工具。有几种工具可以做到这一点,例如lockrun

答案 2 :(得分:1)

我想说为了安全地执行此操作,您应该在脚本开头的文件上acquire an exclusive lock,将其全部读入内存,在内存中修改,然后将其写回文件在末尾。就磁盘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