mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2026-03-31 21:49:55 +00:00
Add inotify to the xml_cdr service
Improve performance when using the file system when inotify is not installed.
This commit is contained in:
@@ -24,6 +24,12 @@ class xml_cdr_service extends service {
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* cdr object
|
||||
* @var settings
|
||||
*/
|
||||
private $cdr;
|
||||
|
||||
/**
|
||||
* hostname variable
|
||||
* @var string
|
||||
@@ -42,19 +48,22 @@ class xml_cdr_service extends service {
|
||||
* @return void
|
||||
*/
|
||||
protected function reload_settings(): void {
|
||||
// re-read the config file to get any possible changes
|
||||
// Read the config file to get any possible changes
|
||||
parent::$config->read();
|
||||
|
||||
// Connect to the database
|
||||
$this->database = new database(['config' => parent::$config]);
|
||||
|
||||
// get the settings using global defaults
|
||||
// Get the settings using global defaults
|
||||
$this->settings = new settings(['database' => $this->database]);
|
||||
|
||||
// get the hostname
|
||||
// Get the hostname
|
||||
$this->hostname = gethostname();
|
||||
|
||||
//get the xml_cdr directory
|
||||
// Initialize the xml cdr object
|
||||
$this->cdr = new xml_cdr;
|
||||
|
||||
// Get the xml_cdr directory
|
||||
$this->xml_cdr_dir = $this->settings->get('switch', 'log', '/var/log/freeswitch').'/xml_cdr';
|
||||
|
||||
// Show the message in the log so we can track when the settings are reloaded
|
||||
@@ -72,108 +81,275 @@ class xml_cdr_service extends service {
|
||||
// Reload the settings
|
||||
$this->reload_settings();
|
||||
|
||||
//rename the directory
|
||||
// Rename the directory
|
||||
if (file_exists($this->xml_cdr_dir.'/failed/invalid_xml')) {
|
||||
rename($this->xml_cdr_dir.'/failed/invalid_xml', $this->xml_cdr_dir.'/failed/xml');
|
||||
}
|
||||
|
||||
//create the invalid xml directory
|
||||
// Create the invalid xml directory
|
||||
if (!file_exists($this->xml_cdr_dir.'/failed/xml')) {
|
||||
mkdir($this->xml_cdr_dir.'/failed/xml', 0770, true);
|
||||
}
|
||||
|
||||
//create the invalid size directory
|
||||
// Create the invalid size directory
|
||||
if (!file_exists($this->xml_cdr_dir.'/failed/size')) {
|
||||
mkdir($this->xml_cdr_dir.'/failed/size', 0770, true);
|
||||
}
|
||||
|
||||
//create the invalid sql directory
|
||||
// Create the invalid SQL directory
|
||||
if (!file_exists($this->xml_cdr_dir.'/failed/sql')) {
|
||||
mkdir($this->xml_cdr_dir.'/failed/sql', 0770, true);
|
||||
}
|
||||
|
||||
//import the call detail records from HTTP POST or file system
|
||||
$cdr = new xml_cdr;
|
||||
// Check if inotify extension is available
|
||||
$use_inotify = extension_loaded('inotify');
|
||||
|
||||
// Initialize inotify if available
|
||||
$inotify = null;
|
||||
if ($use_inotify) {
|
||||
$inotify = inotify_init();
|
||||
if ($inotify === false) {
|
||||
$this->notice("Failed to initialize inotify");
|
||||
$use_inotify = false;
|
||||
} else {
|
||||
$inotify_add = inotify_add_watch($inotify, $this->xml_cdr_dir, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||
if ($inotify_add === false) {
|
||||
$this->notice("Failed to add inotify watch");
|
||||
inotify_free_resources($inotify);
|
||||
$use_inotify = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send message to the log
|
||||
if ($use_inotify) {
|
||||
$this->notice("Using inotify for file monitoring");
|
||||
} else {
|
||||
$this->notice("Using opendir for file monitoring");
|
||||
}
|
||||
|
||||
// Set the initial time
|
||||
$last_poll_time = time();
|
||||
|
||||
// Service work is handled here
|
||||
while ($this->running) {
|
||||
|
||||
//get the list of call detail records, and limit the number of records
|
||||
$xml_cdr_array = array_slice(glob($this->xml_cdr_dir . '/*.cdr.xml'), 0, 100);
|
||||
// Make sure the database connection is available
|
||||
while (!$this->database->is_connected()) {
|
||||
// Connect to the database
|
||||
$this->database->connect();
|
||||
|
||||
//process the call detail records
|
||||
if (!empty($xml_cdr_array)) {
|
||||
//make sure the database connection is available
|
||||
while (!$this->database->is_connected()) {
|
||||
//connect to the database
|
||||
$this->database->connect();
|
||||
// Reload settings after connection to the database
|
||||
$this->settings = new settings(['database' => $this->database]);
|
||||
|
||||
//reload settings after connection to the database
|
||||
$this->settings = new settings(['database' => $this->database]);
|
||||
// Sleep for a moment
|
||||
sleep(3);
|
||||
}
|
||||
|
||||
//sleep for a moment
|
||||
sleep(3);
|
||||
}
|
||||
// Use opendir - inotify not available
|
||||
if (!$use_inotify) {
|
||||
// Read the directory to find files to process
|
||||
$this->process_files();
|
||||
|
||||
foreach ($xml_cdr_array as $xml_cdr_file) {
|
||||
//move the files that are too large or zero file size to the failed size directory
|
||||
if (filesize($xml_cdr_file) >= (3 * 1024 * 1024) || filesize($xml_cdr_file) == 0) {
|
||||
//echo "WARNING: File too large or zero file size. Moving $file to failed\n";
|
||||
if (!empty($this->xml_cdr_dir)) {
|
||||
if (parent::$log_level == 7) {
|
||||
echo "Move the file ".$xml_cdr_file." to ".$this->xml_cdr_dir."/failed/size\n";
|
||||
}
|
||||
rename($xml_cdr_file, $this->xml_cdr_dir.'/failed/size/'.basename($xml_cdr_file));
|
||||
}
|
||||
// Sleep for 100 ms
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Use inotify
|
||||
if ($use_inotify) {
|
||||
// Set up stream select for inotify
|
||||
$read = [$inotify];
|
||||
$write = $except = null;
|
||||
$timeout = 300; // 5 minutes timeout
|
||||
|
||||
if (stream_select($read, $write, $except, $timeout) > 0) {
|
||||
// Process inotify events
|
||||
$events = inotify_read($inotify) ?: [];
|
||||
if ($events === false) {
|
||||
$this->notice("inotify_read failed");
|
||||
$use_inotify = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
//add debug information
|
||||
if (parent::$log_level == 7) {
|
||||
echo $xml_cdr_file."\n";
|
||||
foreach ($events as $event) {
|
||||
// Set as a variable
|
||||
$mask = $event['mask'];
|
||||
|
||||
// Check the event type (mask) inotify detected
|
||||
if ($mask & IN_Q_OVERFLOW) {
|
||||
// More than 20,000 files will cause an overflow, so process all files in the directory
|
||||
$this->warning('Too many files created. Processing in bulk.');
|
||||
$this->process_files();
|
||||
|
||||
// Clear the queue by removing and re-adding the watch
|
||||
inotify_rm_watch($inotify, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||
inotify_add_watch($inotify, $this->xml_cdr_dir, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||
|
||||
// Send a message that opendir processing has completed
|
||||
$this->warning('Bulk Processing completed.');
|
||||
|
||||
// Stop processing the foreach loop
|
||||
break;
|
||||
} elseif (($mask & IN_CLOSE_WRITE) || $mask & IN_MOVED_TO) {
|
||||
// Process individual file detected
|
||||
$this->process_file($event['name']);
|
||||
} else {
|
||||
// Debug any extra events detected
|
||||
$this->debug('Detected event: ' . self::inotify_to_string($mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get the content from the file
|
||||
$call_details = file_get_contents($xml_cdr_file);
|
||||
|
||||
//process the call detail record
|
||||
if (isset($xml_cdr_file) && isset($call_details)) {
|
||||
//set the file
|
||||
$cdr->file = basename($xml_cdr_file);
|
||||
|
||||
//get the leg of the call and the file prefix
|
||||
if (substr(basename($xml_cdr_file), 0, 2) == "a_") {
|
||||
$leg = "a";
|
||||
}
|
||||
else {
|
||||
$leg = "b";
|
||||
}
|
||||
|
||||
//decode the xml string
|
||||
if (substr($call_details, 0, 1) == '%') {
|
||||
$call_details = urldecode($call_details);
|
||||
}
|
||||
|
||||
//parse the xml and insert the data into the database
|
||||
$cdr->xml_array(0, $leg, $call_details);
|
||||
}
|
||||
// Periodic poll for missed files (every 5 minutes)
|
||||
if (time() - $last_poll_time >= 300) {
|
||||
$last_poll_time = time();
|
||||
$this->process_files();
|
||||
}
|
||||
}
|
||||
|
||||
//sleep for 100 ms
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if ($use_inotify && $inotify !== null) {
|
||||
inotify_rm_watch($inotify, $inotify_add);
|
||||
unset($inotify);
|
||||
}
|
||||
|
||||
// Return a successful exit code
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the XML CDR directory, process all files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function process_files(): void {
|
||||
|
||||
// Prepare the directory handle
|
||||
$handle = opendir($this->xml_cdr_dir);
|
||||
|
||||
// No handle, send a return
|
||||
if (!$handle) return;
|
||||
|
||||
// Set the default value
|
||||
$processing = false;
|
||||
|
||||
// Loop through files in the directory
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
// Skip processing directories
|
||||
if (is_dir($this->xml_cdr_dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// File found set to process
|
||||
$processing = true;
|
||||
|
||||
// Process the file
|
||||
$this->process_file($file);
|
||||
}
|
||||
closedir($handle);
|
||||
|
||||
// Only run this again if files were processed
|
||||
if ($processing) {
|
||||
$this->process_files();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the call detail records from the file system
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function process_file($xml_cdr_file): void {
|
||||
|
||||
// Only process XML files
|
||||
if (!str_ends_with($xml_cdr_file, '.cdr.xml')) {
|
||||
$this->notice("Skipped '$xml_cdr_file'");
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the name of the file to the log
|
||||
$this->debug("Processing ".$xml_cdr_file);
|
||||
|
||||
// Prepend the XML CDR directory when not present
|
||||
if (!str_starts_with($xml_cdr_file, $this->xml_cdr_dir)) {
|
||||
$xml_cdr_file = $this->xml_cdr_dir . '/' . $xml_cdr_file;
|
||||
}
|
||||
|
||||
// Move the files that are too large or have a zero file size to the failed size directory
|
||||
if (filesize($xml_cdr_file) >= (3 * 1024 * 1024) || filesize($xml_cdr_file) == 0) {
|
||||
if (!empty($this->xml_cdr_dir)) {
|
||||
$this->notice("Move the file ".$xml_cdr_file." to ".$this->xml_cdr_dir."/failed/size");
|
||||
rename($xml_cdr_file, $this->xml_cdr_dir.'/failed/size/'.basename($xml_cdr_file));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the content from the file
|
||||
$call_details = file_get_contents($xml_cdr_file);
|
||||
|
||||
// Process the call detail record
|
||||
if (isset($xml_cdr_file) && isset($call_details)) {
|
||||
// Set the file
|
||||
$this->cdr->file = basename($xml_cdr_file);
|
||||
|
||||
// Get the leg of the call and the file prefix
|
||||
if (substr(basename($xml_cdr_file), 0, 2) == "a_") {
|
||||
$leg = "a";
|
||||
}
|
||||
else {
|
||||
$leg = "b";
|
||||
}
|
||||
|
||||
// Decode the xml string
|
||||
if (substr($call_details, 0, 1) == '%') {
|
||||
$call_details = urldecode($call_details);
|
||||
}
|
||||
|
||||
// Parse the XML and insert the data into the database
|
||||
$this->cdr->xml_array(0, $leg, $call_details);
|
||||
}
|
||||
}
|
||||
|
||||
protected static function display_version(): void {
|
||||
echo "XML CDR Service version 1.1\n";
|
||||
echo "XML CDR Service version 2.0\n";
|
||||
}
|
||||
|
||||
protected static function set_command_options() {
|
||||
|
||||
}
|
||||
|
||||
public static function inotify_to_string(int $mask): string {
|
||||
$flags = [];
|
||||
$map = [
|
||||
IN_ACCESS => 'ACCESS',
|
||||
IN_MODIFY => 'MODIFY',
|
||||
IN_ATTRIB => 'ATTRIB',
|
||||
IN_CLOSE_WRITE => 'CLOSE_WRITE',
|
||||
IN_CLOSE_NOWRITE => 'CLOSE_NOWRITE',
|
||||
IN_OPEN => 'OPEN',
|
||||
IN_MOVED_TO => 'MOVED_TO',
|
||||
IN_MOVED_FROM => 'MOVED_FROM',
|
||||
IN_CREATE => 'CREATE',
|
||||
IN_DELETE => 'DELETE',
|
||||
IN_DELETE_SELF => 'DELETE_SELF',
|
||||
IN_MOVE_SELF => 'MOVE_SELF',
|
||||
IN_UNMOUNT => 'UNMOUNT',
|
||||
IN_Q_OVERFLOW => 'Q_OVERFLOW',
|
||||
IN_ISDIR => 'SUBJECT IS DIRECTORY',
|
||||
IN_ONLYDIR => 'PATHNAME ONLY',
|
||||
IN_DONT_FOLLOW => 'DONT_FOLLOW',
|
||||
IN_MASK_ADD => 'MASK_ADD',
|
||||
IN_ONESHOT => 'ONESHOT',
|
||||
];
|
||||
foreach ($map as $bit => $name) {
|
||||
if ($mask & $bit)
|
||||
$flags[] = $name;
|
||||
}
|
||||
return implode('|', $flags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user