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