summaryrefslogtreecommitdiffstats
path: root/modules-available/rebootcontrol/inc/scheduler.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/rebootcontrol/inc/scheduler.inc.php')
-rw-r--r--modules-available/rebootcontrol/inc/scheduler.inc.php131
1 files changed, 83 insertions, 48 deletions
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