程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 用PHP構建一個簡易監視引擎

用PHP構建一個簡易監視引擎

編輯:關於PHP編程

  摘要:在本文中,讓我們共同探討基於PHP語言構建一個基本的服務器端監視引擎的諸多技巧及注意事項,並給出完整的源碼實現。

  一. 更改工作目錄的問題

  當你編寫一個監視程序時,讓它設置自己的工作目錄通常更好些。這樣以來,如果你使用一個相對路徑讀寫文件,那麼,它會根據情況自動處理用戶期望存放文件的位置。總是限制程序中使用的路徑盡管是一種良好的實踐;但是,卻失去了應有的靈活性。因此,改變你的工作目錄的最安全的方法是,既使用chdir()也使用chroot()。

  chroot()可用於PHP的CLI和CGI版本中,但是卻要求程序以根權限運行。chroot()實際上把當前進程的路徑從根目錄改變到指定的目錄。這使得當前進程只能執行存在於該目錄下的文件。經常情況下,chroot()由服務器作為一個"安全設備"使用以確保惡意代碼不會修改一個特定的目錄之外的文件。請牢記,盡管chroot()能夠阻止你訪問你的新目錄之外的任何文件,但是,任何當前打開的文件資源仍然能夠被存取。例如,下列代碼能夠打開一個日志文件,調用chroot()並切換到一個數據目錄;然後,仍然能夠成功地登錄並進而打開文件資源:

<?php

$logfile = fopen("/var/log/chroot.log", "w");

chroot("/Users/george");

fputs($logfile, "Hello From Inside The Chroot ");

?>

  如果一個應用程序不能使用chroot(),那麼你可以調用chdir()來設置工作目錄。例如,當代碼需要加載特定的代碼(這些代碼能夠在系統的任何地方被定位時),這是很有用的。注意,chdir()沒有提供安全機制來防止打開未授權的文件。

  二. 放棄特權

  當編寫Unix守護程序時,一種經典的安全預防措施是讓它們放棄所有不需要的特權;否則,擁有不需要的特權容易招致不必要的麻煩。在代碼(或PHP本身)中含有漏洞的情況下,通過確保一個守護程序以最小權限用戶身份運行,往往能夠使損失減到最小。

  一種實現此目的的方法是,以非特權用戶身份執行該守護程序。然而,如果程序需要在一開始就打開非特權用戶無權打開的資源(例如日志文件,數據文件,套接字,等等)的話,這通常是不夠的。

  如果你以根用戶身份運行,那麼你能夠借助於posix_setuid()和posiz_setgid()函數來放棄你的特權。下面的示例把當前運行程序的特權改變為用戶nobody所擁有的那些權限:

$pw=posix_getpwnam('nobody');

posix_setuid($pw['uid']);

posix_setgid($pw['gid']);

  就象chroot()一樣,任何在放棄特權之前被打開的特權資源都會保持為打開,但是不能創建新的資源。

  三. 保證排它性

  你可能經常想實現:一個腳本在任何時刻僅運行一個實例。為了保護腳本,這是特別重要的,因為在後台運行容易導致偶然情況下調用多個實例。

  保證這種排它性的標准技術是,通過使用flock()來讓腳本鎖定一個特定的文件(經常是一個加鎖文件,並且被排它式使用)。如果鎖定失敗,該腳本應該輸出一個錯誤並退出。下面是一個示例:

$fp=fopen("/tmp/.lockfile","a");

if(!$fp || !flock($fp, LOCK_EX | LOCK_NB)) {

 fputs(STDERR, "Failed to acquire lock ");

 exit;

}

/*成功鎖定以安全地執行工作*/

  注意,有關鎖機制的討論涉及較多內容,在此不多加解釋。

  四. 構建監視服務

  在這一節中,我們將使用PHP來編寫一個基本的監視引擎。因為你不會事先知道怎樣改變,所以你應該使它的實現既靈活又具可能性。

該記錄程序應該能夠支持任意的服務檢查(例如,HTTP和FTP服務)並且能夠以任意方式(通過電子郵件,輸出到一個日志文件,等等)記錄事件。你當然想讓它以一個守護程序方式運行;所以,你應該請求它輸出其完整的當前狀態。

  一個服務需要實現下列抽象類:

abstract class ServiceCheck {

 const FAILURE = 0;

 const SUCCESS = 1;

 protected $timeout = 30;

 protected $next_attempt;

 protected $current_status = ServiceCheck::SUCCESS;

 protected $previous_status = ServiceCheck::SUCCESS;

 protected $frequency = 30;

 protected $description;

 protected $consecutive_failures = 0;

 protected $status_time;

 protected $failure_time;

 protected $loggers = array();

 abstract public function __construct($params);

 public function __call($name, $args)

 {

  if(isset($this->$name)) {

   return $this->$name;

  }

 }

 public function set_next_attempt()

 {

  $this->next_attempt = time() + $this->frequency;

 }

 public abstract function run();

 public function post_run($status)

 {

  if($status !== $this->current_status) {

   $this->previous_status = $this->current_status;

  }

  if($status === self::FAILURE) {

   if( $this->current_status === self::FAILURE ) {

    $this->consecutive_failures++;

   }

   else {

    $this->failure_time = time();

   }

  }

  else {

   $this->consecutive_failures = 0;

  }

  $this->status_time = time();

  $this->current_status = $status;

  $this->log_service_event();

 }

 public function log_current_status()

 {

  foreach($this->loggers as $logger) {

   $logger->log_current_status($this);

  }

 }

 private function log_service_event()

 {

  foreach($this->loggers as $logger) {

   $logger->log_service_event($this);

  }

 }

 public function register_logger(ServiceLogger $logger)

 {

  $this->loggers[] = $logger;

 }

}

上面的__call()重載方法提供對一個ServiceCheck對象的參數的只讀存取操作:

  · timeout-在引擎終止檢查之前,這一檢查能夠掛起多長時間。

  · next_attempt-下次嘗試連接到服務器的時間。

  · current_status-服務的當前狀態:SUCCESS或FAILURE。

  · previous_status-當前狀態之前的狀態。

  · frequency-每隔多長時間檢查一次服務。

  · description-服務描述。

  · consecutive_failures-自從上次成功以來,服務檢查連續失敗的次數。

  · status_time-服務被檢查的最後時間。

  · failure_time-如果狀態為FAILED,則它代表發生失敗的時間。

  這個類還實現了觀察者模式,允許ServiceLogger類型的對象注冊自身,然後當調用log_current_status()或log_service_event()時調用它。

  這裡實現的關鍵函數是run(),它負責定義應該怎樣執行檢查。如果檢查成功,它應該返回SUCCESS;否則返回FAILURE。


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved