#!/usr/bin/php
<?php
# vim: syntax=php tabstop=4 softtabstop=0 noexpandtab laststatus=1 ruler

/**
 * wrappers/unl_wrapper.php
 *
 * CLI handler for UNetLab
 *
 * @author Andrea Dainese <andrea.dainese@gmail.com>
 * @copyright 2014-2016 Andrea Dainese
 * @license BSD-3-Clause https://github.com/dainok/unetlab/blob/master/LICENSE
 * @link http://www.unetlab.com/
 * @version 20160719
 */

require_once('/opt/unetlab/html/includes/init.php');

// Checking if called from CLI or Web
if (php_sapi_name() != 'cli') {
	error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][1]);
	exit(1);
}

// Checking privileges
if (posix_getuid() != 0) {
	error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][2]);
	exit(2);
}

// Setting umask
umask(0002);

// Parsing and checking parameters
$options = getopt('oT:U:D:F:a:');

// Checking -a (action)
if (!isset($options['a'])) {
	usage();
	error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][3]);
	exit(3);
}
$action = $options['a'];

// Checking -T (Tenant ID)
if (in_array($action, array('delete', 'export', 'start', 'stop', 'wipe'))) {
	if (!isset($options['T'])) {
		// Tenant ID is missing
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][4]);
		exit(4);
	} else if ((int) $options['T'] < 0) {
		// Tenant ID is not valid
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][5]);
		exit(5);
	} else {
		$tenant = (int) $options['T'];
	}
}

// Checking -U (Username)
if (in_array($action, array('delete', 'export', 'start', 'stop', 'wipe'))) {
	if (!isset($options['U'])) {
		// Username is missing
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][20]);
		exit(20);
	} else if ($options['U'] == "") {
		// Username is not valid
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][21]);
		exit(21);
	} else {
		if (!empty($options['U'])) {
			$username = $options['U'];
		} else {
			echo "Error: Username value for -U is empty.\n";
			exit(1);
		}
	}
}

// Checking -F (Lab file)
if (in_array($action, array('delete', 'export', 'start', 'stop', 'wipe'))) {
	if (!is_file($options['F'])) {
		// File not found
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][6]);
		exit(6);
	}

	try {
		$lab = new Lab($options['F'], $tenant, $username);
	} catch (Exception $e) {
		// Lab file is invalid
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$e->getMessage()]);
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][7]);
		exit(7);
	}
}

// Checking -D (Node ID)
if (isset($options['D'])) {
	if ((int) $options['D'] > 0 && isset($lab->getNodes()[$options['D']])) {
		$node_id = (int) $options['D'];
	} else {
		// Node ID must be numeric, greater than 0 and exists on lab
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][8]);
		exit(8);
	}
}

