| +----------------------------------------------------------------------+ */ if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('inc/presentation'); if (!autoriser('configurer', 'admin_memcache')){ include_spip('inc/minipres'); echo minipres(); } function exec_admin_memcache() { global $MEMCACHE_SERVERS; $MEMCACHE_SERVERS = array(); $VERSION='$Id: memcache.php,v 1.2 2008/09/11 19:21:06 mikl Exp $'; define('DATE_FORMAT','Y/m/d H:i:s'); define('GRAPH_SIZE',200); define('MAX_ITEM_DUMP',50); if (!function_exists('memcache_connect')) die ('PHP compilé sans memcache. Bye.'); $MEMCACHE_SERVERS = MCache::config_memcache_servers(); ////////// END OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // // don't cache this page // header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); // HTTP/1.0 // TODO, AUTH $_GET['op'] = !isset($_GET['op'])? '1':$_GET['op']; global $PHP_SELF; $PHP_SELF = './?exec='._request('exec'); global $time; $time = time(); // sanitize _GET foreach($_GET as $key=>$g){ $_GET[$key]=htmlentities($g); } // singleout // when singleout is set, it only gives details for that server. if (isset($_GET['singleout']) && $_GET['singleout']>=0 && $_GET['singleout'] $mcs) { $free = $mcs['STAT']['limit_maxbytes']-$mcs['STAT']['bytes']; $used = $mcs['STAT']['bytes']; if ($free>0){ // draw free $angle_to = ($free*360)/$tsize; $perc =sprintf("%.2f%%", ($free *100) / $tsize) ; fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_green,$perc); $angle_from = $angle_from + $angle_to ; } if ($used>0){ // draw used $angle_to = ($used*360)/$tsize; $perc =sprintf("%.2f%%", ($used *100) / $tsize) ; fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_red, '('.$perc.')' ); $angle_from = $angle_from+ $angle_to ; } } break; case 2: // hit miss $hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits']; $misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses']; $total = $hits + $misses ; fill_box($image, 30,$size,50,-$hits*($size-21)/$total,$col_black,$col_green,sprintf("%.1f%%",$hits*100/$total)); fill_box($image,130,$size,50,-max(4,($total-$hits)*($size-21)/$total),$col_black,$col_red,sprintf("%.1f%%",$misses*100/$total)); break; } header("Content-type: image/png"); imagepng($image); exit; } echo getHeader(); echo getMenu(); switch ($_GET['op']) { case 1: // host stats $phpversion = phpversion(); $memcacheStats = getMemcacheStats(); if (!$memcacheStats) { echo "

No connection to memcached server!

\n"; echo "

Please check your settings and verify that your servers are up and running

\n"; break; } $memcacheStatsSingle = getMemcacheStats(false); $mem_size = $memcacheStats['limit_maxbytes']; $mem_used = $memcacheStats['bytes']; $mem_avail= $mem_size-$mem_used; $startTime = time()-array_sum($memcacheStats['uptime']); $curr_items = $memcacheStats['curr_items']; $total_items = $memcacheStats['total_items']; $hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits']; $misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses']; $sets = $memcacheStats['cmd_set']; $req_rate = sprintf("%.2f",($hits+$misses)/($time-$startTime)); $hit_rate = sprintf("%.2f",($hits)/($time-$startTime)); $miss_rate = sprintf("%.2f",($misses)/($time-$startTime)); $set_rate = sprintf("%.2f",($sets)/($time-$startTime)); echo <<< EOB

General Cache Information

EOB; echo "\n"; echo "\n"; echo <<
PHP Version$phpversion
Memcached Host". ((count($MEMCACHE_SERVERS)>1) ? 's':'').""; $i=0; if (!isset($_GET['singleout']) && count($MEMCACHE_SERVERS)>1){ foreach($MEMCACHE_SERVERS as $server){ echo ($i+1).'. '.$server.''; if (!$memcacheStatsSingle[$server]) echo " UNREACHABLE!!"; echo "
\n"; } } else{ echo '1.'.$MEMCACHE_SERVERS[0]; if (!$memcacheStatsSingle[$MEMCACHE_SERVERS[0]]) echo " UNREACHABLE!!"; echo "
\n"; } if (isset($_GET['singleout'])){ echo '(all servers)
'; } echo "
Total Memcache Cache".bsize($memcacheStats['limit_maxbytes'])."

Memcache Server Information

EOB; foreach($MEMCACHE_SERVERS as $server) if ($memcacheStatsSingle[$server]) { echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo '
'.$server.'[Flush this server]
Start Time',date(DATE_FORMAT,$memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'
Uptime',duration($memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'
Memcached Server Version'.$memcacheStatsSingle[$server]['STAT']['version'].'
Used Cache Size',bsize($memcacheStatsSingle[$server]['STAT']['bytes']),'
Total Cache Size',bsize($memcacheStatsSingle[$server]['STAT']['limit_maxbytes']),'
'; } echo <<

Host Status Diagrams

EOB; $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10); echo << EOB; echo graphics_avail() ? ''. "". "\n" : "", '', '\n", '\n", '', '', '\n", '\n"; echo <<< EOB
Cache Usage Hits & Misses
\"\"\"\"
 Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size)," Hits: ',$hits.sprintf(" (%.1f%%)",$hits*100/($hits+$misses)),"
 Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size)," Misses: ',$misses.sprintf(" (%.1f%%)",$misses*100/($hits+$misses)),"

