/** * Create generator_dir where all feature and context will be saved. * * @return string * @throws \coding_exception * @throws \invalid_dataroot_permissions */ public static function get_tool_dir() { $dir = self::get_performance_dir() . DIRECTORY_SEPARATOR . 'sitegenerator'; // Create dir if required. if (!is_dir($dir)) { make_writable_directory($dir, true); } return $dir; }
/** * Return directory in which performance data is saved. * * @static * @param bool $create if ture then it will also create directory if not present. * @return string */ public static function get_performance_dir($create = false) { $datapath = self::get_data_path(); // Create dir if required. if ($create && !is_dir($datapath)) { make_writable_directory($datapath, true); } if (!is_writeable($datapath)) { self::performance_exception("Data directory is not writable."); } return $datapath; }
/** * Return directory in which performance data is saved. * * @static * @param bool $create if ture then it will also create directory if not present. * @return string */ public static function get_performance_dir($create = false) { global $CFG; if (empty($CFG->performance_dataroot)) { self::performance_exception("\$CFG->performance_dataroot is not set."); } $dir = $CFG->performance_dataroot; // Create dir if required. if ($create && !is_dir($dir)) { make_writable_directory($dir, true); } return $dir; }
/** * Saves the current configuration. */ protected function config_save() { global $CFG; $cachefile = self::get_config_file_path(); $directory = dirname($cachefile); if ($directory !== $CFG->dataroot && !file_exists($directory)) { $result = make_writable_directory($directory, false); if (!$result) { throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory.'); } } if (!file_exists($directory) || !is_writable($directory)) { throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable.'); } // Prepare a configuration array to store. $configuration = array(); $configuration['stores'] = $this->configstores; $configuration['modemappings'] = $this->configmodemappings; $configuration['definitions'] = $this->configdefinitions; $configuration['definitionmappings'] = $this->configdefinitionmappings; $configuration['locks'] = $this->configlocks; // Prepare the file content. $content = "<?php defined('MOODLE_INTERNAL') || die();\n \$configuration = " . var_export($configuration, true) . ";"; // We need to create a temporary cache lock instance for use here. Remember we are generating the config file // it doesn't exist and thus we can't use the normal API for this (it'll just try to use config). $lockconf = reset($this->configlocks); if ($lockconf === false) { debugging('Your cache configuration file is out of date and needs to be refreshed.', DEBUG_DEVELOPER); // Use the default $lockconf = array('name' => 'cachelock_file_default', 'type' => 'cachelock_file', 'dir' => 'filelocks', 'default' => true); } $factory = cache_factory::instance(); $locking = $factory->create_lock_instance($lockconf); if ($locking->lock('configwrite', 'config', true)) { // Its safe to use w mode here because we have already acquired the lock. $handle = fopen($cachefile, 'w'); fwrite($handle, $content); fflush($handle); fclose($handle); $locking->unlock('configwrite', 'config'); } else { throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Unable to open the cache config file.'); } }
/** * Init session handler. */ public function init() { if (preg_match('/^[0-9]+;/', $this->sessiondir)) { throw new exception('sessionhandlerproblem', 'error', '', null, 'Multilevel session directories are not supported'); } // Make sure session directory exists and is writable. make_writable_directory($this->sessiondir, false); if (!is_writable($this->sessiondir)) { throw new exception('sessionhandlerproblem', 'error', '', null, 'Session directory is not writable'); } // Need to disable debugging since disk_free_space() // will fail on very large partitions (see MDL-19222). $freespace = @disk_free_space($this->sessiondir); if (!($freespace > 2048) and $freespace !== false) { throw new exception('sessiondiskfull', 'error'); } // NOTE: we cannot set any lock acquiring timeout here - bad luck. ini_set('session.save_handler', 'files'); ini_set('session.save_path', $this->sessiondir); }
/** * Checks to make sure that the path for the file cache exists. * * @return bool * @throws coding_exception */ protected function ensure_path_exists() { if (!is_writable($this->path)) { if ($this->custompath && !$this->autocreate) { throw new coding_exception('File store path does not exist. It must exist and be writable by the web server.'); } if (!make_writable_directory($this->path, false)) { throw new coding_exception('File store path does not exist and can not be created.'); } } return true; }
/** * Checks to make sure that the path for the file cache exists. * * @return bool * @throws coding_exception */ protected function ensure_path_exists() { global $CFG; if (!is_writable($this->path)) { if ($this->custompath && !$this->autocreate) { throw new coding_exception('File store path does not exist. It must exist and be writable by the web server.'); } $createdcfg = false; if (!isset($CFG)) { // This can only happen during destruction of objects. // A cache is being used within a destructor, php is ending a request and $CFG has // already being cleaned up. // Rebuild $CFG with directory permissions just to complete this write. $CFG = $this->cfg; $createdcfg = true; } if (!make_writable_directory($this->path, false)) { throw new coding_exception('File store path does not exist and can not be created.'); } if ($createdcfg) { // We re-created it so we'll clean it up. unset($CFG); } } return true; }
/** * Tests application cache event invalidation over a distributed setup. */ public function test_distributed_application_event_invalidation() { global $CFG; // This is going to be an intense wee test. // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally // check that it is not picked up. $instance = cache_config_phpunittest::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent'))); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); cache_helper::invalidate_by_event('crazyevent', array('testkey1')); $this->assertFalse($cache->get('testkey1')); // OK data added, data invalidated, and invalidation time has been set. // Now we need to manually add back the data and adjust the invalidation time. $hash = md5(cache_store::MODE_APPLICATION . '/phpunit/eventinvalidationtest/' . $CFG->wwwroot . 'phpunit'); $timefile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las/lastinvalidation-{$hash}.cache"; // Make sure the file is correct. $this->assertTrue(file_exists($timefile)); $timecont = serialize(cache::now() - 60); // Back 60sec in the past to force it to re-invalidate. make_writable_directory(dirname($timefile)); file_put_contents($timefile, $timecont); $this->assertTrue(file_exists($timefile)); $datafile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes/testkey1-{$hash}.cache"; $datacont = serialize("test data 1"); make_writable_directory(dirname($datafile)); file_put_contents($datafile, $datacont); $this->assertTrue(file_exists($datafile)); // Test 1: Rebuild without the event and test its there. cache_factory::reset(); $instance = cache_config_phpunittest::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true)); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertEquals('test data 1', $cache->get('testkey1')); // Test 2: Rebuild and test the invalidation of the event via the invalidation cache. cache_factory::reset(); $instance = cache_config_phpunittest::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent'))); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertFalse($cache->get('testkey1')); }
public function test_validate_target_location() { $fixtures = __DIR__ . '/fixtures/update_validator'; $validator = testable_core_update_validator::instance($fixtures . '/installed', array('greenbar/' => true, 'greenbar/version.php' => true, 'greenbar/index.php' => true, 'greenbar/lang/' => true, 'greenbar/lang/en/' => true, 'greenbar/lang/en/local_greenbar.php' => true)); $validator->assert_plugin_type('local'); $validator->assert_moodle_version('2013031400.00'); $this->assertTrue($validator->execute()); $this->assertFalse($this->has_message($validator->get_messages(), $validator::WARNING, 'targetexists', $validator->get_plugintype_location('local') . '/greenbar')); $typeroot = $validator->get_plugintype_location('local'); make_writable_directory($typeroot . '/greenbar'); $this->assertTrue($validator->execute()); $this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'targetexists', $validator->get_plugintype_location('local') . '/greenbar')); remove_dir($typeroot . '/greenbar'); file_put_contents($typeroot . '/greenbar', 'This file occupies a place where a plugin should land.'); $this->assertFalse($validator->execute()); $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'targetnotdir', $validator->get_plugintype_location('local') . '/greenbar')); unlink($typeroot . '/greenbar'); $this->assertTrue($validator->execute()); $validator = testable_core_update_validator::instance($fixtures . '/plugindir', array('foobar/' => true, 'foobar/version.php' => true, 'foobar/index.php' => true, 'foobar/lang/' => true, 'foobar/lang/en/' => true, 'foobar/lang/en/local_foobar.php' => true)); $validator->assert_plugin_type('local'); $validator->assert_moodle_version('2013031400.00'); $this->assertTrue($validator->execute()); $this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'pathwritable', $validator->get_plugintype_location('local'))); }
/** * Create a directory under localcachedir and make sure it is writable. * The files in this directory MUST NOT change, use revisions or content hashes to * work around this limitation - this means you can only add new files here. * * The content of this directory gets purged automatically on all cluster nodes * after calling purge_all_caches() before new data is written to this directory. * * Note: this local cache directory does not need to be shared by cluster nodes. * * @param string $directory the relative path of the directory to be created under $CFG->localcachedir * @param bool $exceptiononerror throw exception if error encountered * @return string|false Returns full path to directory if successful, false if not; may throw exception */ function make_localcache_directory($directory, $exceptiononerror = true) { global $CFG; make_writable_directory($CFG->localcachedir, $exceptiononerror); if ($CFG->localcachedir !== "{$CFG->dataroot}/localcache") { protect_directory($CFG->localcachedir); } else { protect_directory($CFG->dataroot); } if (!isset($CFG->localcachedirpurged)) { $CFG->localcachedirpurged = 0; } $timestampfile = "{$CFG->localcachedir}/.lastpurged"; if (!file_exists($timestampfile)) { touch($timestampfile); @chmod($timestampfile, $CFG->filepermissions); } else { if (filemtime($timestampfile) < $CFG->localcachedirpurged) { // This means our local cached dir was not purged yet. remove_dir($CFG->localcachedir, true); if ($CFG->localcachedir !== "{$CFG->dataroot}/localcache") { protect_directory($CFG->localcachedir); } touch($timestampfile); @chmod($timestampfile, $CFG->filepermissions); clearstatcache(); } } if ($directory === '') { return $CFG->localcachedir; } return make_writable_directory("{$CFG->localcachedir}/{$directory}", $exceptiononerror); }
/** * Create a directory under cachedir and make sure it is writable. * * @param string $directory the full path of the directory to be created under $CFG->cachedir * @param bool $exceptiononerror throw exception if error encountered * @return string|false Returns full path to directory if successful, false if not; may throw exception */ function make_cache_directory($directory, $exceptiononerror = true) { global $CFG; protect_directory($CFG->cachedir); return make_writable_directory("{$CFG->cachedir}/{$directory}", $exceptiononerror); }
/** * Makes sure all temp directories exist and are writable. */ protected function init_temp_directories() { make_writable_directory($this->temproot . '/distfiles'); make_writable_directory($this->temproot . '/archive'); }
/** ERROR HANDLING **/ function log_to_file($info, $notused = null) { try { $logdir = get_config('rcommon', 'data_store_log'); $tracer = get_config('rcommon', 'tracer'); if ($tracer == 'checked' && !empty($logdir)) { $logdir = make_writable_directory($logdir . '/log_rcommon', false); if ($handle = @fopen($logdir . "/LogRcommon.log", "a")) { $content = "\r\n" . date("Y-m-d H:i:s") . " - Data: " . $info; @fwrite($handle, $content); @fclose($handle); } } } catch (Exception $e) { return; } }
/** * Return path for the final testplan path, where all file related to test plan will be stored. * * @return string */ public static function get_final_testplan_path() { $dir = self::get_tool_dir() . DIRECTORY_SEPARATOR . "moodle_testplan"; // Create directory if not exist. if (!is_dir($dir)) { make_writable_directory($dir, true); } return $dir; }
/** * @return string|bool */ public function get_dirpath() { global $CFG; if ($this->_dirpath === null) { $sep = DIRECTORY_SEPARATOR; $this->_dirpath = make_writable_directory("{$CFG->dataroot}{$sep}mhaairs", false); } return $this->_dirpath; }
/** * Returns the behat config file path used by the steps definition list * * @return string */ public static function get_steps_list_config_filepath() { global $USER; // We don't cygwin-it as it is called using exec() which uses cmd.exe. $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id; make_writable_directory($userdir); return $userdir . '/behat.yml'; }
/** * Returns the expected path to the configuration file. * * We override this function to add handling for $CFG->altcacheconfigpath. * We want to support it so that people can run unit tests against alternative cache setups. * However we don't want to ever make changes to the file at $CFG->altcacheconfigpath so we * always use dataroot and copy the alt file there as required. * * @throws cache_exception * @return string The absolute path */ protected static function get_config_file_path() { global $CFG; // We always use this path. $configpath = $CFG->dataroot . '/muc/config.php'; if (!empty($CFG->altcacheconfigpath)) { // No need to check we are within a test here, this is the cache config class that gets used // only when one of those is true. if (!defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') || !TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) { // TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH has not being defined or is false, we want to use the default. return $configpath; } $path = $CFG->altcacheconfigpath; if (is_dir($path) && is_writable($path)) { // Its a writable directory, thats fine. Convert it to a file. $path = $CFG->altcacheconfigpath . '/cacheconfig.php'; } if (is_readable($path)) { $directory = dirname($configpath); if ($directory !== $CFG->dataroot && !file_exists($directory)) { $result = make_writable_directory($directory, false); if (!$result) { throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory. Check the permissions on your moodledata directory.'); } } // We don't care that this fails but we should let the developer know. if (!is_readable($configpath) && !@copy($path, $configpath)) { debugging('Failed to copy alt cache config file to required location'); } } } // We always use the dataroot location. return $configpath; }
/** * Tests application cache event invalidation over a distributed setup. */ public function test_distributed_application_event_invalidation() { global $CFG; // This is going to be an intense wee test. // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally // check that it is not picked up. $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent'))); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); cache_helper::invalidate_by_event('crazyevent', array('testkey1')); $this->assertFalse($cache->get('testkey1')); // OK data added, data invalidated, and invalidation time has been set. // Now we need to manually add back the data and adjust the invalidation time. $hash = md5(cache_store::MODE_APPLICATION . '/phpunit/eventinvalidationtest/' . $CFG->wwwroot . 'phpunit'); $timefile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-{$hash}.cache"; // Make sure the file is correct. $this->assertTrue(file_exists($timefile)); $timecont = serialize(cache::now() - 60); // Back 60sec in the past to force it to re-invalidate. make_writable_directory(dirname($timefile)); file_put_contents($timefile, $timecont); $this->assertTrue(file_exists($timefile)); $datafile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-{$hash}.cache"; $datacont = serialize("test data 1"); make_writable_directory(dirname($datafile)); file_put_contents($datafile, $datacont); $this->assertTrue(file_exists($datafile)); // Test 1: Rebuild without the event and test its there. cache_factory::reset(); $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true)); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertEquals('test data 1', $cache->get('testkey1')); // Test 2: Rebuild and test the invalidation of the event via the invalidation cache. cache_factory::reset(); $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent'))); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertFalse($cache->get('testkey1')); // Test 3: Verify that an existing lastinvalidation cache file is updated when needed. // Make a new cache class. This should should invalidate testkey2. $cache = cache::make('phpunit', 'eventinvalidationtest'); // Timestamp should have updated to cache::now(). $this->assertEquals(cache::now(), $cache->get('lastinvalidation')); // Set testkey2 data. $cache->set('testkey2', 'test data 2'); // Backdate the event invalidation time by 30 seconds. $invalidationcache = cache::make('core', 'eventinvalidation'); $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30)); // Lastinvalidation should already be cache::now(). $this->assertEquals(cache::now(), $cache->get('lastinvalidation')); // Set it to 15 seconds ago so that we know if it changes. $cache->set('lastinvalidation', cache::now() - 15); // Make a new cache class. This should not invalidate anything. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation shouldn't change since it was already newer than invalidation event. $this->assertEquals(cache::now() - 15, $cache->get('lastinvalidation')); // Now set the event invalidation to newer than the lastinvalidation time. $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5)); // Make a new cache class. This should should invalidate testkey2. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation timestamp should have updated to cache::now(). $this->assertEquals(cache::now(), $cache->get('lastinvalidation')); // Now simulate a purge_by_event 5 seconds ago. $invalidationcache = cache::make('core', 'eventinvalidation'); $invalidationcache->set('crazyevent', array('purged' => cache::now() - 5)); // Set our lastinvalidation timestamp to 15 seconds ago. $cache->set('lastinvalidation', cache::now() - 15); // Make a new cache class. This should invalidate the cache. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation timestamp should have updated to cache::now(). $this->assertEquals(cache::now(), $cache->get('lastinvalidation')); }
/** * Install core moodle tables and initialize * @param float $version target version * @param bool $verbose * @return void, may throw exception */ function install_core($version, $verbose) { global $CFG, $DB; // We can not call purge_all_caches() yet, make sure the temp and cache dirs exist and are empty. remove_dir($CFG->cachedir.'', true); make_cache_directory('', true); remove_dir($CFG->localcachedir.'', true); make_localcache_directory('', true); remove_dir($CFG->tempdir.'', true); make_temp_directory('', true); remove_dir($CFG->dataroot.'/muc', true); make_writable_directory($CFG->dataroot.'/muc', true); try { core_php_time_limit::raise(600); print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml"); upgrade_started(); // we want the flag to be stored in config table ;-) // set all core default records and default settings require_once("$CFG->libdir/db/install.php"); xmldb_main_install(); // installs the capabilities too // store version upgrade_main_savepoint(true, $version, false); // Continue with the installation log_update_descriptions('moodle'); external_update_descriptions('moodle'); events_update_definition('moodle'); \core\task\manager::reset_scheduled_tasks_for_component('moodle'); message_update_providers('moodle'); \core\message\inbound\manager::update_handlers_for_component('moodle'); // Write default settings unconditionally admin_apply_default_settings(NULL, true); print_upgrade_part_end(null, true, $verbose); // Purge all caches. They're disabled but this ensures that we don't have any persistent data just in case something // during installation didn't use APIs. cache_helper::purge_all(); } catch (exception $ex) { upgrade_handle_exception($ex); } }
/** * Returns the behat config file path used by the steps definition list * * Note this can only be called from web-based scripts so it will return the * production dataroot not behat_dataroot. With this the steps definitions * list is accessible without having to install the behat test site. * * @return string */ public static function get_steps_list_config_filepath() { global $USER; $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id; make_writable_directory($userdir); return $userdir . '/behat.yml'; }
/** * Create a directory under cachedir and make sure it is writable. * * @param string $directory the full path of the directory to be created under $CFG->cachedir * @param bool $exceptiononerror throw exception if error encountered * @return string|false Returns full path to directory if successful, false if not; may throw exception */ function make_cache_directory($directory, $exceptiononerror = true) { global $CFG; if ($CFG->cachedir !== "{$CFG->dataroot}/cache") { check_dir_exists($CFG->cachedir, true, true); protect_directory($CFG->cachedir); } else { protect_directory($CFG->dataroot); } return make_writable_directory("{$CFG->cachedir}/{$directory}", $exceptiononerror); }
/** * Moves the given source into a new location recursively * * This is cross-device safe implementation to be used instead of the native rename() function. * See https://bugs.php.net/bug.php?id=54097 for more details. * * @param string $source full path to the existing directory * @param string $target full path to the new location of the directory */ public function move_directory($source, $target) { if (file_exists($target)) { throw new tool_installaddon_installer_exception('err_folder_already_exists', array('path' => $target)); } if (is_dir($source)) { $handle = opendir($source); } else { throw new tool_installaddon_installer_exception('err_no_such_folder', array('path' => $source)); } make_writable_directory($target); while ($filename = readdir($handle)) { $sourcepath = $source . '/' . $filename; $targetpath = $target . '/' . $filename; if ($filename === '.' or $filename === '..') { continue; } if (is_dir($sourcepath)) { $this->move_directory($sourcepath, $targetpath); } else { rename($sourcepath, $targetpath); } } closedir($handle); rmdir($source); clearstatcache(); }