diff --git a/src/etc/rc.configure_firmware b/src/etc/rc.configure_firmware index 3d795ef98..01ae32f8b 100755 --- a/src/etc/rc.configure_firmware +++ b/src/etc/rc.configure_firmware @@ -48,3 +48,4 @@ system_login_configure(true); /* flush cache */ (new OPNsense\Core\ACL())->persist(); +(new OPNsense\Base\Menu\MenuSystem())->persist(); diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php b/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php index e541ec6e8..b2b8fa39f 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php @@ -42,6 +42,16 @@ class MenuSystem */ private $root = null; + /** + * @var string location to store merged menu xml + */ + private $menuCacheFilename = null; + + /** + * @var int time to live for merged menu xml + */ + private $menuCacheTTL = 3600; + /** * add menu structure to root * @param string $filename menu xml filename @@ -61,10 +71,7 @@ class MenuSystem throw new MenuInitException('Menu xml '.$filename.' seems to be of wrong type'); } - // traverse items - foreach ($menuXml as $key => $node) { - $this->root->addXmlNode($node); - } + return $menuXml; } /** @@ -88,21 +95,80 @@ class MenuSystem } /** - * construct a new menu - * @throws MenuInitException + * Load and persist Menu configuration to disk. + * @param bool $nowait when the cache is locked, skip waiting for it to become available. + * @return SimpleXMLElement */ - public function __construct() + public function persist($nowait=true) { - $this->root = new MenuItem("root"); + // collect all XML menu definitions into a single file + $menuXml = new \DOMDocument('1.0'); + $root = $menuXml->createElement('menu'); + $menuXml->appendChild($root); // crawl all vendors and modules and add menu definitions foreach (glob(__DIR__.'/../../../*') as $vendor) { foreach (glob($vendor.'/*') as $module) { $menu_cfg_xml = $module.'/Menu/Menu.xml'; if (file_exists($menu_cfg_xml)) { - $this->addXML($menu_cfg_xml); + $domNode = dom_import_simplexml($this->addXML($menu_cfg_xml)); + $domNode = $root->ownerDocument->importNode($domNode, true); + $root->appendChild($domNode); } } } + // flush to disk + $fp = fopen($this->menuCacheFilename, file_exists($this->menuCacheFilename) ? "r+" : "w+"); + $lockMode = $nowait ? LOCK_EX | LOCK_NB : LOCK_EX ; + if (flock($fp, $lockMode)) { + ftruncate($fp, 0); + fwrite($fp, $menuXml->saveXML()); + fflush($fp); + flock($fp, LOCK_UN); + fclose($fp); + chmod($this->menuCacheFilename, 0660); + } + // return generated xml + return simplexml_import_dom($root); + } + + /** + * check if stored menu's are expired + * @return bool is expired + */ + public function isExpired() + { + if (file_exists($this->menuCacheFilename)) { + $fstat = stat($this->menuCacheFilename); + return $this->menuCacheTTL < (time() - $fstat['mtime']); + } + return true; + } + + /** + * construct a new menu + * @throws MenuInitException + */ + public function __construct() + { + // set cache location + $this->menuCacheFilename = sys_get_temp_dir(). "/opnsense_menu_cache.xml"; + + // load menu xml's + $menuxml = null; + if (!$this->isExpired()) { + $menuxml = @simplexml_load_file($this->menuCacheFilename); + } + if ($menuxml == null) { + $menuxml = $this->persist(); + } + + // load menu xml's + $this->root = new MenuItem("root"); + foreach ($menuxml as $menu) { + foreach ($menu as $node) { + $this->root->addXmlNode($node); + } + } $config = Config::getInstance()->object();