#!/usr/bin/php -q
<?php
if (PHP_VERSION_ID < 60000) {
	mb_internal_encoding("UTF-8");
	set_time_limit(0);
	ob_implicit_flush();
	declare(ticks = 10);
	date_default_timezone_set("Europe/Paris");
}
$config = array();
$config['thread_list'] = array();
$config['keepalive'] = 5;
$config['to_test_interval']=30;
$config['version']=0.037;
$config['last_test']=0;
$config['last_log_rotate']=0;
$config['last_dhcp_clean']=0;
$config['last_backup_px_files']=0;
$config['ftp_backup_server']="192.168.200.55";
$config['ftp_backup_user']="ftpminion";
$config['ftp_backup_password']="5SvcpdcpPnaWCwMR";
$config["auto_upgrade"]=true;
$sock="";
error_reporting(-1);
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
ini_set('log_errors', true);
ini_set('error_log', '/var/log/minion.log');

//-----------------------------------------------------------------------------
class Thread {
    const FUNCTION_NOT_CALLABLE     = 10;
    const COULD_NOT_FORK            = 15;
    private $errors = array(
        Thread::FUNCTION_NOT_CALLABLE   => 'ERROR : No valid function given to THREAD function...',
        Thread::COULD_NOT_FORK          => 'ERROR : Fork process failed...',
    );
    protected $runnable;
    private $pid;
	//--------------------------------------------
    public static function available() {
        $required_functions = array(
            'pcntl_fork',
        );
        
        foreach( $required_functions as $function ) {
            if ( !function_exists( $function ) ) {
                return false;
            }
        }
        
        return true;
    }
	//-------------------------------------------------
    public function __construct( $_runnable = null ) {
    	if( $_runnable !== null ) {
        	$this->setRunnable( $_runnable );
    	}
    }
    //-------------------------------------------------
    public function setRunnable( $_runnable ) {
        if( self::runnableOk( $_runnable ) ) {
            $this->runnable = $_runnable;
        }
        else {
            throw new Exception( $this->getError( Thread::FUNCTION_NOT_CALLABLE ), Thread::FUNCTION_NOT_CALLABLE );
        }
    }
	//-------------------------------------------------
    public function getRunnable() {
        return $this->runnable;
    }
	//--------------------------------------------------
    public static function runnableOk( $_runnable ) {
        return ( function_exists( $_runnable ) && is_callable( $_runnable ) );
    }
	//--------------------------------------------------
    public function getPid() {
        return $this->pid;
    }
	//---------------------------------------------------
    public function isAlive() {
        $pid = pcntl_waitpid( $this->pid, $status, WNOHANG );
        return ( $pid === 0 );
        
    }
	//---------------------------------------------------------
    public function start() {
        $pid = @ pcntl_fork();
        if( $pid == -1 ) {
            throw new Exception( $this->getError( Thread::COULD_NOT_FORK ), Thread::COULD_NOT_FORK );
        }
        if( $pid ) {
            $this->pid = $pid;
        }
        else {
            pcntl_signal( SIGTERM, array( $this, 'signalHandler' ) );
            $arguments = func_get_args();
            if ( !empty( $arguments ) ) {
                call_user_func_array( $this->runnable, $arguments );
            }
            else {
                call_user_func( $this->runnable );
            }
            
            exit( 0 );
        }
    }
	//------------------------------------------------------------------------
    public function stop( $_signal = SIGTERM, $_wait = false ) {
        if( $this->isAlive() ) {
            posix_kill( $this->pid, $_signal );
            if( $_wait ) {
                pcntl_waitpid( $this->pid, $status = 0 );
            }
        }
    }
	//------------------------------------------------------------------------
    public function kill( $_signal = SIGKILL, $_wait = false ) {
        posix_kill( $this->pid, $_signal );
    }
    //------------------------------------------------------------------------
    public function getError( $_code ) {
        if ( isset( $this->errors[$_code] ) ) {
            return $this->errors[$_code];
        }
        else {
            return "error " . $_code;
        }
    }
	//--------------------------------------------------------------------------
    protected function signalHandler( $_signal ) {
        switch( $_signal ) {
            case SIGTERM:
				exit( 0 );
            break;
        }
    }
	//---------------------------------------------------------------------------
}
//-----------------------------------------------------------------------------
function readconfig_master() { // lit de fichier de configuration
	global $config;
	global $connbdd;
	$reader = new XMLReader(); 
	$reader->open("/etc/minion/minion.conf"); 
	while ($reader->read()) { 
		switch ($reader->nodeType)  { 
			case (XMLREADER::ELEMENT): 
				if ($reader->name == "security_key") { $reader->read(); $config['config_security_key'] = trim( $reader->value ); break; }
				if ($reader->name == "server_ip") { $reader->read(); $config['server_ip'] = trim( $reader->value ); break; }
				if ($reader->name == "server_port") { $reader->read(); $config['server_port'] = trim( $reader->value ); break; }
				if ($reader->name == "hostname") { $reader->read(); $config['hostname'] = trim( $reader->value ); break; }
				if ($reader->name == "ftp_backup_address") { $reader->read(); $config['ftp_backup_address'] = trim( $reader->value ); break; }
		}
	}
	if (!isset($config['hostname'])) {
		$config['hostname']=trim(system("hostname -f"));
	}
	$config['current_os']=trim(system("uname"));
	$config['last_info']=0;
}
//-----------------------------------------------------------------------------
function Crypte($Texte) { // crypte les infos de communications des daemons avec la clé
	global $config;
	global $connbdd;
	$iv=substr(md5(openssl_random_pseudo_bytes(16)), 0, 16);
	if (PHP_VERSION_ID > 80223) {
		return $iv.openssl_encrypt($Texte, "aes256", $config['config_security_key'], 0, $iv);
	}
	else {
		return $iv.openssl_encrypt($Texte, "aes256", $config['config_security_key'], NULL, $iv);
	}
  }