Cache Information

Current Items(total)$curr_items ($total_items)
Hits{$hits}
Misses{$misses}
Request Rate (hits, misses)$req_rate cache requests/second
Hit Rate$hit_rate cache requests/second
Miss Rate$miss_rate cache requests/second
Set Rate$set_rate cache requests/second
EOB; break; case 2: // variables $m=0; $cacheItems= getCacheItems(); $items = $cacheItems['items']; $totals = $cacheItems['counts']; $maxDump = MAX_ITEM_DUMP; foreach(array_filter($items) as $server => $entries) { echo <<< EOB
EOB; foreach($entries as $slabId => $slab) { $dumpUrl = $PHP_SELF.'&op=2&server='.(array_search($server,$MEMCACHE_SERVERS)).'&dumpslab='.$slabId; echo "", "", ""; $m=1-$m; } echo <<
$server
Slab IdInfo
",'',$slabId,'',"
Item count: ",$slab['number'],'
Age:',duration($time-$slab['age']),'
Evicted:',((isset($slab['evicted']) && $slab['evicted']==1)? 'Yes':'No'); if ((isset($_GET['dumpslab']) && $_GET['dumpslab']==$slabId) && (isset($_GET['server']) && $_GET['server']==array_search($server,$MEMCACHE_SERVERS))){ echo "
Items: item
"; $items = dumpCacheSlab($server,$slabId,$slab['number']); // maybe someone likes to do a pagination here :) $i=1; foreach($items['ITEM'] as $itemKey=>$itemInfo){ $itemInfo = trim($itemInfo,'[ ]'); echo '',$itemKey,''; if ($i++ % 10 == 0) { echo '
'; } elseif ($i!=$slab['number']+1){ echo ','; } } } echo "

EOB; } break; break; case 4: //item dump if (!isset($_GET['key']) || !isset($_GET['server'])){ echo "No key set!"; break; } // I'm not doing anything to check the validity of the key string. // probably an exploit can be written to delete all the files in key=base64_encode("\n\r delete all"). // somebody has to do a fix to this. $theKey = htmlentities(base64_decode($_GET['key'])); $theserver = $MEMCACHE_SERVERS[(int)$_GET['server']]; list($h,$p) = explode(':',$theserver); $r = sendMemcacheCommand($h,$p,'get '.$theKey); echo << EOB; echo "", '",""; echo <<
ServerKeyValueDelete
",$theserver,"",$theKey, "
flag:",$r['VALUE'][$theKey]['stat']['flag'], "
Size:",bsize($r['VALUE'][$theKey]['stat']['size']), "
",htmlspecialchars(chunk_split($r['VALUE'][$theKey]['value'],40)),"Delete

