<?php

/*
	Phoronix Test Suite
	URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/
	Copyright (C) 2014 - 2018, Phoronix Media
	Copyright (C) 2014 - 2018, Michael Larabel

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

class start_phoromatic_server implements pts_option_interface
{
	const doc_section = 'Phoromatic';
	const doc_description = 'Start the Phoromatic web server for controlling local Phoronix Test Suite client systems to facilitate automated and repeated test orchestration and other automated features targeted at the enterprise.';

	public static function run($r)
	{
		if(pts_client::create_lock(PTS_USER_PATH . 'phoromatic_server_lock') == false)
		{
			trigger_error('The Phoromatic Server is already running.', E_USER_ERROR);
			return false;
		}

		pts_file_io::unlink(getenv('PTS_EXT_LAUNCH_SCRIPT_DIR') . '/phoromatic-server-launcher');
		if(PHP_VERSION_ID < 50400)
		{
			echo 'Running an unsupported PHP version. PHP 5.4+ is required to use this feature.' . PHP_EOL . PHP_EOL;
			return false;
		}
		if(!function_exists('socket_create_listen'))
		{
			echo 'PHP Sockets support is needed to use the Phoromatic Server.' . PHP_EOL . PHP_EOL;
			return false;
		}

		$server_launcher = '#!/bin/sh' . PHP_EOL;
		$web_port = 0;
		$remote_access_override = getenv('PHOROMATIC_HTTP_PORT');
		if($remote_access_override && is_numeric($remote_access_override) && $remote_access_override > 1)
		{
			$remote_access = $remote_access_override;
		}
		else
		{
			$remote_access = pts_config::read_user_config('PhoronixTestSuite/Options/Server/RemoteAccessPort', 'RANDOM');
		}

		$fp = false;
		$errno = null;
		$errstr = null;

		if($remote_access == 'RANDOM')
		{
			do
			{
				if($fp)
					fclose($fp);

				$remote_access = rand(8000, 8999);
			}
			while(($fp = fsockopen('127.0.0.1', $remote_access, $errno, $errstr, 5)) != false);
			echo 'Port ' . $remote_access . ' chosen as random port for this instance. Change the default port via the Phoronix Test Suite user configuration file.' . PHP_EOL;
		}

		$remote_access = is_numeric($remote_access) && $remote_access > 1 ? $remote_access : false;
		$blocked_ports = array(2049, 3659, 4045, 6000, 9000);

		if($remote_access)
		{
			// ALLOWING SERVER TO BE REMOTELY ACCESSIBLE
			$server_ip = '0.0.0.0';

			if(($fp = fsockopen('127.0.0.1', $remote_access, $errno, $errstr, 5)) != false)
			{
				fclose($fp);
				trigger_error('Port ' . $remote_access . ' is already in use by another server process. Close that process or change the Phoronix Test Suite server port via' . pts_config::get_config_file_location() . ' to proceed.', E_USER_ERROR);
				return false;
			}
			else
			{
				$web_port = $remote_access;

				if(($ws_port = getenv('PTS_WEBSOCKET_PORT')) != false && is_numeric($ws_port) && $ws_port > 1)
				{
					$web_socket_port = $ws_port;
				}
				else
				{
					$web_socket_port = pts_config::read_user_config('PhoronixTestSuite/Options/Server/WebSocketPort', '');
				}

				while($web_socket_port == null || !is_numeric($web_socket_port) || (($fp = fsockopen('127.0.0.1', $web_socket_port, $errno, $errstr, 5)) != false))
				{
					if($fp)
						fclose($fp);
					$web_socket_port = rand(8000, 8999);
				}
			}
		}
		else
		{
			echo PHP_EOL . PHP_EOL . 'You must first configure the remote web / Phoromatic settings via:' . PHP_EOL . '    ' . pts_config::get_config_file_location() . PHP_EOL . PHP_EOL . 'The RemoteAccessPort should be a network port to use for HTTP communication while WebSocketPort should be set to another available network port. Set to RANDOM if wishing to use randomly chosen available ports.' . PHP_EOL . PHP_EOL;
			return false;
		}

		if(!extension_loaded('sqlite3'))
		{
			echo PHP_EOL . PHP_EOL . pts_client::cli_just_bold('PHP SQLite3') . ' support must first be enabled before accessing the Phoromatic server (e.g. installing the php-sqlite or php-pdo package depending on the distribution).' . PHP_EOL . PHP_EOL;
			return false;
		}

		// Setup server logger
		define('PHOROMATIC_SERVER', true);
		// Just create the logger so now it will flush it out
		$pts_logger = new pts_logger();
		$pts_logger->clear_log();
		echo pts_core::program_title(true) . ' starting Phoromatic Server' . PHP_EOL;
		$pts_logger->log(pts_core::program_title(true) . ' starting Phoromatic Server on ' . pts_network::get_local_ip());

		echo 'Phoronix Test Suite User-Data Directory Path: ' . PTS_USER_PATH . PHP_EOL;
		echo 'Phoronix Test Suite Configuration File: ' . pts_config::get_config_file_location() . PHP_EOL;
		echo 'Phoromatic Server Log File: ' . $pts_logger->get_log_file_location() . PHP_EOL;
		$pts_logger->log('PTS_USER_PATH = ' . PTS_USER_PATH);
		$pts_logger->log('PTS_DOWNLOAD_CACHE_PATH = ' . PTS_DOWNLOAD_CACHE_PATH);
		$pts_logger->log('XML Configuration File = ' . pts_config::get_config_file_location());

		// WebSocket Server Setup
		$server_launcher .= 'export PTS_WEB_PORT=' . $web_port . PHP_EOL;
		$server_launcher .= 'export PTS_WEBSOCKET_PORT=' . $web_socket_port . PHP_EOL;
		$server_launcher .= 'export PTS_WEBSOCKET_SERVER=PHOROMATIC' . PHP_EOL;
		$server_launcher .= 'export PTS_NO_FLUSH_LOGGER=1' . PHP_EOL;
		$server_launcher .= 'export PTS_PHOROMATIC_SERVER=1' . PHP_EOL;
		$server_launcher .= 'export PTS_PHOROMATIC_LOG_LOCATION=' . $pts_logger->get_log_file_location() . PHP_EOL;
		$server_launcher .= 'cd ' . getenv('PTS_DIR') . ' && PTS_MODE="CLIENT" ' . getenv('PHP_BIN') . ' pts-core/phoronix-test-suite.php start-ws-server &' . PHP_EOL;
		$server_launcher .= 'websocket_server_pid=$!'. PHP_EOL;
		$pts_logger->log('Starting WebSocket process on port ' . $web_socket_port);
		$server_launcher .= 'cd ' . getenv('PTS_DIR') . ' && PTS_MODE="CLIENT" ' . getenv('PHP_BIN') . ' pts-core/phoronix-test-suite.php start-phoromatic-event-server &' . PHP_EOL;
		$server_launcher .= 'event_server_pid=$!'. PHP_EOL;

		// HTTP Server Setup
		if(getenv('PHOROMATIC_WANTS_APACHE'))
		{
			echo PHP_EOL . PHP_EOL . 'To manually configure Apache, setup the following:' . PHP_EOL;
			echo 'The root web directory: ' . PTS_CORE_PATH . 'phoromatic/public_html/' . PHP_EOL;
			echo 'Set the HTTP port to: ' . $web_port . PHP_EOL;
			echo 'Of course, ensure Apache PHP support is available.' . PHP_EOL . PHP_EOL;
		}
		else if(false && pts_client::executable_in_path('nginx') && is_file('/run/php-fpm/php-fpm.pid'))
		{
			// NGINX
			$nginx_conf = 'error_log /tmp/error.log;
			pid /tmp/nginx.pid;
			worker_processes 1;

			events {
			  worker_connections 1024;
			}

			http {
			  client_body_temp_path /tmp/client_body;
			  fastcgi_temp_path /tmp/fastcgi_temp;
			  proxy_temp_path /tmp/proxy_temp;
			  scgi_temp_path /tmp/scgi_temp;
			  uwsgi_temp_path /tmp/uwsgi_temp;
			  tcp_nopush on;
			  tcp_nodelay on;
			  keepalive_timeout 180;
			  types_hash_max_size 2048;
			  include /etc/nginx/mime.types;
			  index index.php;

			  server {
			    listen ' . $web_port . ';
			    listen [::]:' . $web_port . ' default ipv6only=on;
			    access_log /tmp/access.log;
			    error_log /tmp/error.log;
			    root ' . PTS_CORE_PATH . 'phoromatic/public_html;
				index index.php;
			      try_files $uri $uri/ /index.php;
				location / {
				autoindex on;
				}
				location ~ \.php$ {
				     include        /etc/nginx/fastcgi_params;
				     fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
				     fastcgi_split_path_info ^(.+\.php)(/.+)$;
				     fastcgi_pass   127.0.0.1:9000;
				     fastcgi_index  index.php;
				}
			  }
			}';
			$nginx_conf_file = tempnam(PTS_USER_PATH, 'nginx_conf_');
			file_put_contents($nginx_conf_file, $nginx_conf);

			$server_launcher .= 'nginx -c ' . $nginx_conf_file . PHP_EOL . 'rm -f ' . $nginx_conf_file . PHP_EOL;
		}
		else if(($mongoose = pts_client::executable_in_path('mongoose')) && ($php_cgi = pts_client::executable_in_path('php-cgi')))
		{
			// Mongoose Embedded Web Server
			echo PHP_EOL . 'Launching with Mongoose web server.' . PHP_EOL;
			$server_launcher .= $mongoose . ' -p ' . $web_port . ' -r ' . PTS_CORE_PATH . 'phoromatic/public_html/ -I ' . $php_cgi . ' -i index.php > /dev/null 2>> $PTS_PHOROMATIC_LOG_LOCATION &' . PHP_EOL; //2> /dev/null

		}
		else if(strpos(getenv('PHP_BIN'), 'hhvm'))
		{
			echo PHP_EOL . 'Unfortunately, the HHVM built-in web server has abandoned upstream. Users will need to use the PHP binary or other alternatives.' . PHP_EOL . PHP_EOL;
			return;
		}
		else
		{
			// PHP Web Server
			echo PHP_EOL . 'Launching with PHP built-in web server.' . PHP_EOL;
			$server_launcher .= getenv('PHP_BIN') . ' -S ' . $server_ip . ':' . $web_port . ' -t ' . PTS_CORE_PATH . 'phoromatic/public_html/ > /dev/null 2>> $PTS_PHOROMATIC_LOG_LOCATION &' . PHP_EOL; //2> /dev/null
		}
		$server_launcher .= 'http_server_pid=$!'. PHP_EOL;
		$server_launcher .= 'sleep 1' . PHP_EOL;
		$server_launcher .= 'echo "The Phoromatic Web Interface Is Accessible At: http://localhost:' . $web_port . '"' . PHP_EOL;
		$pts_logger->log('Starting HTTP process @ http://localhost:' . $web_port);

		// Avahi for zeroconf network discovery support
		if(pts_config::read_user_config('PhoronixTestSuite/Options/Server/AdvertiseServiceZeroConf', 'TRUE'))
		{
			if(is_dir('/etc/avahi/services') && is_writable('/etc/avahi/services'))
			{
				file_put_contents('/etc/avahi/services/phoromatic-server.service', '<?xml version="1.0" standalone=\'no\'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">phoromatic-server-%h</name>
  <service>
    <type>_http._tcp</type>
    <port>' . $web_port . '</port>
  </service>
</service-group>');
			}
			else if(pts_client::executable_in_path('avahi-publish'))
			{
				$hostname = phodevi::read_property('system', 'hostname');
				$hostname = $hostname == null ? rand(0, 99) : $hostname;
				$server_launcher .= 'avahi-publish -s phoromatic-server-' . $hostname . ' _http._tcp ' . $web_port . ' "Phoronix Test Suite Phoromatic" > /dev/null 2> /dev/null &' . PHP_EOL;
				$server_launcher .= 'avahi_publish_pid=$!'. PHP_EOL;
			}
		}

		// Zeroconf via OpenBenchmarking.org
		if(pts_config::read_user_config('PhoronixTestSuite/Options/Server/AdvertiseServiceOpenBenchmarkRelay', 'TRUE') && pts_network::internet_support_available())
		{
			pts_openbenchmarking::make_openbenchmarking_request('phoromatic_server_relay', array('local_ip' => pts_network::get_local_ip(), 'local_port' => $web_port));
		}

		// Wait for input to shutdown process..
		if(!PTS_IS_DAEMONIZED_SERVER_PROCESS)
		{
			$server_launcher .= PHP_EOL . 'echo -n "Press [ENTER] to kill server..."' . PHP_EOL;
			$server_launcher .= PHP_EOL . 'read var_name';
		}
		else
		{
			$server_launcher .= PHP_EOL . 'while [ ! -f "/var/lib/phoronix-test-suite/end-phoromatic-server" ];';
			$server_launcher .= PHP_EOL . 'do';
			$server_launcher .= PHP_EOL . 'sleep 1';
			$server_launcher .= PHP_EOL . 'done';
			$server_launcher .= PHP_EOL . 'rm -f /var/lib/phoronix-test-suite/end-phoromatic-server' . PHP_EOL;
		}

		// Shutdown / Kill Servers
		$server_launcher .= PHP_EOL . 'kill $http_server_pid';
		$server_launcher .= PHP_EOL . 'kill $websocket_server_pid';
		$server_launcher .= PHP_EOL . 'kill $event_server_pid';
		if(is_writable('/etc/avahi/services') && is_file('/etc/avahi/services/phoromatic-server.service'))
		{
			$server_launcher .= PHP_EOL . 'rm -f /etc/avahi/services/phoromatic-server.service';
		}
		else
		{
			$server_launcher .= PHP_EOL . 'kill $avahi_publish_pid';
		}
		$server_launcher .= PHP_EOL . 'rm -f ~/.phoronix-test-suite/run-lock*';
		file_put_contents(getenv('PTS_EXT_LAUNCH_SCRIPT_DIR') . '/phoromatic-server-launcher', $server_launcher);
	}
}

?>
