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