function takeIncrementalBackupSnapshot(backupJob $job, $snapshotGroup, $seedSnap)
 {
     global $config;
     $scheduledBackup = $job->getScheduledBackup();
     /****************************
     	 		  TAKING INCREMENTAL BACKUP
     			****************************/
     // Create object for the lock...
     $runningBackup = new runningBackup();
     $runningBackup->setInfoLogStream($this->infolog);
     // Get scheduledBackup info
     $sbInfo = $scheduledBackup->getInfo();
     // Get scheduledBackup host
     $sbHost = $scheduledBackup->getHost();
     // get host info
     $hostInfo = $sbHost->getInfo();
     $mostRecentSnap = $snapshotGroup->getMostRecentCompletedBackupSnapshot();
     // Initialize a backup for sbHost for scheduledBackup
     $runningBackup->init($sbHost, $scheduledBackup);
     // Release our queue tickets
     $queueManager = new queueManager();
     $queueManager->setLogStream($this->log);
     foreach ($this->ticketsToReleaseOnStart as $ticket) {
         $queueManager->releaseTicket($ticket);
     }
     try {
         $lsn = $seedSnap->getLsn();
         // Create new object shell
         $snapshot = new backupSnapshot();
         // Init the snapshot here and use the ID for the tempdirname
         $snapshot->init($scheduledBackup, 'DELTA', 'INCREMENTAL', $snapshotGroup, $mostRecentSnap->id);
         // Get runningBackup info
         $rbInfo = $runningBackup->getInfo();
         // Start another TRY block so we can catch any exceptions and clean up our snapshot status.
         try {
             $snapshotInfo = $snapshot->getInfo();
             $tempDir = $runningBackup->getStagingTmpdir();
             // Build the command to run the snapshot into the staging dir
             $xbBinary = $scheduledBackup->getXtraBackupBinary();
             // Command should look like this:
             $xbCommand = "ssh -o StrictHostKeyChecking=no -p " . $hostInfo['ssh_port'] . " " . $sbInfo['backup_user'] . "@" . $hostInfo['hostname'] . " 'cd {$tempDir} ; innobackupex --ibbackup=" . $xbBinary . " --slave-info --incremental-lsn=" . $lsn . " " . $tempDir . "/deltas" . " --user="******" --safe-slave-backup " . " --password="******" --no-timestamp --incremental --throttle=" . $scheduledBackup->getXtraBackupThrottleValue() . " 1>&2 '";
             // Set up how we'll interact with the IO file handlers of the process
             $xbDescriptors = array(0 => array('file', '/dev/null', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
             // Connect to the remote host and create the backup into a staging directory then copy it back via netcat and tar
             ////////
             ////////
             // KICK OFF XTRABACKUP INCREMENTAL //
             ////////
             ////////
             // Set the state of the snapshot to RUNNING
             $snapshot->setStatus('RUNNING');
             // Info output
             $this->infolog->write("Staging an INCREMENTAL xtrabackup snapshot of " . $sbInfo['datadir_path'] . " via ssh: " . $sbInfo['backup_user'] . "@" . $hostInfo['hostname'] . " to " . $tempDir . "/deltas...", XBM_LOG_INFO);
             // DEBUG
             $this->infolog->write("Attempting to run the incremental backup with command:\n" . preg_replace('/--password=(.+?)\\s+/', '--password=XXXXXXX ', $xbCommand) . " \n", XBM_LOG_DEBUG);
             // Start the xtrabackup process
             $xbProc = proc_open($xbCommand, $xbDescriptors, $xbPipes);
             // Check that we launched OK.
             if (!is_resource($xbProc)) {
                 throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: Unable to use ssh to start xtrabackup with: {$xbCommand} .");
             }
             // Check the status of the backup every second...
             $streamContents = '';
             stream_set_blocking($xbPipes[2], 0);
             do {
                 $streamContents .= stream_get_contents($xbPipes[2]);
                 if (!($xbStatus = proc_get_status($xbProc))) {
                     throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: Unable to retrieve status on backup process.");
                 }
                 if ($job->isKilled()) {
                     throw new KillException('The backup was killed by an administrator.');
                 }
                 sleep(1);
             } while ($xbStatus['running']);
             if ($xbStatus['exitcode'] != 0) {
                 throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: There was an error backing up - The process returned code " . $xbStatus['exitcode'] . "." . " The output from the backup is as follows:\n" . $streamContents);
             }
             /*print_r($xbStatus);
             		echo "\nWe got STDOUT: \n";
             		echo stream_get_contents($xbPipes[1]);
             		echo "\nWe got STDERR: \n";
             		echo stream_get_contents($xbPipes[2]); */
             $this->infolog->write("XtraBackup completed staging the backup with the following output:\n" . $streamContents, XBM_LOG_INFO);
             // Copy it to local machine
             $path = $snapshot->getPath();
             // Fire up a netcat listener
             // Set the command we plan to run
             $ncBuilder = new netcatCommandBuilder();
             $ncServer = $ncBuilder->getServerCommand($rbInfo['port']);
             $ncLogfile = $config['LOGS']['logdir'] . '/hosts/' . $hostInfo['hostname'] . '.netcat.log';
             // Change to the dir where we want to put the files
             $ncCommand = ' cd ' . $path . ' ; ';
             // Check to see if there us a GNU tar - used to make XBM work on Solaris 11, since their tar doesn't work correctly
             $ncCommand .= ' if [ -f /usr/gnu/bin/tar ]; then TAR="/usr/gnu/bin/tar"; else TAR=tar; fi; ';
             // Now start the netcat listener and pipe through the auto-detected tar command
             $ncCommand .= $ncServer . ' | $TAR xvf - >> ' . $ncLogfile . ' 2>&1';
             // Open the process with a stream to read from it
             // Set up how we'll interact with the IO file handlers of the process
             $ncDescriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
             // Open the netcat listener proc
             $this->infolog->write("Attempting to run netcat with command: " . $ncCommand, XBM_LOG_DEBUG);
             $ncProc = proc_open($ncCommand, $ncDescriptors, $ncPipes);
             if (!is_resource($ncProc)) {
                 throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: Unable to start netcat with command: {$ncCommand} .");
             }
             // Info output
             $this->infolog->write("Started Netcat (nc) listener on port " . $rbInfo['port'] . " to receive backup tar stream into directory {$path} ...", XBM_LOG_INFO);
             $ncClient = $ncBuilder->getClientCommand($config['SYSTEM']['xbm_hostname'], $rbInfo['port']);
             // Copy the backup back via the netcat listener
             $copyCommand = "ssh -o StrictHostKeyChecking=no -p " . $hostInfo['ssh_port'] . " " . $sbInfo['backup_user'] . "@" . $hostInfo['hostname'] . " 'cd " . $tempDir . "/deltas; tar cvf - . | " . $ncClient . " '";
             // Set the state of the snapshot to COPYING
             $snapshot->setStatus('COPYING');
             // Set up how we'll interact with the IO file handlers of the process
             $copyDescriptors = array(0 => array('file', '/dev/null', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
             // Start the xtrabackup process
             $this->infolog->write("Attempting to run copy with command: " . $copyCommand, XBM_LOG_DEBUG);
             $copyProc = proc_open($copyCommand, $copyDescriptors, $copyPipes);
             // Check that we launched OK.
             if (!is_resource($copyProc)) {
                 throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: Unable to use ssh to start copy with: {$copyCommand} .");
             }
             // Check the status of the backup every second...
             $streamContents = '';
             stream_set_blocking($copyPipes[2], 0);
             do {
                 $streamContents .= stream_get_contents($copyPipes[2]);
                 if (!($copyStatus = proc_get_status($copyProc))) {
                     throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: Unable to retrieve status on copy process.");
                 }
                 if ($job->isKilled()) {
                     throw new KillException('The backup was killed by an administrator.');
                 }
                 sleep(1);
             } while ($copyStatus['running']);
             if ($copyStatus['exitcode'] != 0) {
                 throw new Exception('genericBackupTaker->takeIncrementalBackupSnapshot: ' . "Error: There was an error copying files - The process returned code " . $copyStatus['exitcode'] . "." . " The output from the backup is as follows:\n" . $streamContents);
             }
             /* For debugging...
             			print_r($copyStatus);
             			echo "\nWe got STDOUT: \n";
             			echo stream_get_contents($copyPipes[1]);
             			echo "\nWe got STDERR: \n";
             			echo stream_get_contents($copyPipes[2]); 
             			*/
             // Close out the copy and netcat processes
             proc_close($copyProc);
             proc_close($ncProc);
             // Set the snapshot time to be now
             $snapshot->setSnapshotTime(date('Y-m-d H:i:s'));
             // Set the state of the snapshot to COMPLETED
             $snapshot->setStatus('COMPLETED');
             $this->infolog->write("Completed copying the backup via netcat with the following output:\n" . $streamContents, XBM_LOG_INFO);
         } catch (Exception $e) {
             if (get_class($e) == 'KillException') {
                 $this->infolog->write("The backup job was killed by an administrator. Aborting...", XBM_LOG_ERROR);
             }
             // If we had a netcat process, check to see if we need to kill it/clean up
             if (isset($ncProc) && is_resource($ncProc)) {
                 proc_terminate($ncProc);
             }
             // Remove the snapshot files and mark it as failed.
             if ($config['SYSTEM']['cleanup_on_failure'] == true) {
                 $this->infolog->write("Cleaning up files...", XBM_LOG_INFO);
                 $snapshot->deleteFiles();
             } else {
                 $this->infolog->write("Skipping cleanup as cleanup_on_failure is turned off...", XBM_LOG_INFO);
             }
             $snapshot->setStatus('FAILED');
             throw $e;
         }
     } catch (Exception $e) {
         // Clean up the running backup entry and rethrow error..
         $runningBackup->finish();
         throw $e;
     }
     // Clean up after ourselves..
     // release our lock on the netcat port, scheduled backup, etc.
     $runningBackup->finish();
     return $snapshot;
 }
 function mergeSnapshots($seedSnapshot, $deltaSnapshot)
 {
     // Create a new snapshot entry
     // Find the scheduled backup we are working in
     $scheduledBackup = $seedSnapshot->getScheduledBackup();
     // Init a new snapshot to be the SEED for the same snapshot group
     $mergeSnapshot = new backupSnapshot();
     $mergeSnapshot->init($scheduledBackup, 'SEED', 'MERGE', $seedSnapshot->getSnapshotGroup());
     // Set status to merging
     $mergeSnapshot->setStatus('MERGING');
     // Merge incremental over seed
     // Get paths for seed and delta
     $seedPath = $seedSnapshot->getPath();
     $deltaPath = $deltaSnapshot->getPath();
     // Find the xtrabackup binary to use
     $xbBinary = $scheduledBackup->getXtraBackupBinary();
     // Merge the snapshots by their paths
     $this->mergePaths($seedPath, $deltaPath, $xbBinary);
     // We have a backup entry with a directory - we will need to remove it before we rename
     $mergePath = $mergeSnapshot->getPath();
     if ($mergePath == '/' || strlen($mergePath) == 0) {
         throw new Exception('backupSnapshotMerger->mergeSnapshots: ' . "Error: Detected unsafe path to remove: {$mergePath}");
     }
     if (!rmdir($mergePath)) {
         throw new Exception('backupSnapshotMerger->mergeSnapshots: ' . "Error: Unable to rmdir on: {$mergePath}");
     }
     // Rename the directory in place
     if (!rename($seedPath, $mergePath)) {
         throw new Exception('backupSnapshotMerger->mergeSnapshots: ' . "Error: Could not move seed from {$seedPath} to {$mergePath} - rename() failed.");
     }
     unset($output);
     unset($returnVar);
     // Remove the incremental files
     // rm -rf on $deltaSnapshot->getPath...
     $rmCmd = 'rm -rf ' . $deltaPath;
     exec($rmCmd, $output, $returnVar);
     if ($returnVar != 0) {
         throw new Exception('backupSnapshotMerger->mergeSnapshots: ' . "Error: Could not remove old deltas with command: {$rmCmd} -- Got output:\n" . implode("\n", $output));
     }
     // Set the time to the time of the $deltaSnapshot
     $deltaInfo = $deltaSnapshot->getInfo();
     $mergeSnapshot->setSnapshotTime($deltaInfo['snapshot_time']);
     // Set any snapshot with the parent id of the merged delta to now have the parent id of the new merge snapshot
     // get mergeInfo first
     $mergeInfo = $mergeSnapshot->getInfo();
     // reassign the children of the seed the new parent (merge)
     $deltaSnapshot->assignChildrenNewParent($mergeInfo['backup_snapshot_id']);
     // Set the status of the delta to MERGED
     $deltaSnapshot->setStatus('MERGED');
     // Set the status of the seed snapshot to MERGED
     $seedSnapshot->setStatus('MERGED');
     // Set status to COMPLETED
     $mergeSnapshot->setStatus('COMPLETED');
     return true;
 }