summaryrefslogtreecommitdiffstats
path: root/modules-available/rebootcontrol
diff options
context:
space:
mode:
authorSimon Rettberg2022-04-29 17:38:00 +0200
committerSimon Rettberg2022-04-29 17:38:00 +0200
commit140062e0b606495f90fd77b8f290987844c79cab (patch)
treead32ce72580fa898f447c66316587ee7404e9854 /modules-available/rebootcontrol
parent[baseconfig_bwlp] Add more HDMI outputs for sound card (diff)
downloadslx-admin-140062e0b606495f90fd77b8f290987844c79cab.tar.gz
slx-admin-140062e0b606495f90fd77b8f290987844c79cab.tar.xz
slx-admin-140062e0b606495f90fd77b8f290987844c79cab.zip
[locations/remoteaccess] Add option to veto remoteaccess mode
Remoteaccess mode can now be forced to be disabled for individual locations in locations module, either unconditionally, or whenever the openingtimes schedule says the room is open. A reboot will be triggered whenever the room opens/closes to force clients into the proper runmode.
Diffstat (limited to 'modules-available/rebootcontrol')
-rw-r--r--modules-available/rebootcontrol/inc/rebootcontrol.inc.php1
-rw-r--r--modules-available/rebootcontrol/inc/scheduler.inc.php131
2 files changed, 83 insertions, 49 deletions
diff --git a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
index b5f47c1d..d68e4dea 100644
--- a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
+++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
@@ -37,7 +37,6 @@ class RebootControl
* @param array $list list of clients containing each keys 'machineuuid', 'clientip' and 'locationid'
* @param string $mode reboot mode: RebootControl::REBOOT ::KEXEC_REBOOT or ::SHUTDOWN
* @param int $minutes delay in minutes for action
- * @param int $locationId meta data only: locationId of clients
* @return array|false the task, or false if it could not be started
*/
public static function execute($list, $mode, $minutes)
diff --git a/modules-available/rebootcontrol/inc/scheduler.inc.php b/modules-available/rebootcontrol/inc/scheduler.inc.php
index 7da4b46b..9e91df25 100644
--- a/modules-available/rebootcontrol/inc/scheduler.inc.php
+++ b/modules-available/rebootcontrol/inc/scheduler.inc.php
@@ -3,9 +3,13 @@
class Scheduler
{
- const SHUTDOWN = 'SHUTDOWN';
- const REBOOT = 'REBOOT';
- const WOL = 'WOL';
+ const ACTION_SHUTDOWN = 'SHUTDOWN';
+ const ACTION_REBOOT = 'REBOOT';
+ const ACTION_WOL = 'WOL';
+ const RA_NEVER = 'NEVER';
+ const RA_SELECTIVE = 'SELECTIVE';
+ const RA_ALWAYS = 'ALWAYS';
+ const SCHEDULE_OPTIONS_DEFAULT = ['wol' => false, 'sd' => false, 'wol-offset' => 0, 'sd-offset' => 0, 'ra-mode' => self::RA_ALWAYS];
/**
* @param int $locationid ID of location to delete WOL/shutdown settings for
@@ -17,7 +21,7 @@ class Scheduler
}
/**
- * Calculate next time the given time description is reached
+ * Calculate next time the given time description is reached.
* @param int $now unix timestamp representing now
* @param string $day Name of weekday
* @param string $time Time, fi. 13:45
@@ -42,18 +46,23 @@ class Scheduler
*/
private static function calculateNext(array $options, array $openingTimes)
{
- if ((!$options['wol'] && !$options['sd']) || empty($openingTimes))
+ // If ra-mode is selective, still execute even if wol and shutdown is disabled,
+ // because we still want to shutdown any sessions in the wrong runmode then
+ $selectiveRa = ($options['ra-mode'] === self::RA_SELECTIVE);
+ if ((!$options['wol'] && !$options['sd'] && !$selectiveRa) || empty($openingTimes))
return false;
$now = time();
$events = [];
+ $findWol = $options['wol'] || $options['ra-mode'] === self::RA_SELECTIVE;
+ $findSd = $options['sd'] || $options['ra-mode'] === self::RA_SELECTIVE;
foreach ($openingTimes as $row) {
foreach ($row['days'] as $day) {
- if ($options['wol']) {
- $events[] = ['action' => self::WOL,
+ if ($findWol) {
+ $events[] = ['action' => self::ACTION_WOL,
'time' => self::calculateTimestamp($now, $day, $row['openingtime'])];
}
- if ($options['sd']) {
- $events[] = ['action' => self::SHUTDOWN,
+ if ($findSd) {
+ $events[] = ['action' => self::ACTION_SHUTDOWN,
'time' => self::calculateTimestamp($now, $day, $row['closingtime'])];
}
}
@@ -67,9 +76,9 @@ class Scheduler
$prev = PHP_INT_MAX;
for ($i = count($events) - 1; $i >= 0; --$i) {
$event =& $events[$i];
- if ($event['action'] === self::WOL) {
+ if ($event['action'] === self::ACTION_WOL) {
$event['time'] -= $wolOffset;
- } elseif ($event['action'] === self::SHUTDOWN) {
+ } elseif ($event['action'] === self::ACTION_SHUTDOWN) {
$event['time'] += $sdOffset;
} else {
error_log('BUG Unhandled event type ' . $event['action']);
@@ -94,9 +103,9 @@ class Scheduler
// If difference to next event is < 5 min, ignore.
continue;
}
- if ($diff < 900 && $event['action'] === self::SHUTDOWN && $events[$i + 1]['action'] === self::WOL) {
+ if ($diff < 900 && $event['action'] === self::ACTION_SHUTDOWN && $events[$i + 1]['action'] === self::ACTION_WOL) {
// If difference to next WOL is < 15 min and this is a shutdown, reboot instead.
- $res['action'] = self::REBOOT;
+ $res['action'] = self::ACTION_REBOOT;
$res['time'] = $event['time'];
} else {
// Use first event.
@@ -119,7 +128,7 @@ class Scheduler
WHERE s.nextexecution < :now AND s.nextexecution > 0", ['now' => $now]);
foreach ($res as $row) {
// Calculate next_execution for the event and update DB.
- $options = json_decode($row['options'], true);
+ $options = json_decode($row['options'], true) + self::SCHEDULE_OPTIONS_DEFAULT;
// Determine proper opening times by waling up tree
$openingTimes = OpeningTimes::forLocation($row['locationid']);
if ($openingTimes !== null) {
@@ -128,24 +137,61 @@ class Scheduler
// Weird clock drift? Server offline for a while? Do nothing.
if ($row['nextexecution'] + 900 < $now)
continue;
- self::executeCronForLocation($row['locationid'], $row['action']);
+ $selectiveRa = ($options['ra-mode'] === self::RA_SELECTIVE);
+ // Now, if selective remote access is active, we might modify the actual event:
+ if ($selectiveRa) {
+ // If this is WOL, and WOL is actually enabled, then reboot any running machines
+ // in remoteaccess mode, in addition to waking the others, so they exit remote access mode.
+ if ($row['action'] === Scheduler::ACTION_WOL && $options['wol']) {
+ self::executeCronForLocation($row['locationid'], Scheduler::ACTION_REBOOT, 'remoteaccess');
+ self::executeCronForLocation($row['locationid'], Scheduler::ACTION_WOL);
+ }
+ // If this is WOL, and WOL is disabled, shut down any running machines, this is so
+ // anybody walking into this room will not mess with a user's session by yanking the
+ // power cord etc.
+ if ($row['action'] === Scheduler::ACTION_WOL && !$options['wol']) {
+ self::executeCronForLocation($row['locationid'], Scheduler::ACTION_SHUTDOWN, 'remoteaccess');
+ }
+ // If this is SHUTDOWN, and SHUTDOWN is enabled, leave it at that.
+ if ($row['action'] === Scheduler::ACTION_SHUTDOWN && $options['sd']) {
+ self::executeCronForLocation($row['locationid'], Scheduler::ACTION_SHUTDOWN);
+ }
+ // If this is SHUTDOWN, and SHUTDOWN is disabled, do a reboot, so the machine ends up
+ // in the proper runmode.
+ if ($row['action'] === Scheduler::ACTION_SHUTDOWN && !$options['sd']) {
+ self::executeCronForLocation($row['locationid'], Scheduler::ACTION_REBOOT, '');
+ }
+ } else {
+ // Regular case, no selective remoteaccess – just do what the cron entry says
+ self::executeCronForLocation($row['locationid'], $row['action']);
+ }
}
}
/**
* Execute the given action for the given location.
+ * @param int $locationId location
+ * @param string $action action to perform, Scheduler::*
+ * @param string|null $onlyRunmode if not null, only process running clients in given runmode
+ * @return void
*/
- private static function executeCronForLocation(int $locationId, string $action)
+ private static function executeCronForLocation(int $locationId, string $action, string $onlyRunmode = null)
{
- $machines = Database::queryAll("SELECT machineuuid, clientip, macaddr, locationid FROM machine
+ if ($onlyRunmode === null) {
+ $machines = Database::queryAll("SELECT machineuuid, clientip, macaddr, locationid FROM machine
WHERE locationid = :locid", ['locid' => $locationId]);
+ } else {
+ $machines = Database::queryAll("SELECT machineuuid, clientip, macaddr, locationid FROM machine
+ WHERE locationid = :locid AND currentrunmode = :runmode AND state <> 'OFFLINE'",
+ ['locid' => $locationId, 'runmode' => $onlyRunmode]);
+ }
if (empty($machines))
return;
- if ($action === Scheduler::SHUTDOWN) {
+ if ($action === Scheduler::ACTION_SHUTDOWN) {
RebootControl::execute($machines, RebootControl::SHUTDOWN, 0);
- } elseif ($action === Scheduler::WOL) {
+ } elseif ($action === Scheduler::ACTION_WOL) {
RebootControl::wakeMachines($machines);
- } elseif ($action === Scheduler::REBOOT) {
+ } elseif ($action === Scheduler::ACTION_REBOOT) {
RebootControl::execute($machines, RebootControl::REBOOT, 0);
} else {
EventLog::warning("Invalid action '$action' in schedule for location " . $locationId);
@@ -155,49 +201,32 @@ class Scheduler
/**
* Get current settings for given location, or false if none.
* @param int $id
- * @return false|array
*/
- public static function getLocationOptions(int $id)
+ public static function getLocationOptions(int $id): array
{
$res = Database::queryFirst("SELECT options FROM `reboot_scheduler`
WHERE locationid = :id", ['id' => $id]);
if ($res !== false) {
- return json_decode($res['options'], true);
+ return (json_decode($res['options'], true) ?? []) + self::SCHEDULE_OPTIONS_DEFAULT;
}
- return false;
+ return self::SCHEDULE_OPTIONS_DEFAULT;
}
/**
* Write new WOL/Shutdown options for given location.
* @param int $locationId
- * @param bool $wol whether WOL is enabled
- * @param bool $sd whether Shutdown is enabled
- * @param int $wolOffset how many minutes prior to opening time the WOL should be triggered
- * @param int $sdOffset how many minutes after closing time a shutdown should be triggered
+ * @param array $options 'wol' 'sd' 'wol-offset' 'sd-offset' 'ra-mode'
*/
- public static function setLocationOptions(int $locationId, bool $wol, bool $sd, int $wolOffset, int $sdOffset)
+ public static function setLocationOptions(int $locationId, array $options)
{
+ $options += self::SCHEDULE_OPTIONS_DEFAULT;
$openingTimes = OpeningTimes::forLocation($locationId);
- if (!$wol && !$sd) {
+ if (!$options['wol'] && !$options['sd'] && $options['ra-mode'] === self::RA_ALWAYS) {
self::deleteSchedule($locationId);
} else {
- // Sanity checks
- if ($wolOffset > 60) {
- $wolOffset = 60;
- } elseif ($wolOffset < 0) {
- $wolOffset = 0;
- }
- if ($sdOffset > 60) {
- $sdOffset = 60;
- } elseif ($sdOffset < 0) {
- $sdOffset = 0;
- }
- $options = [
- 'wol' => $wol,
- 'sd' => $sd,
- 'wol-offset' => $wolOffset,
- 'sd-offset' => $sdOffset,
- ];
+ // Sanitize
+ Util::clamp($options['wol-offset'], 0, 60);
+ Util::clamp($options['sd-offset'], 0, 60);
$json_options = json_encode($options);
// Write settings, reset schedule
Database::exec("INSERT INTO `reboot_scheduler` (locationid, action, nextexecution, options)
@@ -228,7 +257,7 @@ class Scheduler
*/
private static function updateScheduleSingle(int $locationid, array $options, array $openingTimes)
{
- if (!$options['wol'] && !$options['sd']) {
+ if (!$options['wol'] && !$options['sd'] && $options['ra-mode'] === self::RA_ALWAYS) {
self::deleteSchedule($locationid);
return;
}
@@ -284,6 +313,7 @@ class Scheduler
if (!is_array($options)) {
trigger_error("Invalid options for lid:$locationId", E_USER_WARNING);
} else {
+ $options += self::SCHEDULE_OPTIONS_DEFAULT;
self::updateScheduleSingle($locationId, $options, $openingTimes);
}
}
@@ -292,4 +322,9 @@ class Scheduler
}
}
+ public static function isValidRaMode(string $raMode): bool
+ {
+ return $raMode === self::RA_ALWAYS || $raMode === self::RA_NEVER || $raMode === self::RA_SELECTIVE;
+ }
+
} \ No newline at end of file