//----------------------------------------------------------------------------
function Decrypte($Texte) { // decrypte les infos de communications des daemons avec la clé
	global $config;
	global $connbdd;
	$iv=substr($Texte, 0, 16);
	$Texte=substr_replace($Texte, "", 0, 16);
	if (PHP_VERSION_ID > 80223) {
		return openssl_decrypt($Texte, "aes256", $config['config_security_key'], 0, $iv);
	}
	else {
		return openssl_decrypt($Texte, "aes256", $config['config_security_key'], NULL, $iv);
	}
  }
//------------------------------------------------------------------------------------
function myexec($commande) {
	global $config;
	global $connbdd;
	exec ($commande." 2>&1", $result, $return);
	if ($return > 0) logme ("EXEC : ".$commande." -> ".$return);
	return $result;
}
//------------------------------------------------------------------------------------
function myexec_er($commande) {
	global $config;
	global $connbdd;
	exec ($commande." 2>&1; echo $?", $result, $return);
	return $result[0];
}
//------------------------------------------------------------------------------------
function logme ($data) {
	file_put_contents("/var/log/minion.log", $data."\n", FILE_APPEND | LOCK_EX);
}
//------------------------------------------------------------
function become_daemon() { 

	$pid = pcntl_fork(); 
	if ($pid == -1) { 
		exit(); 
    }
	elseif ($pid) { 
		exit(); 
    }
	else { 
		posix_setsid(); 
		chdir('/'); 
		umask(0); 
		return posix_getpid(); 
    } 
}
//-----------------------------------------------------------
function sig_handler($sig) { 
	global $proc;
	global $sock;
	global $config;
    switch($sig) { 
        case SIGTERM:
			logme ("Shutdown via SIGTERM...");
			$config['thread_list']['zstd_start_check_backup']->kill();
			sleep(1);
			passthru("pkill -SIGTERM -f \"zstd -qq --test\"");
			exit(0);
		break;
        case SIGINT:
			logme ("Shutdown via SIGKILL...");
			$config['thread_list']['zstd_start_check_backup']->kill();
			sleep(1);
			passthru("pkill -SIGTERM -f \"zstd -qq --test\"");
			exit(0);
        break; 		
    } 
}
//-----------------------------------------------------------
function listen() {
	global $config;
	global $sock;
	
	$address = $config['server_ip'];
	$port = $config['server_port'];
	$max_clients = 5;
	socket_clear_error();
 
	if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0))) {
		$errorcode = socket_last_error();
		$errormsg = socket_strerror($errorcode);
        returrn;
	} 
 
	if(!socket_connect($sock,$config['server_ip'],$config['server_port'])) {
		logme("MINION : Failed to connect to GODLIKE : ".$config['server_ip'].":".$config['server_port']);
		return;
	}
	
	logme("MINION : is connect to GODLIKE : ".$config['server_ip'].":".$config['server_port']);
	socket_write($sock , Crypte("hostname ".$config['hostname']." ".$config['version'])."\n");
	socket_write($sock , Crypte("send_my_test")."\n");
	
	$keepalive=time();
	
	while (socket_last_error()==0) { //start loop to listen for incoming connections and process existing connections
		$read = array(); //array of sockets to read
		$read[0] = $sock; //first socket is the master socket
		
		//---------------KEEPALIVE---------------------
			if ($keepalive + (rand(ceil($config['keepalive']/1.5), $config['keepalive'])) < time()) {
				socket_write($sock , Crypte("¤")."\n");
			}
			if ($keepalive + ($config['keepalive']+5) < time()) {
				socket_close($sock);
				return;
			}
		//---------------------------------------------
		
		if(@socket_select($read , $write , $except , 0,200000) === false) { //now call select - blocking call
			$errorcode = socket_last_error();
			$errormsg = socket_strerror($errorcode);
            return;
		}
		
		//-----------TO SEND------------------
		//------------------------------------
		
		//-----------TO TEST and send result to master------------
		if ((time() > ($config['last_test'] + (rand($config['to_test_interval']-3, $config['to_test_interval']+3)) )) AND isset($config['to_test'])) {
			$thread_exec_test=new Thread("exec_test");
			$thread_exec_test->start();
			$config['last_test']=time();
		}
		//-------------Report system and version---------------------------------------------
		if ((time() > ($config['last_info']+28800))) {
			$thread_exec_info=new Thread("exec_info");
			$thread_exec_info->start();
			$config['last_info']=time();
		}
		//----------------------DHCP CLEAN LEASE for PFS--------------------------------------
		if ($config['type']=="pfs") {
			if ((time() > ($config['last_dhcp_clean']+86400))) {
				logme("FTP_BACKUP : Start FTP backup for PfSense");
				//$thread_backup_pfs_files=new Thread("backup_pfs_files");                       lib FPT non dispo pfsense
				//$thread_backup_pfs_files->start();
				$config['last_dhcp_clean']=time();
			}
		}
		//----------------------BACKUP proxmox config files--------------------------------------
		if ($config['type']=="px") {
			if ((time() > ($config['last_backup_px_files']+80000)) AND (intval(date("H")) > 4) AND (intval(date("H")) < 6)) {				
				postqueue_empty_all();
				logme("FTP_BACKUP : Start FTP backup for proxmox");
				$thread_backup_px_files=new Thread("backup_px_files");
				$thread_backup_px_files->start();
				$config['thread_list']['zstd_start_check_backup']=new Thread("zstd_start_check_backup");
				$config['thread_list']['zstd_start_check_backup']->start();
				$config['last_backup_px_files']=time();
			}
		}
		//--------Respond to order from master---------------------------------
		if (in_array($sock , $read)) {
			$input = socket_read($sock , 65500, PHP_NORMAL_READ);
			if ($input == null) { // Lost communication close socket
				socket_close($sock);
				logme("MINION : lost GODLIKE ...");
			}
			else { // comunication ok analyse ...
				$a = trim($input);
				$a=Decrypte($a);
				$keepalive=time();
				$n=explode(" ", $a, 2);
				switch ($n[0]) {
					case "upgrade":
						if ($config["auto_upgrade"]) {
							$s=explode(" ", $n[1], 2);
							if ($s[0]==md5($s[1])) {
								file_put_contents("/opt/minion/minion" , $s[1]);
								if (file_exists("/etc/init.d/minion")) {
									myexec("/etc/init.d/minion restart");
									exit();
								}
								if (file_exists("/usr/local/etc/rc.d/minion.sh")) {
									myexec("/usr/local/etc/rc.d/minion.sh");
									exit();
								}
							}
						}
					break;
					case "¤":
						$keepalive=time();
					break;
					case "your_test":
						$config['to_test']=$n[1];
					break;
					case "give_version":
						socket_write($sock , Crypte("my_version ".md5(file_get_contents("/opt/minion/minion")))."\n");
					break;
					case "restart_minion":
						socket_close($sock);
						return;
					break;
					case "reconfig":
						$d=explode(" ", $n[1]);
						foreach ($d AS $h=>$l) {
							$c=explode ("=", $l);
							if (isset($c[0]) AND isset($c[1])) {
								$config[$c[0]]=$c[1];
							}
						}
					break;
					default:
					break;
				}
			}
		}
		//------------------------------------------------------------------------
	}
	
}
//-----------------------------------------------------------------------------------------------
function backup_px_files() {
	global $config;
	$tmp=explode(".", $config['hostname']);
	$host=$tmp[0];
	$domainame=$tmp[1].".".$tmp[2];
	if (ftp_backup_connect()) {
		logme("FTP_BACKUP : Connecté !");
		@ftp_mkdir($config['ftp_backup_connection'], $domainame);
		@ftp_chdir($config['ftp_backup_connection'], "/".$domainame);
		@ftp_mkdir($config['ftp_backup_connection'], $host.".".$domainame);
		@ftp_chdir($config['ftp_backup_connection'], "/".$domainame."/".$host.".".$domainame);
		ftp_backup_copy("/etc/openvpn/postup", "/".$domainame."/".$host.".".$domainame."/postup");
		ftp_close($config['ftp_backup_connection']);
	}
	else {
		logme("FTP_BACKUP : Echec de connexion !");
	}
}
//-----------------------------------------------------------------------------------------------
function backup_pfs_files() {
	global $config;
	$tmp=explode(".", $config['hostname']);
	$host=$tmp[0];
	$domainame=$tmp[1].".".$tmp[2];
	if (ftp_backup_connect()) {
		logme("FTP_BACKUP : Connecté !");
		@ftp_mkdir($config['ftp_backup_connection'], $domainame);
		@ftp_chdir($config['ftp_backup_connection'], "/".$domainame);
		@ftp_mkdir($config['ftp_backup_connection'], $host.".".$domainame);
		@ftp_chdir($config['ftp_backup_connection'], "/".$domainame."/".$host.".".$domainame);
		$dir="/cf/conf/backup";
		if (is_dir($dir)) {
			if ($dh = opendir($dir)) {
				while (($file = readdir($dh)) !== false) {
					if (filetype($dir . $file)=="file") {
						$fileNameParts = explode('.', $file);
						$ext = end($fileNameParts);
						if ($ext=="xml") {
							logme ("FTP_Tranfert -> ".$file);
							ftp_backup_copy($dir.$file, "/".$domainame."/".$host.".".$domainame."/".$file);
						}
					}
				}
				closedir($dh);
			}
		}
		ftp_close($config['ftp_backup_connection']);
		logme("FTP_BACKUP : end, closing connection.");
	}
	else {
		logme("FTP_BACKUP : Echec de connexion !");
	}
}
//-----------------------------------------------------------------------------------------------
function ftp_backup_connect() {
	global $config;
	if (isset($config['ftp_backup_address'])) {
		$config['ftp_backup_connection'] = ftp_connect($config['ftp_backup_address'], 21, 10);
	}
	else {
		$config['ftp_backup_connection'] = ftp_connect($config['ftp_backup_server'], 21, 10);
	}
	if ($config['ftp_backup_connection']!==FALSE) {
		$login_result = ftp_login($config['ftp_backup_connection'], $config['ftp_backup_user'], $config['ftp_backup_password']);
	}
	else return FALSE;
	if ($login_result===TRUE) {
		ftp_pasv($config['ftp_backup_connection'], true);
		return TRUE;
	}
	else return FALSE;
}
//-----------------------------------------------------------------------------
function ftp_backup_copy_if_diff($local, $remote) {
	global $config;
	$local_size=filesize($local);
	$remote_size=ftp_size($config['ftp_backup_connection'], $remote);
	if (($local_size!=$remote_size)) {
		if (ftp_put($config['ftp_backup_connection'], $remote, $local, FTP_BINARY)) {
			logme("FTP_BACKUP : copy ".$local." -> ".$remote." : OK");
		}
		else {
			logme("FTP_BACKUP : copy ".$local." -> ".$remote." : ECHEC !!!");
		}
	}
}
//------------------------------------------------------------------------------
function ftp_backup_copy($local, $remote) {
	global $config;
	if (ftp_put($config['ftp_backup_connection'], $remote, $local, FTP_BINARY)) {
		logme("FTP_BACKUP : copy ".$local." -> ".$remote." : OK");
	}
	else {
		logme("FTP_BACKUP : copy ".$local." -> ".$remote." : ECHEC !!!");
	}
}
//-----------------------------------------------------------------------------------------------
function execute_test($test, $arg) {
	switch($test) {
		case "cpu_temp":
			return get_cpu_temp();
		break;
		case "hdd_temp":
			if (isset($arg)) {
				return get_hdd_temp($arg);
			}
			else {
				return -1;
			}
		break;
		case "hdd_status":
			if (isset($arg)) {
				return get_hdd_status($arg);
			}
			else {
				return -1;
			}
		break;
		case "load":
			return get_load();
		break;
		case "uptime":
			return get_uptime();
		break;
		case "ups":
			return get_ups_status();
		break;
		case "ups_load":
			return get_ups_load();
		break;
		case "memory":
			return get_use_memory();
		break;
		case "ping":
			if (isset($arg)) {
				return get_ping($arg);
			}
			else {
				return -1;
			}
		break;
		case "ubnt_es_temp":
			if (isset($arg)) {
				return get_ubnt_es_temp($arg);
			}
			else {
				return -1;
			}
		break;
		case "ubnt_es_temp_status":
			if (isset($arg)) {
				return get_ubnt_es_temp_status($arg);
			}
			else {
				return -1;
			}
		break;
		case "ubnt_es_throughput":
			if (isset($arg)) {
				return get_ubnt_es_throughput($arg);
			}
			else {
				return -1;
			}
		break;
		case "ubnt_es_cpu":
			if (isset($arg)) {
				return get_ubnt_es_cpu($arg);
			}
			else {
				return -1;
			}
		break;
		case "alive":
			return 1;
		break;
		case "hdd":
			if ($arg=="") $arg="/";
			return usespace($arg);
		break;
		case "drbd_role":
			return drbd_get_role($arg);
		break;
		case "drbd_disk":
			return drbd_get_disk($arg);
		break;
		case "drbd_connect":
			return drbd_get_connect($arg);
		break;
	}
}
//------------------------------------------------------------------------------------------------------
function drbd_get_disk($r=false) {
	if ($r===false) {
		return -1;
	}
	$t=myexec("drbdadm status ".$r);		
	foreach ($t AS $k=>$v) {
		preg_match("/^.*disk\:(?P<disk>[a-zA-Z0-9]+)$/", $v, $match);
		if (isset($match["disk"])) {
			if ($match["disk"]=="UpToDate") return 0;
			if ($match["disk"]=="Diskless") return 1;
			if ($match["disk"]=="Failed") return 2;
			if ($match["disk"]=="Inconsistent") return 3;
			if ($match["disk"]=="Outdated") return 4;
			return 999; 			
		}
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function drbd_get_connect($r=false) {
	if ($r===false) {
		return -1;
	}
	$t=myexec("drbdadm cstate ".$r);
	if (isset($t[0])) {
		$temp=strtolower($t[0]);
		if ($temp=="connected") return 0;
		if ($temp=="disconnect" OR $temp=="disconnected") return 1;
		if ($temp=="standalone") return 2;
		return 999;
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function drbd_get_role($r=false) {
	if ($r===false) {
		return -1;
	}
	$t=myexec("drbdadm status ".$r);
	foreach ($t AS $k=>$v) {
		preg_match("/^".$r."\srole\:(?P<role>[a-zA-Z0-9]+).*$/", $v, $match);
		if (isset($match["role"])) {
			if ($match["role"]=="Primary") return 0;
			if ($match["role"]=="Secondary") return 1;
			return -1;
		}
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function get_load() {
	global $config;
	if ($config['current_os']=="FreeBSD") {
		exec("sysctl vm.loadavg", $r,$errcode);
		if (isset($r[0])) {
			preg_match("/^.*\s\{\s(?P<load>[0-9.]+)\s.*$/", $r[0], $match);
			if ((isset($match['load']))) {
				return $match['load'];
			}
			else return -1;
		}
	}
	else {
		exec("cat /proc/loadavg", $r,$errcode);
		if (isset($r[0])) {
			$t=explode(" ", $r[0]);
			return floatval($t[0]);
		}
		else {
			return -1;
		}
	}
}
//------------------------------------------------------------------------------------------------------
function get_use_memory() {
	global $config;
	if ($config['current_os']=="Linux") {
		exec("free", $r,$errcode);
		if (isset($r[1])) {
            preg_match("/^Mem\:\s+(?P<total>[0-9]+)\s+(?P<used>[0-9]+)\s+(?P<free>[0-9]+)\s+.*\s+.*\s+(?P<cached>[0-9]+)$/", $r[1], $match);
            if (isset($match['total'])) {
				if (isset($r[3])) {
					return intval((($match['used']-$match['cached'])*100)/$match['total']);
                }
                else {
					return intval(100-(($match['cached']*100)/$match['total']));
                }
            }
			else return -1;
        }
        else {
            return -1;
        }
	}
	if ($config['current_os']=="FreeBSD") {
		exec("top -a -n 1", $r,$errcode);
		if (isset($r[3])) {
			preg_match("/^Mem\:\s(?P<active>[0-9KMG]+)\sActive\,\s(?P<inact>[0-9KMG]+)\sInact\,\s(?P<wired>[0-9KMG]+)\sWired\,\s(?P<buf>[0-9KMG]+)\sBuf\,\s(?P<free>[0-9KMG]+)\sFree.*$/", $r[3], $match);
			if ((isset($match['active'])) AND (isset($match['inact'])) AND (isset($match['wired'])) AND (isset($match['buf'])) AND (isset($match['free']))) {
				foreach ($match AS $k=>$v) {
					$t=substr($v, -1);
					$match[$k]=rtrim($match[$k], "KMG");
					if ($t=="K") $match[$k]=$match[$k]*1000;
					if ($t=="M") $match[$k]=$match[$k]*1000000;
					if ($t=="G") $match[$k]=$match[$k]*1000000000;
				}
				return intval((($match['active']+$match['inact']+$match['wired'])*100)/($match['active']+$match['inact']+$match['wired']+$match['free']));
			}
		}
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function get_ping($ip) {
	global $config;
	if ($config['current_os']=="FreeBSD") {
		exec("ping -c 1 -W 1000 ".$ip, $r,$errcode);
		if (isset($r[1])) {
			preg_match("/^.*\stime\=(?P<time>[0-9.]+)\sms$/", $r[1], $match);
			if ((isset($match['time']))) {
				return intval($match['time'])+1;
			}
			else return 9999;
		}
		else {
			return 9999;
		}
	}
	else {
		exec("ping -c 1 -w 1 ".$ip, $r,$errcode);
		if (isset($r[1])) {
			preg_match("/^.*\stime\=(?P<time>[0-9.]+)\sms$/", $r[1], $match);
			if ((isset($match['time']))) {
				return intval($match['time'])+1;
			}
			else return 9999;
		}
		else {
			return 9999;
		}
	}
}
//------------------------------------------------------------------------------------------------------
function get_ups_status() {
	global $config;
	if (file_exists("/sbin/apcaccess")) {
		$t=myexec("apcaccess 2> /dev/null | grep STATUS");
		if (isset($t[0])) {
			preg_match("/^STATUS\s+\:\s+(?P<status>[A-Z]+)$/", $t[0], $match);
			if ((isset($match['status']))) {
				if ($match['status']=="ONLINE") {
					return 0;
				}
				else if ($match['status']=="ONBATTERY") {
					return 2;
				}
				else {
					return 3;
				}
			}
		}
		else {
			return 4;
		}
	}
	if (file_exists("/usr/bin/upsc")) {
		$t=myexec("/usr/bin/upsc UPS1@127.0.0.1 2> /dev/null | grep ups.status:");
		if (isset($t[0])) {
			preg_match("/^ups\.status\:\s+(?P<status>[A-Z]+)$/", $t[0], $match);
			if ((isset($match['status']))) {
				if ($match['status']=="OL") {
					return 0;
				}
				else if ($match['status']=="OB") {
					return 2;
				}
				else {
					return 3;
				}
			}
		}
		else {
			return 4;
		}
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function get_cpu_temp() {
	global $config;
	if (file_exists("/usr/bin/sensors")) {
		$t=myexec("/usr/bin/sensors");
		if (isset($t[0])) {
			$temp2=-1;
			foreach ($t AS $k=>$v) {
				preg_match("/^(Physical|Package)\sid\s0\:\s+\+(?P<temp>[0-9]+)\..*$/", $v, $match);
				if ((isset($match['temp']))) {
					return $match['temp'];
				}
				preg_match("/^Core\s(0|1|2|3|4|5)\:\s+\+(?P<temp2>[0-9]+)\..*$/", $v, $match);
				if ((isset($match['temp2'])) && ($match['temp2']>$temp2)) {
					$temp2=$match['temp2'];
				}
			}
			return $temp2;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_hdd_temp($dev) {
	global $config;
	if (file_exists("/usr/sbin/hddtemp")) {
		$t=myexec("/usr/sbin/hddtemp -w ".$dev);
		if (isset($t[0])) {
			foreach ($t AS $k=>$v) {
				preg_match("/^.*\s(?P<temp>[0-9]+)\°C.*$/", $v, $match);
				if ((isset($match['temp']))) {
					return $match['temp'];
				}
			}
			return -1;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_hdd_status($dev) {
	global $config;
	if (file_exists("/usr/sbin/smartctl")) {
		$t=myexec("/usr/sbin/smartctl -a ".$dev);
		if (isset($t[0])) {
			foreach ($t AS $k=>$v) {
				preg_match("/^SMART overall-health\s.*\:\s(?P<status>[a-zA-Z0-9]+)$/", $v, $match);
				if ((isset($match['status']))) {
					if ($match['status']=="PASSED") {
						return 0;
					}
					else {
						return 3;
					}
				}
			}
			return 3;
		}
		else {
			return 3;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_ups_load() {
	global $config;
	if (file_exists("/sbin/apcaccess")) {
		$t=myexec("apcaccess 2> /dev/null | grep LOADPCT");
		if (isset($t[0])) {
			preg_match("/^LOADPCT\s+\:\s+(?P<load>[0-9]+)\..*$/", $t[0], $match);
			if ((isset($match['load']))) {
				return $match['load'];
			}
			return -1;
		}
		return -1;
	}
	if (file_exists("/usr/bin/upsc")) {
		$t=myexec("/usr/bin/upsc UPS1@127.0.0.1 2> /dev/null | grep ups.load:");
		if (isset($t[0])) {
			preg_match("/^ups\.load\:\s+(?P<load>[0-9]+).*$/", $t[0], $match);
			if ((isset($match['load']))) {
				return $match['load'];
			}
			return -1;
		}
		return -1;
	}
	return -1;
}
//------------------------------------------------------------------------------------------------------
function get_ubnt_es_temp($ip) {
	global $config;
	if (file_exists("/usr/bin/snmptable")) {
		$t=myexec("snmptable -t 2 -v 2c -c public ".$ip." FASTPATH-BOXSERVICES-PRIVATE-MIB::boxServicesTempSensorsTable");
		if (isset($t[0])) {
			$temp=-1;
			foreach ($t AS $k=>$v) {
				preg_match("/^\s+.*\s+.*\s+fixed\s+(?P<status>[a-zA-Z0-9]+)\s+(?P<temp>[0-9]+)$/", $v, $match);
				if ((isset($match['temp'])) AND ($match['temp']>$temp)) {
					$temp=$match['temp'];
				}
			}
			return $temp;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_ubnt_es_temp_status($ip) {
	global $config;
	if (file_exists("/usr/bin/snmptable")) {
		$t=myexec("snmptable -t 2 -v 2c -c public ".$ip." FASTPATH-BOXSERVICES-PRIVATE-MIB::boxServicesTempSensorsTable");
		if (isset($t[0])) {
			$status=0;
			$parse=FALSE;
			foreach ($t AS $k=>$v) {
				preg_match("/^\s+.*\s+.*\s+fixed\s+(?P<status>[a-zA-Z0-9]+)\s+(?P<temp>[0-9]+)$/", $v, $match);
				if ((isset($match['status'])) AND ($match['status']!="normal")) {
					$parse=TRUE;
					$status=1;
				}
				if ((isset($match['status'])) AND ($match['status']=="normal")) {
					$parse=TRUE;
					$status=0;
				}
			}
			if ($parse) return $status;
			else return -1;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_ubnt_es_cpu($ip) {
	global $config;
	if (file_exists("/usr/bin/snmpwalk")) {
		$t=myexec("snmpwalk -t 2 -v 2c -c public ".$ip." FASTPATH-SWITCHING-MIB::agentSwitchCpuProcessTotalUtilization");
		if (isset($t[0])) {
			foreach ($t AS $k=>$v) {
				preg_match("/^.*60\sSecs\s\(\s(?P<cpu>[0-9]+)\..*\%\)\s+.*$/", $v, $match);
				if ((isset($match['cpu']))) {
					return $match['cpu'];
				}
			}
			return -1;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_uptime() {
	global $config;
	if (file_exists("/proc/uptime")) {
		$t=myexec("cat /proc/uptime");
		if (isset($t[0])) {
			preg_match("/^(?P<uptime>[0-9]+)\..*$/", $t[0], $match);
			if ((isset($match['uptime']))) {
				return $match['uptime'];
			}
			else {
				return -1;
			}
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function get_ubnt_es_throughput($ip) {
	global $config;
	if (file_exists("/usr/bin/snmptable")) {
		$t=myexec("snmptable -t 2 -v 2c -c public ".$ip." FASTPATH-SWITCHING-MIB::agentPortStatsRateTable");
		if (isset($t[0])) {
			$total=0;
			$parse=FALSE;
			foreach ($t AS $k=>$v) {
				preg_match("/^\s+(?P<rx>[0-9]+)\sbits\s+(?P<tx>[0-9]+)\sbits.*$/", $v, $match);
				if ((isset($match['rx'])) && (isset($match['rx']))) {
					$parse=TRUE;
					$total=$total + (($match['rx']/1000)/1000);
				}
			}
			if ($parse) return intval($total);
			else return -1;
		}
		else {
			return -1;
		}
	}
	else {
		return -1;
	}
}
//------------------------------------------------------------------------------------------------------
function usespace($mount) {
	exec("df", $r,$errcode);
	$t=array();
	foreach ($r AS $k=>$v) {
		preg_match("/^.*\s+.*\s+.*\s+.*\s+(?P<use>[0-9]+)\%\s+(?P<mount>.*)$/", $v, $match);
		if ((isset($match['use'])) AND (isset($match['mount']))) {
			$t[$match['mount']]=$match['use'];
		}
	}
	if (!isset($t[$mount])) {
		return -1;
	}
	else {
		return $t[$mount];
	}
}
//------------------------------------------------------------------------------------------------------
function get_type_version() {
	global $config;
	$result=array();
	$result['version']="";
	$result['type']="";
	$a=myexec("pveversion 2>/dev/null");
	if (isset($a[0]) AND (strstr($a[0], "pve-manager"))) {
		preg_match("/^.*\/(?P<version>[0-9\.\-]+)\/.*$/", $a[0], $match);
		if (isset($match['version'])) {
			$result['version']=$match['version'];
			$result['type']="px";
		}
	}
	if ($result['type']=="") {
		$a=myexec("aptitude versions wprotect 2>/dev/null");
		foreach($a AS $k=>$v) {
			preg_match("/^i.*\s+(?P<version>[0-9\-\.]+)\s+.*$/", $v, $match);
			if (isset($match['version'])) {
				$result['version']=$match['version'];
				$result['type']="wp";
				break;
			}
		}
	}
	if ($result['type']=="") {
		$a=myexec("cat /etc/version 2>/dev/null");
		if (isset($a[0])) {
			preg_match("/^(?P<version>[0-9\.]+)\-.*$/", $a[0], $match);
			if (isset($match['version'])) {
				$result['version']=$match['version'];
				$result['type']="pfs";
			}
		}
	}
	if ($result['type']=="") {
		$a=myexec("aptitude versions unifi 2>/dev/null");
		if ((PHP_VERSION_ID > 60000) AND (isset($a[0]))) {
			$a[1]=$a[0];
		}
		if (isset($a[1])) {
			preg_match("/^i\s+(?P<version>[0-9\-\.]+)\s+.*$/", $a[1], $match);
			if (isset($match['version'])) {
				$tmp4=explode("-", $match['version']);
				$result['version']=$tmp4[0];
				$result['type']="unifi";
			}
		}
	}
	logme("TYPE/VERSION : ".$result['type']."/".$result['version']);
	return $result;
}
//------------------------------------------------------------------------------------------------------
function exec_test() {
	global $config;
	global $sock;
			$r="";
			$p=explode("|", $config['to_test']);
			foreach ($p AS $a=>$b) { // pour chaque test a effectuer
				$m=explode(",", $b);
				if ( isset($m[0]) AND isset($m[1])) { // take infos for a test (id + action + arg)
					if (!isset($m[2])) $m[2]="";
					$z=execute_test($m[1], $m[2]);
					$r=$r.$m[0].",".$z.",".time()."|";
				}
			}
			socket_write($sock , Crypte("result ".$r)."\n");
}
//------------------------------------------------------------------------------------------------------
function exec_info() {
	global $config;
	global $sock;
	$a=get_type_version();
	$b=json_encode($a);
	socket_write($sock , Crypte("typeversion ".$b)."\n");
	if (file_exists("/etc/openvpn/postup")) {
		$t=file_get_contents("/etc/openvpn/postup");
		socket_write($sock , Crypte("config_file postup ".$t)."\n");
	}
}
//------------------------------------------------------------------------------------------------------
function configure_smart() {
	global $config;
	if (!file_exists("/etc/smartd.conf.ori")) {
		passthru("cp /etc/smartd.conf /etc/smartd.conf.ori");
	}
	$toconf="DEVICESCAN -H -f -t -l error -l selftest -C 0 -U 198 -d removable -n standby -m root -M exec /usr/share/smartmontools/smartd-runner\n";
	$file=file_get_contents("/etc/smartd.conf");
	if ($file!=$toconf) {
		file_put_contents("/etc/smartd.conf", $toconf);
		passthru("/etc/init.d/smartmontools restart");
	}
}
//------------------------------------------------------------------------------------------------------
function configure_apcupsd() {
	global $config;
	if (!file_exists("/etc/apcupsd/commok.ori")) {
		passthru("cp /etc/apcupsd/commok /etc/apcupsd/commok.ori");
	}
	file_put_contents("/etc/apcupsd/commok", "#!/bin/sh\nexit 0\n");
}
//------------------------------------------------------------------------------------------------------
function zstd_check_get_file_list() {
	$list=array();
	if (is_dir("/backup/dump")) {
		$a = scandir("/backup/dump");
		foreach($a AS $k=>$v) {
			if (($v!=".") AND ($v!="..") AND (substr($v, -8)==".vma.zst")) {
				$list[]="/backup/dump/".$v;
			}
		}
	}
	if (is_dir("/backup/auto/dump")) {
		$a = scandir("/backup/auto/dump");
		foreach($a AS $k=>$v) {
			if (($v!=".") AND ($v!="..") AND (substr($v, -8)==".vma.zst")) {
				$list[]="/backup/auto/dump/".$v;
			}
		}
	}
	foreach ($list AS $k=>$v) {
		logme("zstd_check_get_file_list --> ".$v);
	}
	return $list;
}
//------------------------------------------------------------------------------------------------------
function zstd_check_get_already_scan_list() {
	$list=array();
	if (file_exists("/opt/minion/zstd_check_list.txt")) {
		$a=file_get_contents("/opt/minion/zstd_check_list.txt");
		$b=explode("\n", $a);
		foreach ($b AS $k=>$v) {
			$list[]=$v;
		}
	}
	return $list;
}
//------------------------------------------------------------------------------------------------------
function zstd_check_get_list_to_scan($list_to_scan, $list_already_scan) {
	$list=array();
	foreach ($list_to_scan AS $k=>$v) {
		if (!in_array($v, $list_already_scan)) {
			$list[]=$v;
		}
	}
	return $list;
}
//------------------------------------------------------------------------------------------------------
function zstd_check_store_list_scanned($list_to_scan, $list_already_scan) {
	$list=array();
	foreach ($list_already_scan AS $k=>$v) { // purge files doesnt exist anymore
		if (in_array($v, $list_to_scan)) {
			$list[]=$v;
		}
	}
	$to_store="";
	foreach ($list AS $k=>$v) {
		$to_store=$to_store.$v."\n";
	}
	file_put_contents("/opt/minion/zstd_check_list.txt", $to_store);
}
//------------------------------------------------------------------------------------------------------
function zstd_start_check_backup() {
	global $config;
	$found_error=false;
	$list_scanned=array();
	$list_failed=array();
	$already_scan_list=zstd_check_get_already_scan_list();
	$current_file_list=zstd_check_get_file_list();
	$list_to_scan=zstd_check_get_list_to_scan($current_file_list, $already_scan_list);
	logme("ZSTD CHECK HEALTH BACKUP : START ...");
	zstd_check_store_list_scanned($current_file_list, $already_scan_list);
	foreach ($list_to_scan AS $k=>$v) {
		$list_scanned[]=$v;
		logme("ZSTD CHECK HEALTH BACKUP : start check -> ".$v);
		$out=myexec_er("zstd -qq --test ".$v);
		if ($out!="0") {
			$found_error=true;
			logme("ZSTD CHECK HEALTH BACKUP : check failed !!! -> ".$v);
			$list_failed[]=$v;
			send_email($config['hostname']." ZSTD CHECK BACKUP INTEGRITY failed", $v);
		}
		else {
			logme("ZSTD CHECK HEALTH BACKUP : check ok -> ".$v);
		}
		file_put_contents("/opt/minion/zstd_check_list.txt", $v."\n", FILE_APPEND);
	}
	if ($found_error) {
		logme("ZSTD CHECK HEALTH BACKUP : ERRORS found, send email");
	}
	logme("ZSTD CHECK HEALTH BACKUP : FINNISH ...");
}
//------------------------------------------------------------------------------------------------------
function postqueue_empty_all() {
	logme("postqueue_empty_all : Clean all deferred mail");
	myexec("postsuper -d ALL deferred");
}
//------------------------------------------------------------------------------------------------------
function zstd_send_alert_integrity($list) {
	$message="";
	foreach ($list AS $k=>$v) {
		$message=$message.$v."\n";
	}
	send_email($config['hostname']." ZSTD CHECK BACKUP INTEGRITY", $message);
}
//------------------------------------------------------------------------------------------------------
function send_email($subject, $message) {
	myexec("echo \"".$message."\" | mail -s \"".$subject."\" root");
}
//------------------------------------------------------------------------------------------------------
function postfix_set_smtp_server() {
	passthru("sed -i 's/totalynet.fr:587/ssl0.ovh.net:587/' /etc/postfix/main.cf");
	passthru("/etc/init.d/postfix restart");
	passthru("postqueue -f");
}
//------------------------------------------------------------------------------------------------------
logme("MINION : start ...");
logme("MINION : PHP version -> ".PHP_VERSION_ID);
$__parent_pid = become_daemon();
passthru("echo '".getmypid()."' > /var/run/minion");
readconfig_master();
myexec("rm /var/log/minion.log");
pcntl_signal(SIGTERM, 'sig_handler'); 
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, SIG_IGN);
//-----------------------------------------------------------------------------------------------------
$tmpa=get_type_version();
if ($tmpa['type']=="px") {
	configure_smart();
	configure_apcupsd();
	postfix_set_smtp_server();
	//if (substr($tmpa['version'], 0, 1)=="4") {}
}
$config['type']=$tmpa['type'];
if (file_exists("/opt/minion/auto_upgrade.no")) {
	$config["auto_upgrade"]=false;
}
//-----------------------------------------------------------------------------------------------------
While (TRUE) {
	listen();
	sleep(5);
}
?>