switch ($action) {
	default:
		// Invalid action
		usage();
		error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][9]);
		exit(9);
	case 'backupdb':
		shell_exec("mysqldump --databases -uroot -peve-ng eve_ng_db >/dev/null && mysqldump --databases -uroot -peve-ng eve_ng_db | gzip > /opt/unetlab/evedb.gz");
		break;
	case 'restoredb':
		shell_exec("if [ -e /opt/unetlab/evedb.gz ] ; then systemctl stop mysql ; rm -fr /var/lib/mysql/* ; mysqld --initialize-insecure ; systemctl start mysql  ; gunzip < /opt/unetlab/evedb.gz | sed -e '1,/Current Database.*eve_ng_db/d' -e '/Current Database.*guacdb/,\$d' | mysql 2>/dev/null ; systemctl restart mysql ; /opt/unetlab/scripts/mysql-fix.sh ; fi");
		break;
	case 'delete':
		if (isset($node_id)) {
			// Removing temporary files for a single node in all tenants
			$cmd = 'rm -rf /opt/unetlab/tmp/*/' . $lab->getId() . '/' . $node_id . '/';
		} else {
			// Removing all temporary files in all tenants
			$cmd = 'rm -rf /opt/unetlab/tmp/*/' . $lab->getId() . '/';
		}
		exec($cmd, $o, $rc);
		break;
	case 'export':
		// Exporting node(s) running-config
		if (isset($node_id)) {
			// Node ID is set, export a single node
			$rc = export($node_id, $lab->getNodes()[$node_id], $lab, $lab->getTenant());
			if ($rc == 80061 || $rc == 80084) {
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][19]);
				exit(19);
			}
			if ($rc !== 0) {
				// Failed to export config
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][16]);
				exit(16);
			}
		} else {
			// Node ID is not set, export all nodes
			foreach ($lab->getNodes() as $node_id => $node) {
				export($node_id, $node, $lab, $lab->getTenant());
			}
		}
		break;
	case 'fixpermissions':
		// Default permissions for directories
		$cmd = '/usr/bin/find /opt/unetlab -type d \( ! -path *tmp*opt*  ! -path *tmp*sys*   ! -path *tmp*opt*   \) -exec chown root:root {} \; > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = '/usr/bin/find /opt/unetlab -type d \( ! -path *tmp*opt*  ! -path *tmp*sys*   ! -path *tmp*opt*   \) -exec chmod 755 {} \; > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// Default permissions for files
		$cmd = '/usr/bin/find /opt/unetlab -type d \( ! -path *tmp*opt*  ! -path *tmp*sys*   ! -path *tmp*opt*   \) -exec chown root:root {} \; > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = '/usr/bin/find /opt/unetlab -type f \( ! -path *tmp*opt*  ! -path *tmp*sys*   ! -path *tmp*opt*   \) -exec chmod 644 {} \; > /dev/null 2>&1';
		exec($cmd, $o, $rc);


		// /opt/unetlab/scripts
		$cmd = '/bin/chmod 755 /opt/unetlab/scripts/* > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// /opt/unetlab/config_scripts
		$cmd = '/bin/chmod 755 /opt/unetlab/config_scripts/* > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// /opt/unetlab/data and /opt/unetlab/labs
		$cmd = '/bin/chown -R www-data:www-data /opt/unetlab/data /opt/unetlab/labs > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// /opt/unetlab/tmp
		$cmd = '/bin/chown -R root:unl /opt/unetlab/tmp > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = '/bin/chmod -R g+w /opt/unetlab/tmp > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// /opt/unetlab/addons/iol/bin
		$cmd = '/bin/chmod 755 /opt/unetlab/addons/iol/bin/*.bin > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// Wrappers
		$cmd = '/bin/chmod 755 /opt/unetlab/wrappers/nsenter /opt/unetlab/wrappers/*_wrapper* /opt/unetlab/wrappers/eve-* > /dev/null 2>&1';
		exec($cmd, $o, $rc);

		// /tmp
		$cmd = '/bin/chown root:root /tmp 2>&1';
		exec($cmd, $o, $rc);
		$cmd = '/bin/chmod u=rwx,g=rwx,o=rwxt /tmp > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		break;
	case 'stopall':
		// Kill all nodes and clear the system
		$cmd = 'pkill -TERM dynamips > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = 'pkill -TERM iol_wrapper > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = 'pkill -TERM qemu_wrapper > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = 'pkill -TERM vpcs > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		$cmd = 'docker -H=tcp://127.0.0.1:4243 stop $(docker -H=tcp://127.0.0.1:4243 ps -q)';
		exec($cmd, $o, $rc);
		$cmd = 'brctl show | grep vnet | sed \'s/^\(vnet[0-9]\+_[0-9]\+\).*/\1/g\' | while read line; do ifconfig $line down; brctl delbr $line; done';
		exec($cmd, $o, $rc);
		$cmd = 'ovs-vsctl list-br | while read line; do ovs-vsctl del-br $line; done';
		exec($cmd, $o, $rc);
		$cmd = 'ifconfig | grep vunl | cut -d\' \' -f1 | while read line; do tunctl -d $line; done';
		exec($cmd, $o, $rc);
		$cmd = 'find /opt/unetlab/labs/ -name "*.lock" -exec rm -f {} \;';
		exec($cmd, $o, $rc);
		break;
	case 'platform':
		$cmd = '(egrep vmx /proc/cpuinfo | grep -q ept && echo vmx )';
		exec($cmd, $o, $rc);
		$cmd = '/usr/sbin/dmidecode -s system-product-name';
		exec($cmd, $o, $rc);
		print(implode('', $o) . "\n");
		break;
	case 'start':
		// Starting node(s)
		if (!checkUsername($lab->getTenant())) {
			// Cannot create username
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][14]);
			exit(14);
		}

		if (isset($node_id)) {
			// Node ID is set, create attached networks, prepare node and start it

			foreach ($lab->getNodes()[$node_id]->getEthernets() as $interface) {
				if ($interface->getNetworkId() !== 0 && !isset($lab->getNetworks()[$interface->getNetworkId()])) {
					// Interface is set but network does not exist
					error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][10]);
					exit(10);
				} else if ($interface->getNetworkId() !== 0) {
					// Create attached networks only
					$p = array(
						'name' => 'vnet' . $lab->getTenant() . '_' . $interface->getNetworkId(),
						'type' => $lab->getNetworks()[$interface->getNetworkId()]->getNType(),
						'count' => $lab->getNetworks()[$interface->getNetworkId()]->getCount()
					);
					$rc = addNetwork($p);
					if ($rc !== 0) {
						// Failed to create network
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$rc]);
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][11]);
						exit(11);
					}
				}
			}

			// Starting the node
			$rc = start($lab->getNodes()[$node_id], $node_id, $tenant, $lab->getNetworks(), $lab->getScriptTimeout());
			if ($rc !== 0) {
				// Failed to start the node
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$rc]);
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][12]);
				exit(12);
			}
		} else {
			// Node ID is not set, start all nodes

			// Create all networks
			foreach ($lab->getNetworks() as $network_id => $network) {
				$p = array(
					'name' => 'vnet' . $lab->getTenant() . '_' . $network_id,
					'type' => $network->getNType()
				);
				$rc = addNetwork($p);
				if ($rc !== 0) {
					// Failed to create network
					error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$rc]);
					error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][11]);
					exit(11);
				}
			}

			// Starting all non-IOL nodes
			foreach ($lab->getNodes() as $node_id => $node) {
				if ($node->getNType() != 'iol') {
					// IOL nodes drop privileges, so need to be postponed
					$rc = start($node, $node_id, $tenant, $lab->getNetworks(), $lab->getScriptTimeout());
					if ($rc !== 0) {
						// Failed to start the node
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$rc]);
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][12]);
						exit(12);
					}
				}
			}

			// Starting all IOL nodes
			foreach ($lab->getNodes() as $node_id => $node) {
				if ($node->getNType() == 'iol') {
					// IOL nodes drop privileges, so need to be postponed
					$rc = start($node, $node_id, $tenant, $lab->getNetworks(), $lab->getScriptTimeout());
					if ($rc !== 0) {
						// Failed to start the node
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][$rc]);
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][12]);
						exit(12);
					}
				}
			}
		}
		break;
	case 'stop':
		// Stopping node(s)
		if (isset($node_id)) {
			// Node ID is set, stop and wipe the node
			stop($lab->getNodes()[$node_id]);
			umount($lab->getNodes()[$node_id]);
		} else {
			// Node ID is not set, stop and wipe all nodes
			foreach ($lab->getNodes() as $node) {
				stop($node);
				umount($node);
			}
		}
		break;
	case 'update':
		// Check Internet connection
		$cmd = 'curl -s http://www.google.com > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			exit(18);
		}

		// Update repos
		$cmd = 'apt-get -o APT::List-Cleanup=0 -o Dir::Etc::sourcelist="/etc/apt/sources.list.d/unetlab.list" -o Dir::Etc::sourceparts="/var/tmp/" update > /dev/null 2>&1';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			exit(17);
		}

		// Compare versions
		$cmd = 'dpkg -s unetlab | grep Version | sed "s/Version: //"';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			exit(17);
		}
		$installed_version = $o[0];
		$cmd = 'apt-cache show unetlab | grep Version | head -n1 | sed "s/Version: //"';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			exit(17);
		}
		$available_version = $o[0];

		// Update UNetLab
		if ($installed_version != $available_version) {
			$cmd = 'apt-get -y install unetlab';
			exec($cmd, $o, $rc);
			if ($rc !== 0) {
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
				exit(17);
			}
		}
		break;
	case 'wipe':
		// Removing temporary files
		if (isset($node_id)) {
			// Node ID is set, stop and wipe the node
			stop($lab->getNodes()[$node_id]);
			umount($lab->getNodes()[$node_id]);
			// Delete Docker image
			if ($lab->getNodes()[$node_id]->getNType() == 'docker') {
				$cmd = '/usr/bin/docker -H=tcp://127.0.0.1:4243 rm ' . $lab->getNodes()[$node_id]->getUuid();
				exec($cmd, $o, $rc);
				if ($rc !== 0) {
					error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
					error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
					exit(13);
				}
			}
			$cmd = 'rm -rf "/opt/unetlab/tmp/' . $tenant . '/' . $lab->getId() . '/' . $node_id . '/"';
			exec($cmd, $o, $rc);
			if ($rc !== 0) {
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
				exit(13);
			}
		} else {
			// Node ID is not set, stop and wipe all nodes
			foreach ($lab->getNodes() as $node_id => $node) {
				stop($node);
				umount($node);
				// Delete Docker image
				if ($lab->getNodes()[$node_id]->getNType() == 'docker') {
					$cmd = '/usr/bin/docker -H=tcp://127.0.0.1:4243 rm ' . $lab->getNodes()[$node_id]->getUuid();
					exec($cmd, $o, $rc);
					if ($rc !== 0) {
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
						error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
						exit(13);
					}
				}
			}
			$cmd = 'rm -rf "/opt/unetlab/tmp/' . $tenant . '/' . $lab->getId() . '/"';
			exec($cmd, $o, $rc);
			if ($rc !== 0) {
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
				error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
				exit(13);
			}
		}
		break;
	case 'cpulimitoff':
		$cmd = 'systemctl stop cpulimit.service';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		error_log(date('M d H:i:s ') . implode("\n", $o));
		$cmd = 'systemctl disable cpulimit.service';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		error_log(date('M d H:i:s ') . implode("\n", $o));
		break;
	case 'cpulimiton':
		$cmd = 'systemctl start cpulimit.service';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		$cmd = 'systemctl enable cpulimit.service';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		break;
	case 'uksmoff':
		$cmd = 'echo 0 > /sys/kernel/mm/uksm/run';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		$cmd = 'echo 0 > /opt/unetlab/uksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		break;
	case 'uksmon':
		$cmd = 'echo 1 > /sys/kernel/mm/uksm/run';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		$cmd = 'echo 1 > /opt/unetlab/uksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		break;
	case 'ksmoff':
		$cmd = 'systemctl stop ksmtuned ksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		$cmd = 'echo 0 > /opt/unetlab/ksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		break;
	case 'ksmon':
		$cmd = 'systemctl start ksmtuned ksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		$cmd = 'echo 1 > /opt/unetlab/ksm';
		exec($cmd, $o, $rc);
		if ($rc !== 0) {
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][13]);
			error_log(date('M d H:i:s ') . date('M d H:i:s ') . implode("\n", $o));
			exit(13);
		}
		break;
}
exit(0);
?>