EOB; break; case 5: // item delete if (!isset($_GET['key']) || !isset($_GET['server'])){ echo "No key set!"; break; } $theKey = htmlentities(base64_decode($_GET['key'])); $theserver = $MEMCACHE_SERVERS[(int)$_GET['server']]; list($h,$p) = explode(':',$theserver); $r = sendMemcacheCommand($h,$p,'delete '.$theKey); echo 'Deleting '.$theKey.':'.$r; break; case 6: // flush server $theserver = $MEMCACHE_SERVERS[(int)$_GET['server']]; $r = flushServer($theserver); echo 'Flush '.$theserver.":".$r; break; } echo getFooter(); } ///////////MEMCACHE FUNCTIONS ///////////////////////////////////////////////////////////////////// function sendMemcacheCommands($command){ global $MEMCACHE_SERVERS; $result = array(); foreach($MEMCACHE_SERVERS as $server){ $strs = explode(':',$server); $host = $strs[0]; $port = $strs[1]; $result[$server] = sendMemcacheCommand($host,$port,$command); } return $result; } function sendMemcacheCommand($server,$port,$command){ $s = @fsockopen($server,$port); if (!$s){ #echo ("Cant connect to:".$server.':'.$port); return false; } fwrite($s, $command."\r\n"); $buf=''; while ((!feof($s))) { $buf .= fgets($s, 256); if (strpos($buf,"END\r\n")!==false){ // stat says end break; } if (strpos($buf,"DELETED\r\n")!==false || strpos($buf,"NOT_FOUND\r\n")!==false){ // delete says these break; } if (strpos($buf,"OK\r\n")!==false){ // flush_all says ok break; } } fclose($s); return parseMemcacheResults($buf); } function parseMemcacheResults($str){ $res = array(); $lines = explode("\r\n",$str); $cnt = count($lines); for($i=0; $i< $cnt; $i++){ $line = $lines[$i]; $l = explode(' ',$line,3); if (count($l)==3){ $res[$l[0]][$l[1]]=$l[2]; if ($l[0]=='VALUE'){ // next line is the value $res[$l[0]][$l[1]] = array(); list ($flag,$size)=explode(' ',$l[2]); $res[$l[0]][$l[1]]['stat']=array('flag'=>$flag,'size'=>$size); $res[$l[0]][$l[1]]['value']=$lines[++$i]; } }elseif($line=='DELETED' || $line=='NOT_FOUND' || $line=='OK'){ return $line; } } return $res; } function dumpCacheSlab($server,$slabId,$limit){ list($host, $port) = explode(':', $server); $resp = sendMemcacheCommand($host, $port, 'stats cachedump ' . $slabId . ' ' . $limit); $g = function ($f) { return strpos($f, $_SERVER['HTTP_HOST']) === 0; }; foreach ($resp['ITEM'] as $key => $ignore) { if (!$g($key)) { unset($resp['ITEM'][$key]); } } return $resp; } function flushServer($server){ list($host,$port) = explode(':',$server); $resp = sendMemcacheCommand($host,$port,'flush_all'); return $resp; } function getCacheItems(){ $items = sendMemcacheCommands('stats items'); $serverItems = array(); $totalItems = array(); foreach (array_filter($items) as $server=>$itemlist){ $serverItems[$server] = array(); $totalItems[$server]=0; if (!isset($itemlist['STAT'])){ continue; } $iteminfo = $itemlist['STAT']; foreach($iteminfo as $keyinfo=>$value){ if (preg_match('/items\:(\d+?)\:(.+?)$/',$keyinfo,$matches)){ $serverItems[$server][$matches[1]][$matches[2]] = $value; if ($matches[2]=='number'){ $totalItems[$server] +=$value; } } } } return array('items'=>$serverItems,'counts'=>$totalItems); } function getMemcacheStats($total=true){ $resp = sendMemcacheCommands('stats'); if ($total){ $res = array(); foreach(array_filter($resp) as $server=>$r){ foreach($r['STAT'] as $key=>$row){ if (!isset($res[$key])){ $res[$key]=null; } switch ($key){ case 'pid': $res['pid'][$server]=$row; break; case 'uptime': $res['uptime'][$server]=$row; break; case 'time': $res['time'][$server]=$row; break; case 'version': $res['version'][$server]=$row; break; case 'pointer_size': $res['pointer_size'][$server]=$row; break; case 'rusage_user': $res['rusage_user'][$server]=$row; break; case 'rusage_system': $res['rusage_system'][$server]=$row; break; case 'curr_items': $res['curr_items']+=$row; break; case 'total_items': $res['total_items']+=$row; break; case 'bytes': $res['bytes']+=$row; break; case 'curr_connections': $res['curr_connections']+=$row; break; case 'total_connections': $res['total_connections']+=$row; break; case 'connection_structures': $res['connection_structures']+=$row; break; case 'cmd_get': $res['cmd_get']+=$row; break; case 'cmd_set': $res['cmd_set']+=$row; break; case 'get_hits': $res['get_hits']+=$row; break; case 'get_misses': $res['get_misses']+=$row; break; case 'evictions': $res['evictions']+=$row; break; case 'bytes_read': $res['bytes_read']+=$row; break; case 'bytes_written': $res['bytes_written']+=$row; break; case 'limit_maxbytes': $res['limit_maxbytes']+=$row; break; case 'threads': $res['rusage_system'][$server]=$row; break; } } } return $res; } return $resp; } function duration($ts) { global $time; $years = (int)((($time - $ts)/(7*86400))/52.177457); $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400)); $weeks = (int)(($rem)/(7*86400)); $days = (int)(($rem)/86400) - $weeks*7; $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24; $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60; $str = ''; if($years==1) $str .= "$years year, "; if($years>1) $str .= "$years years, "; if($weeks==1) $str .= "$weeks week, "; if($weeks>1) $str .= "$weeks weeks, "; if($days==1) $str .= "$days day,"; if($days>1) $str .= "$days days,"; if($hours == 1) $str .= " $hours hour and"; if($hours>1) $str .= " $hours hours and"; if($mins == 1) $str .= " 1 minute"; else $str .= " $mins minutes"; return $str; } // create graphics // function graphics_avail() { return extension_loaded('gd'); } function bsize($s) { foreach (array('','K','M','G') as $i => $k) { if ($s < 1024) break; $s/=1024; } return sprintf("%5.1f %sBytes",$s,$k); } // create menu entry function menu_entry($ob,$title) { global $PHP_SELF; if ($ob==$_GET['op']){ return "
  • $title
  • "; } return "
  • $title
  • "; } function getHeader(){ $commencer_page = charger_fonction('commencer_page', 'inc'); $head = $commencer_page(_T('memoization:memcached_serveur'), "configuration", "cache"); $head .= "


    "; $head .= gros_titre(_T('memoization:memcached_serveur'),'', false); $head .= debut_gauche("",true); $head .= debut_boite_info(true); $head .= _T('memoization:memcached_script'); $head .= fin_boite_info(true); $head .= debut_droite("",true); $head .= "\n

    "._T('memoization:memcached_donnes')."

    "; $head .= << EOB; return $head; } function getFooter(){ global $VERSION; $footer = ''; $footer .= "
    " . fin_gauche() . fin_page(); return $footer; } function getMenu(){ global $PHP_SELF; echo "