#!/usr/bin/env php <?php /* Check that the memcache library is enabled. */ if(!class_exists('Memcache')) { echo("Error: the memcache library appears to be unavailable.\n"); echo("\n"); echo("This is most likely because PHP doesn't load it for the command line\n"); echo("version. You probably need to enable it somehow.\n"); echo("\n"); if(is_dir('/etc/php5/cli/conf.d')) { echo("It is possible that running the following command as root will fix it:\n"); echo(" echo 'extension=memcache.so' >/etc/php5/cli/conf.d/memcache.ini\n"); } exit(1); } /* This is the base directory of the simpleSAMLphp installation. */ $baseDir = dirname(dirname(__FILE__)); /* Add library autoloader. */ require_once($baseDir . '/lib/_autoload.php'); /* Initialize the configuration. */ $configdir = $baseDir . '/config'; SimpleSAML_Configuration::setConfigDir($configdir); /* Things we should warn the user about. */ $warnServerDown = 0; $warnBigSlab = 0; /* We use the stats interface to determine which servers exists. */ $stats = SimpleSAML_Memcache::getRawStats(); $keys = array(); foreach($stats as $group) { foreach($group as $server => $state) { if($state === FALSE) { echo("WARNING: Server " . $server . " is down.\n"); $warnServerDown++; continue; } $items = $state['curr_items']; echo("Server " . $server . " has " . $items . " items.\n"); $serverKeys = getServerKeys($server); $keys = array_merge($keys, $serverKeys); } } echo("Total number of keys: " . count($keys) . "\n"); $keys = array_unique($keys); echo("Total number of unique keys: " . count($keys) . "\n"); echo("Starting synchronization.\n"); $skipped = 0; $sync = 0; foreach($keys as $key) { $res = SimpleSAML_Memcache::get($key); if($res === NULL) { $skipped += 1; } else { $sync += 1; } } echo("Synchronization done.\n"); echo($sync . " keys in sync.\n"); if($skipped > 0) { echo($skipped . " keys skipped.\n"); echo("Keys are skipped because they are either expired, or are of a type unknown\n"); echo("to simpleSAMLphp.\n"); } if($warnServerDown > 0) { echo("WARNING: " . $warnServerDown . " server(s) down. Not all servers are synchronized.\n"); } if($warnBigSlab > 0) { echo("WARNING: " . $warnBigSlab . " slab(s) may have contained more keys than we were told about.\n"); } /** * Fetch all keys available in an server. * * @param $server The server, as a string with <hostname>:<port>. * @return An array with all the keys available on the server. */ function getServerKeys($server) { $server = explode(':', $server); $host = $server[0]; $port = (int)$server[1]; echo("Connecting to: " . $host . ":" . $port . "\n"); $socket = fsockopen($host, $port); echo("Connected. Finding keys.\n"); if(fwrite($socket, "stats slabs\r\n") === FALSE) { echo("Error requesting slab dump from server.\n"); exit(1); } /* Read list of slabs. */ $slabs = array(); while( ($line = fgets($socket)) !== FALSE) { $line = rtrim($line); if($line === 'END') { break; } if(preg_match('/^STAT (\d+):/', $line, $matches)) { $slab = (int)$matches[1]; if(!in_array($slab, $slabs, TRUE)) { $slabs[] = $slab; } } } /* Dump keys in slabs. */ $keys = array(); foreach($slabs as $slab) { if(fwrite($socket, "stats cachedump " . $slab . " 1000000\r\n") === FALSE) { echo("Error requesting cache dump from server.\n"); exit(1); } /* We keep track of the result size, to be able to warn the user if it is * so big that keys may have been lost. */ $resultSize = 0; while( ($line = fgets($socket)) !== FALSE) { $resultSize += strlen($line); $line = rtrim($line); if($line === 'END') { break; } if(preg_match('/^ITEM (.*) \[\d+ b; \d+ s\]/', $line, $matches)) { $keys[] = $matches[1]; } else { echo("Unknown result from cache dump: " . $line . "\n"); } } if($resultSize > 1900000 || count($keys) >= 1000000) { echo("WARNING: Slab " . $slab . " on server " . $host . ":" . $port . " may have contained more keys than we were told about.\n"); $GLOBALS['warnBigSlab'] += 1; } } echo("Found " . count($keys) . " key(s).\n"); fclose($socket); return $keys; }