<?php
class Page_Backup extends Page
{
/** @var ?string */
private $action = null;
private $templateData = array();
protected function doPreprocess()
{
User::load();
if (!User::isLoggedIn()) {
Message::addError('main.no-permission');
Util::redirect('?do=Main');
}
$this->action = Request::post('action');
if ($this->action === 'backup') {
User::assertPermission("create");
$this->backup();
} elseif ($this->action === 'restore') {
User::assertPermission("restore");
$this->restore();
} elseif ($this->action === 'config') {
User::assertPermission("config");
$this->config();
}
User::assertPermission('*');
}
protected function doRender()
{
if ($this->action === 'restore') { // TODO: We're in post mode, redirect with all the taskids first...
Render::addTemplate('restore', $this->templateData);
} elseif (($taskid = Request::get('errtaskid', false, 'string')) !== false) {
Render::addTemplate('task-error', ['taskid' => $taskid]);
} else {
// Normal page
$title = Property::get('page-title-prefix', '');
$bgcolor = Property::get('logo-background', '');
$lastBackup = (int)Property::get(BackupRestore::PROP_LAST_BACKUP, 0);
if ($lastBackup === 0) {
$lastBackup = false;
} else {
$lastBackup = date('d.m.Y', $lastBackup);
}
$params = [
'id_color' => $bgcolor,
'id_prefix' => $title,
'last_backup' => $lastBackup,
'backup_' . Property::get(BackupRestore::PROP_AUTO_BACKUP_MODE, BackupRestore::BACKUP_MODE_OFF)
. '_checked' => 'checked',
'autoBackupPw' => Property::get(BackupRestore::PROP_AUTO_BACKUP_PASS, ''),
];
Permission::addGlobalTags($params['perms'], NULL, ['create', 'restore', 'config']);
Render::addTemplate('_page', $params);
}
}
private function backup()
{
$password = trim(Request::post('passwd', '', 'string'));
if (empty($password)) {
$password = null;
}
EventLog::info('Creating backup on ' . Property::getServerIp());
$task = Taskmanager::submit('BackupRestore', [
'mode' => 'backup',
'password' => $password,
]);
if (!isset($task['id'])) {
Message::addError('backup-failed');
Util::redirect('?do=Backup');
}
$task = Taskmanager::waitComplete($task, 60000);
if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) {
Util::redirect('?do=backup&errtaskid=' . $task['id']);
}
while ((@ob_get_level()) > 0)
@ob_end_clean();
$fh = @fopen($task['data']['backupFile'], 'rb');
if ($fh === false) {
Message::addError('main.error-read', $task['data']['backupFile']);
Util::redirect('?do=Backup');
}
$userFn = 'satellite-backup_' . Property::getServerIp() . '_' . date('Y.m.d-H.i.s') . '.tgz';
if ($password !== null) {
$userFn .= '.aes';
}
Header('Content-Type: application/octet-stream', true);
Header('Content-Disposition: attachment; filename=' . $userFn);
Header('Content-Length: ' . @filesize($task['data']['backupFile']));
while (!feof($fh)) {
$data = fread($fh, 16000);
if ($data === false) {
EventLog::failure('Could not stream system backup to browser - backup corrupted!');
die("\r\n\nDOWNLOAD INTERRUPTED!\n");
}
echo $data;
@ob_flush();
@flush();
}
@fclose($fh);
@unlink($task['data']['backupFile']);
Property::set(BackupRestore::PROP_LAST_BACKUP, time());
die();
}
private function restore()
{
if (!isset($_FILES['backupfile'])) {
Message::addError('missing-file');
Util::redirect('?do=Backup');
}
if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) {
Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error']));
Util::redirect('?do=Backup');
}
$password = trim(Request::post('passwd', '', 'string'));
if (empty($password)) {
$password = null;
}
$tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tgz';
if (!move_uploaded_file($_FILES['backupfile']['tmp_name'], $tempfile)) {
Message::addError('main.error-write', $tempfile);
Util::redirect('?do=Backup');
}
copy($tempfile, $tempfile . '2');
// Check if correct password first etc.
$task = Taskmanager::submit('BackupRestore', [
'mode' => 'test',
'password' => $password,
'backupFile' => $tempfile . '2',
]);
$task = Taskmanager::waitComplete($task, 5000);
@unlink($tempfile . '2');
if (Taskmanager::isFailed($task)) {
@unlink($tempfile);
Util::redirect('?do=backup&errtaskid=' . $task['id']);
}
// Got uploaded file, now shut down all the daemons etc.
$parent = Trigger::stopDaemons(null, $this->templateData);
// Unmount store
$task = Taskmanager::submit('MountVmStore', array(
'address' => 'null',
'type' => 'images',
'parentTask' => $parent,
'failOnParentFail' => false
));
if (isset($task['id'])) {
$this->templateData['mountid'] = $task['id'];
$parent = $task['id'];
}
// Finally run restore
$task = Taskmanager::submit('BackupRestore', [
'mode' => 'restore',
'password' => $password,
'backupFile' => $tempfile,
'parentTask' => $parent,
'failOnParentFail' => false,
'restoreOpenslx' => Request::post('restore_openslx', 'off') === 'on',
'restoreDozmod' => Request::post('restore_dozmod', 'off') === 'on',
]);
if (isset($task['id'])) {
$this->templateData['restoreid'] = $task['id'];
$parent = $task['id'];
TaskmanagerCallback::addCallback($task, 'dbRestored');
}
// Wait a bit
$task = Taskmanager::submit('SleepTask', array(
'seconds' => 6,
'parentTask' => $parent,
'failOnParentFail' => false
));
if (isset($task['id']))
$parent = $task['id'];
// Reboot
$task = Taskmanager::submit('Reboot', array(
'parentTask' => $parent,
'failOnParentFail' => false
));
// Leave this comment so the i18n scanner finds it:
// Message::addSuccess('restore-done');
if (isset($task['id'])) {
$this->templateData['rebootid'] = $task['id'];
}
}
private function config()
{
$password = trim(Request::post('passwd', '', 'string'));
if (empty($password)) {
$password = null;
}
$mode = Request::post('auto-backup-mode', false, 'string');
if ($mode !== BackupRestore::BACKUP_MODE_OFF && $mode !== BackupRestore::BACKUP_MODE_ROOTHOME
&& $mode !== BackupRestore::BACKUP_MODE_VMSTORE) {
Message::addError('invalid-auto-backup-mode', $mode);
Util::redirect('?do=backup');
}
Property::set(BackupRestore::PROP_AUTO_BACKUP_MODE, $mode);
Property::set(BackupRestore::PROP_AUTO_BACKUP_PASS, $password);
Util::redirect('?do=backup');
}
}