Guest User

JPI

a guest
Jul 4th, 2013
277
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/php -q
  2. <?php
  3. /**
  4.  * @author Patrik Hartwig
  5.  * @copyright 2013
  6.  * Version 2.0.2
  7.  * nohup php /jpi/jpi < /dev/null > /dev/null 2>&1 &
  8.  */
  9. error_reporting(E_ERROR | E_WARNING | E_PARSE);
  10.  
  11. $jpiPool = "/jpi/putpath"; //path to storage pool;
  12.  
  13. $jpiStore[] = "/mnt/first"; //path to storage point;
  14. $jpiStore[] = "/mnt/second"; //path to storage point;
  15. $jpiStore[] = "/mnt/third"; //path to storage point;
  16.  
  17. //Dont Edit Below this line.
  18.  
  19. $script_path = dirname(__FILE__);
  20. chdir($script_path);
  21.  
  22. if($argv[1] == "status") {
  23.     $total = 0;
  24.     $total_free = 0;
  25.    
  26.     function bytesToSize1024($bytes, $precision = 2)
  27.     {
  28.         $unit = array('B','KB','MB','GB','TB','PB','EB');
  29.         return @round(
  30.             $bytes / pow(1024, ($i = floor(log($bytes, 1024)))), $precision
  31.         ).$unit[$i];
  32.     }
  33.    
  34.     foreach($jpiStore as $value) {
  35.         if(is_dir($value)) {
  36.             $total_free = $total_free + disk_free_space($value);
  37.             $total = $total + disk_total_space($value);
  38.             echo $value."\t".' => '."\t".bytesToSize1024(disk_free_space($value)).'/'.bytesToSize1024(disk_total_space($value))."\t".round((disk_free_space($value)/disk_total_space($value))*100)."%\n";
  39.         }
  40.     }
  41.     echo "Total\t".' => '."\t".bytesToSize1024($total_free).'/'.bytesToSize1024($total)."\t".round(($total_free/$total)*100)."%\n";
  42.     exit;
  43. }
  44.  
  45. if($argv[1] == "fix") jpiFix();
  46.  
  47. if($argv[1] == "stop") {
  48.     fwrite(STDOUT, "Stopping JPI Daemon\n");
  49.     shell_exec("kill -9 $(ps aux | grep '[j]pi dae' | awk '{print $2}')");
  50.     exit;
  51. }
  52. if($argv[1] == "restart") {
  53.     global $script_path;
  54.     shell_exec("kill $(ps aux | grep '[j]pi dae' | awk '{print $2}')");
  55.     fwrite(STDOUT, "Stopping JPI Daemon\n");
  56. }
  57.  
  58. if($argv[1] != "dae") {
  59.     fwrite(STDOUT, "Starting JPI Daemon\n");
  60.     sleep(1);
  61.     shell_exec("nohup php ".__FILE__." dae < /dev/null > /dev/null 2>&1 &");
  62.     exit;
  63. }
  64.  
  65. //Pre Checks/Runs
  66. //Fix file system
  67. if(!is_dir($jpiPool)) jpiLog('Pool path "'.$jpiPool.'" is not a Directory, if it was there and isnt now i suggest "jpi fix" command', 'exit');
  68. $jpiIndex = getIndex();
  69.  
  70.  
  71.  
  72. //functions
  73.  
  74. function jpiFix() {
  75.     global $jpiPool;
  76.     global $jpiIndex;
  77.     $jpiIndex = getIndex();
  78.     if(count($jpiIndex) < 1) {
  79.         fwrite(STDOUT, 'Index file is empty attempting to rebuild.'."\n");
  80.         rebuildIndex();
  81.         if(count($jpiIndex) < 1) {
  82.             fwrite(STDOUT, 'Index rebuild failed.'."\n");
  83.         }
  84.     } else {
  85.         fwrite(STDOUT, 'Index is in good shape :).'."\n");
  86.     }
  87.     if(!is_dir($jpiPool)) {
  88.         fwrite(STDOUT, 'jpiPool is not a directory trying to recreate.'."\n");
  89.         if(mkdir($jpiPool)) {
  90.             fwrite(STDOUT, 'Success creating jpiPool Directory.'."\n");
  91.         }else{
  92.             fwrite(STDOUT, 'failed Creating jpiPool Directory, check the jpiPool directory exists and run "jpi fix" again.'."\n");
  93.             exit;
  94.         }
  95.     }
  96.     if((count(scandir($jpiPool)) == 2)&&(count($jpiIndex) > 1)) {
  97.         fwrite(STDOUT, 'jpiPool seems to be empty, refilling from index'."\n");
  98.         foreach($jpiIndex as $key => $value) {
  99.             foreach(jpiStore($key) as $skey => $sval) {
  100.                 if($key == $skey) {
  101.                     $blank = '';
  102.                     foreach(explode(DIRECTORY_SEPARATOR, dirname($value)) as $dir){
  103.                         $blank .= DIRECTORY_SEPARATOR.$dir;
  104.                         if(!is_dir($blank)) mkdir($blank);
  105.                     }
  106.                     symlink($sval, $value);
  107.                 }
  108.             }
  109.         }
  110.     }
  111.     fwrite(STDOUT, 'All should be in good order now! :D'."\n");
  112.     exit;
  113. }
  114.  
  115. function getIndex() {
  116.     $jpiIndex = '';
  117.     foreach(sList() as $sl) {
  118.         $nIndex = @file_get_contents($sl.DIRECTORY_SEPARATOR.'jpiIndex.json');
  119.         $jpiIndex = (strlen($nIndex) > strlen($jpiIndex)) ? $nIndex : $jpiIndex;
  120.     }
  121.     $jpiIndex = json_decode($jpiIndex, true);
  122.     if(!is_array($jpiIndex)) $jpiIndex = array();
  123.     return $jpiIndex;
  124. }
  125. $jpiLastError = '';
  126. function jpiLog($text, $output = false) {
  127.     global $jpiLastError;
  128.     if($text == $jpiLastError) return false;
  129.     $jpiLastError = $text;
  130.     if($output) fwrite(STDOUT, $text."\n");
  131.     fwrite(fopen('/var/log/jpi.log', 'a'), date("Y-m-d H:i:s").' - '.$text."\n");
  132.     if($output === 'exit') {
  133.         jpiDex();
  134.         exit;
  135.     }else {
  136.         okay();
  137.     }
  138. }
  139.  
  140. function okay() {
  141.     global $argv, $jpiIndex, $jpiPool;
  142.     if($argv[1] == 'fix') return false;
  143.     if(!is_dir($jpiPool)) jpiLog('Pool is Missing! run "jpi fix" to rebuild pool.', 'exit'); //checks to see pool hasnt gone anywhere
  144.     if((count(scandir($jpiPool)) == 2)&&(count($jpiIndex) > 1)) jpiLog('Pool is Missing! run "jpi fix" to rebuild pool.', 'exit'); //checks to see pool isnt empty if index
  145.     if((count(scandir($jpiPool)) > 2)&&(count($jpiIndex) < 1)&&(count(scandir(end(slist()))) > 10)) jpiLog('Index seems to be missing! run "jpi fix" to rebuild index.', 'exit'); //checks to see pool isnt empty if index
  146. }
  147.  
  148. function sList() {
  149.     global $jpiStore;
  150.     shuffle($jpiStore);
  151.     if(is_array($jpiStore)) foreach($jpiStore as $path) if((is_dir($path))&&(is_writable($path))) $ret[] = rtrim(realpath($path), DIRECTORY_SEPARATOR);
  152.     if(is_array($jpiStore)) foreach($jpiStore as $path) if((!is_dir($path))OR(!is_writable($path))) jpiLog('store path "'.$path.'" is not a valid directory', true);
  153.     foreach($ret as $spath) if(!is_dir($spath.DIRECTORY_SEPARATOR.'recycle')) mkdir($spath.DIRECTORY_SEPARATOR.'recycle');
  154.     return $ret;
  155. }
  156.  
  157. function jpiCode($str) {
  158.     global $jpiIndex;
  159.     if(preg_match('/^[a-f0-9]{32}$/', substr($str,3))) return $jpiIndex[$str];
  160.     $hash = 'jpi'.md5($str);
  161.     while(isset($jpiIndex[$hash])) $hash = 'jpi'.md5($str).rand(0,999);
  162.     $jpiIndex[$hash] = $str;
  163.     return $hash;
  164. }
  165.  
  166. function jpiLocation($sL = 0) {
  167.     foreach(sList() as $path) {
  168.         $dF = disk_free_space($path);
  169.         $aPath = ($dF > $sL) ? $path : $aPath;
  170.         $sL = ($dF > $sL) ? $dF : $sL;
  171.     }
  172.     return $aPath;
  173. }
  174.  
  175. function jpiMove($file) {
  176.     if(!file_exists($file)) return false;
  177.     if(is_link($file)) return false;
  178.     $from = $file;
  179.     $to = jpiLocation().DIRECTORY_SEPARATOR.jpiCode($file);
  180.     jpiLog('Storing file '.$file.'" => "'.$to.'"', true);
  181.     copy($from, $to);
  182.     if(!file_exists($to)) {
  183.         exit('Failed to store "'.$from.'" to '.$to."\n");
  184.     }else{
  185.         unlink((filesize($from) == filesize($to)) ? $from : $to);
  186.         if(file_exists($to)) symlink($to, $from);
  187.         $from = $to;
  188.         preg_match('/\[RL([1,2,3,4,5])\]/', $file, $match);
  189.         $noDupe[] = dirname($to);
  190.         while($match[1] > 0) {
  191.             global $jpiStore;
  192.             $match[1] = ((count($jpiStore)-1) < $match[1]) ? (count($jpiStore)-1) : $match[1];
  193.             $to = end(sList()).DIRECTORY_SEPARATOR.str_replace('jpi', 'RL'.$match[1], basename($to));
  194.             if(!in_array(dirname($to), $noDupe)) {
  195.                 sleep(1);
  196.                 jpiLog('Storing redundant "'.$file.'" => "'.$to.'"', true);
  197.                 copy($from, $to);
  198.                 $noDupe[] = dirname($to);
  199.                 if(filesize($from) == filesize($to)) $match[1]--;
  200.             }
  201.         }
  202.         unset($noDupe);
  203.     }
  204. }
  205.  
  206. function jpiFileList($dir) {
  207.     global $jpiPool;
  208.     $dir = rtrim($dir, '/');
  209.     $contents = array();
  210.     okay();
  211.     foreach(scandir($dir) as $node) {
  212.         if ($node == '.')  continue;
  213.         if ($node == '..') continue;
  214.         if (is_dir($dir.DIRECTORY_SEPARATOR.$node)) {
  215.             $contents = array_merge($contents, jpiFileList($dir.DIRECTORY_SEPARATOR.$node));
  216.         } else {
  217.             $contents[] = $dir.DIRECTORY_SEPARATOR.$node;
  218.         }
  219.     }
  220.     return $contents;
  221. }
  222.  
  223. function jpiStore($key = null) {
  224.     $contents = array();
  225.     $nc = array();
  226.     foreach(sList() as $path) $contents = array_merge($contents, jpiFileList($path));
  227.     foreach($contents as $path) {
  228.         $bn = basename($path);
  229.         if(!$key) {
  230.             if(preg_match('/^[a-f0-9]{32}$/', substr($bn,3))) $nc[$bn] = $path;
  231.         }else{
  232.             if(substr($bn, 3) == substr($key, 3)) $nc[$bn] = $path;
  233.         }
  234.     }
  235.     return $nc;
  236. }
  237.  
  238. function rebuildIndex() {  //rebuilds index if need be, should never be run but its just incase.
  239.     global $jpiIndex, $jpiPool;
  240.     $jpiIndex = array();
  241.     foreach(jpiFileList($jpiPool) as $file) {
  242.         if(is_link($file)) {
  243.            $bn = basename(readlink($file));
  244.            $jpiIndex["$bn"] = $file;
  245.         }
  246.     }
  247. }
  248.  
  249. function jpiCheck() { //checks for missing files and replaces redundants
  250.     global $jpiIndex;
  251.     foreach($jpiIndex as $key => $file) {
  252.         if(!is_link($file)) continue;
  253.         if(!file_exists(@readlink($file))){
  254.             preg_match('/\[RL([1,2,3,4,5])\]/', $file, $match);    
  255.             if($match[1] > 0) {
  256.                 $jpiStore = jpiStore($key);
  257.                
  258.                 foreach($jpiStore as $jkey => $red) {
  259.                     if(is_writable(dirname(readlink($file)))) {
  260.                         jpiLog('Missing Source for "'.$file.'", putting back "'.basename($red).'"', true);
  261.                         copy($red, readlink($file));
  262.                         break;
  263.                     }else{
  264.                         jpiLog('Missing Source for "'.$file.'", putting back "'.basename($red).'"', true);
  265.                         unlink($file);
  266.                         rename($red, dirname($red).DIRECTORY_SEPARATOR.'jpi'.substr($jkey, 3));
  267.                         symlink(dirname($red).DIRECTORY_SEPARATOR.'jpi'.substr($jkey, 3), $file);
  268.                     }
  269.                 }
  270.             }else{
  271.                 //source files is missing and wasnt redundant, not much we can do except hope it comes back one day. will be a good day
  272.             }
  273.         }
  274.     }
  275.  
  276. }
  277.  
  278. function jpiSyncRed() { //sync modified redunant files
  279.     $jpiStore = jpiStore();
  280.     foreach($jpiStore as $key => $path) {
  281.         if(substr($key, 0, 2) == 'RL') {
  282.             foreach($jpiStore as $ck => $checks) {
  283.                 if((strstr($ck, substr($key, 3)))&&(substr($ck, 0, 3) == "jpi")){
  284.                     if((filesize($path) != filesize($checks))&&(file_exists($checks))) {
  285.                         jpiLog('Updating redunant modified source', true);
  286.                         copy($checks, $path);
  287.                     }
  288.                 }
  289.             }
  290.         }
  291.     }
  292. }
  293.  
  294. function jpiRenDel() { //checks for renamed or deleted files
  295.     global $jpiIndex, $jpiPool;
  296.     if(!is_array($jpiIndex)) return false;
  297.     foreach($jpiIndex as $key => $file) {
  298.         if(!is_link($file)) {
  299.             foreach(jpiFileList($jpiPool) as $chLink) {
  300.                 if(basename(readlink($chLink)) == $key) {
  301.                     jpiLog('Link renamed, updating index.', true);
  302.                     $jpiIndex[$key] = $chLink;
  303.                     break;
  304.                 }
  305.             }
  306.             if($jpiIndex[$key] == $file){
  307.                 $jpiStore = jpiStore($key);
  308.                 if(file_exists($jpiStore[$key])) {
  309.                     jpiLog('Link deleted, recycling source file', true);
  310.                     foreach($jpiStore as $jkey => $jval) {
  311.                         rename($jval, dirname($jval).DIRECTORY_SEPARATOR.'recycle'.DIRECTORY_SEPARATOR.basename($file));
  312.                         unset($jpiIndex[$key]);
  313.                     }
  314.                 }
  315.             }
  316.         }
  317.     }
  318. }
  319.  
  320. function jpiDex() {
  321.     global $jpiIndex;
  322.    
  323.     if(is_array($jpiIndex)) {
  324.         fwrite(fopen(end(sList()).DIRECTORY_SEPARATOR.'jpiIndex.json', 'w+'), json_encode($jpiIndex));
  325.     }else{
  326.         return false;
  327.     }
  328. }
  329.  
  330. //start Jpi loop
  331. while(1) {
  332.     foreach(jpiFileList($jpiPool) as $file) {
  333.         jpiMove($file);
  334.     }
  335.     jpiRenDel();
  336.     jpiDex();
  337.     jpiSyncRed();
  338.     jpiCheck();
  339. }
  340.  
  341. ?>
RAW Paste Data