ソースを参照

add readme markup, allow framework, move service to its own project

Tim Fry 1 年間 前
コミット
69d067b4cd

+ 1 - 1
app_config.php

@@ -156,7 +156,7 @@
 	$apps[$x]['default_settings'][$y]['default_setting_description'] = "Number of seconds to wait before testing for the execute time.";
 	$y++;
 	$apps[$x]['default_settings'][$y]['default_setting_uuid'] = "13c9211f-fa66-4255-86dc-bd73094a8f52";
-	$apps[$x]['default_settings'][$y]['default_setting_category'] = "maintenance";
+	$apps[$x]['default_settings'][$y]['default_setting_category'] = "maintenance_logs";
 	$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = "database_retention_days";
 	$apps[$x]['default_settings'][$y]['default_setting_name'] = "numeric";
 	$apps[$x]['default_settings'][$y]['default_setting_value'] = "30";

+ 15 - 9
app_defaults.php

@@ -27,14 +27,20 @@
  */
 
 if ($domains_processed == 1) {
-	//use global config and database objects if available
-	global $config, $database;
-	if ($config === null) {
-		$config = new config();
-	}
-	if ($database === null) {
-		$database = new database(['config' => $config]);
-	}
+	if (class_exists('framework')) {
+		//use global config and database objects if available
+		maintenance::app_defaults(framework::database());
+	} else {
+		global $database;
+		if ($database === null) {
+			$database = database::new();
+		}
 
-	maintenance::app_defaults($database);
+		//set the app information for database accounting
+		$database->app_name = maintenance::APP_NAME;
+		$database->app_uuid = maintenance::APP_UUID;
+
+		//maintenance class handles setting app defaults
+		maintenance::app_defaults($database);
+	}
 }

+ 3 - 8
maintenance.php

