<?php
//vim: ts=2 sw=2
set_include_path(get_include_path() . PATH_SEPARATOR . '../../app');
include_once "base_controller.php";
function cmp($a, $b) {
if ($a['order'] == $b['order'])
return 0;
else
return $a['order'] > $b['order'];
}
class ServiceControlController extends BaseController {
function __construct() {
$this->service_mapping = array(
array(
'name' => 'http',
'label' => 'HTTP',
'proc_name' => 'nginx',
'service_name' => 'http',
'order' => 1
),
array(
'name' => 'https',
'label' => 'HTTPS',
'proc_name' => 'nginx',
'service_name' => 'https',
'order' => 2
),
array(
'name' => 'mqtt',
'label' => 'MQTT',
'proc_name' => 'mqtt-service',
'order' => 30
),
);
$pname = platformName();
if ($pname == 'FS') {
array_push($this->service_mapping,
array(
'name' => 'ftp', //service name
'label' => 'Ftp',
'proc_name' => 'pure-ftpd', // service process pattern
'order' => 10
),
array(
'name' => 'ssh',
'label' => 'SSH',
'proc_name' => 'sshd',
'order' => 20
),
array(
'name' => 'openvpn',
'label' => 'OpenVPN',
'proc_name' => 'openvpn',
'order' => 25
),
array(
'name' => 'smb',
'label' => 'Samba',
'proc_name' => 'smbd',
'service_name' => 'smb',
'order' => 40
)
);
} else if ($pname == "FW") {
array_push($this->service_mapping,
array(
'name' => 'ssh',
'label' => 'SSH',
'proc_name' => 'dropbear',
'order' => 20
),
array(
'name' => 'smb',
'label' => 'Samba',
'proc_name' => 'smbd',
'service_name' => 'samba',
'order' => 40
)
);
}
//sort service tuples by 'order' value
usort($this->service_mapping, "cmp");
}
protected function signinRequired() {
return true;
}
protected function adminRequired() {
return true;
}
protected function isProcessRunning($proc_name) {
$cmd = 'ps | grep "' . $proc_name . '" | grep -v grep';
return !is_null(shell_exec($cmd))
}
protected function isServiceEnabled($name) {
$cmd = null;
if ($name == 'http') {
$cmd = 'cat /etc/nginx/nginx.conf | grep "^\s*include http_server.conf;\s*$"';
} else if ($name == 'https') {
$cmd = 'cat /etc/nginx/nginx.conf | grep "^\s*include https_server.conf;\s*$"';
} else {
$pname = platformName();
if ($pname == "FS")
$cmd = 'ls /etc/init.d/S??' . $name ;
else if ($pname == "FW")
$cmd = 'ls /etc/rc.d/S??' . $name ;
else {
error_log("$pname is not supported yet");
return;
}
}
return !is_null(shell_exec($cmd))
}
protected function doAjaxGet() {
$response = array('services' => array());
foreach($this->service_mapping as $service) {
$service_name = isset($service['service_name']) ? $service['service_name'] : $service['proc_name'];
$response['services'][] = array(
'name' => $service['name'],
'label' => $service['label'],
'running' => $this->isProcessRunning($service['proc_name']),
'enabled' => $this->isServiceEnabled($service_name),
);
}
$this->renderAjaxSuccess2($response);
}
protected function controlWeb($service_name, $action) {
$cmd = null;
if ($action == 'enable') {
$cmd = 'sed -i -re "s/^\s*#(\s*include ' . $service_name . '_server.conf;\s*)$/\1/" /etc/nginx/nginx.conf';
} else if ($action == 'disable') {
if ( ($service_name == 'http' && !$this->isServiceEnabled('https'))
|| ($service_name == 'https' && !$this->isServiceEnabled('http')) )
$this->renderAjaxError($response, "can not disable both http and https service");
$cmd = 'sed -i -re "s/^(\s*include ' . $service_name . '_server.conf;\s*)$/#\1/" /etc/nginx/nginx.conf';
}
else {
error_log("action '$action' is not supported for web service");
return;
}
$pname = platformName();
$cmd = $cmd . ' && ' . ($pname == 'FS' ? '/etc/init.d/S??nginx restart' : '/etc/rc.d/S??nginx restart');
shell_exec($cmd)
}
private function controlCmd($service_name, $action) {
$cmd = null;
$pname = platformName();
if ($action == 'enable') {
if ($pname == 'FS')
$cmd = 'for s in /etc/init.d/D??' . $service_name . ' ; do $s start && mv $s /etc/init.d/S${s#/etc/init.d/D}; done';
else if ($pname == 'FW')
$cmd = '/etc/init.d/' . $service_name . ' enable && /etc/init.d/' . $service_name . ' restart';
} else if ($action == 'disable') {
if ($pname == 'FS')
$cmd = 'for s in /etc/init.d/S??' . $service_name . ' ; do $s stop && mv $s /etc/init.d/D${s#/etc/init.d/S}; done';
else
$cmd = '/etc/init.d/' . $service_name . ' disable && /etc/init.d/' . $service_name . ' stop';
} else {
error_log("action '$action' is not supported");
}
return $cmd;
}
protected function doAction($service, $action) {
if ($service['name'] == 'http' || $service['name'] == 'https') {
$this->controlWeb($service['service_name'], $action);
return;
}
$cmd = null;
$service_name = isset($service['service_name']) ? $service['service_name'] : $service['proc_name'];
$cmd = $this->controlCmd($service_name, $action);
if (!is_null($cmd))
shell_exec($cmd)
}
protected function doAjaxPost() {
$response = array();
if (!isset($_POST['name']) || !isset($_POST['action'])) {
$this->renderAjaxError($response, "missing parameter");
return;
}
$name = $_POST['name'];
$action = $_POST['action'];
$valid_actions = array('enable', 'disable');
if (!in_array($action, $valid_actions)) {
$this->renderAjaxError($response, "invalid service action");
return;
}
$found = false;
foreach($this->service_mapping as $service) {
if ($service['name'] != $name)
continue;
$this->doAction($service, $action);
$found = true;
break;
}
if ($found)
$this->renderAjaxSuccess(array('resultCode' => 0));
else
$this->renderAjaxError($response, "invalid service name");
}
}
$controller = new ServiceControlController();
$controller->run();
?>
<!DOCTYPE html>
<html>
<head>
<eta charset="utf-8" />
<title>Service Control</title>
<eta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../css/bootstrap.min.css" media="screen" />
</head>
<body>
<div class="container" id="service_control_content">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="/">EasyIO</a>
<!-- <ul class="nav"> -->
<!-- <li class="divider-vertical"></li> -->
<!-- <li v-for="sec in sections" :class="{active:isSectionActive(sec)}"><a href="#" @click="active_sec_id=sec.sec_id">{{ sec.name }}</a></li> -->
<!-- </ul> -->
</div>
</div>
<div class="row">
<div><h2 style="text-align:center">Service Control Panel</h2></div>
<service-control></service-control>
</div>
</div>
<!-- component templates -->
<script type="text/x-template" id="service-control-template">
<div class="span12">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr v-for="service in service_list" :class="{warning:!service.enabled}">
<td>{{ service.label }}</td>
<td><span class="label" :class="[service.enabled ? 'label-success' : 'label-warning',]">{{ service.enabled ? "Enabled" : "Disabled" }}</span></td>
<td>
<button class="btn" :class="{'btn-danger':service.enabled, 'disabled':!isServiceControllable(service)}" :disabled="!isServiceControllable(service)" @click="service.enabled ? disableService(service.name) : enableService(service.name)">
{{ service.enabled ? "Disable" : "Enable" }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</script>
<script type="text/javascript" src="../../js/jquery-1.10.0.min.js"></script>
<script type="text/javascript" src="../../js/bootstrap.min.js"></script>
<script type="text/javascript" src="../../js/underscore-min.js"></script>
<script type="text/javascript" src="../../js/spin.min.js"></script>
<?php
if (isset($_GET["dev"])) {
?>
<script type="text/javascript" src="../../js/vue.js"></script>
<?php
} else {
?>
<script type="text/javascript" src="../../js/vue.min.js"></script>
<?php
}
?>
<script type="text/javascript" src="../../js/plugin-utils.js"></script>
<script type="text/javascript" src="js/service_control.js"></script>
</body>
</html>