บทคัดย่อ: ในบทความนี้ เราจะมาพูดถึงเทคนิคและข้อควรระวังมากมายสำหรับการสร้างกลไกตรวจสอบฝั่งเซิร์ฟเวอร์ขั้นพื้นฐานโดยใช้ภาษา PHP และให้การใช้งานซอร์สโค้ดโดยสมบูรณ์
1. ปัญหาในการเปลี่ยนไดเร็กทอรีการทำงาน
เมื่อคุณเขียนโปรแกรมตรวจสอบ โดยปกติแล้ว ปล่อยให้โปรแกรมตั้งค่าไดเร็กทอรีการทำงานของตัวเองจะดีกว่า ด้วยวิธีนี้ หากคุณใช้พาธสัมพัทธ์ในการอ่านและเขียนไฟล์ ระบบจะจัดการตำแหน่งที่ผู้ใช้คาดว่าไฟล์จะถูกจัดเก็บโดยอัตโนมัติตามสถานการณ์ แม้ว่าจะเป็นแนวปฏิบัติที่ดีที่จะจำกัดเส้นทางที่ใช้ในโปรแกรมเสมอไป แต่ก็สูญเสียความยืดหยุ่นที่สมควรได้รับ ดังนั้น วิธีที่ปลอดภัยที่สุดในการเปลี่ยนไดเร็กทอรีการทำงานของคุณคือการใช้ทั้ง chdir() และ chroot()
chroot() สามารถใช้ใน PHP เวอร์ชัน CLI และ CGI ได้ แต่ต้องการให้โปรแกรมทำงานด้วยสิทธิ์ root chroot() เปลี่ยนพาธของกระบวนการปัจจุบันจากไดเร็กทอรีรากไปเป็นไดเร็กทอรีที่ระบุ ซึ่งช่วยให้กระบวนการปัจจุบันดำเนินการเฉพาะไฟล์ที่มีอยู่ในไดเร็กทอรีนั้นเท่านั้น บ่อยครั้งที่เซิร์ฟเวอร์ใช้ chroot() เป็น "อุปกรณ์รักษาความปลอดภัย" เพื่อให้แน่ใจว่าโค้ดที่เป็นอันตรายจะไม่แก้ไขไฟล์นอกไดเร็กทอรีที่ระบุ โปรดทราบว่าแม้ว่า chroot() จะป้องกันไม่ให้คุณเข้าถึงไฟล์ใดๆ นอกไดเร็กทอรีใหม่ของคุณ แต่ทรัพยากรไฟล์ที่เปิดอยู่ในปัจจุบันยังคงสามารถเข้าถึงได้ ตัวอย่างเช่น โค้ดต่อไปนี้สามารถเปิดไฟล์บันทึก เรียก chroot() และสลับไปยังไดเร็กทอรีข้อมูล จากนั้น ยังสามารถเข้าสู่ระบบและเปิดทรัพยากรไฟล์ได้สำเร็จ:
<?php
$logfile = fopen("/var/log/chroot.log", "w");
chroot("/ผู้ใช้/จอร์จ");
fputs($logfile, "สวัสดีจากภายใน Chrootn");
?>
หากแอปพลิเคชันไม่สามารถใช้ chroot() คุณสามารถเรียก chdir() เพื่อตั้งค่าไดเร็กทอรีการทำงานได้ สิ่งนี้มีประโยชน์ ตัวอย่างเช่น เมื่อโค้ดจำเป็นต้องโหลดโค้ดเฉพาะที่สามารถอยู่ที่ใดก็ได้ในระบบ โปรดทราบว่า chdir() ไม่มีกลไกรักษาความปลอดภัยเพื่อป้องกันการเปิดไฟล์โดยไม่ได้รับอนุญาต
2. ละทิ้งสิทธิพิเศษ
เมื่อเขียน Unix daemons ข้อควรระวังด้านความปลอดภัยแบบคลาสสิกคือการให้พวกเขาละทิ้งสิทธิพิเศษที่ไม่จำเป็นทั้งหมด มิฉะนั้น การมีสิทธิ์ที่ไม่จำเป็นอาจนำไปสู่ปัญหาที่ไม่จำเป็นได้อย่างง่ายดาย ในกรณีของช่องโหว่ในโค้ด (หรือ PHP เอง) ความเสียหายมักจะลดลงได้โดยทำให้แน่ใจว่า daemon ทำงานในฐานะผู้ใช้ที่ได้รับสิทธิพิเศษน้อยที่สุด
วิธีหนึ่งในการบรรลุผลสำเร็จคือการรัน daemon ในฐานะผู้ใช้ที่ไม่มีสิทธิ์ อย่างไรก็ตาม โดยปกติแล้วจะไม่เพียงพอหากโปรแกรมจำเป็นต้องเปิดทรัพยากรที่ผู้ใช้ที่ไม่มีสิทธิ์ไม่มีสิทธิ์ในการเปิด (เช่น ไฟล์บันทึก ไฟล์ข้อมูล ซ็อกเก็ต ฯลฯ)
หากคุณใช้งานในฐานะ root คุณสามารถสละสิทธิ์ของคุณได้โดยใช้ฟังก์ชัน posix_setuid() และ posiz_setgid() ตัวอย่างต่อไปนี้เปลี่ยนสิทธิ์ของโปรแกรมที่รันอยู่เป็นสิทธิ์ของผู้ใช้ none:
$pw=posix_getpwnam('nobody');
posix_setuid($pw['uid']);
posix_setgid($pw['gid']);
เช่นเดียวกับ chroot() ทรัพยากรที่มีสิทธิพิเศษใดๆ ที่ถูกเปิดก่อนที่จะสละสิทธิ์จะยังคงเปิดอยู่ แต่ไม่สามารถสร้างทรัพยากรใหม่ได้
3. รับประกันความพิเศษ
คุณอาจต้องการบรรลุผล: มีสคริปต์เพียงอินสแตนซ์เดียวเท่านั้นที่ทำงานอยู่ตลอดเวลา นี่เป็นสิ่งสำคัญอย่างยิ่งในการปกป้องสคริปต์ เนื่องจากการเรียกใช้สคริปต์ในเบื้องหลังอาจทำให้เกิดการเรียกใช้หลายอินสแตนซ์โดยไม่ตั้งใจได้อย่างง่ายดาย
เทคนิคมาตรฐานในการรับรองความพิเศษเฉพาะนี้คือการให้สคริปต์ล็อกไฟล์เฉพาะ (ซึ่งมักจะเป็นไฟล์ที่ถูกล็อก และใช้เฉพาะ) โดยใช้ flock() หากการล็อคล้มเหลว สคริปต์ควรพิมพ์ข้อผิดพลาดและออก นี่คือตัวอย่าง:
$fp=fopen("/tmp/.lockfile","a");
ถ้า(!$fp || !ฝูง($fp, LOCK_EX | LOCK_NB)) {
fputs(STDERR, "ไม่สามารถรับการล็อคn");
ออก;
-
/*ล็อกเรียบร้อยแล้วเพื่อให้ทำงานได้อย่างปลอดภัย*/
โปรดทราบว่าการอภิปรายเกี่ยวกับกลไกการล็อกเกี่ยวข้องกับเนื้อหาเพิ่มเติม และจะไม่มีการอธิบายไว้ที่นี่
4. การสร้างบริการตรวจสอบ
ในส่วนนี้ เราจะใช้ PHP เพื่อเขียนเครื่องมือตรวจสอบขั้นพื้นฐาน เนื่องจากคุณจะไม่ทราบล่วงหน้าว่าจะต้องเปลี่ยนแปลงอย่างไร คุณจึงควรทำให้การใช้งานมีความยืดหยุ่นและเป็นไปได้
คนตัดไม้ควรสามารถรองรับการตรวจสอบบริการที่กำหนดเองได้ (เช่น บริการ HTTP และ FTP) และสามารถบันทึกเหตุการณ์ได้ไม่ว่าด้วยวิธีใดก็ตาม (ผ่านอีเมล ส่งออกไปยังไฟล์บันทึก ฯลฯ) แน่นอนคุณต้องการให้มันทำงานเป็น daemon ดังนั้นคุณควรขอให้มันแสดงสถานะปัจจุบันที่สมบูรณ์
บริการจำเป็นต้องใช้คลาสนามธรรมต่อไปนี้:
คลาสนามธรรม ServiceCheck {
const ล้มเหลว = 0;
ความสำเร็จ = 1;
ป้องกัน $timeout = 30;
ได้รับการคุ้มครอง $next_attempt;
ป้องกัน $current_status = ServiceCheck::SUCCESS;
ป้องกัน $previous_status = ServiceCheck::SUCCESS;
ป้องกัน $frequency = 30;
ป้องกันคำอธิบาย $;
ป้องกัน $consecutive_failures = 0;
ได้รับการคุ้มครอง $status_time;
ป้องกัน $failure_time;
ป้องกัน $loggers = array();
ฟังก์ชั่นสาธารณะนามธรรม __construct($params);
ฟังก์ชั่นสาธารณะ __call($name, $args)
-
ถ้า(isset($this->$name)) {
ส่งคืน $this->$name;
-
-
ฟังก์ชั่นสาธารณะ set_next_attempt()
-
$this->next_attempt = เวลา() + $this->ความถี่;
-
ฟังก์ชั่นนามธรรมสาธารณะ run();
ฟังก์ชั่นสาธารณะ post_run ($ สถานะ)
-
ถ้า($status !== $this->current_status) {
$this->previous_status = $this->current_status;
-
ถ้า($สถานะ === ตัวเอง :: ล้มเหลว) {
ถ้า( $this->current_status === ตัวเอง :: ล้มเหลว ) {
$นี่->consecutive_failures++;
-
อื่น {
$นี่->failure_time = เวลา();
-
-
อื่น {
$นี่->consecutive_failures = 0;
-
$นี่->status_time = เวลา();
$this->current_status = $status;
$นี่->log_service_event();
-
ฟังก์ชั่นสาธารณะ log_current_status()
-
foreach($this->คนตัดไม้เป็น $logger) {
$logger->log_current_status($นี้);
-
-
ฟังก์ชั่นส่วนตัว log_service_event()
-
foreach($this->คนตัดไม้เป็น $logger) {
$logger->log_service_event($นี่);
-
-
ฟังก์ชั่นสาธารณะ register_logger (ServiceLogger $ logger)
-
$this->คนตัดไม้[] = $คนตัดไม้;
-
}
วิธีการโอเวอร์โหลด __call() ด้านบนให้การเข้าถึงพารามิเตอร์ของออบเจ็กต์ ServiceCheck แบบอ่านอย่างเดียว:
· หมดเวลา - ระยะเวลาที่การตรวจสอบนี้จะถูกระงับก่อนที่กลไกจะยุติการตรวจสอบ
· next_attempt - ครั้งต่อไปที่พยายามเชื่อมต่อกับเซิร์ฟเวอร์
· current_status - สถานะปัจจุบันของบริการ: SUCCESS หรือ FAILURE
· Previous_status - สถานะก่อนสถานะปัจจุบัน
· ความถี่ - ความถี่ในการตรวจสอบบริการ
· คำอธิบาย - คำอธิบายบริการ
· second_failures - จำนวนความล้มเหลวในการตรวจสอบบริการติดต่อกันนับตั้งแต่ความสำเร็จครั้งล่าสุด
· status_time - ครั้งสุดท้ายที่มีการตรวจสอบบริการ
· failed_time - หากสถานะเป็น FAILED จะแสดงเวลาที่เกิดความล้มเหลว
คลาสนี้ยังใช้รูปแบบ Observer โดยอนุญาตให้อ็อบเจ็กต์ประเภท ServiceLogger สามารถลงทะเบียนตัวเองได้ จากนั้นจึงเรียกมันเมื่อมีการเรียกใช้ log_current_status() หรือ log_service_event()
ฟังก์ชันหลักที่นำมาใช้ที่นี่คือ run() ซึ่งมีหน้าที่กำหนดวิธีดำเนินการตรวจสอบ หากการตรวจสอบสำเร็จ ควรส่งคืน SUCCESS มิฉะนั้นควรส่งคืน FAILURE
เมื่อการตรวจสอบบริการที่กำหนดใน run() ส่งคืน post_run() วิธีการจะถูกเรียก มีหน้าที่รับผิดชอบในการตั้งค่าสถานะของออบเจ็กต์และดำเนินการบันทึก
อินเทอร์เฟซ ServiceLogger: การระบุคลาสบันทึกจำเป็นต้องใช้สองวิธีเท่านั้น: log_service_event() และ log_current_status() ซึ่งจะถูกเรียกเมื่อมีการส่งกลับการตรวจสอบ run() และเมื่อมีการใช้คำขอสถานะปกติ
อินเทอร์เฟซมีดังนี้:
อินเทอร์เฟซ ServiceLogger {
ฟังก์ชั่นสาธารณะ log_service_event (ServiceCheck $ service);
ฟังก์ชั่นสาธารณะ log_current_status (ServiceCheck $ service);
}
สุดท้ายนี้ คุณจะต้องเขียนเอ็นจิ้นเอง แนวคิดนี้คล้ายกับแนวคิดที่ใช้ในการเขียนโปรแกรมอย่างง่ายในส่วนก่อนหน้า: เซิร์ฟเวอร์ควรสร้างกระบวนการใหม่เพื่อจัดการการตรวจสอบแต่ละครั้ง และใช้ตัวจัดการ SIGCHLD เพื่อตรวจจับค่าตอบแทนเมื่อการตรวจสอบเสร็จสิ้น ควรกำหนดค่าจำนวนสูงสุดที่สามารถตรวจสอบพร้อมกันได้ เพื่อป้องกันการใช้ทรัพยากรระบบมากเกินไป บริการและบันทึกทั้งหมดจะกำหนดไว้ในไฟล์ XML
นี่คือคลาส ServiceCheckRunner ที่กำหนดกลไกนี้:
class ServiceCheckRunner {
ส่วนตัว $num_children;
$services ส่วนตัว = array();
ส่วนตัว $children = array();
ฟังก์ชั่นสาธารณะ _ _สร้าง ($conf, $num_children)
-
$loggers = array();
$นี่->num_children = $num_children;
$conf = simplexml_load_file($conf);
foreach($conf->loggers->logger as $logger) {
$class = ใหม่ Reflection_Class("$logger->class");
ถ้า($class->isInstantiable()) {
$loggers["$logger->id"] = $class->newInstance();
-
อื่น {
fputs(STDERR, "{$logger->class} ไม่สามารถสร้างอินสแตนซ์ได้n");
ออก;
-
-
foreach($conf->บริการ->บริการเป็น $service) {
$class = ใหม่ Reflection_Class("$service->class");
ถ้า($class->isInstantiable()) {
$item = $class->newInstance($service->params);
foreach($service->loggers->logger as $logger) {
$item->register_logger($loggers["$logger"]);
-
$this->services[] = $item;
-
อื่น {
fputs(STDERR, "{$service->class} ไม่สามารถสร้างอินสแตนซ์ได้n");
ออก;
-
-
-
ฟังก์ชั่นส่วนตัว next_attempt_sort($a, $b){
ถ้า($a->next_attempt() == $b->next_attempt()) {
กลับ 0;
-
กลับ ($a->next_attempt() < $b->next_attempt())? -1 : 1;
-
ฟังก์ชั่นส่วนตัวถัดไป () {
usort($this->services, array($this, 'next_attempt_sort'));
ส่งคืน $this->services[0];
-
ลูปฟังก์ชันสาธารณะ () {
ประกาศ(ติ๊ก=1);
pcntl_signal(SIGCHLD, array($this, "sig_child"));
pcntl_signal(SIGUSR1, อาร์เรย์($นี่, "sig_usr1"));
ในขณะที่(1) {
$ตอนนี้ = เวลา();
if(count($this->children)< $this->num_children) {
$service = $this->next();
ถ้า($ตอนนี้ < $บริการ->next_attempt()) {
นอนหลับ(1);
ดำเนินการต่อ;
-
$บริการ->set_next_attempt();
ถ้า($pid = pcntl_fork()) {
$this->children[$pid] = $บริการ;
-
อื่น {
pcntl_alarm($บริการ->หมดเวลา());
ทางออก($บริการ->รัน());
-
-
-
-
ฟังก์ชั่นสาธารณะ log_current_status(){
foreach($this->บริการเป็น $service) {
$บริการ->log_current_status();
-
-
ฟังก์ชั่นส่วนตัว sig_child($signal){
$status = ServiceCheck::ล้มเหลว;
pcntl_signal(SIGCHLD, array($this, "sig_child"));
ในขณะที่(($pid = pcntl_wait($สถานะ WNOHANG)) > 0){
$service = $this->children[$pid];
unset($this->children[$pid]);
ถ้า (pcntl_wifexited($สถานะ) && pcntl_wexitstatus($สถานะ) ==ServiceCheck::SUCCESS)
-
$สถานะ = ServiceCheck::SUCCESS;
-
$บริการ->post_run($สถานะ);
-
-
ฟังก์ชั่นส่วนตัว sig_usr1($signal){
pcntl_signal(SIGUSR1, อาร์เรย์($นี่, "sig_usr1"));
$นี่->log_current_status();
-
}
นี่เป็นคลาสที่ซับซ้อนมาก ตัวสร้างจะอ่านและแยกวิเคราะห์ไฟล์ XML สร้างบริการทั้งหมดที่จะตรวจสอบ และสร้างตัวบันทึกเพื่อบันทึก
เมธอด loop() เป็นเมธอดหลักในคลาสนี้ โดยจะตั้งค่าตัวจัดการสัญญาณของคำขอและตรวจสอบว่าสามารถสร้างกระบวนการลูกใหม่ได้หรือไม่ ตอนนี้ หากเหตุการณ์ถัดไป (เรียงลำดับโดยเวลา next_attempt CHUO) ทำงานได้ดี กระบวนการใหม่จะถูกสร้างขึ้น ภายในกระบวนการย่อยใหม่นี้ ให้ออกคำเตือนเพื่อป้องกันไม่ให้ระยะเวลาการทดสอบเกินขีดจำกัดเวลา จากนั้นดำเนินการการทดสอบที่กำหนดโดย run()
นอกจากนี้ยังมีตัวจัดการสัญญาณสองตัว: ตัวจัดการ SIGCHLD sig_child() ซึ่งรับผิดชอบในการรวบรวมกระบวนการลูกที่ถูกยุติและดำเนินการเมธอด post_run() ของบริการ ตัวจัดการ SIGUSR1 sig_usr1() ซึ่งเรียกวิธีการบันทึกที่ลงทะเบียนทั้งหมด log_current_status() ซึ่ง สามารถใช้รับสถานะปัจจุบันของทั้งระบบได้
แน่นอนว่าสถาปัตยกรรมกล้องวงจรปิดนี้ไม่ได้ช่วยอะไรในทางปฏิบัติเลย แต่ก่อนอื่นคุณต้องตรวจสอบบริการก่อน คลาสต่อไปนี้ตรวจสอบว่าคุณได้รับการตอบสนอง "200 เซิร์ฟเวอร์ตกลง" จากเซิร์ฟเวอร์ HTTP หรือไม่:
คลาส HTTP_ServiceCheck ขยาย ServiceCheck {
สาธารณะ $url;
ฟังก์ชั่นสาธารณะ _ _construct($params){
foreach($params เป็น $k => $v) {
$เค = "$เค";
$this->$k = "$v";
-
-
ฟังก์ชั่นสาธารณะทำงาน () {
if(is_resource(@fopen($this->url, "r"))) {
กลับ ServiceCheck::SUCCESS;
-
อื่น {
กลับ ServiceCheck::FAILURE;
-
-
}
เมื่อเทียบกับเฟรมเวิร์กที่คุณสร้างไว้ก่อนหน้านี้ บริการนี้เรียบง่ายมากและจะไม่มีการอธิบายรายละเอียดที่นี่
5. กระบวนการ ServiceLogger ตัวอย่าง
ต่อไปนี้คือกระบวนการ ServiceLogger ตัวอย่าง เมื่อบริการหยุดทำงาน จะมีหน้าที่ส่งอีเมลไปยังบุคคลที่โทรมา:
class EmailMe_ServiceLogger ดำเนิน ServiceLogger {
ฟังก์ชั่นสาธารณะ log_service_event (ServiceCheck $ service)
-
if($service->current_status ==ServiceCheck::FAILURE) {
$message = "ปัญหาเกี่ยวกับ{$service->description()}rn";
mail( '[email protected]' , 'กิจกรรมการบริการ', $message);
ถ้า($บริการ->ติดต่อกัน_ล้มเหลว()> 5) {
mail( '[email protected]' , 'กิจกรรมการบริการ', $message);
-
-
-
ฟังก์ชั่นสาธารณะ log_current_status (ServiceCheck $ service) {
กลับ;
-
}
หากล้มเหลวห้าครั้งติดต่อกัน กระบวนการจะส่งข้อความไปยังที่อยู่สำรองด้วย โปรดทราบว่าไม่ได้ใช้เมธอด log_current_status() ที่มีความหมาย
เมื่อใดก็ตามที่คุณเปลี่ยนสถานะของบริการดังต่อไปนี้ คุณควรใช้กระบวนการ ServiceLogger ที่เขียนลงในบันทึกข้อผิดพลาด PHP:
class ErrorLog_ServiceLogger ดำเนิน ServiceLogger {
ฟังก์ชั่นสาธารณะ log_service_event (ServiceCheck $ service)
-
ถ้า($บริการ->current_status() !==$บริการ->previous_status()) {
ถ้า($บริการ->current_status() ===ServiceCheck::FAILURE) {
$สถานะ = 'ลง';
-
อื่น {
$สถานะ = 'ขึ้น';
-
error_log("{$service->description()} เปลี่ยนสถานะเป็น $status");
-
-
ฟังก์ชั่นสาธารณะ log_current_status (ServiceCheck $ service)
-
error_log("{$service->description()}: $status");
-
}
เมธอด log_current_status() หมายความว่าหากกระบวนการส่งสัญญาณ SIGUSR1 กระบวนการจะคัดลอกสถานะปัจจุบันที่สมบูรณ์ไปยังบันทึกข้อผิดพลาด PHP ของคุณ
กลไกใช้ไฟล์การกำหนดค่าดังนี้:
<config>
<คนตัดไม้>
<คนบันทึก>
<รหัส>บันทึกข้อผิดพลาด<//id>
<คลาส>ErrorLog_ServiceLogger</คลาส>
</คนบันทึก>
<คนบันทึก>
<id><ส่งอีเมลถึง</id>
<คลาส>EmailMe_ServiceLogger</คลาส>
</คนบันทึก>
</คนตัดไม้>
<บริการ>
<บริการ>
<คลาส>HTTP_ServiceCheck</คลาส>
<พารามิเตอร์>
<คำอธิบาย>การตรวจสอบ HTTP ของ OmniTI</คำอธิบาย>
<url> <http://www.omniti.com </url>
<หมดเวลา><30</หมดเวลา>
<ความถี่>900</ความถี่>
</พารามิเตอร์><
<คนตัดไม้>
<ตัวบันทึก>บันทึกข้อผิดพลาด</ตัวบันทึก>
<คนตัดไม้>ส่งอีเมลถึงผม</คนตัดไม้>
</คนตัดไม้>
</บริการ><
<บริการ>
<คลาส>HTTP_ServiceCheck</คลาส>
<พารามิเตอร์>
<คำอธิบาย><การตรวจสอบ HTTP ของหน้าแรก</คำอธิบาย>
<url> <http://www.schlossnagle.org/~george </url>
<หมดเวลา><30</หมดเวลา>
<ความถี่>3600</ความถี่>
</พารามิเตอร์><
<คนตัดไม้>
<ตัวบันทึก>บันทึกข้อผิดพลาด</ตัวบันทึก>
</คนตัดไม้>
</บริการ><
</บริการ>
</config>
เมื่อส่งผ่านไฟล์ XML นี้ ตัวสร้างของ ServiceCheckRunner จะสร้างโปรแกรมการบันทึกสำหรับบันทึกที่ระบุแต่ละรายการ จากนั้นจะสร้างอินสแตนซ์ออบเจ็กต์ ServiceCheck ที่สอดคล้องกับบริการที่ระบุแต่ละรายการ
โปรดทราบว่าตัวสร้างใช้คลาส Reflection_Class เพื่อใช้การตรวจสอบภายในของบริการและคลาสการบันทึก - ก่อนที่คุณจะพยายามสร้างอินสแตนซ์เหล่านั้น แม้ว่าจะไม่จำเป็น แต่ก็แสดงให้เห็นอย่างชัดเจนถึงการใช้ Reflection API ใหม่ใน PHP 5 นอกเหนือจากคลาสเหล่านี้แล้ว Reflection API ยังมีคลาสเพื่อใช้การตรวจสอบภายในของเอนทิตีภายในเกือบทั้งหมด (คลาส วิธีการ หรือฟังก์ชัน) ใน PHP
หากต้องการใช้เครื่องยนต์ที่คุณสร้างขึ้น คุณยังต้องมีโค้ด Wrapper อยู่ โปรแกรมเฝ้าระวังควรป้องกันไม่ให้คุณพยายามเริ่มต้นสองครั้ง คุณไม่จำเป็นต้องสร้างข้อความสองข้อความสำหรับทุกกิจกรรม แน่นอนว่าจอภาพควรได้รับตัวเลือกบางอย่าง เช่น:
คำอธิบายตัวเลือก
[-f] ตำแหน่งสำหรับไฟล์การกำหนดค่ากลไก ค่าเริ่มต้นคือ monitor.xml
[-n] ขนาดของพูลกระบวนการย่อยที่กลไกอนุญาต ค่าเริ่มต้นคือ 5
[-d] ธงที่ปิดการใช้งานการทำงานของ daemon ของเอ็นจิ้นนี้ สิ่งนี้มีประโยชน์เมื่อคุณเขียนกระบวนการ ServiceLogger ตรวจแก้จุดบกพร่องที่ส่งออกข้อมูลไปยัง stdout หรือ stderr
นี่คือสคริปต์เฝ้าระวังสุดท้ายที่จะแยกวิเคราะห์ตัวเลือก รับประกันความพิเศษเฉพาะตัว และดำเนินการตรวจสอบบริการ:
need_once "Service.inc";
need_once "คอนโซล/Getopt.php";
$shortoptions = "n:f:d";
$default_opts = array('n' => 5, 'f' =>'monitor.xml');
$args = getOptions($default_opts, $shortoptions, null);
$fp = fopen("/tmp/.lockfile", "a");
ถ้า(!$fp || !ฝูง($fp, LOCK_EX | LOCK_NB)) {
fputs($stderr, "ไม่สามารถรับการล็อคn");
ออก;
-
ถ้า(!$args['d']) {
ถ้า (pcntl_fork ()) {
ออก;
-
posix_setsid();
ถ้า (pcntl_fork ()) {
ออก;
-
-
fwrite($fp, getmypid());
ปุย($fp);
$engine = ServiceCheckRunner ใหม่($args['f'], $args['n']);
$engine->loop();
โปรดทราบว่าตัวอย่างนี้ใช้ฟังก์ชัน getOptions() ที่กำหนดเอง
หลังจากเขียนไฟล์การกำหนดค่าที่เหมาะสมแล้ว คุณสามารถเริ่มสคริปต์ได้ดังต่อไปนี้:
> ./monitor.php -f /etc/monitor.xml
ซึ่งจะช่วยปกป้องและติดตามตรวจสอบต่อไปจนกว่าเครื่องจะปิดหรือสคริปต์ถูกปิด
สคริปต์นี้ค่อนข้างซับซ้อน แต่ก็ยังมีบางส่วนที่ปรับปรุงได้ง่าย ซึ่งเหลือไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน:
· เพิ่มตัวจัดการ SIGHUP ที่วิเคราะห์ไฟล์การกำหนดค่าอีกครั้ง เพื่อให้คุณสามารถเปลี่ยนการกำหนดค่าโดยไม่ต้องเริ่มเซิร์ฟเวอร์
· เขียน ServiceLogger ที่สามารถเข้าสู่ระบบฐานข้อมูลเพื่อจัดเก็บข้อมูลการสืบค้น
· เขียนโปรแกรมส่วนหน้าของเว็บเพื่อสร้าง GUI ที่ดีสำหรับระบบการตรวจสอบทั้งหมด