@@ -48,8 +48,8 @@ if (!empty($_REQUEST['search'])) {
 $language = new text;
 $text = $language->get();
 
-//create a new settings object ignoring the current domain
-$database = new database();
+//create a database object
+$database = database::new();
 
 //process registering maintenance applications
 if (!empty($_REQUEST['action'])) {
@@ -64,7 +64,7 @@ if (!empty($_REQUEST['action'])) {
 	$checked_apps = $_REQUEST['maintenance_apps'] ?? [];
 	switch($action) {
 		case 'toggle':
-			if (permission_exists('maintenance_register')) {
+			if (permission_exists('maintenance_edit')) {
 				if (maintenance::register_applications($database, $checked_apps)) {
 					message::add($text['message-toggle']);
 				} else {
@@ -110,15 +110,10 @@ $document['title'] = $text['title-maintenance'];
 	echo "<div class='action_bar' id='action_bar'>";
 	echo "<div class='heading'><b>Maintenance (" . count($maintenance_classes) . ")</b></div>";
 	echo "<div class='actions'>";
-		//logs button
 		echo button::create(['type'=>'button','label'=>$text['button-logs'],'icon'=>'fas fa-scroll fa-fw','id'=>'btn_logs', 'link'=>'maintenance_logs.php']);
-		//register button
 		echo button_toggle::create(['label'=>$text['button-register'],'icon'=>'fas fa-registered fa-fw']);
-		//search input box
 		echo "<input type='text' class='txt list-search' name='search' id='search' value=\"".escape($search)."\" placeholder=\"".$text['label-search']."\" onkeydown=''>";
-		//search button
 		echo button_search::create(empty($search));
-		//reset button
 		echo button_reset::create(empty($search));
 	echo "</div>";
 

+ 0 - 222
resources/classes/cli_option.php

@@ -1,222 +0,0 @@
-<?php
-
-/*
- * FusionPBX
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is FusionPBX
- *
- * The Initial Developer of the Original Code is
- * Mark J Crane <[email protected]>
- * Portions created by the Initial Developer are Copyright (C) 2008-2024
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Mark J Crane <[email protected]>
- * Tim Fry <[email protected]>
- */
-
-/**
- * Container object for creating command line options
- *
- * @author Tim Fry <[email protected]>
- */
-class cli_option {
-
-	private $short_option;
-	private $long_option;
-	private $description;
-	private $short_description;
-	private $long_description;
-	private $functions;
-
-	/**
-	 * Constructs an empty cli_option
-	 */
-	public function __construct() {
-		$this->short_option = '';
-		$this->long_option = '';
-		$this->description = '';
-		$this->short_description = '';
-		$this->long_description = '';
-		$this->functions = [];
-	}
-
-	/**
-	 * A factory method to create a new cli_option
-	 * @param type $options
-	 * @return cli_option
-	 */
-	public static function new(...$options): cli_option {
-		$obj = new cli_option();
-
-		//automatically assign properties to the object that were passed in key/value pairs
-		self::parse_options($obj, $options);
-
-		//return the cli_option with all properties filled in that were passed
-		return $obj;
-	}
-
-	// used to parse object values when created
-	private static function parse_options($obj, $options) {
-		foreach ($options as $key => $value) {
-			if (is_array($value)) {
-				self::parse_options($obj, $value);
-			}
-			//call the method with the name of $key and pass it $value
-			if (method_exists($obj, $key)) {
-				$obj->{$key}($value);
-			} elseif (property_exists($obj, $key)) {
-				$obj->{$key} = $value;
-			}
-		}
-	}
-
-	/**
-	 * Sets or returns the short option value
-	 * @param string|null $short_option
-	 * @return $this
-	 */
-	public function short_option(?string $short_option = null) {
-		if (!empty($short_option)) {
-			$this->short_option = $short_option;
-			return $this;
-		}
-		return $this->short_option;
-	}
-
-	/**
-	 * Sets or returns the long option value
-	 * @param string|null $long_option
-	 * @return $this
-	 */
-	public function long_option(?string $long_option = null) {
-		if (!empty($long_option)) {
-			$this->long_option = $long_option;
-			return $this;
-		}
-		return $this->long_option;
-	}
-
-	/**
-	 * Set the general description
-	 * @param string|null $description
-	 * @return $this
-	 */
-	public function description(?string $description = null) {
-		if (!empty($description)) {
-			$this->description = $description;
-			return $this;
-		}
-		return $this->description;
-	}
-
-	/**
-	 * Sets or returns the short_description. If short_description is empty then the short_option is used as a default.
-	 * @param string|null $short_description When parameter is null, it returns the currently set value. When not null the short description is set to the passed value.
-	 * @return $this
-	 */
-	public function short_description(?string $short_description = null) {
-		if (!empty($short_description)) {
-			$this->short_description = $short_description;
-			return $this;
-		}
-		if (empty($this->short_description)) {
-			if (str_ends_with($this->short_option, ':')) {
-				$short = rtrim($this->short_option, ':');
-				$short_description = "-$short <value>";
-			} else {
-				$short_description = '-' . $this->short_option;
-			}
-		} else {
-			$short_description = $this->short_description;
-		}
-		return $short_description;
-	}
-
-	/**
-	 * Sets or returns the long_description. If long_description is empty then the long_option is used as a default.
-	 * @param string|null $long_description When parameter is null, it returns the currently set value. When not null the long description is set to the passed value.
-	 * @return $this
-	 */
-	public function long_description(?string $long_description = null) {
-		if ($long_description !== null) {
-			$this->long_description = $long_description;
-			return $this;
-		}
-		if (empty($this->long_description)) {
-			if (str_ends_with($this->long_option, ':')) {
-				$long = rtrim($this->long_option, ':');
-				$long_description = "--$long <value>";
-			} else {
-				$long_description = '--' . $this->long_option;
-			}
-		} else {
-			$long_description = $this->long_description;
-		}
-		return $long_description;
-	}
-
-	/**
-	 * Adds an array of callback functions replacing the existing callback functions
-	 * @param array|null $functions
-	 * @return $this
-	 */
-	public function functions(?array $functions = null) {
-		if ($functions !== null) {
-			$this->functions = $functions;
-			return $this;
-		}
-		return $this->functions;
-	}
-
-	/**
-	 * Appends the callback function to the array of existing callback functions
-	 * @param string|null $function When function param is set, the callback function will be appended to the list of functions. When called without a param, the array will be returned of current callbacks.
-	 * @return $this|array Returns the array of callbacks if no parameters passed or this object when appending a callback
-	 */
-	public function callback(?string $function = null) {
-		if ($function !== null) {
-			$this->functions += [$function];
-			return $this;
-		}
-		return $this->functions;
-	}
-
-	/**
-	 * Appends the callback function to the array of existing callback functions
-	 * @param string|null $function
-	 * @return $this
-	 */
-	public function function_append(?string $function = null) {
-		if ($function !== null) {
-			$this->functions += [$function];
-			return $this;
-		}
-		return $this->functions;
-	}
-
-	/**
-	 * Returns the array structure required for service
-	 * @return array
-	 */
-	public function to_array(): array {
-		$arr['short_option'] = $this->short_option();
-		$arr['long_option'] = $this->long_option();
-		$arr['description'] = $this->description();
-		$arr['short_description'] = $this->short_description();
-		$arr['long_description'] = $this->long_description();
-		$arr['functions'] = $this->functions();
-		return $arr;
-	}
-}

+ 13 - 7
resources/classes/maintenance.php

@@ -126,7 +126,7 @@ class maintenance {
 			require_once $file;
 		}
 
-		//get the loaded declared classes in an array
+		//get the loaded declared classes in an array from the php engine
 		$declared_classes = get_declared_classes();
 
 		//initialize the array
@@ -146,6 +146,12 @@ class maintenance {
 			self::register_applications($database, $found_applications);
 		}
 
+		//check the type of chart and make sure that it has 'none' as default
+		$result = $database->select("select dashboard_chart_type from v_dashboard where dashboard_name='Maintenance'", null, 'column');
+		if ($result !== 'none' || $result !== 'doughnut') {
+			$database->execute("update v_dashboard set dashboard_chart_type='none' where dashboard_name='Maintenance'");
+		}
+
 	}
 
 	public static function get_registered_applications(database $database): array {
@@ -181,22 +187,22 @@ class maintenance {
 		$registered_apps = self::get_registered_applications($database);
 
 		//register each app
-		$array = [];
+		$new_maintenance_apps = [];
 		$index = 0;
 		foreach ($maintenance_apps as $application) {
 			//format the array for what the database object needs for saving data in the global default settings
-			self::add_maintenance_app_to_array($registered_apps, $application, $array, $index);
+			self::add_maintenance_app_to_array($registered_apps, $application, $new_maintenance_apps, $index);
 
 			//get the application settings from the class for database maintenance
-			self::add_database_maintenance_to_array($database, $application, $array, $index);
+			self::add_database_maintenance_to_array($database, $application, $new_maintenance_apps, $index);
 
 			//get the application settings from the class for filesystem maintenance
-			self::add_filesystem_maintenance_to_array($database, $application, $array, $index);
+			self::add_filesystem_maintenance_to_array($database, $application, $new_maintenance_apps, $index);
 		}
-		if (count($array) > 0) {
+		if (count($new_maintenance_apps) > 0) {
 			$database->app_name = self::APP_NAME;
 			$database->app_uuid = self::APP_UUID;
-			$database->save($array);
+			$database->save($new_maintenance_apps);
 			return true;
 		}
 		return false;

+ 0 - 1042
resources/classes/service.php

@@ -1,1042 +0,0 @@
-<?php
-
-/*
- * FusionPBX
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is FusionPBX
- *
- * The Initial Developer of the Original Code is
- * Mark J Crane <[email protected]>
- * Portions created by the Initial Developer are Copyright (C) 2008-2024
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Mark J Crane <[email protected]>
- * Tim Fry <[email protected]>
- */
-
-/**
- * Service class
- * @version 1.00
- * @author Tim Fry <[email protected]>
- */
-abstract class service {
-
-	const VERSION = "1.00";
-
-	/**
-	 * Track the internal loop. It is recommended to use this variable to control the loop inside the run function. See the example
-	 * below the class for a more complete explanation
-	 * @var bool
-	 */
-	protected $running;
-
-	/**
-	 * current debugging level for output to syslog
-	 * @var int Syslog level
-	 */
-	protected static $log_level = LOG_INFO;
-
-	/**
-	 * config object
-	 * @var config config object
-	 */
-	protected static $config;
-
-	/**
-	 * Holds the parsed options from the command line
-	 * @var array
-	 */
-	protected static $parsed_cli_options;
-
-	/**
-	 * Operating System process identification file
-	 * @var string
-	 */
-	private static $pid_file = "";
-
-	/**
-	 * Cli Options Array
-	 * @var array
-	 */
-	protected static $available_cli_options = [];
-
-	/**
-	 * Holds the configuration file location
-	 * @var string
-	 */
-	protected static $config_file = "";
-
-	/**
-	 * Child classes must provide a mechanism to reload settings
-	 */
-	abstract protected function reload_settings(): void;
-
-	/**
-	 * Method to start the child class internal loop
-	 */
-	abstract public function run(): int;
-
-	/**
-	 * Display version notice
-	 */
-	abstract protected static function display_version(): void;
-
-	/**
-	 * Called when the display_help_message is run in the base class for extra command line parameter explanation
-	 */
-	abstract protected static function set_cli_options();
-
-	/**
-	 * Open a log when created.
-	 * <p>NOTE:<br>
-	 * This is a protected function so it can not be called using the keyword 'new' outside of this class or a child
-	 * class. This is due to the requirement to set signal handlers for the POSIX system outside of the constructor.
-	 * PHP seems to have an issue on some versions where setting a signal handler while in the constructor (even
-	 * calling another method from the constructor) will fail to register the signal handlers.</p>
-	 */
-	protected function __construct() {
-		openlog('[php][' . self::class . ']', LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
-	}
-
-	public function __destruct() {
-		//ensure we unlink the correct PID file if needed
-		if (self::is_running()) {
-			unlink(self::$pid_file);
-			self::log("Initiating Shutdown...", LOG_NOTICE);
-			$this->running = false;
-		}
-		//this should remain the last statement to execute before exit
-		closelog();
-	}
-
-	/**
-	 * Shutdown process gracefully
-	 */
-	public static function shutdown() {
-		exit();
-	}
-
-	public static function send_shutdown() {
-		if (self::is_any_running()) {
-			self::send_signal(SIGTERM);
-		} else {
-			die("Service Not Started\n");
-		}
-	}
-
-	// register signal handlers
-	private function register_signal_handlers() {
-		// Allow the calls to be made while the main loop is running
-		pcntl_async_signals(true);
-
-		// A signal listener to reload the service for any config changes in the database
-		pcntl_signal(SIGUSR1, [$this, 'reload_settings']);
-		pcntl_signal(SIGHUP, [$this, 'reload_settings']);
-
-		// A signal listener to stop the service
-		pcntl_signal(SIGUSR2, [self::class, 'shutdown']);
-		pcntl_signal(SIGTERM, [self::class, 'shutdown']);
-	}
-
-	/**
-	 * Extracts the short options from the cli options array and returns a string. The resulting string must
-	 * return a single string with all options in the string such as 'rxc:'.
-	 * This can be overridden by the child class.
-	 * @return string
-	 */
-	protected static function get_short_options(): string {
-		return implode('' , array_map(function ($option) { return $option['short_option']; }, self::$available_cli_options));
-	}
-
-	/**
-	 * Extracts the long options from the cli options array and returns an array. The resulting array must
-	 * return a single dimension array with an integer indexed key but does not have to be sequential order.
-	 * This can be overridden by the child class.
-	 * @return array
-	 */
-	protected static function get_long_options(): array {
-		return array_map(function ($option) { return $option['long_option']; }, self::$available_cli_options);
-	}
-
-	/**
-	 * Method that will retrieve the callbacks from the cli options array
-	 * @param string $set_option
-	 * @return array
-	 */
-	protected static function get_user_callbacks_from_available_options(string $set_option): array {
-		//match the available option to the set option and return the callback function that needs to be called
-		foreach(self::$available_cli_options as $option) {
-			$short_option = $option['short_option'] ?? '';
-			if (str_ends_with($short_option, ':')) {
-				$short_option = rtrim($short_option, ':');
-			}
-			$long_option = $option['long_option'] ?? '';
-			if (str_ends_with($long_option, ':')) {
-				$long_option = rtrim($long_option, ':');
-			}
-			if ($short_option === $set_option ||
-				$long_option  === $set_option) {
-					return $option['functions'] ?? [$option['function']] ?? [];
-			}
-		}
-		return [];
-	}
-
-	/**
-	 *  Parse CLI options using getopt()
-	 * @return void
-	 */
-	protected static function parse_service_cli_options(): void {
-		//base class short options
-		self::$available_cli_options = self::base_cli_options();
-
-		//get the options from the child class
-		static::set_cli_options();
-
-		//collapse short options to a string
-		$short_options = self::get_short_options();
-
-		//isolate long options
-		$long_options = self::get_long_options();
-
-		//parse the short and long options
-		$options = getopt($short_options, $long_options);
-
-		//make the options available to the child object
-		if ($options !== false) {
-			self::$parsed_cli_options = $options;
-		} else {
-			//make sure the cli_options are reset
-			self::$parsed_cli_options = [];
-			//if the options are empty there is nothing left to do
-			return;
-		}
-
-		//notify user
-		self::log("CLI Options detected: " . implode(",", self::$parsed_cli_options), LOG_DEBUG);
-
-		//loop through the parsed options given on the command line
-		foreach ($options as $option_key => $option_value) {
-
-			//get the function responsible for handling the cli option
-			$funcs = self::get_user_callbacks_from_available_options($option_key);
-
-			//ensure it was found before we take action
-			if (!empty($funcs)) {
-				//check for more than one function to be called is permitted
-				if (is_array($funcs)) {
-					//call each one
-					foreach($funcs as $func) {
-						//use the best method to call the function
-						self::call_function($func, $option_value);
-					}
-				} else {
-					//single function call
-					self::call_function($func, $option_value);
-				}
-			}
-		}
-	}
-
-	//
-	// Calls a function using the best suited PHP method
-	//
-	private static function call_function($function, $args) {
-		if ($function === 'exit') {
-			//check for exit
-			exit($args);
-		} elseif ($function instanceof Closure || function_exists($function)) {
-			//globally available function or closure
-			$function($args);
-		} else {
-			static::$function($args);
-		}
-	}
-
-	/**
-	 * Checks the file system for a pid file that matches the process ID from this running instance
-	 * @return bool true if pid exists and false if not
-	 */
-	public static function is_running(): bool {
-		return posix_getpid() === self::get_service_pid();
-	}
-
-	public static function is_any_running(): bool {
-		return self::get_service_pid() !== false;
-	}
-
-	/**
-	 * Returns the operating system service PID or false if it is not yet running
-	 * @return bool|int PID or false if not running
-	 */
-	protected static function get_service_pid() {
-		if (file_exists(self::$pid_file)) {
-			$pid = file_get_contents(self::$pid_file);
-			if (function_exists('posix_getsid')) {
-				if (posix_getsid($pid) !== false) {
-					//return the pid for reloading configuration
-					return $pid;
-				}
-			} else {
-				if (file_exists('/proc/' . $pid)) {
-					//return the pid for reloading configuration
-					return $pid;
-				}
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * Create an operating system PID file removing any existing PID file
-	 */
-	private function create_service_pid() {
-		// Set the pid filename
-		$basename = basename(self::$pid_file, '.pid');
-		$pid = getmypid();
-
-		// Remove the old pid file
-		if (file_exists(self::$pid_file)) {
-			unlink(self::$pid_file);
-		}
-
-		// Show the details to the user
-		self::log("Service   : $basename", LOG_INFO);
-		self::log("Process ID: $pid", LOG_INFO);
-		self::log("PID File  : " . self::$pid_file, LOG_INFO);
-
-		// Save the pid file
-		file_put_contents(self::$pid_file, $pid);
-	}
-
-	/**
-	 * Creates the service directory to store the PID
-	 * @throws Exception thrown when the service directory is unable to be created
-	 */
-	private function create_service_directory() {
-		//make sure the /var/run/fusionpbx directory exists
-		if (!file_exists('/var/run/fusionpbx')) {
-			$result = mkdir('/var/run/fusionpbx', 0777, true);
-			if (!$result) {
-				throw new Exception('Failed to create /var/run/fusionpbx');
-			}
-		}
-	}
-
-	/**
-	 * Parses the debug level to an integer and stores it in the class for syslog use
-	 * @param string $debug_level Debug level with any of the Linux system log levels
-	 */
-	protected static function set_debug_level(string $debug_level) {
-		// Map user input log level to syslog constant
-		switch ($debug_level) {
-			case '0':
-			case 'emergency':
-				self::$log_level = LOG_EMERG; // Hardware failures
-				break;
-			case '1':
-			case 'alert':
-				self::$log_level = LOG_ALERT; // Loss of network connection or a condition that should be corrected immediately
-				break;
-			case '2':
-			case 'critical':
-				self::$log_level = LOG_CRIT; // Condition like low disk space
-				break;
-			case '3':
-			case 'error':
-				self::$log_level = LOG_ERR;  // Database query failure, file not found
-				break;
-			case '4':
-			case 'warning':
-				self::$log_level = LOG_WARNING; // Deprecated function usage, approaching resource limits
-				break;
-			case '5':
-			case 'notice':
-				self::$log_level = LOG_NOTICE; // Normal conditions
-				break;
-			case '6':
-			case 'info':
-				self::$log_level = LOG_INFO; // Informational
-				break;
-			case '7':
-			case 'debug':
-				self::$log_level = LOG_DEBUG; // Debugging
-				break;
-			default:
-				self::$log_level = LOG_NOTICE; // Default to NOTICE if invalid level
-		}
-	}
-
-	/**
-	 * Show memory usage to the user
-	 */
-	protected static function show_mem_usage() {
-		//current memory
-		$memory_usage = memory_get_usage();
-		//peak memory
-		$memory_peak = memory_get_peak_usage();
-		self::log('Current memory: ' . round($memory_usage / 1024) . " KB", LOG_INFO);
-		self::log('Peak memory: ' . round($memory_peak / 1024) . " KB", LOG_INFO);
-	}
-
-	/**
-	 * Logs to the system log
-	 * @param string $message
-	 * @param int $level
-	 */
-	protected static function log(string $message, int $level = null) {
-		// Use default log level if not provided
-		if ($level === null) {
-			$level = self::$log_level;
-		}
-
-		// Log the message to syslog
-		syslog($level, 'fusionpbx[' . posix_getpid() . ']: ['.self::class.'] '.$message);
-	}
-
-	/**
-	 * Returns a file safe class name with \ from namespaces converted to _
-	 * @return string file safe name
-	 */
-	protected static function base_file_name(): string {
-		return str_replace('\\', "_", static::class);
-	}
-
-	/**
-	 * Returns only the name of the class without namespace
-	 * @return string base class name
-	 */
-	protected static function base_class_name(): string {
-		$class_and_namespace = explode('\\', static::class);
-		return array_pop($class_and_namespace);
-	}
-
-	/**
-	 * Write a standard copyright notice to the console
-	 * @return void
-	 */
-	public static function display_copyright(): void {
-		echo "FusionPBX\n";
-		echo "Version: MPL 1.1\n";
-		echo "\n";
-		echo "The contents of this file are subject to the Mozilla Public License Version\n";
-		echo "1.1 (the \"License\"); you may not use this file except in compliance with\n";
-		echo "the License. You may obtain a copy of the License at\n";
-		echo "http://www.mozilla.org/MPL/\n";
-		echo "\n";
-		echo "Software distributed under the License is distributed on an \"AS IS\" basis,\n";
-		echo "WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n";
-		echo "for the specific language governing rights and limitations under the\n";
-		echo "License.\n";
-		echo "\n";
-		echo "The Original Code is FusionPBX\n";
-		echo "\n";
-		echo "The Initial Developer of the Original Code is\n";
-		echo "Mark J Crane <[email protected]>\n";
-		echo "Portions created by the Initial Developer are Copyright (C) 2008-2023\n";
-		echo "the Initial Developer. All Rights Reserved.\n";
-		echo "\n";
-		echo "Contributor(s):\n";
-		echo "Mark J Crane <[email protected]>\n";
-		echo "Tim Fry <[email protected]>\n";
-		echo "\n";
-	}
-
-	/**
-	 * Sends the shutdown signal to the service using a posix signal.
-	 * <p>NOTE:<br>
-	 * The signal will not be received from the service if the
-	 * command is sent from a user that has less privileges then
-	 * the running service. For example, if the service is started
-	 * by user root and then the command line option '-r' is given
-	 * as user www-data, the service will not receive this signal
-	 * because the OS will not allow the signal to be passed to a
-	 * more privileged user due to security concerns. This would
-	 * be the main reason why you must run a 'systemctl' or a
-	 * 'service' command as root user. It is possible to start the
-	 * service with user www-data and then the web UI would in fact
-	 * be able to send the reload signal to the running service.</p>
-	 */
-	public static function send_signal($posix_signal) {
-		$signal_name = "";
-		switch ($posix_signal) {
-			case SIGHUP:
-			case SIGUSR1:
-				$signal_name = "Reload";
-				break;
-			case SIGTERM:
-			case SIGUSR2:
-				$signal_name = "Shutdown";
-				break;
-		}
-		$pid = self::get_service_pid();
-		if ($pid === false) {
-			self::log("service not running", LOG_EMERG);
-		} else {
-			if (posix_kill((int) $pid, $posix_signal) ) {
-				echo "Sent $signal_name\n";
-			} else {
-				$err = posix_strerror(posix_get_last_error());
-				echo "Failed to send $signal_name: $err\n";
-			}
-		}
-	}
-
-	/**
-	 * Display a basic help message to the user for using service
-	 */
-	protected static function display_help_message(): void {
-		//get the classname of the child class
-		$class_name = self::base_class_name();
-
-		//get the widest options for proper alignment
-		$width_short = max(array_map(function ($arr) { return strlen($arr['short_description'] ?? ''); }, self::$available_cli_options));
-		$width_long  = max(array_map(function ($arr) { return strlen($arr['long_description' ] ?? ''); }, self::$available_cli_options));
-
-		//display usage help using the class name of child
-		echo "Usage: php $class_name [options]\n";
-
-		//display the options aligned to the widest short and long options
-		echo "Options:\n";
-		foreach (self::$available_cli_options as $option) {
-			printf("%-{$width_short}s %-{$width_long}s %s\n",
-				$option['short_description'],
-				$option['long_description'],
-				$option['description']
-			);
-		}
-	}
-
-	public static function send_reload() {
-		if (self::is_any_running()) {
-			self::send_signal(SIGUSR1);
-		} else {
-			die("Service Not Started\n");
-		}
-		exit();
-	}
-
-	//
-	// Options built-in to the base service class. These can be overridden with the child class
-	// or they can be extended using the array
-	//
-	private static function base_cli_options(): array {
-		//put the display for help in an array so we can calculate width
-		$help_options = [];
-		$index = 0;
-		$help_options[$index]['short_option'] = 'v';
-		$help_options[$index]['long_option'] = 'version';
-		$help_options[$index]['description'] = 'Show the version information';
-		$help_options[$index]['short_description'] = '-v';
-		$help_options[$index]['long_description'] = '--version';
-		$help_options[$index]['functions'][] = 'display_version';
-		$help_options[$index]['functions'][] = 'shutdown';
-		$index++;
-		$help_options[$index]['short_option'] = 'h';
-		$help_options[$index]['long_option'] = 'help';
-		$help_options[$index]['description'] = 'Show the version and help message';
-		$help_options[$index]['short_description'] = '-h';
-		$help_options[$index]['long_description'] = '--help';
-		$help_options[$index]['functions'][] = 'display_version';
-		$help_options[$index]['functions'][] = 'display_help_message';
-		$help_options[$index]['functions'][] = 'shutdown';
-		$index++;
-		$help_options[$index]['short_option'] = 'a';
-		$help_options[$index]['long_option'] = 'about';
-		$help_options[$index]['description'] = 'Show the version and copyright information';
-		$help_options[$index]['short_description'] = '-a';
-		$help_options[$index]['long_description'] = '--about';
-		$help_options[$index]['functions'][] = 'display_version';
-		$help_options[$index]['functions'][] = 'display_copyright';
-		$help_options[$index]['functions'][] = 'shutdown';
-		$index++;
-		$help_options[$index]['short_option'] = 'r';
-		$help_options[$index]['long_option'] = 'reload';
-		$help_options[$index]['description'] = 'Reload settings for an already running service';
-		$help_options[$index]['short_description'] = '-r';
-		$help_options[$index]['long_description'] = '--reload';
-		$help_options[$index]['functions'][] = 'send_reload';
-		$index++;
-		$help_options[$index]['short_option'] = 'd:';
-		$help_options[$index]['long_option'] = 'debug:';
-		$help_options[$index]['description'] = 'Set the syslog level between 0 (EMERG) and 7 (DEBUG). 5 (INFO) is default';
-		$help_options[$index]['short_description'] = '-d <level>';
-		$help_options[$index]['long_description'] = '--debug <level>';
-		$help_options[$index]['functions'][] = 'set_debug_level';
-		$index++;
-		$help_options[$index]['short_option'] = 'c:';
-		$help_options[$index]['long_option'] = 'config:';
-		$help_options[$index]['description'] = 'Full path and file name of the configuration file to use. /etc/fusionpbx/config.conf or /usr/local/etc/fusionpbx/config.conf on FreeBSD is default';
-		$help_options[$index]['short_description'] = '-c <path>';
-		$help_options[$index]['long_description'] = '--config <path>';
-		$help_options[$index]['functions'][] = 'set_config_file';
-		$index++;
-		$help_options[$index]['short_option'] = 'x';
-		$help_options[$index]['long_option'] = 'exit';
-		$help_options[$index]['description'] = 'Exit the service gracefully';
-		$help_options[$index]['short_description'] = '-x';
-		$help_options[$index]['long_description'] = '--exit';
-		$help_options[$index]['functions'][] = 'send_shutdown';
-		$help_options[$index]['functions'][] = 'shutdown';
-		return $help_options;
-	}
-
-	/**
-	 * Set the configuration file location to use for a config object
-	 */
-	public static function set_config_file(string $file = '/etc/fusionpbx/config.conf') {
-		if (empty(self::$config_file)) {
-			self::$config_file = $file;
-		}
-		self::$config = new config(self::$config_file);
-	}
-
-	/**
-	 * Appends the CLI option to the list given to the user as a command line argument.
-	 * @param cli_option $option
-	 * @return int The index of the item added
-	 */
-	public static function append_cli_option(cli_option $option): int {
-		$index = count(self::$available_cli_options);
-		self::$available_cli_options[$index] = $option->to_array();
-		return $index;
-	}
-
-	/**
-	 * Adds an option to the command line parameters
-	 * @param string $short_option
-	 * @param string $long_option
-	 * @param string $description
-	 * @param string $short_description
-	 * @param string $long_description
-	 * @param string $callback
-	 * @return int The index of the item added
-	 */
-	public static function add_cli_option(string $short_option, string $long_option, string $description, string $short_description = '', string $long_description = '', ...$callback): int {
-		//use the option as the description if not filled in
-		if (empty($short_description)) {
-			$short_description = '-' . $short_option;
-			if (str_ends_with($short_option, ':')) {
-				$short_description .= " <setting>";
-			}
-		}
-		if (empty($long_description)) {
-			$long_description = '-' . $long_option;
-			if (str_ends_with($long_option, ':')) {
-				$long_description .= " <setting>";
-			}
-		}
-		$index = count(self::$available_cli_options);
-		self::$available_cli_options[$index]['short_option'] = $short_option;
-		self::$available_cli_options[$index]['long_option'] = $long_option;
-		self::$available_cli_options[$index]['description'] = $description;
-		self::$available_cli_options[$index]['short_description'] = $short_description;
-		self::$available_cli_options[$index]['long_description'] = $long_description;
-		self::$available_cli_options[$index]['functions'] = $callback;
-		return $index;
-	}
-
-	/**
-	 * Returns the process ID filename used for a service
-	 * @return string file name used for the process identifier
-	 */
-	public static function get_pid_filename(): string {
-		return '/var/run/fusionpbx/' . self::base_file_name() . '.pid';
-	}
-
-	/**
-	 * Sets the following:
-	 *   - execution time to unlimited
-	 *   - location for PID file
-	 *   - parses CLI options
-	 *   - ensures folder structure exists
-	 *   - registers signal handlers
-	 */
-	private function init() {
-
-		// Increase limits
-		set_time_limit(0);
-		ini_set('max_execution_time', 0);
-		ini_set('memory_limit', '512M');
-
-		//set the PID file
-		self::$pid_file = self::get_pid_filename();
-
-		//register the shutdown function
-		register_shutdown_function([$this, 'shutdown']);
-
-		// Ensure we have only one instance
-		if (self::is_any_running()) {
-			self::log("Service already running", LOG_ERR);
-			exit();
-		}
-
-		// Ensure directory creation for pid location
-		$this->create_service_directory();
-
-		// Create a process identifier file
-		$this->create_service_pid();
-
-		// Set the signal handlers for reloading
-		$this->register_signal_handlers();
-
-		// We are now considered running
-		$this->running = true;
-	}
-
-	/**
-	 * Creates a system service that will run in the background
-	 * @return self
-	 */
-	public static function create(): self {
-		//can only start from command line
-		defined('STDIN') or die('Unauthorized');
-
-		//force launching in a seperate process
-		if ($pid = pcntl_fork()) {
-			exit;
-		}
-
-		if ($cid = pcntl_fork()) {
-			exit;
-		}
-
-		//set the PID file we will use
-		self::$pid_file = self::get_pid_filename();
-
-		//TODO remove updated settings object after merge
-		if (file_exists( __DIR__ . '/settings.php')) {
-			require_once __DIR__ . '/settings.php';
-		}
-
-		//TODO remove global functions after merge
-		if (file_exists(dirname(__DIR__).'/functions.php')) {
-			require_once dirname(__DIR__).'/functions.php';
-		}
-
-		//parse the cli options and store them statically
-		self::parse_service_cli_options();
-
-		//create the config object if not already created
-		if (self::$config === null) {
-			self::$config = new config(self::$config_file);
-		}
-
-		//get the name of child object
-		$class = self::base_class_name();
-
-		//create the child object
-		$service = new $class();
-
-		//initialize the service
-		$service->init();
-
-		//return the initialized object
-		return $service;
-	}
-
-}
-
-/*
- * Example
- *
- * The child_service class must be used to demonstrate the base_service because base_service is abstract. This means that you
- * cannot use the syntax of:
- *   $service = new service();		//throws fatal error
- *   $service->run();				//never reaches this statement
- *
- * Instead, you must use a class that will extend the service class like this:
- *   $service = child_service::create();
- *   $service->run();
- * (make the code below more readable by putting)
- * ( in the '/' line below to complete the comment section )
- *
-
-//
-// A class that extends base_service must implement 4 functions:
-//   - run()               This is the entry point called from an external source after the create method is called
-//   - reload_settings     This is called when the CLI option -r or --reload is used
-//   - display_version
-//   - cli_options
-//
-// Using the class below use the commands
-//   $simple_example = simple_example::create();
-//   $simple_example->run();
-//
-// This will create the class and then run it once and exit with a success code.
-//
-//
-class simple_example extends service {
-
-	protected function reload_settings(): void {
-
-	}
-
-	protected static function display_version(): void {
-		echo "Version 1.00\n";
-	}
-
-	protected static function set_cli_options() {
-
-	}
-
-	public function run(): int {
-		echo "Successfully ran child service\n";
-		echo "Try command line options -h or -v\n";
-		return 0;
-	}
-}
-
-//*/
-/*
-//
-// This class is more complex in that it will continue to run with a connection to a database
-//
-// The service class is divided between static and non-static methods. The static methods are
-// used and called before the service is run allowing the CLI options to be read and parsed
-// before the object is initialized. This allows for configuration options to be available
-// when the child class is first started up. Keep in mind that these are called statically
-// so that all callback functions declared in the cli options must be static.
-//
-class child_service extends service {
-
-	//
-	// Using a version constant is ideal for tracking and reporting
-	//
-	const CHILD_SERVICE_VERSION = '1.00';
-
-	//
-	// The parent service does not create a database connection as the child service may not need it. This example
-	// demonstrates how the config object is passed from the parent and then used in the child service to connect
-	// to other resources or use other settings the base class loaded so the child class automatically inherits.
-	//
-	private $database;
-
-	// This example uses a settings object to demonstrate how the config is passed through to the child class
-	// and is then used again in the reload_settings to demonstrate how the settings could be reloaded
-	// with changes in the configuration, database connection, and default settings without the need to create
-	// new instances of the config object.
-	private $settings;
-
-	//
-	// This function is required from the base service class because it is used when the reload command line option is used
-	//
-	protected function reload_settings(): void {
-		//informing the user in this example is simple but can use the parent class log functions
-		echo "Reloading settings\n";
-
-		//
-		// Reload the configuration file
-		//
-		self::$config->read();
-
-		//
-		// If services have their own configuration file that was passed in using the -c or --config option, the options
-		// would be available here as well to the child class
-		// By allowing the config file to be specified, it is possible for services to have a configuration specific to them
-		// while it could still be possible to allow access to the original making it very flexible with a wide degree of
-		// choices.
-		//
-		// For example, specifying a configuration file that could be used for an archive or backup server would allow
-		// the backup service to connect to another system remotely.
-		//
-		// It could also be used to separate the web configuration from system services to keep them organized and allow for
-		// configuration settings to be available should the database fail. One possible scenario where this could be useful
-		// is to send an email if the database stops responding. Currently, this is not possible as the database class uses
-		// the 'die' command to immediately exit. I think it would be good to remove that and instead set the error message
-		// to be something that would reflect the error allowing a system service to detect and even possibly correct that.
-		//
-		$alert_email = self::$config->get('alert_email', '');
-		$smtp_host = self::$config->get('smtp_host', '');
-		$smtp_port = self::$config->get('smtp_port', '');
-
-		//
-		// Ensure the database is connected with the new configuration parameters
-		//
-		$this->database->connect();
-
-		//
-		// The reload settings here completes the chain
-		//
-		$this->settings->reload();
-
-	}
-
-	//
-	// This run function is required as it is called to launch child_service. This
-	// is the entry point for the child class.
-	//
-	public function run(): int {
-
-		//
-		// Create the database object once passing a reference to the config object
-		//
-		$this->database = new database(['config' => self::$config]);
-
-		//
-		// Create the settings object using the database connection
-		//
-		$this->settings = new settings(['database' => $this->database]);
-
-		//
-		// In this example I have used the reload_settings because it is required by the parent class
-		// whenever the '-r' or '--reload' option is given on the CLI. The base class is responsible for
-		// parsing the information given on the CLI. Whenever the base class detects a '-r' option, the
-		// reload_settings method in the child class is called. This gives the responsibility to the the
-		// child class to reload any settings that might be needed during long execution of the service
-		// without stopping and starting the service. The method is called here to initialize any and all
-		// objects within the child service.
-		//
-		$this->reload_settings();
-
-		//
-		// The $running property is declared in the base service class as a boolean and it is responsible
-		// to enable this so that the child class can run. The base service class will set this to false
-		// if it receives a shutdown command from either the OS, PHP, or a posix signal allowing the child
-		// class to respond or clean up after the while loop.
-		//
-		while($this->running) {
-			//
-			// This is where the actual heart of the code for the new service will be created
-			//
-			echo "Doing something..." . date("Y-m-d H:i:s") . "\n";
-			sleep(1);
-		}
-
-
-		//
-		// Returning a non-zero value would indicate there was an issue. Here we return zero to indicate graceful shutdown.
-		//
-		return 0;
-	}
-
-	//
-	// This is the version that will be displayed when the option '-v' or '--version' is used on the command line.
-	// This run function is required
-	//
-	protected static function display_version(): void {
-		echo "Child service example version " . self::CHILD_SERVICE_VERSION . "\n";
-	}
-
-	//
-	// set_cli_options can either add to or replace options. Replacing the base options would allow an override for default behaviour.
-	// This run function is required
-	//
-	protected static function set_cli_options() {
-
-		//
-		// The options below are added to the CLI options and displayed whenever the -h or --help option is used.
-		// There are multiple methods are used to suite the style of the creator
-		//
-
-		//
-		// The callbacks set here are used to demonstrate multiple calls can be used
-		//
-
-		//using the parameter in the function
-		self::add_cli_option(
-			't:'
-			, 'template:'
-			, 'Full path and file name of the template file to use'
-			, '-t <path>'
-			, '--template <path>'
-			, ['set_template_path']
-		);
-		//using a container object
-		self::append_cli_option(cli_option::new()
-			->short_option('n')
-			->long_option('null')
-			->description('This option is to demonstrate using a cli object to create cli options')
-			->functions(['null_function_method'])
-		);
-		//using an array of key/value pairs
-		self::append_cli_option(cli_option::new([
-			'short_option' => 'z:'
-			,'long_option' => 'zero:'
-			,'description' => 'This has zero effect on behavior'
-			,'function' => 'call_single_function'
-		]));
-
-		//
-		// These options are here but are commented out to allow the functionality to still exist in the parent
-		//
-//
-//		//replace cli options in the parent class using array
-//		$index = 0;
-//		$arr_options = [];
-//		$arr_options[$index]['short_option'] = 'z';
-//		$arr_options[$index]['long_option'] = 'zero';
-//		$arr_options[$index]['description'] = 'This has zero effect on behavior';
-//		$arr_options[$index]['short_description'] = '-z';
-//		$arr_options[$index]['long_description'] = '--zero';
-//		$arr_options[$index]['function'][] = 'call_single_function';
-//		self::$available_cli_options = $arr_options;
-//
-//		//replace all cli options using container object
-//		$arr_options = [];
-//		self::$available_cli_options = [];
-//		$arr_options[0] = cli_option::new()
-//			->short_option('z')
-//			->short_description('-z')
-//			->function('call_a_function')
-//			->function('call_another_function_after_first')
-//			->description('This option does nothing')
-//			->to_array();
-//
-//		$arr_options[1] = cli_option::new([
-//			'short_option' => 'z'
-//			,'long_option' => '--zero'
-//			,'description' => 'This option does nothing'
-//			,'functions' => ['call_a_function', 'call_another_function']
-//		])->to_array();
-		//self::$available_cli_options = $arr_options;
-	}
-} // class child_service
-
-//*/
-
-/*
-//
-// Standard includes do not apply for the base class because the require.php has included many other php files. These other files
-// or objects may not be required for some services. Thus, only the config is required for base_service. Child services may then
-// create a database class and use it by passing the config object to the database constructor. This is why the 'require.php' is
-// left out of the initial setup class.
-//
-
-// Use the auto_loader to find any classes needed so we don't have a lot of include statements
-// In this example, the auto_loader should not be using the PROJECT_ROOT or any other defined constants
-// because they are not needed in the initial stage of loading
-require_once __DIR__ . '/auto_loader.php';
-
-// We don't need to ever reference the object so don't assign a variable. It
-// would be a good idea to remove the auto_loader as a class declaration so
-// that there would only need to be one line. It seems illogical to have an
-// object that never needs to be referenced.
-new auto_loader();
-
-// The base_service class has a 'protected' constructor, meaning you are not able to use "new" to create the object. Instead, you
-// must use the 'create' static method to create an object. This technique is employed because some PHP versions have an issue with
-// registering signal listeners in the constructor. See the link https://www.php.net/manual/en/function.pcntl-signal.php in the user
-// comments section.
-// The child_service class does not override the parent constructor so parent constructor is used. If the child_service class does
-// have a constructor then the child class must call:
-//   parent::__construct($config);
-// as the first line of the child constructor. This is because the parent constructor uses the config class. This also means
-// that the child class must receive the config object in the constructor as a minimum.
-$service = child_service::create();
-
-// The run class is declared as abstract in the parent. So the child class must have one.
-$service->run();
-//*/