Merge branch 'master' of github.com:ZoneMinder/zoneminder
commit
c02cbd86f8
|
@ -7,6 +7,8 @@ configure_file(logrotate.conf.in "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" @O
|
|||
configure_file(syslog.conf.in "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" @ONLY)
|
||||
configure_file(com.zoneminder.systemctl.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" @ONLY)
|
||||
configure_file(com.zoneminder.systemctl.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" @ONLY)
|
||||
configure_file(com.zoneminder.dnsmasq.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.dnsmasq.policy" @ONLY)
|
||||
configure_file(com.zoneminder.dnsmasq.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.dnsmasq.rules" @ONLY)
|
||||
configure_file(com.zoneminder.arp-scan.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.policy" @ONLY)
|
||||
configure_file(com.zoneminder.arp-scan.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.rules" @ONLY)
|
||||
configure_file(zoneminder.service.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.service" @ONLY)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
|
||||
<vendor>The ZoneMinder Project</vendor>
|
||||
<vendor_url>https://www.zoneminder.com/</vendor_url>
|
||||
|
||||
<action id="com.zoneminder.policykit.pkexec.run-systemctl">
|
||||
<description>Allow the ZoneMinder webuser to start/stop the dnsmasq service</description>
|
||||
<message>The ZoneMinder webuser is trusted to start/stop dnsmasq</message>
|
||||
<defaults>
|
||||
<allow_any>yes</allow_any>
|
||||
<allow_inactive>yes</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/bin/systemctl</annotate>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
|
@ -0,0 +1,9 @@
|
|||
// Allow www-data to manage dnsmasq.service;
|
||||
// fall back to implicit authorization otherwise.
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||
action.lookup("unit") == "dnsmasq.service" &&
|
||||
subject.user == "@WEB_USER@") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
|
@ -5,7 +5,7 @@
|
|||
<policyconfig>
|
||||
|
||||
<vendor>The ZoneMinder Project</vendor>
|
||||
<vendor_url>http://www.zoneminder.com/</vendor_url>
|
||||
<vendor_url>https://www.zoneminder.com/</vendor_url>
|
||||
|
||||
<action id="com.zoneminder.policykit.pkexec.run-zmsystemctl">
|
||||
<description>Allow the ZoneMinder webuser to run zmsystemctl.pl</description>
|
||||
|
|
|
@ -3,5 +3,4 @@ polkit.addRule(function(action, subject) {
|
|||
subject.user != "@WEB_USER@") {
|
||||
return polkit.Result.NO;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1095,5 +1095,9 @@ public static function getStatuses() {
|
|||
if (isset($gp_permissions['Edit'])) return 'Edit';
|
||||
return $u->Monitors();
|
||||
}
|
||||
|
||||
public function link_to($text='') {
|
||||
return '<a href="?view=monitor&mid='.$this->Id().'">'.($text ? $text : $this->Name()).'</a>';
|
||||
}
|
||||
} // end class Monitor
|
||||
?>
|
||||
|
|
|
@ -108,5 +108,50 @@ if ( $action == 'delete' ) {
|
|||
#generateAuthHash(ZM_AUTH_HASH_IPS, true);
|
||||
}
|
||||
return;
|
||||
} else if ($action == 'save') {
|
||||
if (isset($_REQUEST['object'])) {
|
||||
if ($_REQUEST['object'] == 'dnsmasq') {
|
||||
$config = isset($_REQUEST['config']) ? $_REQUEST['config'] : [];
|
||||
$conf = '';
|
||||
foreach ($config as $name=>$value) {
|
||||
if ($name == 'dhcp-host') {
|
||||
foreach ($value as $mac=>$ip) {
|
||||
$conf .= $name.'='.$mac.','.$ip.PHP_EOL;
|
||||
}
|
||||
} else if (
|
||||
($name == 'bind-interfaces')
|
||||
or
|
||||
($name == 'dhcp-authoritative')
|
||||
) {
|
||||
if ($value=='yes') {
|
||||
$conf .= $name.PHP_EOL;
|
||||
}
|
||||
} else if ($name == 'dhcp-range') {
|
||||
$conf .= $name.'='.$value['min'].','.$value['max'].','.$value['expires'].PHP_EOL;
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
}
|
||||
} else {
|
||||
$conf .= $name.'='.$value.PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_put_contents(ZM_PATH_DNSMASQ_CONF, $conf);
|
||||
exec('sudo -n /bin/systemctl restart dnsmasq.service');
|
||||
}
|
||||
}
|
||||
} else if ($action == 'start') {
|
||||
if (isset($_REQUEST['object'])) {
|
||||
if ($_REQUEST['object'] == 'dnsmasq') {
|
||||
exec('sudo -n /bin/systemctl start dnsmasq.service');
|
||||
}
|
||||
}
|
||||
} else if ($action == 'stop') {
|
||||
if (isset($_REQUEST['object'])) {
|
||||
if ($_REQUEST['object'] == 'dnsmasq') {
|
||||
exec('sudo -n /bin/systemctl stop dnsmasq.service');
|
||||
}
|
||||
}
|
||||
} // end if object vs action
|
||||
?>
|
||||
|
|
|
@ -258,9 +258,8 @@ if ( defined('ZM_TIMEZONE') and ZM_TIMEZONE )
|
|||
ini_set('date.timezone', ZM_TIMEZONE);
|
||||
|
||||
function process_configfile($configFile) {
|
||||
if ( is_readable($configFile) ) {
|
||||
$configvals = array();
|
||||
|
||||
$configvals = array();
|
||||
if (is_readable($configFile)) {
|
||||
$cfg = fopen($configFile, 'r') or error_log('Could not open config file: '.$configFile);
|
||||
while ( !feof($cfg) ) {
|
||||
$str = fgets($cfg, 256);
|
||||
|
@ -273,11 +272,10 @@ function process_configfile($configFile) {
|
|||
}
|
||||
}
|
||||
fclose($cfg);
|
||||
return $configvals;
|
||||
} else {
|
||||
error_log('WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on '.$configFile);
|
||||
return false;
|
||||
}
|
||||
return $configvals;
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -2459,4 +2459,9 @@ function getHomeView() {
|
|||
}
|
||||
return 'console';
|
||||
}
|
||||
|
||||
function systemd_isactive($service) {
|
||||
$output = shell_exec("systemctl is-active $service");
|
||||
return (trim($output) == 'active');
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -587,7 +587,7 @@ function probeNetwork() {
|
|||
$results = array();
|
||||
|
||||
$monitors = array();
|
||||
foreach ( ZM\Monitor::find(['Type'=>'Ffmpeg']) as $monitor) {
|
||||
foreach (ZM\Monitor::find(['Type'=>'Remote']) as $monitor) {
|
||||
if ( preg_match('/^(.+)@(.+)$/', $monitor->Host(), $matches) ) {
|
||||
//echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."<br/>";
|
||||
$monitors[gethostbyname($matches[2])] = $monitor;
|
||||
|
@ -596,7 +596,7 @@ function probeNetwork() {
|
|||
$monitors[gethostbyname($monitor->Host())] = $monitor;
|
||||
}
|
||||
}
|
||||
foreach ( ZM\Monitor::find(['Type'=>'Ffmpeg']) as $monitor) {
|
||||
foreach (ZM\Monitor::find(['Type'=>'Ffmpeg']) as $monitor) {
|
||||
$url_parts = parse_url($monitor->Path());
|
||||
if ($url_parts !== false) {
|
||||
#ZM\Debug("Ffmpeg monitor ${url_parts['host']} = ${monitor['Id']} ${monitor['Name']}");
|
||||
|
|
|
@ -105,3 +105,27 @@ form {
|
|||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.Config .config .row > label {
|
||||
width: 300px;
|
||||
text-align: right;
|
||||
}
|
||||
.Config .config .row {
|
||||
min-height:36px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-content: space-around;
|
||||
}
|
||||
#leasesTable td,
|
||||
#leasesTable th {
|
||||
text-align: left;
|
||||
}
|
||||
input[name="config[dhcp-range][min]"],
|
||||
input[name="config[dhcp-range][max]"] {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
}
|
||||
input[name="config[dhcp-range][expires]"] {
|
||||
width: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
if (!canView('System')) {
|
||||
$view = 'error';
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="Config">
|
||||
<h2>DHCP Server Config</h2>
|
||||
<form name="contentForm" action="?view=options" method="post">
|
||||
<input type="hidden" name="object" value="dnsmasq"/>
|
||||
<input type="hidden" name="tab" value="dnsmasq"/>
|
||||
<?php
|
||||
if (canEdit('System')) {
|
||||
?>
|
||||
<div id="contentButtons">
|
||||
<button type="submit" name="action" value="save"><?php echo translate('Save') ?></button>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="service"><h2><?php echo translate('Service') ?></h2>
|
||||
<?php
|
||||
$active = systemd_isactive('dnsmasq');
|
||||
echo 'Dnsmasq service is '.($active ? 'active' : 'inactive').'.';
|
||||
if (canEdit('System')) {
|
||||
if ($active) {
|
||||
echo '<button type="submit" name="action" value="stop">'.translate('Stop').'</button>';
|
||||
} else {
|
||||
echo '<button type="submit" name="action" value="start">'.translate('Start').'</button>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="config"><h2><?php echo translate('Configuration') ?></h2>
|
||||
<div class="container">
|
||||
<?php
|
||||
$dnsmasq_config = [
|
||||
'interface'=>'',
|
||||
'bind-interfaces'=>'',
|
||||
'dhcp-range'=>'192.168.9.50,192.168.9.150,12h',
|
||||
#'dhcp-rapid-commit'=>'',
|
||||
'dhcp-authoritative'=>''
|
||||
];
|
||||
if (defined('ZM_PATH_DNSMASQ_CONF') and file_exists(ZM_PATH_DNSMASQ_CONF))
|
||||
$dnsmasq_config += process_dnsmasq_configfile(ZM_PATH_DNSMASQ_CONF);
|
||||
ZM\Debug(print_r($dnsmasq_config, true));
|
||||
foreach ($dnsmasq_config as $name=>$value) {
|
||||
if ($name == 'interface') {
|
||||
$interfaces = get_networks();
|
||||
$default_interface = $interfaces['default'];
|
||||
unset($interfaces['default']);
|
||||
|
||||
echo '<div class="row"><label class="form-label" for="interface">'.translate('Interface').'</label>'.PHP_EOL.
|
||||
'<span class="value">'.
|
||||
htmlSelect('config[interface]', $interfaces,
|
||||
(isset($dnsmasq_config['interface']) ? $dnsmasq_config['interface'] : $default_interface),
|
||||
).'</span></div>'.PHP_EOL;
|
||||
} else if ($name == 'bind-interfaces') {
|
||||
echo '<div class="row"><label class="form-label" for="bind-interfaces">'.translate('Bind Interfaces').'</label>'.PHP_EOL.
|
||||
'<span class="value">'.
|
||||
html_radio('config[bind-interfaces]', ['yes'=>translate('Yes'), 'no'=>translate('No')], $dnsmasq_config[$name], ['default'=>'yes']).
|
||||
'</span></div>'.PHP_EOL;
|
||||
} else if ($name == 'dhcp-authoritative') {
|
||||
echo '<div class="row"><label class="form-label" for="dhcp-authoritative">'.translate('DHCP Authoritative').'</label>'.PHP_EOL.
|
||||
'<span class="value">'.
|
||||
html_radio('config[dhcp-authoritative]', ['yes'=>translate('Yes'), 'no'=>translate('No')], $dnsmasq_config[$name], ['default'=>'yes']).
|
||||
'</span></div>'.PHP_EOL;
|
||||
} else if ($name == 'dhcp-range') {
|
||||
$values = explode(',', $value);
|
||||
|
||||
echo '<div class="row"><label class="form-label" for="dhcp-range"> '.translate('DHCP Range').'</label><span class="value">';
|
||||
echo '<input type="text" name="config[dhcp-range][min]" value="'.$values[0].'"/>';
|
||||
echo ' to <input type="text" name="config[dhcp-range][max]" value="'.$values[1].'"/>';
|
||||
echo ' <input type="text" name="config[dhcp-range][expires]" value="'.$values[2].'"/></span></div>'.PHP_EOL;
|
||||
} else if ($name == 'dhcp-host') {
|
||||
# Handled below
|
||||
} else {
|
||||
echo '<div class="row"><label class="form-label">'.$name.'</label><span class="value">'.PHP_EOL;
|
||||
echo '<input type="text" name="config['.validHtmlStr($name).']" value="'.validHtmlStr($value).'"/></span></div>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div><!--Config-->
|
||||
<div class="leases"><h2>Leases</h2>
|
||||
<?php
|
||||
function process_dnsmasq_configfile($configFile) {
|
||||
$configvals = array();
|
||||
if (is_readable($configFile)) {
|
||||
$cfg = fopen($configFile, 'r') or ZM\Error('Could not open config file: '.$configFile);
|
||||
while ( !feof($cfg) ) {
|
||||
$str = fgets($cfg, 256);
|
||||
if ( preg_match('/^\s*(#.*)?$/', $str) ) {
|
||||
continue;
|
||||
} else if ( preg_match('/^\s*([^=\s]+)\s*(=\s*[\'"]*(.*?)[\'"]*\s*)?$/', $str, $matches) ) {
|
||||
$configvals[$matches[1]] = isset($matches[2]) ? $matches[2] : 'yes';
|
||||
} else {
|
||||
ZM\Error("Malformed line in config $configFile\n$str");
|
||||
}
|
||||
}
|
||||
fclose($cfg);
|
||||
} else {
|
||||
Error('WARNING: dnsmasq configuration file found but is not readable. Check file permissions on '.$configFile);
|
||||
}
|
||||
return $configvals;
|
||||
}
|
||||
|
||||
function read_leasefile($file) {
|
||||
$leases = [];
|
||||
$contents = file_get_contents($file);
|
||||
foreach (explode("\n", $contents) as $line) {
|
||||
$row = explode(' ', $line);
|
||||
if (count($row) != 5) continue;
|
||||
$lease = [
|
||||
'expiry' => $row[0],
|
||||
'mac' => $row[1],
|
||||
'ip' => $row[2],
|
||||
'name' => $row[3],
|
||||
'id' => $row[4]
|
||||
];
|
||||
$leases[] = $lease;
|
||||
}
|
||||
return $leases;
|
||||
}
|
||||
?>
|
||||
<table id="leasesTable" class="table bootstraptable"
|
||||
data-check-on-init="true"
|
||||
data-mobile-responsive="true"
|
||||
data-min-width="562"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable="true" data-field="hostname" class="hostname"><?php echo translate('Hostname') ?></th>
|
||||
<th data-sortable="true" data-field="mac" class="mac"><?php echo translate('Mac Address') ?></th>
|
||||
<th data-sortable="true" data-field="ip" class="ip"><?php echo translate('IP Address') ?></th>
|
||||
<th data-sortable="true" data-field="expires" class="expires"><?php echo translate('Expires') ?></th>
|
||||
<th data-sortable="true" data-field="Monitor" class="expires"><?php echo translate('Monitor') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$monitors_by_ip = array();
|
||||
foreach (ZM\Monitor::find(['Type'=>'Remote']) as $monitor) {
|
||||
if (preg_match('/^(.+)@(.+)$/', $monitor->Host(), $matches)) {
|
||||
$monitors_by_ip[gethostbyname($matches[2])] = $monitor;
|
||||
} else {
|
||||
$monitors_by_ip[gethostbyname($monitor->Host())] = $monitor;
|
||||
}
|
||||
}
|
||||
foreach (ZM\Monitor::find(['Type'=>'Ffmpeg']) as $monitor) {
|
||||
$url_parts = parse_url($monitor->Path());
|
||||
if ($url_parts !== false) {
|
||||
$monitors_by_ip[gethostbyname($url_parts['host'])] = $monitor;
|
||||
} else {
|
||||
ZM\Debug('Unable to parse '.$monitor->Path());
|
||||
}
|
||||
}
|
||||
$leases = read_leasefile('/var/lib/misc/dnsmasq.leases');
|
||||
foreach ($leases as $lease) {
|
||||
if (!isset($monitors_by_ip[$lease['ip']])) {
|
||||
ZM\Debug("No monitor for".$lease['ip']);
|
||||
}
|
||||
echo '
|
||||
<tr>
|
||||
<td class="hostname">'.$lease['name'].'</td>
|
||||
<td class="mac">'.$lease['mac'].'</td>
|
||||
<td class="ip"><input type="text" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" name="config[dhcp-host]['.$lease['mac'].']" value="'.$lease['ip'].'"/></td>
|
||||
<td class="expiry">'.$dateTimeFormatter->format($lease['expiry']).'</td>
|
||||
<td class="Monitor">'.(isset($monitors_by_ip[$lease['ip']]) ? $monitors_by_ip[$lease['ip']]->link_to() : '').'</td>
|
||||
</tr>';
|
||||
} # end foreach
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
|
@ -57,6 +57,8 @@ function initPage() {
|
|||
|
||||
NewStorageBtn.prop('disabled', !canEdit.System);
|
||||
NewServerBtn.prop('disabled', !canEdit.System);
|
||||
|
||||
$j('.bootstraptable').bootstrapTable({icons: icons}).show();
|
||||
}
|
||||
|
||||
$j(document).ready(function() {
|
||||
|
|
|
@ -29,6 +29,9 @@ $tabs = array();
|
|||
$tabs['skins'] = translate('Display');
|
||||
$tabs['system'] = translate('System');
|
||||
$tabs['config'] = translate('Config');
|
||||
if (defined('ZM_PATH_DNSMASQ_CONF') and file_exists(ZM_PATH_DNSMASQ_CONF)) {
|
||||
$tabs['dnsmasq'] = translate('DHCP');
|
||||
}
|
||||
$tabs['API'] = translate('API');
|
||||
$tabs['servers'] = translate('Servers');
|
||||
$tabs['storage'] = translate('Storage');
|
||||
|
@ -109,8 +112,6 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
|
|||
</div>
|
||||
</form>
|
||||
<?php
|
||||
} else if ($tab == 'users') {
|
||||
include('_options_users.php');
|
||||
} else if ($tab == 'control') {
|
||||
if (canView('Control')) {
|
||||
$redirect = '?view=controlcaps';
|
||||
|
@ -262,6 +263,10 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
|
|||
</div>
|
||||
</form>
|
||||
<?php
|
||||
} else if ($tab == 'dnsmasq' and file_exists('skins/classic/views/_options_dnsmasq.php')) {
|
||||
include('_options_dnsmasq.php');
|
||||
} else if ($tab == 'users') {
|
||||
include('_options_users.php');
|
||||
} else if ($tab == 'API') {
|
||||
include('_options_api.php');
|
||||
} // $tab == API
|
||||
|
|
Loading…
Reference in New Issue