Newer
Older
backup-commander / html / svc / main.php
@John Pearcey John Pearcey on 10 Dec 10 KB wip
  1. <?php
  2.  
  3. //apt-get update 1>/dev/null 2>>/root/logs/sys-update.log
  4.  
  5. //$argv[0] is always the name that was used to run the script.
  6. if(count($argv)>1){
  7.  
  8. include __DIR__."/main-svc.php";
  9. if($argv[1]==='stop'){
  10. $rtn = run_main_command( 'f:cli-stop' );
  11. print_r( $rtn );
  12. exit(0);
  13. }
  14. if($argv[1]==='reload'){
  15. $rtn = run_main_command( 'f:cli-reload' );
  16. print_r( $rtn );
  17. exit(0);
  18. }
  19. $rtn = run_main_command( $argv[1] );
  20. print_r( $rtn );
  21. exit(0);
  22.  
  23. }
  24.  
  25. $g_shutdown = false;
  26.  
  27. /*
  28. * This is the backup process CLI application.
  29. *
  30. * Rather than use 2 threads, I've decided to poll. This is because the threading in PHP is no longer supported.
  31. * Instead we should use parallel:
  32. * https://www.php.net/manual/en/parallel.run.php
  33. * which I don't like. Basically, it doesn't look like you can share code (by way of a reference)?!!
  34. *
  35. * So we are polling the RPC layer every 0.1secs. In practice, this will be plenty for the backup app.
  36. *
  37. */
  38. include __DIR__."/../lib/sysVcom.php";
  39.  
  40.  
  41. function debug_print( $msg ){
  42. file_put_contents( __DIR__."/../data/rpc_op.log", $msg, FILE_APPEND);
  43. }
  44.  
  45. debug_print( date_format( date_create("now", new DateTimeZone("UTC")), 'Y-m-d H:i:s') . " started\n");
  46.  
  47. //create the comms file
  48. touch('BU-commander');
  49.  
  50. //remove all queues at startup
  51. clear_IPC( '../svc/BU-commander' );
  52.  
  53. rpc_setGlobals( 'procName', 'BU-commander' );
  54. rpc_setGlobals( 'max_chan', 8182 );
  55. rpc_setGlobals( 'main_pid', getmypid() );
  56.  
  57. print("rpc using ". __DIR__ . "/$g_procName with key = $g_k_caller\n" );
  58.  
  59. // problem is that the paths are not correct wrt this location, so we need to adjust them
  60. include __DIR__."/../inc/paths.php";
  61. $g_database_path = '../' . $g_database_path;
  62.  
  63. $g_data_dir = realpath( __DIR__."/../data" );
  64.  
  65. include "db-reads.php";
  66. include "db-writes.php";
  67. include "file-line-reader.php";
  68.  
  69. // get the latest data and cache it
  70. $g_bu_states = null;
  71. $g_bu_table = array();
  72. $g_bu_colIdxs = array();
  73. getbu_list_all( $g_bu_table, $g_bu_colIdxs, 'order by BU_DATE desc, BU_TIME desc' );
  74. $g_fork_cnt=0;
  75.  
  76. while(true){
  77.  
  78. if($g_shutdown){
  79. print("CLI request to shutdown: googbye!\n");
  80. exit(0);
  81. }
  82.  
  83. $bDidWork = false;
  84. $bDidWork |= check_rpc_clients();
  85. $bDidWork |= check_bu_sched();
  86. if(!$bDidWork){
  87. time_nanosleep( 0, 100000000);
  88. }
  89. }
  90.  
  91. /**
  92. * Clears all cached data. This is called after any DB updates. It can be called at any time since loading is lazy.
  93. */
  94. function clear_cached_data(){
  95. global $g_bu_states;
  96. global $g_bu_table;
  97. $g_bu_states = null;
  98. $g_bu_table = array();
  99. }
  100.  
  101. /**
  102. * Make sure that the main table data is populated
  103. */
  104. function ensure_cached_table(){
  105. global $g_bu_table;
  106. if(count($g_bu_table)>0) return;
  107. $colIdxs = null; //we don't ever reload these
  108. getbu_list_all( $g_bu_table, $colIdxs, 'order by BU_DATE desc, BU_TIME desc' );
  109. }
  110.  
  111. /**
  112. * Return true if the backup was kicked off
  113. */
  114. function check_bu_sched(){
  115. global $g_bu_table;
  116. global $g_bu_colIdxs;
  117. global $g_fork_cnt;
  118.  
  119. if($g_fork_cnt>0){
  120. //we limit to a single fork for now
  121. return;
  122. }
  123.  
  124. //get the first non null entry which is not running
  125. $keys = array_keys( $g_bu_table );
  126. $idfound = 0;
  127. foreach ($keys as $key) {
  128. if( $g_bu_table[$key][ $g_bu_colIdxs['BU_DATE'] ] == '' ) continue; //skip anything without a date
  129. if( $g_bu_table[$key][ $g_bu_colIdxs['BuRunning'] ] == 1 ) continue; //skip any already running
  130. if( $g_bu_table[$key][ $g_bu_colIdxs['BuError'] ] != null ) continue; //skip entries with problems
  131. $idfound = $g_bu_table[$key][ $g_bu_colIdxs['BUID'] ];
  132. }
  133. if($idfound==0) return;
  134. // is it time...?
  135. $str_bu_dt = $g_bu_table[$idfound][ $g_bu_colIdxs['BU_DATE']] . ' ' . $g_bu_table[$idfound][ $g_bu_colIdxs['BU_TIME']] . ':00';
  136. $schedTime = new DateTime( $str_bu_dt, new DateTimeZone("UTC") );
  137. if( $schedTime > new DateTime('now', new DateTimeZone("UTC") ) ){
  138. //...no it's not
  139. return;
  140. }
  141. kickoff_backup( $idfound );
  142.  
  143. //print( "check after forked: running = ". $g_bu_table[$idfound][ $g_bu_colIdxs['BuRunning'] ] . "\n");
  144.  
  145. return true;
  146. }
  147.  
  148. /**
  149. * Kicks off a back up. Returns immediately. Deligates the work to a client fork.
  150. *
  151. * $scheduled:
  152. * true if it is run according to the $schedule info in the db
  153. * false if it has been manually initiated
  154. */
  155. function kickoff_backup( $buid, $scheduled = true, $bu_test = false ){
  156.  
  157. ensure_cached_table();
  158. global $g_bu_table;
  159. global $g_bu_colIdxs;
  160. global $g_fork_cnt;
  161.  
  162. $isRunning = $g_bu_table[$buid][ $g_bu_colIdxs['BuRunning'] ];
  163. if( !$bu_test && $isRunning==1) return array("e:Error", "Backup already running" );
  164. $i=0;
  165. $bu_row[$i++] = $g_bu_table[$buid][ $g_bu_colIdxs['BUName'] ];
  166. $bu_row[$i++] = $g_bu_table[$buid][ $g_bu_colIdxs['Dir_Src'] ];
  167. $bu_row[$i++] = $g_bu_table[$buid][ $g_bu_colIdxs['Dir_Dest'] ];
  168. $bu_row[$i++] = $g_bu_table[$buid][ $g_bu_colIdxs['Files_Ex'] ];
  169. $bu_row[$i++] = $g_bu_table[$buid][ $g_bu_colIdxs['BuType'] ];
  170. $bu_row[$i++] = $isRunning;
  171. // we should be able to use pcntl_fork()
  172. $pid = pcntl_fork();
  173. if ( $pid == -1 ) {
  174. debug_print("fork failed");
  175. return array("e:Error", "fork failed" );
  176. }
  177.  
  178. if ( $pid ) {
  179. // parent process
  180. debug_print( "parentpid = ". getmypid() . "\n" );
  181. $g_bu_table[$buid][ $g_bu_colIdxs['BuRunning'] ] = 1;
  182. return array("ok:", "running" );
  183. } else {
  184. // child process
  185. $g_fork_cnt++;
  186.  
  187. $g_bu_table = array(); //data here is unique to the parent
  188. $pid = getmypid();
  189. debug_print( "childpid = $pid\n" );
  190. debug_print( "kickoff_backup for $buid\n" );
  191. include_once __DIR__."/backup.php";
  192. $bu_result = '';
  193. global $g_data_dir;
  194. $g_data_dir = __DIR__."/../data";
  195. $bu_result = run_backup_block( $buid, $bu_row, $bu_test );
  196. debug_print( "backup ended for $buid, result = $bu_result\n" );
  197. $obj = rpc( 'm:fork-complete', $pid, "backup-complete", $buid, $scheduled ); //we use our rpc mechanism to notify parent
  198. //print_r( $obj );
  199. //print( "\n" );
  200. exit(0);
  201. }
  202. }
  203.  
  204.  
  205.  
  206. $msgArr = null;
  207.  
  208. /**
  209. * Return true if a request was found and handled
  210. */
  211. function check_rpc_clients(){
  212. global $msgArr;
  213. global $g_fork_cnt;
  214.  
  215. $caller_id = 0;
  216. $obj_in = rpc_listen( $caller_id, $msgArr, false );
  217. $obj_out = null;
  218.  
  219. if($caller_id==0) return false; //nothing to do
  220. try{
  221. $obj_out = handle_rpc_req( $obj_in );
  222. }catch( Exception $e ){
  223. debug_print( "Exception: " . $e->getMessage() ."\n" );
  224. $obj_out = array( 'e:Exception', $e );
  225. }
  226. //send msg back
  227. rpc_reply( $caller_id, $obj_out );
  228.  
  229. if( $obj_in[0] === 'm:fork-complete' ){
  230. //clean up child process
  231. debug_print("clean up fork child pid = {$obj_in[1]} \n");
  232. $status;
  233. pcntl_waitpid( $obj_in[1], $status );
  234.  
  235. $g_fork_cnt--;
  236. debug_print("clean up done pid = {$obj_in[1]} \n");
  237.  
  238. //finish any requirements for specific child processes (db updates etc)
  239. if( count($obj_in)>2 && $obj_in[2]="backup-complete"){
  240. backupComplete( $obj_in[3], $obj_in[4] );
  241. }
  242. }
  243. return true;
  244. }
  245.  
  246. /**
  247. * Called by the parent after a client notified that a backup completed.
  248. */
  249. function backupComplete( $buid, $scheduled ){
  250. global $g_bu_table;
  251. global $g_bu_colIdxs;
  252. //reload the table data - client updates the error field (might move this)
  253. clear_cached_data();
  254. $nothanks = null;
  255. getbu_list_all( $g_bu_table, $nothanks, 'order by BU_DATE desc, BU_TIME desc' );
  256.  
  257. if($scheduled){
  258. //if DB error, do nothing
  259. $err = $g_bu_table[$buid][ $g_bu_colIdxs['BuError'] ];
  260. if($err==''){
  261. //update date and time according to the repeat field.
  262. $date = date_create( $g_bu_table[$buid][ $g_bu_colIdxs['BU_DATE'] ] );
  263. $dt_new = null;
  264. switch( $g_bu_table[$buid][ $g_bu_colIdxs['BU_REPEAT'] ] ){
  265. case 'M':
  266. //monthly
  267. date_add( $date, new DateInterval('P1M') );
  268. $dt_new = date_format( $date, "Y-m-d");
  269. break;
  270. case 'W':
  271. //weekly
  272. date_add( $date, new DateInterval('P1W') );
  273. $dt_new = date_format( $date, "Y-m-d");
  274. break;
  275. case 'D':
  276. //daily
  277. date_add( $date, new DateInterval('P1D') );
  278. $dt_new = date_format( $date, "Y-m-d");
  279. break;
  280. }
  281. $g_bu_table[$buid][ $g_bu_colIdxs['BU_DATE'] ] = $dt_new;
  282.  
  283. set_sched_date_db( $buid, $dt_new );
  284. }
  285. }//if manually kicked off - nothing to do
  286.  
  287. //TODO: remove the outputfiles
  288. }
  289.  
  290.  
  291. /**
  292. * Carries out the task requested from any RPC client.
  293. * That client may be from the webserver or from a forked child or any other process.
  294. */
  295. function handle_rpc_req( $obj ){
  296. // The idea will be that the indicator before the colon will be handled generically in cases
  297. // where it is a simple function call, i.e. 'f:' (provided it is a trusted source and/or has been previously checked)
  298. global $g_data_dir;
  299. global $g_shutdown;
  300. if( 'f:getbu_list_state' !== $obj[0] ){
  301. //print_r( $obj );
  302. }
  303. switch( $obj[0] ){
  304. case 'f:bu-test-file-lines':
  305. $buid = $obj[1];
  306. $lineNum = $obj[2];
  307. $lineData = '';
  308. $objFile = getObject_outputFile( $g_data_dir, $buid );
  309. $cnt = getLinesFrom( $objFile, $lineNum, $lineData );
  310. if($cnt==0){
  311. closeFile( $buid );
  312. return array( 'r:bu-test-file-lines', 0 );
  313. }
  314. return array( 'r:bu-test-file-lines', $cnt, $lineData );
  315. case 'm:fork-complete':
  316. //send the same message back to the caller
  317. return $obj;
  318. case 'f:getbu_list_all':
  319. //($table, $colIdxs) = rpc( 'f:getbu_list_all', "order by BUID" );
  320. $table = array();
  321. $colIdxs = array();
  322. getbu_list_all( $table, $colIdxs, $obj[1] );
  323. return array( $table, $colIdxs );
  324. case 'f:getbu_list_state':
  325. global $g_bu_states;
  326. if($g_bu_states==null){
  327. //print("getbu_list_state (refresh\n");
  328. $g_bu_states = getbu_list_state();
  329. }
  330. return $g_bu_states;
  331. case 'f:save_bu_schedule':
  332. $err = save_bu_schedule( $obj[1] );
  333. clear_cached_data();
  334. return array($err);
  335. case 'f:save_bu_item':
  336. //[$err] = rpc( 'f:save_bu_item', $postVars, $bu_id );
  337. $err = save_bu_item( $obj[1], $obj[2] );
  338. clear_cached_data();
  339. return array($err);
  340. case 'f:set_bu_error':
  341. //print("re/setting backup error\n");
  342. $err = set_bu_error_db( $obj[1], $obj[2] );
  343. clear_cached_data();
  344. return array($err);
  345. case 'f:set_bu_running':
  346. $err = set_bu_running_db( $obj[1], $obj[2] );
  347. clear_cached_data();
  348. return array($err);
  349. case 'f:kickoff-backup':
  350. $buid = $obj[1];
  351. $isTest = $obj[2];
  352. return kickoff_backup( $buid, false, $isTest );
  353.  
  354. case 'f:set_last_run_date':
  355. //[$err] = rpc( 'f:set_last_run_date', $buid, $str_dt );
  356. $err = set_last_run_date_db( $obj[1], $obj[2] );
  357. clear_cached_data();
  358. return array($err);
  359.  
  360. case 'f:cli-reload':
  361. clear_cached_data();
  362. return array('ok:reload');
  363.  
  364. case 'f:cli-stop':
  365. $g_shutdown = true;
  366. return array('ok:shutdown');
  367.  
  368. case 'f:delete_bu_item':
  369. //[$err] = rpc( 'f:delete_bu_item', $buid );
  370. $err = delete_bu_item_db( $obj[1] );
  371. clear_cached_data();
  372. return array($err);
  373.  
  374.  
  375. }
  376. array_unshift( $obj, "Unknown Command" );
  377. print_r( $obj );
  378. return $obj;
  379. }
  380.  
  381. ?>