diff --git a/.vagrant-puppet/files/etc/init.d/icinga_command_proxy b/.vagrant-puppet/files/etc/init.d/icinga_command_proxy index 68553b94a..77059981f 100644 --- a/.vagrant-puppet/files/etc/init.d/icinga_command_proxy +++ b/.vagrant-puppet/files/etc/init.d/icinga_command_proxy @@ -25,7 +25,7 @@ ICINGA_MYSQL_CMD=${ICINGA_MYSQL_CMD:-"/usr/local/icinga-mysql/var/rw/icinga.cmd" ICINGA_PGSQL_CMD=${ICINGA_PGSQL_CMD:-"/usr/local/icinga-pgsql/var/rw/icinga.cmd"} LOCKFILE=${LOCKFILE:-/var/lock/subsys/$PROG} -PIDFILE=${PIDFILE:-/var/lock/subsys/$PROG/$PROD.pid} +PIDFILE=${PIDFILE:-/var/lock/subsys/$PROG/$PROG.pid} RETVAL=0 diff --git a/.vagrant-puppet/files/etc/motd b/.vagrant-puppet/files/etc/motd index 5b5c7973f..7e6677c20 100644 --- a/.vagrant-puppet/files/etc/motd +++ b/.vagrant-puppet/files/etc/motd @@ -1,17 +1,16 @@ - ______ ___ -/\__ _\ __ /'___`\ -\/_/\ \/ ___ /\_\ ___ __ __ /\_\ /\ \ - \ \ \ /'___\/\ \ /' _ `\ /'_ `\ /'__`\ \/_/// /__ + ______ ___ +/\__ _\ __ /'___`\ +\/_/\ \/ ___ /\_\ ___ __ __ /\_\ /\ \ + \ \ \ /'___\/\ \ /' _ `\ /'_ `\ /'__`\ \/_/// /__ \_\ \__/\ \__/\ \ \/\ \/\ \/\ \L\ \/\ \L\.\_ // /_\ \ /\_____\ \____\\ \_\ \_\ \_\ \____ \ \__/.\_\ /\______/ - \/_____/\/____/ \/_/\/_/\/_/\/___L\ \/__/\/_/ \/_____/ - /\____/ - \_/__/ - __ __ __ -/\ \ __/\ \ /\ \ -\ \ \/\ \ \ \ __\ \ \____ - \ \ \ \ \ \ \ /'__`\ \ '__`\ + \/_____/\/____/ \/_/\/_/\/_/\/___L\ \/__/\/_/ \/_____/ + /\____/ + \_/__/ + __ __ __ +/\ \ __/\ \ /\ \ +\ \ \/\ \ \ \ __\ \ \____ + \ \ \ \ \ \ \ /'__`\ \ '__`\ \ \ \_/ \_\ \/\ __/\ \ \L\ \ \ `\___x___/\ \____\\ \_,__/ - '\/__//__/ \/____/ \/___/ - + '\/__//__/ \/____/ \/___/ diff --git a/config/config.ini.in b/config/config.ini.in index e19f2fa53..0203511d4 100644 --- a/config/config.ini.in +++ b/config/config.ini.in @@ -6,9 +6,6 @@ indexController = "dashboard" dateFormat = "d/m/Y" timeFormat = "g:i A" -; The directory that contains the symlinks to all enabled directories -moduleFolder = "@icingaweb_config_path@/enabledModules" - ; Contains the directories that will be searched for available modules. Modules that ; don't exist in these directories can still be symlinked in the module folder, but ; won't show up in the list of disabled modules diff --git a/library/Icinga/Application/ApplicationBootstrap.php b/library/Icinga/Application/ApplicationBootstrap.php index 13845c4d6..24244db32 100644 --- a/library/Icinga/Application/ApplicationBootstrap.php +++ b/library/Icinga/Application/ApplicationBootstrap.php @@ -31,9 +31,12 @@ namespace Icinga\Application; use DateTimeZone; use Exception; +use Zend_Config; +use Icinga\Application\Logger; use Icinga\Application\Modules\Manager as ModuleManager; use Icinga\Data\ResourceFactory; use Icinga\Exception\ConfigurationError; +use Icinga\Exception\NotReadableError; use Icinga\Util\DateTimeFactory; use Icinga\Util\Translator; @@ -78,7 +81,7 @@ abstract class ApplicationBootstrap /** * Config object * - * @var Config + * @var Zend_Config */ protected $config; @@ -256,6 +259,13 @@ abstract class ApplicationBootstrap public static function start($configDir) { $application = new static($configDir); + // TODO(el): This is subject to change (Feature #5683) + date_default_timezone_set('UTC'); + // Log to the System Log + Logger::create(new Zend_Config(array( + 'type' => 'syslog', + 'application' => 'Icinga Web' + ))); $application->bootstrap(); return $application; } @@ -309,7 +319,12 @@ abstract class ApplicationBootstrap $this->moduleManager = new ModuleManager( $this, $this->configDir . '/enabledModules', - explode(':', $this->config->global->get('modulePath', ICINGA_APPDIR . '/../modules')) + explode( + ':', + $this->config->global !== null + ? $this->config->global->get('modulePath', ICINGA_APPDIR . '/../modules') + : ICINGA_APPDIR . '/../modules' + ) ); return $this; } @@ -323,21 +338,26 @@ abstract class ApplicationBootstrap { try { $this->moduleManager->loadEnabledModules(); - } catch (Exception $e) { + } catch (NotReadableError $e) { Logger::exception(new Exception('Cannot load enabled modules. An exception was thrown:', 0, $e)); } return $this; } /** - * Load configuration + * Load application configuration * * @return self */ protected function loadConfig() { Config::$configDir = $this->configDir; - $this->config = Config::app(); + try { + $this->config = Config::app(); + } catch (NotReadableError $e) { + Logger::exception(new Exception('Cannot load application configuration. An exception was thrown:', 0, $e)); + $this->config = new Zend_Config(array()); + } return $this; } @@ -361,19 +381,28 @@ abstract class ApplicationBootstrap */ protected function setupLogger() { - Logger::create($this->config->logging); + if ($this->config->logging !== null) { + Logger::create($this->config->logging); + } return $this; } /** - * Setup factories that provide access to the resources + * Set up the resource factory * * @return self */ protected function setupResourceFactory() { - $config = Config::app('resources'); - ResourceFactory::setConfig($config); + try { + $config = Config::app('resources'); + ResourceFactory::setConfig($config); + } catch (NotReadableError $e) { + Logger::exception( + new Exception('Cannot load resource configuration. An exception was thrown:', 0, $e) + ); + } + return $this; } @@ -385,7 +414,7 @@ abstract class ApplicationBootstrap */ protected function setupTimezone() { - $timeZoneString = $this->config->global->get('timezone', 'UTC'); + $timeZoneString = $this->config->global !== null ? $this->config->global->get('timezone', 'UTC') : 'UTC'; try { $tz = new DateTimeZone($timeZoneString); } catch (Exception $e) { @@ -406,7 +435,10 @@ abstract class ApplicationBootstrap protected function setupInternationalization() { try { - Translator::setupLocale($this->config->global->get('language', Translator::DEFAULT_LOCALE)); + Translator::setupLocale( + $this->config->global !== null ? $this->config->global->get('language', Translator::DEFAULT_LOCALE) + : Translator::DEFAULT_LOCALE + ); } catch (Exception $error) { Logger::info($error->getMessage()); } diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php index 277f6c0d7..a7b5e0bfb 100644 --- a/library/Icinga/Application/Config.php +++ b/library/Icinga/Application/Config.php @@ -29,8 +29,9 @@ namespace Icinga\Application; -use \Icinga\Exception\ProgrammingError; -use \Zend_Config_Ini; +use Zend_Config_Ini; +use Icinga\Exception\NotReadableError; +use Icinga\Exception\ProgrammingError; /** * Global registry of application and module configuration. @@ -68,22 +69,25 @@ class Config extends Zend_Config_Ini /** * Load configuration from the config file $filename * - * @param string $filename The filename to parse + * @param string $filename The filename to parse - * @see Zend_Config_Ini::__construct - * @throws Exception When the file can't be read + * @throws NotReadableError When the file does not exist or cannot be read */ public function __construct($filename) { - if (!@is_readable($filename)) { - throw new \Exception('Cannot read config file: ' . $filename); + $canonical = realpath($filename); + if ($canonical === false) { + throw new NotReadableError('Cannot read config file "' . $filename . '". Config file does not exist'); + } + if (!is_readable($canonical)) { + throw new NotReadableError('Cannot read config file "' . $filename . '". Permission denied'); }; - $this->configFile = $filename; + $this->configFile = $canonical; $section = null; $options = array( 'allowModifications' => true ); - parent::__construct($filename, $section, $options); + parent::__construct($canonical, $section, $options); } /** @@ -99,7 +103,7 @@ class Config extends Zend_Config_Ini { if (!isset(self::$app[$configname]) || $fromDisk) { $filename = self::$configDir . '/' . $configname . '.ini'; - self::$app[$configname] = new Config(realpath($filename)); + self::$app[$configname] = new Config($filename); } return self::$app[$configname]; } diff --git a/library/Icinga/Application/Logger.php b/library/Icinga/Application/Logger.php index 5bb92e905..bf26d142c 100644 --- a/library/Icinga/Application/Logger.php +++ b/library/Icinga/Application/Logger.php @@ -40,6 +40,8 @@ use Icinga\Util\File; /** * Zend_Log wrapper + * + * TODO(el): This is subject to change (Feature #5683) */ class Logger { @@ -64,17 +66,6 @@ class Logger */ private static $instance; - /** - * Format for logging exceptions - */ - const LOG_EXCEPTION_FORMAT = <<<'EOD' -%s: %s - -Stacktrace ----------- -%s -EOD; - /** * Create a new Logger * @@ -135,17 +126,17 @@ EOD; default: throw new ConfigurationError('Logger configuration defines an invalid log type "' . $type . '"'); } - if (($priority = $config->priority) === null) { + if ($config->priority === null) { $priority = Zend_Log::WARN; } else { - $priority = (int) $priority; + $priority = (int) $config->priority; } $writer->addFilter(new Zend_Log_Filter_Priority($priority)); $this->logger->addWriter($writer); $this->writers[] = $writer; } catch (Zend_Log_Exception $e) { throw new ConfigurationError( - 'Cannot not add log writer of type "' . $type . '". An exception was thrown: '. $e->getMessage() + 'Cannot not add log writer of type "' . $type . '". An exception was thrown: ', 0, $e ); } } @@ -235,19 +226,19 @@ EOD; /** * Log a exception at a priority * - * @param Exception $e Exception to log - * @param int $priority Priority of message + * @param Exception $e Exception to log + * @param int $priority Priority of message */ public static function exception(Exception $e, $priority = Zend_Log::ERR) { $message = array(); do { $message[] = self::formatMessage( - array(self::LOG_EXCEPTION_FORMAT, get_class($e), $e->getMessage(), $e->getTraceAsString()) + array('%s in %s:%d %s', get_class($e), $e->getFile(), $e->getLine(), $e->getMessage()) ); } while($e = $e->getPrevious()); self::log( - implode(PHP_EOL, $message), + implode(' ', $message), $priority ); } diff --git a/library/Icinga/Application/Modules/Manager.php b/library/Icinga/Application/Modules/Manager.php index 356e3458e..ff18f677f 100644 --- a/library/Icinga/Application/Modules/Manager.php +++ b/library/Icinga/Application/Modules/Manager.php @@ -37,6 +37,7 @@ use Icinga\Data\DataArray\Query as ArrayQuery; use Icinga\Exception\ConfigurationError; use Icinga\Exception\SystemPermissionException; use Icinga\Exception\ProgrammingError; +use Icinga\Exception\NotReadableError; /** * Module manager that handles detecting, enabling and disabling of modules @@ -99,22 +100,10 @@ class Manager * the given path * @param array $availableDirs Installed modules location **/ - public function __construct($app, $enabledDir = null, array $availableDirs = array()) + public function __construct($app, $enabledDir, array $availableDirs) { $this->app = $app; - if (empty($availableDirs)) { - $availableDirs = array(realpath(ICINGA_APPDIR . '/../modules')); - } else { - foreach($availableDirs as $key => $dir) { - $dir[$key] = realpath($dir); - } - } $this->modulePaths = $availableDirs; - if ($enabledDir === null) { - $enabledDir = $this->app->getConfigDir() . '/enabledModules'; - } - $enabledDir = realpath($enabledDir); - $this->enableDir = $enabledDir; } @@ -134,54 +123,59 @@ class Manager * * Update the internal $enabledDirs property with the enabled modules. * - * @throws ConfigurationError If module dir is not a directory or not readable + * @throws ConfigurationError If module dir does not exist, is not a directory or not readable */ private function detectEnabledModules() { + $canonical = $this->enableDir; + if ($canonical === false) { + throw new NotReadableError( + 'Cannot read enabled modules. Module directory "' . $this->enableDir . '" does not exist' + ); + } if (!is_dir($this->enableDir)) { - throw new ConfigurationError( - 'Could not read enabled modules: Module directory is not a directory: ' . $this->enableDir + throw new NotReadableError( + 'Cannot read enabled modules. Module directory "' . $this->enableDir . '" is not a directory' ); } - if (!is_readable($this->enableDir)) { - throw new ConfigurationError( - 'Could not read enabled modules: Module directory is not readable: ' . $this->enableDir + throw new NotReadableError( + 'Cannot read enabled modules. Module directory "' . $this->enableDir . '" is not readable' ); } + if (($dh = opendir($canonical)) !== false) { + $this->enabledDirs = array(); + while (($file = readdir($dh)) !== false) { - $fh = opendir($this->enableDir); + if ($file[0] === '.' || $file === 'README') { + continue; + } - $this->enabledDirs = array(); - while (false !== ($file = readdir($fh))) { + $link = $this->enableDir . '/' . $file; + if (! is_link($link)) { + Logger::warn( + 'Found invalid module in enabledModule directory "%s": "%s" is not a symlink', + $this->enableDir, + $link + ); + continue; + } - if ($file[0] === '.' || $file === 'README') { - continue; + $dir = realpath($link); + if (!file_exists($dir) || !is_dir($dir)) { + Logger::warn( + 'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"', + $this->enableDir, + $link, + $dir + ); + continue; + } + + $this->enabledDirs[$file] = $dir; + ksort($this->enabledDirs); } - - $link = $this->enableDir . '/' . $file; - if (! is_link($link)) { - Logger::warn( - 'Found invalid module in enabledModule directory "%s": "%s" is not a symlink', - $this->enableDir, - $link - ); - continue; - } - - $dir = realpath($link); - if (!file_exists($dir) || !is_dir($dir)) { - Logger::warn( - 'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"', - $this->enableDir, - $link, - $dir - ); - continue; - } - - $this->enabledDirs[$file] = $dir; - ksort($this->enabledDirs); + closedir($dh); } } @@ -517,29 +511,37 @@ class Manager public function detectInstalledModules() { foreach ($this->modulePaths as $basedir) { - if (!file_exists($basedir)) { - Logger::warn('Module path "%s" does not exist.', $basedir); + $canonical = realpath($basedir); + if ($canonical === false) { + Logger::warn('Module path "%s" does not exist', $basedir); continue; } - $fh = opendir($basedir); - if ($fh === false) { - return $this; + if (!is_dir($canonical)) { + Logger::err('Module path "%s" is not a directory', $canonical); + continue; } - while ($name = readdir($fh)) { - if ($name[0] === '.') { - continue; - } - if (is_dir($basedir . '/' . $name)) { - if (! array_key_exists($name, $this->installedBaseDirs)) { - $this->installedBaseDirs[$name] = $basedir . '/' . $name; - } else { - Logger::warn( - 'Module "%s" already exists in installation path "%s" and is ignored.', - $basedir . '/' . $name, - $this->installedBaseDirs[$name] - ); + if (!is_readable($canonical)) { + Logger::err('Module path "%s" is not readable', $canonical); + continue; + } + if (($dh = opendir($canonical)) !== false) { + while (($file = readdir($dh)) !== false) { + if ($file[0] === '.') { + continue; + } + if (is_dir($canonical . '/' . $file)) { + if (! array_key_exists($file, $this->installedBaseDirs)) { + $this->installedBaseDirs[$file] = $canonical . '/' . $file; + } else { + Logger::warn( + 'Module "%s" already exists in installation path "%s" and is ignored.', + $canonical . '/' . $file, + $this->installedBaseDirs[$file] + ); + } } } + closedir($dh); } } ksort($this->installedBaseDirs); diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 4e6aa7f5a..1670d2d3a 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -457,25 +457,7 @@ class Module } $this->registerLocales() - ->registerRoutes() - ->registerMenuEntries(); - return $this; - } - - /** - * Register menu entries - * - * @return self - */ - protected function registerMenuEntries() - { - $cfg = $this->app - ->getConfig() - ->module($this->name, 'menu'); - $view = $this->app->getViewRenderer(); - if ($cfg) { - $view->view->navigation = $cfg->merge($view->view->navigation); - } + ->registerRoutes(); return $this; } diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index d3ae8016d..db5df7ff9 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -44,6 +44,7 @@ use \Zend_Controller_Front; use Icinga\Application\Logger; use Icinga\Authentication\Manager as AuthenticationManager; use Icinga\Exception\ConfigurationError; +use Icinga\Exception\NotReadableError; use Icinga\User; use Icinga\Web\Request; use Icinga\Web\View; @@ -208,7 +209,15 @@ class Web extends ApplicationBootstrap */ private function setupUser() { - $authenticationManager = AuthenticationManager::getInstance(); + try { + $config = Config::app('authentication'); + } catch (NotReadableError $e) { + Logger::exception( + new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e) + ); + $config = null; + } + $authenticationManager = AuthenticationManager::getInstance($config); if ($authenticationManager->isAuthenticated() === true) { $this->user = $authenticationManager->getUser(); } @@ -279,11 +288,10 @@ class Web extends ApplicationBootstrap $view->view->setEncoding('UTF-8'); $view->view->headTitle()->prepend( - $this->getConfig()->{'global'}->get('project', 'Icinga') + $this->config->global !== null ? $this->config->global->get('project', 'Icinga') : 'Icinga' ); $view->view->headTitle()->setSeparator(' :: '); - $view->view->navigation = $this->getConfig()->app('menu'); $this->viewRenderer = $view; diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index f2bd15b13..6c8821061 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -36,12 +36,12 @@ use Icinga\Web\Session; use Icinga\Data\ResourceFactory; use Icinga\Application\Logger; use Icinga\Exception\ConfigurationError; +use Icinga\Exception\NotReadableError; use Icinga\Application\Config as IcingaConfig; use Icinga\Authentication\Backend\DbUserBackend; use Icinga\Authentication\Backend\LdapUserBackend; use Icinga\User\Preferences; use Icinga\User\Preferences\PreferencesStore; -use Icinga\Exception\NotReadableError; /** * The authentication manager allows to identify users and @@ -90,8 +90,10 @@ class Manager **/ private function __construct(Zend_Config $config = null) { - $this->config = $config === null ? IcingaConfig::app('authentication') : $config; - $this->setupBackends($this->config); + if ($config !== null) { + $this->setupBackends($config); + $this->config = $config; + } } /** diff --git a/library/Icinga/Web/Menu.php b/library/Icinga/Web/Menu.php index 34e5d40d0..e904ff3a0 100644 --- a/library/Icinga/Web/Menu.php +++ b/library/Icinga/Web/Menu.php @@ -31,7 +31,8 @@ namespace Icinga\Web; use Icinga\Application\Config; use Icinga\Application\Icinga; -use Icinga\Web\MenuItem; +use Icinga\Application\Logger; +use Icinga\Exception\NotReadableError; class Menu extends MenuItem { @@ -43,12 +44,22 @@ class Menu extends MenuItem public static function fromConfig() { $menu = new static('menu'); $manager = Icinga::app()->getModuleManager(); - $menuConfigs = array(Config::app('menu')); - foreach ($manager->listEnabledModules() as $moduleName) { - $moduleMenuConfig = Config::module($moduleName, 'menu'); - if ($moduleMenuConfig) { - $menuConfigs[] = $moduleMenuConfig; + try { + $menuConfigs = array(Config::app('menu')); + } catch (NotReadableError $e) { + Logger::exception($e); + $menuConfigs = array(); + } + try { + + foreach ($manager->listEnabledModules() as $moduleName) { + $moduleMenuConfig = Config::module($moduleName, 'menu'); + if ($moduleMenuConfig) { + $menuConfigs[] = $moduleMenuConfig; + } } + } catch (NotReadableError $e) { + Logger::exception($e); } return $menu->loadMenuItems($menu->flattenConfigs($menuConfigs)); }