* Portions created by the Initial Developer are Copyright (C) 2008-2025 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mark J Crane * Tim Fry */ //includes files require_once dirname(__DIR__, 4) . "/resources/require.php"; require_once "resources/check_auth.php"; //check permissions if (!permission_exists('xml_cdr_view')) { echo "access denied"; exit; } //function to parse a FusionPBX service from a .service file if (!function_exists('get_classname')) { /** * Retrieves the name of a PHP class from an ExecStart directive in a service file. * * @param string $file Path to the service file. * * @return string The name of the PHP class, or empty string if not found. */ function get_classname(string $file) { if (!file_exists($file)) { return ''; } $parsed = parse_ini_file($file); $exec_cmd = $parsed['ExecStart'] ?? ''; $parts = explode(' ', $exec_cmd ?? ''); $php_file = $parts[1] ?? ''; if (!empty($php_file)) { return $php_file; } return ''; } } //function to check for running process: returns [running, pid, etime] if (!function_exists('is_running')) { /** * Checks if a process with the given name is currently running. * * @param string $name The name of the process to check for. * * @return array An array containing information about the process's status, * including whether it's running, its PID, and how long it's been running. */ function is_running(string $name) { $name = escapeshellarg($name); $pid = trim(shell_exec("ps -aux | grep $name | grep -v grep | awk '{print \$2}' | head -n 1") ?? ''); if ($pid && is_numeric($pid)) { $etime = trim(shell_exec("ps -p $pid -o etime= | tr -d '\n'") ?? ''); return ['running' => true, 'pid' => $pid, 'etime' => $etime]; } return ['running' => false, 'pid' => null, 'etime' => null]; } } //function to format etime into friendly display if (!function_exists('format_etime')) { /** * Formats a time duration string into a human-readable format. * * The input string can be in one of the following formats: * - dd-hh:mm:ss * - hh:mm:ss * - mm:ss * - seconds (no units) * * If the input string is empty or invalid, an empty string will be returned. * * @param string $etime Time duration string to format. * * @return string Formatted time duration string in human-readable format. */ function format_etime($etime) { // Format: [[dd-]hh:]mm:ss if (empty($etime)) return '-'; $days = 0; $hours = 0; $minutes = 0; $seconds = 0; // Handle dd-hh:mm:ss if (preg_match('/^(\d+)-(\d+):(\d+):(\d+)$/', $etime, $m)) { [$_, $days, $hours, $minutes, $seconds] = $m; } // Handle hh:mm:ss elseif (preg_match('/^(\d+):(\d+):(\d+)$/', $etime, $m)) { [$_, $hours, $minutes, $seconds] = $m; } // Handle mm:ss elseif (preg_match('/^(\d+):(\d+)$/', $etime, $m)) { [$_, $minutes, $seconds] = $m; } $out = []; if ($days) $out[] = $days . 'd'; if ($hours) $out[] = $hours . 'h'; if ($minutes) $out[] = $minutes . 'm'; if ($seconds || empty($out)) $out[] = $seconds . 's'; return implode(' ', $out); } } //friendly labels $service_labels = [ 'email_queue' => 'Email Queue', 'event_guard' => 'Event Guard', 'fax_queue' => 'Fax Queue', 'maintenance_service' => 'Maintenance Service', 'message_events' => 'Message Events', 'message_queue' => 'Message Queue', 'xml_cdr' => 'XML CDR', 'freeswitch' => 'FreeSWITCH', 'nginx' => 'Nginx', 'postgresql' => 'PostgreSQL', 'event_guard' => 'Event Guard', 'sshd' => 'SSH Server' ]; $files = glob(PROJECT_ROOT . '/*/*/resources/service/*.service'); $services = []; $total_running = 0; // load FusionPBX installed services foreach ($files as $file) { $service = get_classname($file); //check if the service name was found if (!empty($service)) { $basename = basename($service, '.php'); $info = is_running($service); $info['label'] = $service_labels[$basename] ?? ucwords(str_replace('_', ' ', $basename)); $services[$basename] = $info; if ($info['running']) $total_running++; } } // Get extra system services from default settings $extra_services_string = $settings->get('theme', 'dashboard_extra_system_services'); // Only proceed if the setting is not empty if (!empty($extra_services_string) && is_string($extra_services_string)) { // Convert comma-separated list to array $extra_services = array_filter(array_map('trim', explode(',', $extra_services_string))); // Loop through extra services if array is not empty if (!empty($extra_services)) { foreach ($extra_services as $extra) { if (!isset($services[$extra])) { $info = is_running($extra); $info['label'] = $service_labels[$extra] ?? ucwords($extra); $services[$extra] = $info; if ($info['running']) $total_running++; } } } } //track total installed services for charts $total_services = count($services); //convert to a key $widget_key = str_replace(' ', '_', strtolower($widget_name)); //add multi-lingual support $text = (new text())->get($settings->get('domain','language','en-us'), 'core/user_settings'); //get the dashboard label $widget_label = $text['label-'.$widget_key] ?? $widget_name; //show the results echo "
\n"; echo "
\n"; echo " ".escape($widget_label)."\n"; //doughnut chart if (!isset($widget_chart_type) || $widget_chart_type == "doughnut") { echo "
\n"; echo " \n"; } if ($widget_chart_type == "number") { echo " ".$total_services.""; } echo "
\n"; if ($widget_details_state != 'disabled') { echo "
\n"; echo " \n"; echo " \n"; echo " \n"; echo " \n"; echo " \n"; echo " \n"; $row_style[false] = "row_style0"; $row_style[true] = "row_style1"; $c = true; foreach ($services as $info) { $label = $info['label']; $status = $info['running'] ? "Yes" : "No"; $etime = isset($info['etime']) ? format_etime($info['etime']) : '-'; $pid = $info['pid'] ?? ''; $tooltip_attr = $pid ? "title='PID: $pid'" : ''; echo " \n"; echo " \n"; echo " \n"; echo " \n"; echo " \n"; $c = !$c; } echo "
".($text['label-service'] ?? 'Service')."".($text['label-running'] ?? 'Running')."".($text['label-runtime'] ?? 'Runtime')."
$label$status$etime
\n"; echo "
\n"; echo ""; } echo "
\n";