Falko Timme
2013-11-21 0564653685f7fb1aebc1556361ad26e74076f750
- Added options to use a mounted backup directory. This allows for remote backups, e.g. vis sshfs to a backup server. The backup script checks if the backup directory is mounted, and if not, tries to mount it.
7 files modified
544 ■■■■■ changed files
install/tpl/server.ini.master 2 ●●●●● patch | view | raw | blame | history
interface/web/admin/form/server_config.tform.php 14 ●●●●● patch | view | raw | blame | history
interface/web/admin/lib/lang/de_server_config.lng 2 ●●●●● patch | view | raw | blame | history
interface/web/admin/lib/lang/en_server_config.lng 2 ●●●●● patch | view | raw | blame | history
interface/web/admin/templates/server_config_server_edit.htm 10 ●●●●● patch | view | raw | blame | history
server/cron_daily.php 413 ●●●● patch | view | raw | blame | history
server/plugins-available/backup_plugin.inc.php 101 ●●●●● patch | view | raw | blame | history
install/tpl/server.ini.master
@@ -15,6 +15,8 @@
loglevel=2
admin_notify_events=1
backup_dir=/var/backup
backup_dir_is_mount=n
backup_dir_mount_cmd=
backup_mode=rootgz
monit_url=
monit_user=
interface/web/admin/form/server_config.tform.php
@@ -171,6 +171,20 @@
            'width' => '40',
            'maxlength' => '255'
        ),
        'backup_dir_is_mount' => array(
            'datatype' => 'VARCHAR',
            'formtype' => 'CHECKBOX',
            'default' => 'n',
            'value' => array(0 => 'n', 1 => 'y')
        ),
        'backup_dir_mount_cmd' => array(
            'datatype' => 'VARCHAR',
            'formtype' => 'TEXT',
            'default' => '',
            'value' => '',
            'width' => '40',
            'maxlength' => '255'
        ),
        'backup_mode' => array(
            'datatype' => 'VARCHAR',
            'formtype' => 'SELECT',
interface/web/admin/lib/lang/de_server_config.lng
@@ -188,4 +188,6 @@
$wb['munin_password_txt'] = 'Munin-Passwort';
$wb['munin_url_error_regex'] = 'Ungültige Munin-URL';
$wb['munin_url_note_txt'] = 'Platzhalter:';
$wb['backup_dir_is_mount_txt'] = 'Backupverzeichnis ist ein eigener Mount?';
$wb['backup_dir_mount_cmd_txt'] = 'Mount-Befehl, falls Backupverzeichnis nicht gemountet';
?>
interface/web/admin/lib/lang/en_server_config.lng
@@ -188,4 +188,6 @@
$wb['munin_password_txt'] = 'Munin Password';
$wb['munin_url_error_regex'] = 'Invalid Munin URL';
$wb['munin_url_note_txt'] = 'Placeholder:';
$wb['backup_dir_is_mount_txt'] = 'Backup directory is a mount?';
$wb['backup_dir_mount_cmd_txt'] = 'Mount command, if backup directory not mounted';
?>
interface/web/admin/templates/server_config_server_edit.htm
@@ -59,6 +59,16 @@
                <label for="backup_dir">{tmpl_var name='backup_dir_txt'}</label>
                <input name="backup_dir" id="backup_dir" value="{tmpl_var name='backup_dir'}" size="40" maxlength="255" type="text" class="textInput" />
            </div>
            <div class="ctrlHolder">
                <p class="label">{tmpl_var name='backup_dir_is_mount_txt'}</p>
                <div class="multiField">
                    {tmpl_var name='backup_dir_is_mount'}
                </div>
            </div>
            <div class="ctrlHolder">
                <label for="backup_dir_mount_cmd">{tmpl_var name='backup_dir_mount_cmd_txt'}</label>
                <input name="backup_dir_mount_cmd" id="backup_dir_mount_cmd" value="{tmpl_var name='backup_dir_mount_cmd'}" size="40" maxlength="255" type="text" class="textInput" />
            </div>
            <div class="ctrlHolder">
                <label for="backup_mode">{tmpl_var name='backup_mode_txt'}</label>
                <select name="backup_mode" id="backup_mode" class="selectInput">
server/cron_daily.php
@@ -562,7 +562,6 @@
$sql = "SELECT domain_id, domain, document_root, system_user FROM web_domain WHERE server_id = ".$conf['server_id'];
$records = $app->db->queryAllRecords($sql);
$app->uses('system');
if(is_array($records)) {
    foreach($records as $rec){
        $tmp_path = realpath(escapeshellcmd($rec['document_root'].'/tmp'));
@@ -1104,225 +1103,245 @@
    } else {
        chmod(escapeshellcmd($backup_dir), $backup_dir_permissions);
    }
    $sql = "SELECT * FROM web_domain WHERE server_id = ".$conf['server_id']." AND (type = 'vhost' OR type = 'vhostsubdomain')";
    $records = $app->db->queryAllRecords($sql);
    if(is_array($records)) {
        foreach($records as $rec) {
            //* Do the website backup
            if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
                $web_path = $rec['document_root'];
                $web_user = $rec['system_user'];
                $web_group = $rec['system_group'];
                $web_id = $rec['domain_id'];
                $web_backup_dir = $backup_dir.'/web'.$web_id;
                if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750);
                chmod($web_backup_dir, 0750);
                //if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
                chown($web_backup_dir, $rec['system_user']);
                chgrp($web_backup_dir, $rec['system_group']);
                /*} else {
                    chown($web_backup_dir, 'root');
                    chgrp($web_backup_dir, 'root');
                }*/
                $backup_excludes = '';
                $b_excludes = explode(',', trim($rec['backup_excludes']));
                if(is_array($b_excludes) && !empty($b_excludes)){
                    foreach($b_excludes as $b_exclude){
                        $b_exclude = trim($b_exclude);
                        if($b_exclude != ''){
                            $backup_excludes .= ' --exclude='.escapeshellarg($b_exclude);
                        }
                    }
                }
                if($backup_mode == 'userzip') {
                    //* Create a .zip backup as web user and include also files owned by apache / nginx user
                    $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.zip';
                    exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print 2> /dev/null | zip -b /tmp --exclude=backup\*'.$backup_excludes.' --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                    if($retval == 0) exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -user '.escapeshellarg($http_server_user).' -print 2> /dev/null | zip -b /tmp --exclude=backup\*'.$backup_excludes.' --update --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                } else {
                    //* Create a tar.gz backup as root user
                    $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.tar.gz';
                    exec('tar pczf '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' --exclude=backup\*'.$backup_excludes.' --directory '.escapeshellarg($web_path).' .', $tmp_output, $retval);
                }
                if($retval == 0 || $backup_mode != 'userzip'){ // tar can return 1 (due to harmless warings) and still create valid backups
                    if(is_file($web_backup_dir.'/'.$web_backup_file)){
                        chown($web_backup_dir.'/'.$web_backup_file, 'root');
                        chgrp($web_backup_dir.'/'.$web_backup_file, 'root');
                        chmod($web_backup_dir.'/'.$web_backup_file, 0750);
                        //* Insert web backup record in database
                        //$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')";
                        //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
                        $sql = "INSERT INTO web_backup (server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')";
                        $app->db->query($sql);
                        if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                    }
                } else {
                    if(is_file($web_backup_dir.'/'.$web_backup_file)) unlink($web_backup_dir.'/'.$web_backup_file);
                }
                //* Remove old backups
                $backup_copies = intval($rec['backup_copies']);
                $dir_handle = dir($web_backup_dir);
                $files = array();
                while (false !== ($entry = $dir_handle->read())) {
                    if($entry != '.' && $entry != '..' && substr($entry, 0, 3) == 'web' && is_file($web_backup_dir.'/'.$entry)) {
                        $files[] = $entry;
                    }
                }
                $dir_handle->close();
                rsort($files);
                for ($n = $backup_copies; $n <= 10; $n++) {
                    if(isset($files[$n]) && is_file($web_backup_dir.'/'.$files[$n])) {
                        unlink($web_backup_dir.'/'.$files[$n]);
                        //$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
                        //$tmp = $app->dbmaster->queryOneRecord($sql);
                        //$app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']);
                        //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
                        $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
                        $app->db->query($sql);
                        if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                    }
                }
                unset($files);
                unset($dir_handle);
                //* Remove backupdir symlink and create as directory instead
                $app->uses('system');
                $app->system->web_folder_protection($web_path, false);
                if(is_link($web_path.'/backup')) {
                    unlink($web_path.'/backup');
                }
                if(!is_dir($web_path.'/backup')) {
                    mkdir($web_path.'/backup');
                    chown($web_path.'/backup', $rec['system_user']);
                    chgrp($web_path.'/backup', $rec['system_group']);
                }
                $app->system->web_folder_protection($web_path, true);
            }
            /* If backup_interval is set to none and we have a
            backup directory for the website, then remove the backups */
            if($rec['backup_interval'] == 'none' || $rec['backup_interval'] == '') {
                $web_id = $rec['domain_id'];
                $web_user = $rec['system_user'];
                $web_backup_dir = realpath($backup_dir.'/web'.$web_id);
                if(is_dir($web_backup_dir)) {
                    exec('sudo -u '.escapeshellarg($web_user).' rm -f '.escapeshellarg($web_backup_dir.'/*'));
                }
            }
    //* mount backup directory, if necessary
    $run_backups = true;
    $server_config['backup_dir_mount_cmd'] = trim($server_config['backup_dir_mount_cmd']);
    if($server_config['backup_dir_is_mount'] == 'y' && $server_config['backup_dir_mount_cmd'] != ''){
        if(!$app->system->is_mounted($backup_dir)){
            exec(escapeshellcmd($server_config['backup_dir_mount_cmd']));
            sleep(1);
            if(!$app->system->is_mounted($backup_dir)) $run_backups = false;
        }
    }
    $sql = "SELECT * FROM web_database WHERE server_id = ".$conf['server_id']." AND backup_interval != 'none' AND backup_interval != ''";
    $records = $app->db->queryAllRecords($sql);
    if(is_array($records)) {
    if($run_backups){
        $sql = "SELECT * FROM web_domain WHERE server_id = ".$conf['server_id']." AND (type = 'vhost' OR type = 'vhostsubdomain')";
        $records = $app->db->queryAllRecords($sql);
        if(is_array($records)) {
            foreach($records as $rec) {
        include 'lib/mysql_clientdb.conf';
                //* Do the website backup
                if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
        foreach($records as $rec) {
            //* Do the database backup
            if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
                $web_id = $rec['parent_domain_id'];
                $db_backup_dir = $backup_dir.'/web'.$web_id;
                if(!is_dir($db_backup_dir)) mkdir($db_backup_dir, 0750);
                chmod($db_backup_dir, 0750);
                chown($db_backup_dir, 'root');
                chgrp($db_backup_dir, 'root');
                //* Do the mysql database backup with mysqldump
                $db_id = $rec['database_id'];
                $db_name = $rec['database_name'];
                $db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i').'.sql';
                //$command = "mysqldump -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
                $command = "mysqldump -h ".escapeshellarg($clientdb_host)." -u ".escapeshellarg($clientdb_user)." -p".escapeshellarg($clientdb_password)." -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
                exec($command, $tmp_output, $retval);
                //* Compress the backup with gzip
                if($retval == 0) exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'", $tmp_output, $retval);
                if($retval == 0){
                    if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')){
                        chmod($db_backup_dir.'/'.$db_backup_file.'.gz', 0750);
                        chown($db_backup_dir.'/'.$db_backup_file.'.gz', fileowner($db_backup_dir));
                        chgrp($db_backup_dir.'/'.$db_backup_file.'.gz', filegroup($db_backup_dir));
                        //* Insert web backup record in database
                        //$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')";
                        //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
                        $sql = "INSERT INTO web_backup (server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')";
                        $app->db->query($sql);
                        if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                    $web_path = $rec['document_root'];
                    $web_user = $rec['system_user'];
                    $web_group = $rec['system_group'];
                    $web_id = $rec['domain_id'];
                    $web_backup_dir = $backup_dir.'/web'.$web_id;
                    if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750);
                    chmod($web_backup_dir, 0750);
                    //if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
                    chown($web_backup_dir, $rec['system_user']);
                    chgrp($web_backup_dir, $rec['system_group']);
                    /*} else {
                        chown($web_backup_dir, 'root');
                        chgrp($web_backup_dir, 'root');
                    }*/
                    $backup_excludes = '';
                    $b_excludes = explode(',', trim($rec['backup_excludes']));
                    if(is_array($b_excludes) && !empty($b_excludes)){
                        foreach($b_excludes as $b_exclude){
                            $b_exclude = trim($b_exclude);
                            if($b_exclude != ''){
                                $backup_excludes .= ' --exclude='.escapeshellarg($b_exclude);
                            }
                        }
                    }
                } else {
                    if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')) unlink($db_backup_dir.'/'.$db_backup_file.'.gz');
                }
                //* Remove the uncompressed file
                if(is_file($db_backup_dir.'/'.$db_backup_file)) unlink($db_backup_dir.'/'.$db_backup_file);
                //* Remove old backups
                $backup_copies = intval($rec['backup_copies']);
                $dir_handle = dir($db_backup_dir);
                $files = array();
                while (false !== ($entry = $dir_handle->read())) {
                    if($entry != '.' && $entry != '..' && preg_match('/^db_(.*?)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.sql.gz$/', $entry, $matches) && is_file($db_backup_dir.'/'.$entry)) {
                        if(array_key_exists($matches[1], $files) == false) $files[$matches[1]] = array();
                        $files[$matches[1]][] = $entry;
                    if($backup_mode == 'userzip') {
                        //* Create a .zip backup as web user and include also files owned by apache / nginx user
                        $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.zip';
                        exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print 2> /dev/null | zip -b /tmp --exclude=backup\*'.$backup_excludes.' --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                        if($retval == 0) exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -user '.escapeshellarg($http_server_user).' -print 2> /dev/null | zip -b /tmp --exclude=backup\*'.$backup_excludes.' --update --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                    } else {
                        //* Create a tar.gz backup as root user
                        $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.tar.gz';
                        exec('tar pczf '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' --exclude=backup\*'.$backup_excludes.' --directory '.escapeshellarg($web_path).' .', $tmp_output, $retval);
                    }
                }
                $dir_handle->close();
                    if($retval == 0 || $backup_mode != 'userzip'){ // tar can return 1 (due to harmless warings) and still create valid backups
                        if(is_file($web_backup_dir.'/'.$web_backup_file)){
                            chown($web_backup_dir.'/'.$web_backup_file, 'root');
                            chgrp($web_backup_dir.'/'.$web_backup_file, 'root');
                            chmod($web_backup_dir.'/'.$web_backup_file, 0750);
                reset($files);
                foreach($files as $db_name => $filelist) {
                    rsort($filelist);
                            //* Insert web backup record in database
                            //$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')";
                            //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
                            $sql = "INSERT INTO web_backup (server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')";
                            $app->db->query($sql);
                            if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                        }
                    } else {
                        if(is_file($web_backup_dir.'/'.$web_backup_file)) unlink($web_backup_dir.'/'.$web_backup_file);
                    }
                    //* Remove old backups
                    $backup_copies = intval($rec['backup_copies']);
                    $dir_handle = dir($web_backup_dir);
                    $files = array();
                    while (false !== ($entry = $dir_handle->read())) {
                        if($entry != '.' && $entry != '..' && substr($entry, 0, 3) == 'web' && is_file($web_backup_dir.'/'.$entry)) {
                            $files[] = $entry;
                        }
                    }
                    $dir_handle->close();
                    rsort($files);
                    for ($n = $backup_copies; $n <= 10; $n++) {
                        if(isset($filelist[$n]) && is_file($db_backup_dir.'/'.$filelist[$n])) {
                            unlink($db_backup_dir.'/'.$filelist[$n]);
                            //$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
                        if(isset($files[$n]) && is_file($web_backup_dir.'/'.$files[$n])) {
                            unlink($web_backup_dir.'/'.$files[$n]);
                            //$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
                            //$tmp = $app->dbmaster->queryOneRecord($sql);
                            //$app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']);
                            //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
                            $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
                            $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
                            $app->db->query($sql);
                            if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                        }
                    }
                    unset($files);
                    unset($dir_handle);
                    //* Remove backupdir symlink and create as directory instead
                    $app->system->web_folder_protection($web_path, false);
                    if(is_link($web_path.'/backup')) {
                        unlink($web_path.'/backup');
                    }
                    if(!is_dir($web_path.'/backup')) {
                        mkdir($web_path.'/backup');
                        chown($web_path.'/backup', $rec['system_user']);
                        chgrp($web_path.'/backup', $rec['system_group']);
                    }
                    $app->system->web_folder_protection($web_path, true);
                }
                unset($files);
                unset($dir_handle);
                /* If backup_interval is set to none and we have a
                backup directory for the website, then remove the backups */
                if($rec['backup_interval'] == 'none' || $rec['backup_interval'] == '') {
                    $web_id = $rec['domain_id'];
                    $web_user = $rec['system_user'];
                    $web_backup_dir = realpath($backup_dir.'/web'.$web_id);
                    if(is_dir($web_backup_dir)) {
                        exec('sudo -u '.escapeshellarg($web_user).' rm -f '.escapeshellarg($web_backup_dir.'/*'));
                    }
                }
            }
        }
        unset($clientdb_host);
        unset($clientdb_user);
        unset($clientdb_password);
        $sql = "SELECT * FROM web_database WHERE server_id = ".$conf['server_id']." AND backup_interval != 'none' AND backup_interval != ''";
        $records = $app->db->queryAllRecords($sql);
        if(is_array($records)) {
    }
            include 'lib/mysql_clientdb.conf';
    // remove non-existing backups from database
    $backups = $app->db->queryAllRecords("SELECT * FROM web_backup WHERE server_id = ".$conf['server_id']);
    if(is_array($backups) && !empty($backups)){
        foreach($backups as $backup){
            $backup_file = $backup_dir.'/web'.$backup['parent_domain_id'].'/'.$backup['filename'];
            if(!is_file($backup_file)){
                $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = ".$backup['parent_domain_id']." AND filename = '".$backup['filename']."'";
                $app->db->query($sql);
                if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
            foreach($records as $rec) {
                //* Do the database backup
                if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
                    $web_id = $rec['parent_domain_id'];
                    $db_backup_dir = $backup_dir.'/web'.$web_id;
                    if(!is_dir($db_backup_dir)) mkdir($db_backup_dir, 0750);
                    chmod($db_backup_dir, 0750);
                    chown($db_backup_dir, 'root');
                    chgrp($db_backup_dir, 'root');
                    //* Do the mysql database backup with mysqldump
                    $db_id = $rec['database_id'];
                    $db_name = $rec['database_name'];
                    $db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i').'.sql';
                    //$command = "mysqldump -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
                    $command = "mysqldump -h ".escapeshellarg($clientdb_host)." -u ".escapeshellarg($clientdb_user)." -p".escapeshellarg($clientdb_password)." -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
                    exec($command, $tmp_output, $retval);
                    //* Compress the backup with gzip
                    if($retval == 0) exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'", $tmp_output, $retval);
                    if($retval == 0){
                        if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')){
                            chmod($db_backup_dir.'/'.$db_backup_file.'.gz', 0750);
                            chown($db_backup_dir.'/'.$db_backup_file.'.gz', fileowner($db_backup_dir));
                            chgrp($db_backup_dir.'/'.$db_backup_file.'.gz', filegroup($db_backup_dir));
                            //* Insert web backup record in database
                            //$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')";
                            //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
                            $sql = "INSERT INTO web_backup (server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')";
                            $app->db->query($sql);
                            if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                        }
                    } else {
                        if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')) unlink($db_backup_dir.'/'.$db_backup_file.'.gz');
                    }
                    //* Remove the uncompressed file
                    if(is_file($db_backup_dir.'/'.$db_backup_file)) unlink($db_backup_dir.'/'.$db_backup_file);
                    //* Remove old backups
                    $backup_copies = intval($rec['backup_copies']);
                    $dir_handle = dir($db_backup_dir);
                    $files = array();
                    while (false !== ($entry = $dir_handle->read())) {
                        if($entry != '.' && $entry != '..' && preg_match('/^db_(.*?)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.sql.gz$/', $entry, $matches) && is_file($db_backup_dir.'/'.$entry)) {
                            if(array_key_exists($matches[1], $files) == false) $files[$matches[1]] = array();
                            $files[$matches[1]][] = $entry;
                        }
                    }
                    $dir_handle->close();
                    reset($files);
                    foreach($files as $db_name => $filelist) {
                        rsort($filelist);
                        for ($n = $backup_copies; $n <= 10; $n++) {
                            if(isset($filelist[$n]) && is_file($db_backup_dir.'/'.$filelist[$n])) {
                                unlink($db_backup_dir.'/'.$filelist[$n]);
                                //$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
                                //$tmp = $app->dbmaster->queryOneRecord($sql);
                                //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
                                $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
                                $app->db->query($sql);
                                if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                            }
                        }
                    }
                    unset($files);
                    unset($dir_handle);
                }
            }
            unset($clientdb_host);
            unset($clientdb_user);
            unset($clientdb_password);
        }
        // remove non-existing backups from database
        $backups = $app->db->queryAllRecords("SELECT * FROM web_backup WHERE server_id = ".$conf['server_id']);
        if(is_array($backups) && !empty($backups)){
            foreach($backups as $backup){
                $backup_file = $backup_dir.'/web'.$backup['parent_domain_id'].'/'.$backup['filename'];
                if(!is_file($backup_file)){
                    $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = ".$backup['parent_domain_id']." AND filename = '".$backup['filename']."'";
                    $app->db->query($sql);
                    if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                }
            }
        }
    } else {
        //* send email to admin that backup directory could not be mounted
        $global_config = $app->getconf->get_global_config('mail');
        if($global_config['admin_mail'] != ''){
            $subject = 'Backup directory '.$backup_dir.' could not be mounted';
            $message = "Backup directory ".$backup_dir." could not be mounted.\n\nThe command\n\n".$server_config['backup_dir_mount_cmd']."\n\nfailed.";
            mail($global_config['admin_mail'], $subject, $message);
        }
    }
}
server/plugins-available/backup_plugin.inc.php
@@ -65,63 +65,78 @@
        if(is_array($backup)) {
            $app->uses('ini_parser,file,getconf');
            $app->uses('ini_parser,file,getconf,system');
            $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$backup['parent_domain_id']);
            $server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
            $backup_dir = $server_config['backup_dir'].'/web'.$web['domain_id'];
            //* Make backup available for download
            if($action_name == 'backup_download') {
                //* Copy the backup file to the backup folder of the website
                if(file_exists($backup_dir.'/'.$backup['filename']) && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                    copy($backup_dir.'/'.$backup['filename'], $web['document_root'].'/backup/'.$backup['filename']);
                    chgrp($web['document_root'].'/backup/'.$backup['filename'], $web['system_group']);
                    $app->log('cp '.$backup_dir.'/'.$backup['filename'].' '.$web['document_root'].'/backup/'.$backup['filename'], LOGLEVEL_DEBUG);
            //* mount backup directory, if necessary
            $backup_dir_is_ready = true;
            $server_config['backup_dir_mount_cmd'] = trim($server_config['backup_dir_mount_cmd']);
            if($server_config['backup_dir_is_mount'] == 'y' && $server_config['backup_dir_mount_cmd'] != ''){
                if(!$app->system->is_mounted($backup_dir)){
                    exec(escapeshellcmd($server_config['backup_dir_mount_cmd']));
                    sleep(1);
                    if(!$app->system->is_mounted($backup_dir)) $backup_dir_is_ready = false;
                }
            }
            //* Restore a mysql backup
            if($action_name == 'backup_restore' && $backup['backup_type'] == 'mysql') {
                //* Load sql dump into db
                include 'lib/mysql_clientdb.conf';
                if(file_exists($backup_dir.'/'.$backup['filename'])) {
                    //$parts = explode('_',$backup['filename']);
                    //$db_name = $parts[1];
                    preg_match('@^db_(.+)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.sql\.gz$@', $backup['filename'], $matches);
                    $db_name = $matches[1];
                    $command = "gunzip --stdout ".escapeshellarg($backup_dir.'/'.$backup['filename'])." | mysql -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' '".$db_name."'";
                    exec($command);
                }
                unset($clientdb_host);
                unset($clientdb_user);
                unset($clientdb_password);
                $app->log('Restored MySQL backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
            }
            //* Restore a web backup
            if($action_name == 'backup_restore' && $backup['backup_type'] == 'web') {
                if($backup['backup_mode'] == 'userzip') {
                    if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                        if(file_exists($web['document_root'].'/backup/'.$backup['filename'])) rename($web['document_root'].'/backup/'.$backup['filename'], $web['document_root'].'/backup/'.$backup['filename'].'.bak');
            if($backup_dir_is_ready){
                //* Make backup available for download
                if($action_name == 'backup_download') {
                    //* Copy the backup file to the backup folder of the website
                    if(file_exists($backup_dir.'/'.$backup['filename']) && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                        copy($backup_dir.'/'.$backup['filename'], $web['document_root'].'/backup/'.$backup['filename']);
                        chgrp($web['document_root'].'/backup/'.$backup['filename'], $web['system_group']);
                        //chown($web['document_root'].'/backup/'.$backup['filename'],$web['system_user']);
                        $command = 'sudo -u '.escapeshellarg($web['system_user']).' unzip -qq -o  '.escapeshellarg($web['document_root'].'/backup/'.$backup['filename']).' -d '.escapeshellarg($web['document_root']).' 2> /dev/null';
                        exec($command);
                        unlink($web['document_root'].'/backup/'.$backup['filename']);
                        if(file_exists($web['document_root'].'/backup/'.$backup['filename'].'.bak')) rename($web['document_root'].'/backup/'.$backup['filename'].'.bak', $web['document_root'].'/backup/'.$backup['filename']);
                        $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
                        $app->log('cp '.$backup_dir.'/'.$backup['filename'].' '.$web['document_root'].'/backup/'.$backup['filename'], LOGLEVEL_DEBUG);
                    }
                }
                if($backup['backup_mode'] == 'rootgz') {
                    if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                        $command = 'tar xzf '.escapeshellarg($backup_dir.'/'.$backup['filename']).' --directory '.escapeshellarg($web['document_root']);
                //* Restore a mysql backup
                if($action_name == 'backup_restore' && $backup['backup_type'] == 'mysql') {
                    //* Load sql dump into db
                    include 'lib/mysql_clientdb.conf';
                    if(file_exists($backup_dir.'/'.$backup['filename'])) {
                        //$parts = explode('_',$backup['filename']);
                        //$db_name = $parts[1];
                        preg_match('@^db_(.+)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.sql\.gz$@', $backup['filename'], $matches);
                        $db_name = $matches[1];
                        $command = "gunzip --stdout ".escapeshellarg($backup_dir.'/'.$backup['filename'])." | mysql -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' '".$db_name."'";
                        exec($command);
                        $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
                    }
                    unset($clientdb_host);
                    unset($clientdb_user);
                    unset($clientdb_password);
                    $app->log('Restored MySQL backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
                }
                //* Restore a web backup
                if($action_name == 'backup_restore' && $backup['backup_type'] == 'web') {
                    if($backup['backup_mode'] == 'userzip') {
                        if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                            if(file_exists($web['document_root'].'/backup/'.$backup['filename'])) rename($web['document_root'].'/backup/'.$backup['filename'], $web['document_root'].'/backup/'.$backup['filename'].'.bak');
                            copy($backup_dir.'/'.$backup['filename'], $web['document_root'].'/backup/'.$backup['filename']);
                            chgrp($web['document_root'].'/backup/'.$backup['filename'], $web['system_group']);
                            //chown($web['document_root'].'/backup/'.$backup['filename'],$web['system_user']);
                            $command = 'sudo -u '.escapeshellarg($web['system_user']).' unzip -qq -o  '.escapeshellarg($web['document_root'].'/backup/'.$backup['filename']).' -d '.escapeshellarg($web['document_root']).' 2> /dev/null';
                            exec($command);
                            unlink($web['document_root'].'/backup/'.$backup['filename']);
                            if(file_exists($web['document_root'].'/backup/'.$backup['filename'].'.bak')) rename($web['document_root'].'/backup/'.$backup['filename'].'.bak', $web['document_root'].'/backup/'.$backup['filename']);
                            $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
                        }
                    }
                    if($backup['backup_mode'] == 'rootgz') {
                        if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'], '..') && !stristr($backup_dir.'/'.$backup['filename'], 'etc')) {
                            $command = 'tar xzf '.escapeshellarg($backup_dir.'/'.$backup['filename']).' --directory '.escapeshellarg($web['document_root']);
                            exec($command);
                            $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'], LOGLEVEL_DEBUG);
                        }
                    }
                }
            } else {
                $app->log('Backup directory not ready.', LOGLEVEL_DEBUG);
            }
        } else {