diff --git a/front/agent.form.php b/front/agent.form.php index 240d5e99..bd48cd31 100644 --- a/front/agent.form.php +++ b/front/agent.form.php @@ -65,6 +65,13 @@ '_ping' => '', ]); Html::back(); +} else if (isset($_POST['reboot'])) { + $agent->check($_POST['id'], UPDATE); + $agent->update([ + 'id' => $_POST['id'], + '_reboot' => '', + ]); + Html::back(); } else if (isset($_POST['geolocate'])) { $agent->check($_POST['id'], UPDATE); $agent->update([ diff --git a/inc/agent.class.php b/inc/agent.class.php index fe6e0060..0c1ca375 100644 --- a/inc/agent.class.php +++ b/inc/agent.class.php @@ -246,6 +246,7 @@ public function showForm($ID, array $options = []) { 'canUpdate' => $canUpdate, 'agent' => $fields, 'pingButton' => Html::submit(_x('button', 'Ping'), ['name' => 'ping']), + 'rebootButton' => Html::submit(_x('button', 'Reboot'), ['name' => 'reboot']), 'geolocateButton' => Html::submit(_x('button', 'Geolocate'), ['name' => 'geolocate']), 'inventoryButton' => Html::submit(_x('button', 'Inventory'), ['name' => 'inventory']), @@ -505,7 +506,7 @@ public function prepareInputForUpdate($input) { } //Send a connection status request to the device - if (isset($input['_ping']) || isset($input['_geolocate']) || isset($input['_inventory'])) { + if (isset($input['_ping']) || isset($input['_reboot']) || isset($input['_geolocate']) || isset($input['_inventory'])) { if ($this->getTopic() === null) { Session::addMessageAfterRedirect(__("The device is not enrolled yet", 'flyvemdm')); return false; @@ -521,6 +522,15 @@ public function prepareInputForUpdate($input) { } } + if (isset($input['_reboot'])) { + try { + $this->sendRebootQuery(); + } catch (AgentSendQueryException $exception) { + Session::addMessageAfterRedirect($exception->getMessage()); + return false; + } + } + if (isset($input['_geolocate'])) { try { $this->sendGeolocationQuery(); @@ -1505,6 +1515,7 @@ public static function getTopicsToCleanup() { return array_merge($topics, [ 'Command/Subscribe', 'Command/Ping', + 'Command/Reboot', 'Command/Geolocate', 'Command/Inventory', 'Command/Lock', @@ -1579,6 +1590,30 @@ private function sendInventoryQuery() { throw new AgentSendQueryException(__("Timeout querying the device inventory", 'flyvemdm')); } + /** + * Sends a message on the subtopic dedicated to ping requests + * + * @return bool + * @throws AgentSendQueryException + */ + private function sendRebootQuery() { + $this->notify($this->topic . "/Command/Reboot", + json_encode(['query' => 'Reboot'], JSON_UNESCAPED_SLASHES), 0, 0); + + $loopCount = 25; + $updatedAgent = new self(); + while ($loopCount > 0) { + usleep(200000); // 200 milliseconds + $loopCount--; + $updatedAgent->getFromDB($this->getID()); + if ($updatedAgent->getField('last_contact') != $this->fields['last_contact']) { + Session::addMessageAfterRedirect(__('The device restarted', 'flyvemdm')); + return true; + } + } + throw new AgentSendQueryException(__("Timeout querying the device", 'flyvemdm')); + } + /** * Sends a message on the subtopic dedicated to ping requests * diff --git a/tests/src/Flyvemdm/Tests/CommonTestCase.php b/tests/src/Flyvemdm/Tests/CommonTestCase.php index 6c986422..c6ee8bba 100644 --- a/tests/src/Flyvemdm/Tests/CommonTestCase.php +++ b/tests/src/Flyvemdm/Tests/CommonTestCase.php @@ -532,6 +532,7 @@ public static function commandList() { return [ 'Command/Subscribe', 'Command/Ping', + 'Command/Reboot', 'Command/Geolocate', 'Command/Inventory', 'Command/Lock', diff --git a/tests/suite-integration/PluginFlyvemdmAgent.php b/tests/suite-integration/PluginFlyvemdmAgent.php index 4ab16943..a19c1bc7 100644 --- a/tests/suite-integration/PluginFlyvemdmAgent.php +++ b/tests/suite-integration/PluginFlyvemdmAgent.php @@ -623,6 +623,48 @@ public function testPingRequest() { $this->string($row['message'])->isEqualTo(json_encode($mqttMessage, JSON_UNESCAPED_SLASHES)); } + /** + * test ping message + * @tags testPingRequest + */ + public function testRebootRequest() { + list($user, $serial, $guestEmail, $invitation) = $this->createUserInvitation(\User::getForeignKeyField()); + $agent = $this->agentFromInvitation($user, $guestEmail, $serial, + $invitation->getField('invitation_token')); + + // Get enrolment data to enable the agent's MQTT account + $this->boolean($agent->getFromDB($agent->getID()))->isTrue(); + + // Find the last existing ID of logged MQTT messages + $log = new \PluginFlyvemdmMqttlog(); + $lastLogId = \PluginFlyvemdmCommon::getMax($log, '', 'id'); + + $updateSuccess = $agent->update([ + 'id' => $agent->getID(), + '_reboot' => '', + ]); + + // Update shall fail because the ping answer will not occur + $this->boolean($updateSuccess)->isFalse(); + + // Get the latest MQTT message + sleep(2); + + $logEntryFound = 0; + $rows = $log->find("`direction` = 'O' AND `id` > '$lastLogId'"); + foreach ($rows as $row) { + if ($row['topic'] == $agent->getTopic() . '/Command/Reboot') { + $logEntryFound = $row['id']; + break; + } + } + $this->integer((int) $logEntryFound)->isGreaterThan(0); + + // check the message + $mqttMessage = ['query' => 'Reboot']; + $this->string($row['message'])->isEqualTo(json_encode($mqttMessage, JSON_UNESCAPED_SLASHES)); + } + /** * test geolocate message * @tags testGeolocateRequest diff --git a/tpl/agent.html.twig b/tpl/agent.html.twig index 20254dbd..0866c410 100644 --- a/tpl/agent.html.twig +++ b/tpl/agent.html.twig @@ -39,18 +39,21 @@ {% if canUpdate == true %} -