/includes/common.php
PHP | 1537 lines | 1438 code | 67 blank | 32 comment | 162 complexity | 9cc57d5546ce8ef4ab25a605a1e15c74 MD5 | raw file
- <?php
- /*
- Copyright 2009-2011 Guillaume Boudreau, Andrew Hopkinson
- This file is part of Greyhole.
- Greyhole is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Greyhole is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Greyhole. If not, see <http://www.gnu.org/licenses/>.
- */
- define('PERF', 9);
- define('TEST', 8);
- define('DEBUG', 7);
- define('INFO', 6);
- define('WARN', 4);
- define('ERROR', 3);
- define('CRITICAL', 2);
- $action = 'initialize';
- date_default_timezone_set(date_default_timezone_get());
- set_error_handler("gh_error_handler");
- register_shutdown_function("gh_shutdown");
- umask(0);
- setlocale(LC_COLLATE, "en_US.UTF-8");
- setlocale(LC_CTYPE, "en_US.UTF-8");
- if (!defined('PHP_VERSION_ID')) {
- $version = explode('.', PHP_VERSION);
- define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
- }
- $constarray = get_defined_constants(true);
- foreach($constarray['user'] as $key => $val) {
- eval(sprintf('$_CONSTANTS[\'%s\'] = ' . (is_int($val) || is_float($val) ? '%s' : "'%s'") . ';', addslashes($key), addslashes($val)));
- }
- // Cached df results
- $last_df_time = 0;
- $last_dfs = array();
- $sleep_before_task = array();
- if (!isset($config_file)) {
- $config_file = '/etc/greyhole.conf';
- }
- if (!isset($smb_config_file)) {
- $smb_config_file = '/etc/samba/smb.conf';
- }
- $trash_share_names = array('Greyhole Attic', 'Greyhole Trash', 'Greyhole Recycle Bin');
- function recursive_include_parser($file) {
-
- $regex = '/^[ \t]*include[ \t]*=[ \t]*([^#\r\n]+)/im';
- $ok_to_execute = FALSE;
- if (is_array($file) && count($file) > 1) {
- $file = $file[1];
- }
- $file = trim($file);
- if (file_exists($file)) {
- if (is_executable($file)) {
- $perms = fileperms($file);
- // Not user-writable, or owned by root
- $ok_to_execute = !($perms & 0x0080) || fileowner($file) === 0;
- // Not group-writable, or group owner is root
- $ok_to_execute &= !($perms & 0x0010) || filegroup($file) === 0;
- // Not world-writable
- $ok_to_execute &= !($perms & 0x0002);
- if (!$ok_to_execute) {
- gh_log(WARN, "Config file '{$file}' is executable but file permissions are insecure, only the file's contents will be included.");
- }
- }
- $contents = $ok_to_execute ? shell_exec(escapeshellcmd($file)) : file_get_contents($file);
-
- return preg_replace_callback($regex, 'recursive_include_parser', $contents);
- } else {
- return false;
- }
- }
- function parse_config() {
- global $_CONSTANTS, $log_level, $storage_pool_drives, $shares_options, $minimum_free_space_pool_drives, $df_command, $config_file, $smb_config_file, $sticky_files, $db_options, $frozen_directories, $trash_share_names, $max_queued_tasks, $memory_limit, $delete_moves_to_trash, $greyhole_log_file, $email_to, $log_memory_usage, $check_for_open_files, $allow_multiple_sp_per_device, $df_cache_time;
- $deprecated_options = array(
- 'delete_moves_to_attic' => 'delete_moves_to_trash',
- 'storage_pool_directory' => 'storage_pool_drive',
- 'dir_selection_groups' => 'drive_selection_groups',
- 'dir_selection_algorithm' => 'drive_selection_algorithm'
- );
- $parsing_drive_selection_groups = FALSE;
- $shares_options = array();
- $storage_pool_drives = array();
- $frozen_directories = array();
- $config_text = recursive_include_parser($config_file);
-
- // Defaults
- $log_level = DEBUG;
- $greyhole_log_file = '/var/log/greyhole.log';
- $email_to = 'root';
- $log_memory_usage = FALSE;
- $check_for_open_files = TRUE;
- $allow_multiple_sp_per_device = FALSE;
- $df_cache_time = 15;
- $delete_moves_to_trash = TRUE;
- $memory_limit = '128M';
-
- foreach (explode("\n", $config_text) as $line) {
- if (preg_match("/^[ \t]*([^=\t]+)[ \t]*=[ \t]*([^#]+)/", $line, $regs)) {
- $name = trim($regs[1]);
- $value = trim($regs[2]);
- if ($name[0] == '#') {
- continue;
- }
-
- foreach ($deprecated_options as $old_name => $new_name) {
- if (mb_strpos($name, $old_name) !== FALSE) {
- $fixed_name = str_replace($old_name, $new_name, $name);
- #gh_log(WARN, "Deprecated option found in greyhole.conf: $name. You should change that to: $fixed_name");
- $name = $fixed_name;
- }
- }
- $parsing_drive_selection_groups = FALSE;
- switch($name) {
- case 'log_level':
- $log_level = $_CONSTANTS[$value];
- break;
- case 'delete_moves_to_trash': // or delete_moves_to_attic
- case 'log_memory_usage':
- case 'check_for_open_files':
- case 'allow_multiple_sp_per_device':
- global ${$name};
- ${$name} = trim($value) === '1' || mb_stripos($value, 'yes') !== FALSE || mb_stripos($value, 'true') !== FALSE;
- break;
- case 'storage_pool_drive': // or storage_pool_directory
- if (preg_match("/(.*) ?, ?min_free ?: ?([0-9]+) ?([gmk])b?/i", $value, $regs)) {
- $storage_pool_drives[] = trim($regs[1]);
- if (strtolower($regs[3]) == 'g') {
- $minimum_free_space_pool_drives[trim($regs[1])] = (float) trim($regs[2]);
- } else if (strtolower($regs[3]) == 'm') {
- $minimum_free_space_pool_drives[trim($regs[1])] = (float) trim($regs[2]) / 1024.0;
- } else if (strtolower($regs[3]) == 'k') {
- $minimum_free_space_pool_drives[trim($regs[1])] = (float) trim($regs[2]) / 1024.0 / 1024.0;
- }
- }
- break;
- case 'wait_for_exclusive_file_access':
- $shares = explode(',', str_replace(' ', '', $value));
- foreach ($shares as $share) {
- $shares_options[$share]['wait_for_exclusive_file_access'] = TRUE;
- }
- break;
- case 'sticky_files':
- $last_sticky_files_dir = trim($value, '/');
- $sticky_files[$last_sticky_files_dir] = array();
- break;
- case 'stick_into':
- $sticky_files[$last_sticky_files_dir][] = '/' . trim($value, '/');
- break;
- case 'frozen_directory':
- $frozen_directories[] = trim($value, '/');
- break;
- case 'memory_limit':
- ini_set('memory_limit',$value);
- $memory_limit = $value;
- break;
- case 'drive_selection_groups': // or dir_selection_groups
- if (preg_match("/(.+):(.*)/", $value, $regs)) {
- global $drive_selection_groups;
- $group_name = trim($regs[1]);
- $drive_selection_groups[$group_name] = array_map('trim', explode(',', $regs[2]));
- $parsing_drive_selection_groups = TRUE;
- }
- break;
- case 'drive_selection_algorithm': // or dir_selection_algorithm
- global $drive_selection_algorithm;
- $drive_selection_algorithm = DriveSelection::parse($value, @$drive_selection_groups);
- break;
- default:
- if (mb_strpos($name, 'num_copies') === 0) {
- $share = mb_substr($name, 11, mb_strlen($name)-12);
- if (mb_stripos($value, 'max') === 0) {
- $value = 9999;
- }
- $shares_options[$share]['num_copies'] = (int) $value;
- } else if (mb_strpos($name, 'delete_moves_to_trash') === 0) {
- $share = mb_substr($name, 22, mb_strlen($name)-23);
- $shares_options[$share]['delete_moves_to_trash'] = trim($value) === '1' || mb_strpos(strtolower(trim($value)), 'yes') !== FALSE || mb_strpos(strtolower(trim($value)), 'true') !== FALSE;
- } else if (mb_strpos($name, 'drive_selection_groups') === 0) { // or dir_selection_groups
- $share = mb_substr($name, 23, mb_strlen($name)-24);
- if (preg_match("/(.+):(.+)/", $value, $regs)) {
- $group_name = trim($regs[1]);
- $shares_options[$share]['drive_selection_groups'][$group_name] = array_map('trim', explode(',', $regs[2]));
- $parsing_drive_selection_groups = $share;
- }
- } else if (mb_strpos($name, 'drive_selection_algorithm') === 0) { // or dir_selection_algorithm
- $share = mb_substr($name, 26, mb_strlen($name)-27);
- if (!isset($shares_options[$share]['drive_selection_groups'])) {
- $shares_options[$share]['drive_selection_groups'] = @$drive_selection_groups;
- }
- $shares_options[$share]['drive_selection_algorithm'] = DriveSelection::parse($value, $shares_options[$share]['drive_selection_groups']);
- } else {
- global ${$name};
- if (is_numeric($value)) {
- ${$name} = (int) $value;
- } else {
- ${$name} = $value;
- }
- }
- }
- } else if ($parsing_drive_selection_groups !== FALSE) {
- $value = trim($line);
- if (strlen($value) == 0 || $value[0] == '#') {
- continue;
- }
- if (preg_match("/(.+):(.+)/", $value, $regs)) {
- $group_name = trim($regs[1]);
- $drives = array_map('trim', explode(',', $regs[2]));
- if (is_string($parsing_drive_selection_groups)) {
- $share = $parsing_drive_selection_groups;
- $shares_options[$share]['drive_selection_groups'][$group_name] = $drives;
- } else {
- $drive_selection_groups[$group_name] = $drives;
- }
- }
- }
- }
-
- if (is_array($storage_pool_drives) && count($storage_pool_drives) > 0) {
- $df_command = "df -k";
- foreach ($storage_pool_drives as $key => $sp_drive) {
- $df_command .= " " . escapeshellarg($sp_drive);
- $storage_pool_drives[$key] = '/' . trim($sp_drive, '/');
- }
- $df_command .= " 2>&1 | grep '%' | grep -v \"^df: .*: No such file or directory$\"";
- } else {
- gh_log(WARN, "You have no storage_pool_drive defined. Greyhole can't run.");
- return FALSE;
- }
- exec('testparm -s ' . escapeshellarg($smb_config_file) . ' 2> /dev/null', $config_text);
- foreach ($config_text as $line) {
- $line = trim($line);
- if (mb_strlen($line) == 0) { continue; }
- if ($line[0] == '[' && preg_match('/\[([^\]]+)\]/', $line, $regs)) {
- $share_name = $regs[1];
- }
- if (isset($share_name) && !isset($shares_options[$share_name]) && array_search($share_name, $trash_share_names) === FALSE) { continue; }
- if (isset($share_name) && preg_match('/^\s*path[ \t]*=[ \t]*(.+)$/i', $line, $regs)) {
- $shares_options[$share_name]['landing_zone'] = '/' . trim($regs[1], '/');
- $shares_options[$share_name]['name'] = $share_name;
- }
- }
- global $drive_selection_algorithm;
- if (isset($drive_selection_algorithm)) {
- foreach ($drive_selection_algorithm as $ds) {
- $ds->update();
- }
- } else {
- // Default drive_selection_algorithm
- $drive_selection_algorithm = DriveSelection::parse('most_available_space', null);
- }
- foreach ($shares_options as $share_name => $share_options) {
- if (array_search($share_name, $trash_share_names) !== FALSE) {
- global $trash_share;
- $trash_share = array('name' => $share_name, 'landing_zone' => $shares_options[$share_name]['landing_zone']);
- unset($shares_options[$share_name]);
- continue;
- }
- if ($share_options['num_copies'] > count($storage_pool_drives)) {
- $share_options['num_copies'] = count($storage_pool_drives);
- }
- if (!isset($share_options['landing_zone'])) {
- global $config_file, $smb_config_file;
- gh_log(WARN, "Found a share ($share_name) defined in $config_file with no path in $smb_config_file. Either add this share in $smb_config_file, or remove it from $config_file, then restart Greyhole.");
- return FALSE;
- }
- if (!isset($share_options['delete_moves_to_trash'])) {
- $share_options['delete_moves_to_trash'] = $delete_moves_to_trash;
- }
- if (isset($share_options['drive_selection_algorithm'])) {
- foreach ($share_options['drive_selection_algorithm'] as $ds) {
- $ds->update();
- }
- } else {
- $share_options['drive_selection_algorithm'] = $drive_selection_algorithm;
- }
- if (isset($share_options['drive_selection_groups'])) {
- unset($share_options['drive_selection_groups']);
- }
- $shares_options[$share_name] = $share_options;
-
- // Validate that the landing zone is NOT a subdirectory of a storage pool drive!
- foreach ($storage_pool_drives as $key => $sp_drive) {
- if (mb_strpos($share_options['landing_zone'], $sp_drive) === 0) {
- gh_log(CRITICAL, "Found a share ($share_name), with path " . $share_options['landing_zone'] . ", which is INSIDE a storage pool drive ($sp_drive). Share directories should never be inside a directory that you have in your storage pool.\nFor your shares to use your storage pool, you just need them to have 'vfs objects = greyhole' in their (smb.conf) config; their location on your file system is irrelevant.");
- }
- }
- }
-
- if (!isset($db_engine)) {
- $db_engine = 'mysql';
- } else {
- $db_engine = mb_strtolower($db_engine);
- }
- global ${"db_use_$db_engine"};
- ${"db_use_$db_engine"} = TRUE;
-
- if (!isset($max_queued_tasks)) {
- if ($db_engine == 'sqlite') {
- $max_queued_tasks = 1000;
- } else {
- $max_queued_tasks = 10000000;
- }
- }
- if (!isset($memory_limit)) {
- ini_set('memory_limit', $memory_limit);
- }
- if (isset($memory_limit)) {
- if(preg_match('/M$/',$memory_limit)) {
- $memory_limit = preg_replace('/M$/','',$memory_limit);
- $memory_limit = $memory_limit * 1048576;
- }elseif(preg_match('/K$/',$memory_limit)) {
- $memory_limit = preg_replace('/K$/','',$memory_limit);
- $memory_limit = $memory_limit * 1024;
- }
- }
- $db_options = (object) array(
- 'engine' => $db_engine,
- 'schema' => "/usr/share/greyhole/schema-$db_engine.sql"
- );
- if ($db_options->engine == 'sqlite') {
- $db_options->db_path = $db_path;
- $db_options->dbh = null; // internal handle to use with sqlite
- } else {
- $db_options->host = $db_host;
- $db_options->user = $db_user;
- $db_options->pass = $db_pass;
- $db_options->name = $db_name;
- }
-
- include('includes/sql.php');
-
- if (strtolower($greyhole_log_file) == 'syslog') {
- openlog("Greyhole", LOG_PID, LOG_USER);
- }
-
- return TRUE;
- }
- function clean_dir($dir) {
- if ($dir[0] == '.' && $dir[1] == '/') {
- $dir = mb_substr($dir, 2);
- }
- while (mb_strpos($dir, '//') !== FALSE) {
- $dir = str_replace("//", "/", $dir);
- }
- return $dir;
- }
- function explode_full_path($full_path) {
- return array(dirname($full_path), basename($full_path));
- }
- function gh_log($local_log_level, $text) {
- global $greyhole_log_file, $log_level, $log_memory_usage, $action, $log_to_stdout;
- if ($local_log_level > $log_level) {
- return;
- }
- $date = date("M d H:i:s");
- if ($log_level >= PERF) {
- $utimestamp = microtime(true);
- $timestamp = floor($utimestamp);
- $date .= '.' . round(($utimestamp - $timestamp) * 1000000);
- }
- $log_text = sprintf("%s%s%s\n",
- "$date $local_log_level $action: ",
- $text,
- @$log_memory_usage ? " [" . memory_get_usage() . "]" : ''
- );
- if (isset($log_to_stdout)) {
- echo $log_text;
- } else {
- if (strtolower($greyhole_log_file) == 'syslog') {
- $worked = syslog($local_log_level, $log_text);
- } else if (!empty($greyhole_log_file)) {
- $worked = error_log($log_text, 3, $greyhole_log_file);
- } else {
- $worked = FALSE;
- }
- if (!$worked) {
- error_log(trim($log_text));
- }
- }
-
- if ($local_log_level === CRITICAL) {
- exit(1);
- }
- }
- function gh_shutdown() {
- if ($err = error_get_last()) {
- gh_log(ERROR, "PHP Fatal Error: " . $err['message'] . "; BT: " . basename($err['file']) . '[L' . $err['line'] . '] ');
- }
- }
- function gh_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
- if (error_reporting() === 0) {
- // Ignored (@) warning
- return TRUE;
- }
- switch ($errno) {
- case E_ERROR:
- case E_PARSE:
- case E_CORE_ERROR:
- case E_COMPILE_ERROR:
- gh_log(CRITICAL, "PHP Error [$errno]: $errstr in $errfile on line $errline");
- break;
- case E_WARNING:
- case E_COMPILE_WARNING:
- case E_CORE_WARNING:
- case E_NOTICE:
- global $greyhole_log_file;
- if ($errstr == "fopen($greyhole_log_file): failed to open stream: Permission denied") {
- // We want to ignore this warning. Happens when regular users try to use greyhole, and greyhole tries to log something.
- // What would have been logged will be echoed instead.
- return TRUE;
- }
- gh_log(WARN, "PHP Warning [$errno]: $errstr in $errfile on line $errline; BT: " . get_debug_bt());
- break;
- default:
- gh_log(WARN, "PHP Unknown Error [$errno]: $errstr in $errfile on line $errline");
- break;
- }
- // Don't execute PHP internal error handler
- return TRUE;
- }
- function get_debug_bt() {
- $bt = '';
- foreach (debug_backtrace() as $d) {
- if ($d['function'] == 'gh_error_handler' || $d['function'] == 'get_debug_bt') { continue; }
- if ($bt != '') {
- $bt = " => $bt";
- }
- $prefix = '';
- if (isset($d['file'])) {
- $prefix = basename($d['file']) . '[L' . $d['line'] . '] ';
- }
- foreach ($d['args'] as $k => $v) {
- if (is_object($v)) {
- $d['args'][$k] = 'stdClass';
- }
- if (is_array($v)) {
- $d['args'][$k] = str_replace("\n", "", var_export($v, TRUE));
- }
- }
- $bt = $prefix . $d['function'] .'(' . implode(',', $d['args']) . ')' . $bt;
- }
- return $bt;
- }
- function bytes_to_human($bytes, $html=TRUE) {
- $units = 'B';
- if (abs($bytes) > 1024) {
- $bytes /= 1024;
- $units = 'KB';
- }
- if (abs($bytes) > 1024) {
- $bytes /= 1024;
- $units = 'MB';
- }
- if (abs($bytes) > 1024) {
- $bytes /= 1024;
- $units = 'GB';
- }
- if (abs($bytes) > 1024) {
- $bytes /= 1024;
- $units = 'TB';
- }
- $decimals = (abs($bytes) > 100 ? 0 : (abs($bytes) > 10 ? 1 : 2));
- if ($html) {
- return number_format($bytes, $decimals) . " <span class=\"i18n-$units\">$units</span>";
- } else {
- return number_format($bytes, $decimals) . $units;
- }
- }
- function duration_to_human($seconds) {
- $displayable_duration = '';
- if ($seconds > 60*60) {
- $hours = floor($seconds / (60*60));
- $displayable_duration .= $hours . 'h ';
- $seconds -= $hours * (60*60);
- }
- if ($seconds > 60) {
- $minutes = floor($seconds / 60);
- $displayable_duration .= $minutes . 'm ';
- $seconds -= $minutes * 60;
- }
- $displayable_duration .= $seconds . 's';
- return $displayable_duration;
- }
- function get_share_landing_zone($share) {
- global $shares_options, $trash_share_names;
- if (isset($shares_options[$share]['landing_zone'])) {
- return $shares_options[$share]['landing_zone'];
- } else if (array_search($share, $trash_share_names) !== FALSE) {
- global $trash_share;
- return $trash_share['landing_zone'];
- } else {
- global $config_file, $smb_config_file;
- gh_log(WARN, " Found a share ($share) with no path in $smb_config_file, or missing it's num_copies[$share] config in $config_file. Skipping.");
- return FALSE;
- }
- }
- $arch = exec('uname -m');
- if ($arch != 'x86_64') {
- gh_log(DEBUG, "32-bit system detected: Greyhole will NOT use PHP built-in file functions.");
- function gh_filesize($filename) {
- $result = exec("stat -c %s ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return (float) $result;
- }
-
- function gh_fileowner($filename) {
- $result = exec("stat -c %u ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return (int) $result;
- }
-
- function gh_filegroup($filename) {
- $result = exec("stat -c %g ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return (int) $result;
- }
- function gh_fileperms($filename) {
- $result = exec("stat -c %a ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return "0" . $result;
- }
- function gh_is_file($filename) {
- exec('[ -f '.escapeshellarg($filename).' ]', $tmp, $result);
- return $result === 0;
- }
- function gh_fileinode($filename) {
- // This function returns deviceid_inode to make sure this value will be different for files on different devices.
- $result = exec("stat -c '%d_%i' ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return (string) $result;
- }
- function gh_file_deviceid($filename) {
- $result = exec("stat -c '%d' ".escapeshellarg($filename)." 2>/dev/null");
- if (empty($result)) {
- return FALSE;
- }
- return (string) $result;
- }
-
- function gh_rename($filename, $target_filename) {
- exec("mv ".escapeshellarg($filename)." ".escapeshellarg($target_filename)." 2>/dev/null", $output, $result);
- return $result === 0;
- }
- } else {
- gh_log(DEBUG, "64-bit system detected: Greyhole will use PHP built-in file functions.");
- function gh_filesize($filename) {
- return filesize($filename);
- }
-
- function gh_fileowner($filename) {
- return fileowner($filename);
- }
- function gh_filegroup($filename) {
- return filegroup($filename);
- }
- function gh_fileperms($filename) {
- return mb_substr(decoct(fileperms($filename)), -4);
- }
- function gh_is_file($filename) {
- return is_file($filename);
- }
- function gh_fileinode($filename) {
- // This function returns deviceid_inode to make sure this value will be different for files on different devices.
- $stat = @stat($filename);
- if ($stat === FALSE) {
- return FALSE;
- }
- return $stat['dev'] . '_' . $stat['ino'];
- }
- function gh_file_deviceid($filename) {
- $stat = @stat($filename);
- if ($stat === FALSE) {
- return FALSE;
- }
- return $stat['dev'];
- }
- function gh_rename($filename, $target_filename) {
- return @rename($filename, $target_filename);
- }
- }
- function memory_check() {
- global $memory_limit;
- $usage = memory_get_usage();
- $used = $usage/$memory_limit;
- $used = $used * 100;
- if ($used > 95) {
- gh_log(CRITICAL, $used . '% memory usage, exiting. Please increase memory_limit in /etc/greyhole.conf');
- }
- }
- class metafile_iterator implements Iterator {
- private $path;
- private $share;
- private $load_nok_metafiles;
- private $quiet;
- private $check_symlink;
- private $metafiles;
- private $metastores;
- private $dir_handle;
- public function __construct($share, $path, $load_nok_metafiles=FALSE, $quiet=FALSE, $check_symlink=TRUE) {
- $this->quiet = $quiet;
- $this->share = $share;
- $this->path = $path;
- $this->check_symlink = $check_symlink;
- $this->load_nok_metafiles = $load_nok_metafiles;
- }
- public function rewind() {
- $this->metastores = get_metastores();
- $this->directory_stack = array($this->path);
- $this->dir_handle = NULL;
- $this->metafiles = array();
- $this->next();
- }
- public function current() {
- return $this->metafiles;
- }
- public function key() {
- return count($this->metafiles);
- }
- public function next() {
- $this->metafiles = array();
- while(count($this->directory_stack)>0 && $this->directory_stack !== NULL) {
- $this->dir = array_pop($this->directory_stack);
- if (!$this->quiet) {
- gh_log(DEBUG, "Loading metadata files for (dir) " . clean_dir($this->share . (!empty($this->dir) ? "/" . $this->dir : "")) . " ...");
- }
- for( $i = 0; $i < count($this->metastores); $i++ ) {
- $metastore = $this->metastores[$i];
- $this->base = "$metastore/".$this->share."/";
- if(!file_exists($this->base.$this->dir)) {
- continue;
- }
- if($this->dir_handle = opendir($this->base.$this->dir)) {
- while (false !== ($file = readdir($this->dir_handle))) {
- memory_check();
- if($file=='.' || $file=='..')
- continue;
- if(!empty($this->dir)) {
- $full_filename = $this->dir . '/' . $file;
- }else
- $full_filename = $file;
- if(is_dir($this->base.$full_filename))
- $this->directory_stack[] = $full_filename;
- else{
- $full_filename = str_replace("$this->path/",'',$full_filename);
- if(isset($this->metafiles[$full_filename])) {
- continue;
- }
- $this->metafiles[$full_filename] = get_metafiles_for_file($this->share, "$this->dir", $file, $this->load_nok_metafiles, $this->quiet, $this->check_symlink);
- }
- }
- closedir($this->dir_handle);
- $this->directory_stack = array_unique($this->directory_stack);
- }
- }
- if(count($this->metafiles) > 0) {
- break;
- }
-
- }
- if (!$this->quiet) {
- gh_log(DEBUG, 'Found ' . count($this->metafiles) . ' metadata files.');
- }
- return $this->metafiles;
- }
-
- public function valid() {
- return count($this->metafiles) > 0;
- }
- }
- function _getopt ( ) {
- /* _getopt(): Ver. 1.3 2009/05/30
- My page: http://www.ntu.beautifulworldco.com/weblog/?p=526
- Usage: _getopt ( [$flag,] $short_option [, $long_option] );
- Note that another function split_para() is required, which can be found in the same
- page.
- _getopt() fully simulates getopt() which is described at
- http://us.php.net/manual/en/function.getopt.php , including long options for PHP
- version under 5.3.0. (Prior to 5.3.0, long options was only available on few systems)
- Besides legacy usage of getopt(), I also added a new option to manipulate your own
- argument lists instead of those from command lines. This new option can be a string
- or an array such as
- $flag = "-f value_f -ab --required 9 --optional=PK --option -v test -k";
- or
- $flag = array ( "-f", "value_f", "-ab", "--required", "9", "--optional=PK", "--option" );
- So there are four ways to work with _getopt(),
- 1. _getopt ( $short_option );
- it's a legacy usage, same as getopt ( $short_option ).
- 2. _getopt ( $short_option, $long_option );
- it's a legacy usage, same as getopt ( $short_option, $long_option ).
- 3. _getopt ( $flag, $short_option );
- use your own argument lists instead of command line arguments.
- 4. _getopt ( $flag, $short_option, $long_option );
- use your own argument lists instead of command line arguments.
- */
- if ( func_num_args() == 1 ) {
- $flag = $flag_array = $GLOBALS['argv'];
- $short_option = func_get_arg ( 0 );
- $long_option = array ();
- return getopt($short_option);
- } else if ( func_num_args() == 2 ) {
- if ( is_array ( func_get_arg ( 1 ) ) ) {
- $flag = $GLOBALS['argv'];
- $short_option = func_get_arg ( 0 );
- $long_option = func_get_arg ( 1 );
- if (PHP_VERSION_ID >= 50300) { return getopt($short_option, $long_option); }
- } else {
- $flag = func_get_arg ( 0 );
- $short_option = func_get_arg ( 1 );
- $long_option = array ();
- return getopt($short_option);
- }
- } else if ( func_num_args() == 3 ) {
- $flag = func_get_arg ( 0 );
- $short_option = func_get_arg ( 1 );
- $long_option = func_get_arg ( 2 );
- if (PHP_VERSION_ID >= 50300) { return getopt($short_option, $long_option); }
- } else {
- exit ( "wrong options\n" );
- }
- $short_option = trim ( $short_option );
- $short_no_value = array();
- $short_required_value = array();
- $short_optional_value = array();
- $long_no_value = array();
- $long_required_value = array();
- $long_optional_value = array();
- $options = array();
- for ( $i = 0; $i < strlen ( $short_option ); ) {
- if ( $short_option{$i} != ":" ) {
- if ( $i == strlen ( $short_option ) - 1 ) {
- $short_no_value[] = $short_option{$i};
- break;
- } else if ( $short_option{$i+1} != ":" ) {
- $short_no_value[] = $short_option{$i};
- $i++;
- continue;
- } else if ( $short_option{$i+1} == ":" && $short_option{$i+2} != ":" ) {
- $short_required_value[] = $short_option{$i};
- $i += 2;
- continue;
- } else if ( $short_option{$i+1} == ":" && $short_option{$i+2} == ":" ) {
- $short_optional_value[] = $short_option{$i};
- $i += 3;
- continue;
- }
- } else {
- continue;
- }
- }
- foreach ( $long_option as $a ) {
- if ( substr( $a, -2 ) == "::" ) {
- $long_optional_value[] = substr( $a, 0, -2);
- continue;
- } else if ( substr( $a, -1 ) == ":" ) {
- $long_required_value[] = substr( $a, 0, -1 );
- continue;
- } else {
- $long_no_value[] = $a;
- continue;
- }
- }
- if ( is_array ( $flag ) )
- $flag_array = $flag;
- else {
- $flag = "- $flag";
- $flag_array = split_para( $flag );
- }
- for ( $i = 0; $i < count( $flag_array ); ) {
- if ( $i >= count ( $flag_array ) )
- break;
- if ( ! $flag_array[$i] || $flag_array[$i] == "-" ) {
- $i++;
- continue;
- }
- if ( $flag_array[$i]{0} != "-" ) {
- $i++;
- continue;
- }
- if ( substr( $flag_array[$i], 0, 2 ) == "--" ) {
- if (strpos($flag_array[$i], '=') != false) {
- list($key, $value) = explode('=', substr($flag_array[$i], 2), 2);
- if ( in_array ( $key, $long_required_value ) || in_array ( $key, $long_optional_value ) )
- $options[$key][] = $value;
- $i++;
- continue;
- }
- if (strpos($flag_array[$i], '=') == false) {
- $key = substr( $flag_array[$i], 2 );
- if ( in_array( substr( $flag_array[$i], 2 ), $long_required_value ) ) {
- $options[$key][] = $flag_array[$i+1];
- $i += 2;
- continue;
- } else if ( in_array( substr( $flag_array[$i], 2 ), $long_optional_value ) ) {
- if ( $flag_array[$i+1] != "" && $flag_array[$i+1]{0} != "-" ) {
- $options[$key][] = $flag_array[$i+1];
- $i += 2;
- } else {
- $options[$key][] = FALSE;
- $i ++;
- }
- continue;
- } else if ( in_array( substr( $flag_array[$i], 2 ), $long_no_value ) ) {
- $options[$key][] = FALSE;
- $i++;
- continue;
- } else {
- $i++;
- continue;
- }
- }
- } else if ( $flag_array[$i]{0} == "-" && $flag_array[$i]{1} != "-" ) {
- for ( $j=1; $j < strlen($flag_array[$i]); $j++ ) {
- if ( in_array( $flag_array[$i]{$j}, $short_required_value ) || in_array( $flag_array[$i]{$j}, $short_optional_value )) {
- if ( $j == strlen($flag_array[$i]) - 1 ) {
- if ( in_array( $flag_array[$i]{$j}, $short_required_value ) ) {
- $options[$flag_array[$i]{$j}][] = $flag_array[$i+1];
- $i += 2;
- } else if ( in_array( $flag_array[$i]{$j}, $short_optional_value ) && $flag_array[$i+1] != "" && $flag_array[$i+1]{0} != "-" ) {
- $options[$flag_array[$i]{$j}][] = $flag_array[$i+1];
- $i += 2;
- } else {
- $options[$flag_array[$i]{$j}][] = FALSE;
- $i ++;
- }
- $plus_i = 0;
- break;
- } else {
- $options[$flag_array[$i]{$j}][] = substr ( $flag_array[$i], $j + 1 );
- $i ++;
- $plus_i = 0;
- break;
- }
- } else if ( in_array ( $flag_array[$i]{$j}, $short_no_value ) ) {
- $options[$flag_array[$i]{$j}][] = FALSE;
- $plus_i = 1;
- continue;
- } else {
- $plus_i = 1;
- break;
- }
- }
- $i += $plus_i;
- continue;
- }
- $i++;
- continue;
- }
- foreach ( $options as $key => $value ) {
- if ( count ( $value ) == 1 ) {
- $options[ $key ] = $value[0];
- }
- }
- return $options;
- }
- function split_para ( $pattern ) {
- /* split_para() version 1.0 2008/08/19
- My page: http://www.ntu.beautifulworldco.com/weblog/?p=526
- This function is to parse parameters and split them into smaller pieces.
- preg_split() does similar thing but in our function, besides "space", we
- also take the three symbols " (double quote), '(single quote),
- and \ (backslash) into consideration because things in a pair of " or '
- should be grouped together.
- As an example, this parameter list
- -f "test 2" -ab --required "t\"est 1" --optional="te'st 3" --option -v 'test 4'
- will be splited into
- -f
- t"est 2
- -ab
- --required
- test 1
- --optional=te'st 3
- --option
- -v
- test 4
- see the code below,
- $pattern = "-f \"test 2\" -ab --required \"t\\\"est 1\" --optional=\"te'st 3\" --option -v 'test 4'";
- $result = split_para( $pattern );
- echo "ORIGINAL PATTERN: $pattern\n\n";
- var_dump( $result );
- */
- $begin=0;
- $backslash = 0;
- $quote = "";
- $quote_mark = array();
- $result = array();
- $pattern = trim ( $pattern );
- for ( $end = 0; $end < strlen ( $pattern ) ; ) {
- if ( ! in_array ( $pattern{$end}, array ( " ", "\"", "'", "\\" ) ) ) {
- $backslash = 0;
- $end ++;
- continue;
- }
- if ( $pattern{$end} == "\\" ) {
- $backslash++;
- $end ++;
- continue;
- } else if ( $pattern{$end} == "\"" ) {
- if ( $backslash % 2 == 1 || $quote == "'" ) {
- $backslash = 0;
- $end ++;
- continue;
- }
- if ( $quote == "" ) {
- $quote_mark[] = $end - $begin;
- $quote = "\"";
- } else if ( $quote == "\"" ) {
- $quote_mark[] = $end - $begin;
- $quote = "";
- }
- $backslash = 0;
- $end ++;
- continue;
- } else if ( $pattern{$end} == "'" ) {
- if ( $backslash % 2 == 1 || $quote == "\"" ) {
- $backslash = 0;
- $end ++;
- continue;
- }
- if ( $quote == "" ) {
- $quote_mark[] = $end - $begin;
- $quote = "'";
- } else if ( $quote == "'" ) {
- $quote_mark[] = $end - $begin;
- $quote = "";
- }
- $backslash = 0;
- $end ++;
- continue;
- } else if ( $pattern{$end} == " " ) {
- if ( $quote != "" ) {
- $backslash = 0;
- $end ++;
- continue;
- } else {
- $backslash = 0;
- $cand = substr( $pattern, $begin, $end-$begin );
- for ( $j = 0; $j < strlen ( $cand ); $j ++ ) {
- if ( in_array ( $j, $quote_mark ) )
- continue;
- $cand1 .= $cand{$j};
- }
- if ( $cand1 ) {
- eval( "\$cand1 = \"$cand1\";" );
- $result[] = $cand1;
- }
- $quote_mark = array();
- $cand1 = "";
- $end ++;
- $begin = $end;
- continue;
- }
- }
- }
- $cand = substr( $pattern, $begin, $end-$begin );
- for ( $j = 0; $j < strlen ( $cand ); $j ++ ) {
- if ( in_array ( $j, $quote_mark ) )
- continue;
- $cand1 .= $cand{$j};
- }
- eval( "\$cand1 = \"$cand1\";" );
- if ( $cand1 )
- $result[] = $cand1;
- return $result;
- }
- function kshift(&$arr) {
- if (count($arr) == 0) {
- return FALSE;
- }
- foreach ($arr as $k => $v) {
- unset($arr[$k]);
- break;
- }
- return array($k, $v);
- }
- function kshuffle(&$array) {
- if (!is_array($array)) { return $array; }
- $keys = array_keys($array);
- shuffle($keys);
- $random = array();
- foreach ($keys as $key) {
- $random[$key] = $array[$key];
- }
- $array = $random;
- }
- class DriveSelection {
- var $num_drives_per_draft;
- var $selection_algorithm;
- var $drives;
- var $is_custom;
-
- var $sorted_target_drives;
- var $last_resort_sorted_target_drives;
-
- function __construct($num_drives_per_draft, $selection_algorithm, $drives, $is_custom) {
- $this->num_drives_per_draft = $num_drives_per_draft;
- $this->selection_algorithm = $selection_algorithm;
- $this->drives = $drives;
- $this->is_custom = $is_custom;
- }
-
- function init(&$sorted_target_drives, &$last_resort_sorted_target_drives) {
- // Sort by used space (asc) for least_used_space, or by available space (desc) for most_available_space
- if ($this->selection_algorithm == 'least_used_space') {
- $sorted_target_drives = $sorted_target_drives['used_space'];
- $last_resort_sorted_target_drives = $last_resort_sorted_target_drives['used_space'];
- asort($sorted_target_drives);
- asort($last_resort_sorted_target_drives);
- } else if ($this->selection_algorithm == 'most_available_space') {
- $sorted_target_drives = $sorted_target_drives['available_space'];
- $last_resort_sorted_target_drives = $last_resort_sorted_target_drives['available_space'];
- arsort($sorted_target_drives);
- arsort($last_resort_sorted_target_drives);
- } else {
- gh_log(CRITICAL, "Unknown drive_selection_algorithm found: " . $this->selection_algorithm);
- }
- // Only keep drives that are in $this->drives
- $this->sorted_target_drives = array();
- foreach ($sorted_target_drives as $sp_drive => $space) {
- if (array_search($sp_drive, $this->drives) !== FALSE) {
- $this->sorted_target_drives[$sp_drive] = $space;
- }
- }
- $this->last_resort_sorted_target_drives = array();
- foreach ($last_resort_sorted_target_drives as $sp_drive => $space) {
- if (array_search($sp_drive, $this->drives) !== FALSE) {
- $this->last_resort_sorted_target_drives[$sp_drive] = $space;
- }
- }
- }
-
- function draft() {
- $drives = array();
- $drives_last_resort = array();
-
- while (count($drives)<$this->num_drives_per_draft) {
- $arr = kshift($this->sorted_target_drives);
- if ($arr === FALSE) {
- break;
- }
- list($sp_drive, $space) = $arr;
- if (!is_greyhole_owned_drive($sp_drive)) { continue; }
- $drives[$sp_drive] = $space;
- }
- while (count($drives)+count($drives_last_resort)<$this->num_drives_per_draft) {
- $arr = kshift($this->last_resort_sorted_target_drives);
- if ($arr === FALSE) {
- break;
- }
- list($sp_drive, $space) = $arr;
- if (!is_greyhole_owned_drive($sp_drive)) { continue; }
- $drives_last_resort[$sp_drive] = $space;
- }
-
- return array($drives, $drives_last_resort);
- }
-
- static function parse($config_string, $drive_selection_groups) {
- $ds = array();
- if ($config_string == 'least_used_space' || $config_string == 'most_available_space') {
- global $storage_pool_drives;
- $ds[] = new DriveSelection(count($storage_pool_drives), $config_string, $storage_pool_drives, FALSE);
- return $ds;
- }
- if (!preg_match('/forced ?\((.+)\) ?(least_used_space|most_available_space)/i', $config_string, $regs)) {
- gh_log(CRITICAL, "Can't understand the drive_selection_algorithm value: $config_string");
- }
- $selection_algorithm = $regs[2];
- $groups = array_map('trim', explode(',', $regs[1]));
- foreach ($groups as $group) {
- $group = explode(' ', preg_replace('/^([0-9]+)x/', '\\1 ', $group));
- $num_drives = trim($group[0]);
- $group_name = trim($group[1]);
- if (!isset($drive_selection_groups[$group_name])) {
- //gh_log(WARN, "Warning: drive selection group named '$group_name' is undefined.");
- continue;
- }
- if ($num_drives == 'all' || $num_drives > count($drive_selection_groups[$group_name])) {
- $num_drives = count($drive_selection_groups[$group_name]);
- }
- $ds[] = new DriveSelection($num_drives, $selection_algorithm, $drive_selection_groups[$group_name], TRUE);
- }
- return $ds;
- }
- function update() {
- // Make sure num_drives_per_draft and drives have been set, in case storage_pool_drive lines appear after drive_selection_algorithm line(s) in the config file
- if (!$this->is_custom && ($this->selection_algorithm == 'least_used_space' || $this->selection_algorithm == 'most_available_space')) {
- global $storage_pool_drives;
- $this->num_drives_per_draft = count($storage_pool_drives);
- $this->drives = $storage_pool_drives;
- }
- }
- }
- $greyhole_owned_drives = array();
- function is_greyhole_owned_drive($sp_drive) {
- global $going_drive, $df_cache_time, $greyhole_owned_drives;
- if (isset($going_drive) && $sp_drive == $going_drive) {
- return FALSE;
- }
- $is_greyhole_owned_drive = isset($greyhole_owned_drives[$sp_drive]);
- if ($is_greyhole_owned_drive && $greyhole_owned_drives[$sp_drive] < time() - $df_cache_time) {
- unset($greyhole_owned_drives[$sp_drive]);
- $is_greyhole_owned_drive = FALSE;
- }
- if (!$is_greyhole_owned_drive) {
- $drives_definitions = Settings::get('sp_drives_definitions', TRUE);
- if (!$drives_definitions) {
- $drives_definitions = convert_sp_drives_tag_files();
- }
- $drive_uuid = gh_dir_uuid($sp_drive);
- $is_greyhole_owned_drive = @$drives_definitions[$sp_drive] === $drive_uuid && $drive_uuid !== FALSE;
- if (!$is_greyhole_owned_drive) {
- // Maybe this is a remote mount? Those don't have UUIDs, so we use the .greyhole_uses_this technique.
- $is_greyhole_owned_drive = file_exists("$sp_drive/.greyhole_uses_this");
- if ($is_greyhole_owned_drive && isset($drives_definitions[$sp_drive])) {
- // This remote drive was listed in MySQL; it shouldn't be. Let's remove it.
- unset($drives_definitions[$sp_drive]);
- Settings::set('sp_drives_definitions', $drives_definitions);
- }
- }
- if ($is_greyhole_owned_drive) {
- $greyhole_owned_drives[$sp_drive] = time();
- }
- }
- return $is_greyhole_owned_drive;
- }
- // Is it OK for a drive to be gone?
- function gone_ok($sp_drive, $refresh=FALSE) {
- global $gone_ok_drives;
- if ($refresh || !isset($gone_ok_drives)) {
- $gone_ok_drives = get_gone_ok_drives();
- }
- if (isset($gone_ok_drives[$sp_drive])) {
- return TRUE;
- }
- return FALSE;
- }
- function get_gone_ok_drives() {
- global $gone_ok_drives;
- $gone_ok_drives = Settings::get('Gone-OK-Drives', TRUE);
- if (!$gone_ok_drives) {
- $gone_ok_drives = array();
- Settings::set('Gone-OK-Drives', $gone_ok_drives);
- }
- return $gone_ok_drives;
- }
- function mark_gone_ok($sp_drive, $action='add') {
- global $storage_pool_drives;
- if (array_search($sp_drive, $storage_pool_drives) === FALSE) {
- $sp_drive = '/' . trim($sp_drive, '/');
- }
- if (array_search($sp_drive, $storage_pool_drives) === FALSE) {
- return FALSE;
- }
- global $gone_ok_drives;
- $gone_ok_drives = get_gone_ok_drives();
- if ($action == 'add') {
- $gone_ok_drives[$sp_drive] = TRUE;
- } else {
- unset($gone_ok_drives[$sp_drive]);
- }
- Settings::set('Gone-OK-Drives', $gone_ok_drives);
- return TRUE;
- }
- function gone_fscked($sp_drive, $refresh=FALSE) {
- global $fscked_gone_drives;
- if ($refresh || !isset($fscked_gone_drives)) {
- $fscked_gone_drives = get_fsck_gone_drives();
- }
- if (isset($fscked_gone_drives[$sp_drive])) {
- return TRUE;
- }
- return FALSE;
- }
- function get_fsck_gone_drives() {
- global $fscked_gone_drives;
- $fscked_gone_drives = Settings::get('Gone-FSCKed-Drives', TRUE);
- if (!$fscked_gone_drives) {
- $fscked_gone_drives = array();
- Settings::set('Gone-FSCKed-Drives', $fscked_gone_drives);
- }
- return $fscked_gone_drives;
- }
- function mark_gone_drive_fscked($sp_drive, $action='add') {
- global $fscked_gone_drives;
- $fscked_gone_drives = get_fsck_gone_drives();
- if ($action == 'add') {
- $fscked_gone_drives[$sp_drive] = TRUE;
- } else {
- unset($fscked_gone_drives[$sp_drive]);
- }
- Settings::set('Gone-FSCKed-Drives', $fscked_gone_drives);
- }
- function check_storage_pool_drives($skip_fsck=FALSE) {
- global $storage_pool_drives, $email_to, $gone_ok_drives;
- $needs_fsck = FALSE;
- $returned_drives = array();
- $missing_drives = array();
- $i = 0; $j = 0;
- foreach ($storage_pool_drives as $sp_drive) {
- if (!is_greyhole_owned_drive($sp_drive) && !gone_fscked($sp_drive, $i++ == 0) && !file_exists("$sp_drive/.greyhole_used_this")) {
- if($needs_fsck !== 2){
- $needs_fsck = 1;
- }
- mark_gone_drive_fscked($sp_drive);
- $missing_drives[] = $sp_drive;
- gh_log(WARN, "Warning! It seems the partition UUID of $sp_drive changed. This probably means this mount is currently unmounted, or that you replaced this drive and didn't use 'greyhole --replace'. Because of that, Greyhole will NOT use this drive at this time.");
- gh_log(DEBUG, "Email sent for gone drive: $sp_drive");
- $gone_ok_drives[$sp_drive] = TRUE; // The upcoming fsck should not recreate missing copies just yet
- } else if ((gone_ok($sp_drive, $j++ == 0) || gone_fscked($sp_drive, $i++ == 0)) && is_greyhole_owned_drive($sp_drive)) {
- // $sp_drive is now back
- $needs_fsck = 2;
- $returned_drives[] = $sp_drive;
- gh_log(DEBUG, "Email sent for revived drive: $sp_drive");
- mark_gone_ok($sp_drive, 'remove');
- mark_gone_drive_fscked($sp_drive, 'remove');
- $i = 0; $j = 0;
- }
- }
- if(count($returned_drives) > 0) {
- $body = "This is an automated email from Greyhole.\n\nOne (or more) of your storage pool drives came back:\n";
- foreach ($returned_drives as $sp_drive) {
- $body .= "$sp_drive was missing; it's now available again.\n";
- }
- if (!$skip_fsck) {
- $body .= "\nA fsck will now start, to fix the symlinks found in your shares, when possible.\nYou'll receive a report email once that fsck run completes.\n";
- }
- $drive_string = join(", ", $returned_drives);
- $subject = "Storage pool drive now online on " . exec ('hostname') . ": ";
- $subject = $subject . $drive_string;
- if (strlen($subject) > 255) {
- $subject = substr($subject, 0, 255);
- }
- mail($email_to, $subject, $body);
- }
- if(count($missing_drives) > 0) {
- $body = "This is an automated email from Greyhole.\n\nOne (or more) of your storage pool drives has disappeared:\n";
- $drives_definitions = Settings::get('sp_drives_definitions', TRUE);
- foreach ($missing_drives as $sp_drive) {
- if (!is_dir($sp_drive)) {
- $body .= "$sp_drive: directory doesn't exists\n";
- } else {
- $current_uuid = gh_dir_uuid($sp_drive);
- if (empty($current_uuid)) {
- $current_uuid = 'N/A';
- }
- $body .= "$sp_drive: expected partition UUID: " . $drives_definitions[$sp_drive] . "; current partition UUID: $current_uuid\n";
- }
- }
- $sp_drive = $missing_drives[0];
- $body .= "\nThis either means this mount is currently unmounted, or you forgot to use 'greyhole --replace' when you changed this drive.\n\n";
- $body .= "Here are your options:\n\n";
- $body .= "- If you forgot to use 'greyhole --replace', you should do so now. Until you do, this drive will not be part of your storage pool.\n\n";
- $body .= "- If the drive is gone, you should either re-mount it manually (if possible), or remove it from your storage pool. To do so, use the following command:\n greyhole --gone=" . escapeshellarg($sp_drive) . "\n Note that the above command is REQUIRED for Greyhole to re-create missing file copies before the next fsck runs. Until either happens, missing file copies WILL NOT be re-created on other drives.\n\n";
- $body .= "- If you know this drive will come back soon, and do NOT want Greyhole to re-create missing file copies for this drive until it reappears, you should execute this command:\n greyhole --wait-for=" . escapeshellarg($sp_drive) . "\n\n";
- if (!$skip_fsck) {
- $body .= "A fsck will now start, to fix the symlinks found in your shares, when possible.\nYou'll receive a report email once that fsck run completes.\n";
- }
- $subject = "Missing storage pool drives on " . exec('hostname') . ": ";
- $drive_string = join(",",$missing_drives);
- $subject = $subject . $drive_string;
- if (strlen($subject) > 255) {
- $subject = substr($subject, 0, 255);
- }
- mail($email_to, $subject, $body);
- }
- if ($needs_fsck !== FALSE) {
- set_metastore_backup();
- get_metastores(FALSE); // FALSE => Resets the metastores cache
- clearstatcache();
- if (!$skip_fsck) {
- global $shares_options;
- initialize_fsck_report('All shares');
- if($needs_fsck === 2) {
- foreach ($returned_drives as $drive) {
- $metastores = get_metastores_from_storage_volume($drive);
- gh_log(INFO, "Starting fsck for metadata store on $drive which came back online.");
- foreach($metastores as $metastore) {
- foreach($shares_options as $share_name => $share_options) {
- gh_fsck_metastore($metastore,"/$share_name", $share_name);
- }
- }
- gh_log(INFO, "fsck for returning drive $drive's metadata store completed.");
- }
- gh_log(INFO, "Starting fsck for all shares - caused by missing drive that came back online.");
- }else{
- gh_log(INFO, "Starting fsck for all shares - caused by missing drive. Will just recreate symlinks to existing copies when possible; won't create new copies just yet.");
- fix_all_symlinks();
- }
- schedule_fsck_all_shares(array('email'));
- gh_log(INFO, " fsck for all shares scheduled.");
- }
- // Refresh $gone_ok_drives to it's real value (from the DB)
- get_gone_ok_drives();
- }
- }
- class FSCKLogFile {
- const PATH = '/usr/share/greyhole';
- private $path;
- private $filename;
- private $lastEmailSentTime = 0;
-
- public function __construct($filename, $path=self::PATH) {
- $this->filename = $filename;
- $this->path = $path;
- }
-
- public function emailAsRequired() {
- $logfile = "$this->path/$this->filename";
- if (!file_exists($logfile)) { return; }
- $last_mod_date = filemtime($logfile);
- if ($last_mod_date > $this->getLastEmailSentTime()) {
- global $email_to;
- gh_log(WARN, "Sending $logfile by email to $email_to");
- mail($email_to, $this->getSubject(), $this->getBody());
- $this->lastEmailSentTime = $last_mod_date;
- Settings::set("last_email_$this->filename", $this->lastEmailSentTime);
- }
- }
- private function getBody() {
- $logfile = "$this->path/$this->filename";
- if ($this->filename == 'fsck_checksums.log') {
- return file_get_contents($logfile) . "\nNote: You should manually delete the $logfile file once you're done with it.";
- } else if ($this->filename == 'fsck_files.log') {
- global $fsck_report;
- $fsck_report = unserialize(file_get_contents($logfile));
- unlink($logfile);
- return get_fsck_report() . "\nNote: This report is a complement to the last report you've received. It details possible errors with files for which the fsck was postponed.";
- } else {
- return '[empty]';
- }
- }
-
- private function getSubject() {
- if ($this->filename == 'fsck_checksums.log') {
- return 'Mismatched checksums in Greyhole file copies';
- } else if ($this->filename == 'fsck_files.log') {
- return 'fsck_files of Greyhole shares on ' . exec('hostname');
- } else {
- return 'Unknown FSCK report';
- }
- }
-
- private function getLastEmailSentTime() {
- if ($this->lastEmailSentTime == 0) {
- $setting = Settings::get("last_email_$this->filename");
- if ($setting) {
- $this->lastEmailSentTime = (int) $setting;
- }
- }
- return $this->lastEmailSentTime;
- }
-
- public static function loadFSCKReport($what) {
- $logfile = self::PATH . '/fsck_files.log';
- if (file_exists($logfile)) {
- global $fsck_report;
- $fsck_report = unserialize(file_get_contents($log