diff --git a/server/lib/classes/backup.inc.php b/server/lib/classes/backup.inc.php index 7f072aacf1e1f79201eebecae19368e016bbbfc8..532a0fa7b8d9aa9e528999eedeac658143258105 100644 --- a/server/lib/classes/backup.inc.php +++ b/server/lib/classes/backup.inc.php @@ -1749,131 +1749,138 @@ class backup include '/usr/local/ispconfig/server/lib/mysql_clientdb.conf'; - //* Check mysqldump capabilities - exec('mysqldump --help', $tmp); - $mysqldump_routines = (strpos(implode($tmp), '--routines') !== false) ? '--routines' : ''; - unset($tmp); - - foreach ($records as $rec) { - if (self::backupModeIsRepos($backup_mode)) { - //@todo get $password from server config - $repos_password = ''; - //@todo get compression from server config - $compression = 'zlib'; - $db_name = $rec['database_name']; - $db_repos_folder = self::getBackupReposFolder($backup_mode, 'mysql') . '_' . $db_name; - $backup_repos_path = $db_backup_dir . '/' . $db_repos_folder; - $backup_format_db = ''; - if (self::prepareRepos($backup_mode, $backup_repos_path, $repos_password)) { - $db_backup_archive = ($backup_job == 'manual' ? 'manual-' : '') . 'db_' . $db_name . '_' . date('Y-m-d_H-i'); - $full_archive_path = $backup_repos_path . '::' . $db_backup_archive; - $dump_command = "mysqldump -h ? -u ? -p? -c --add-drop-table --create-options --quick --max_allowed_packet=512M " . $mysqldump_routines . " ?"; - switch ($backup_mode) { - case 'borg': - $borg_cmd = self::getBorgCommand('borg create', $repos_password); - $borg_options = self::getBorgCreateOptions($compression); - $command = $dump_command . ' | ' . $borg_cmd . ' ' . $borg_options . ' ? -'; - /** @var string $clientdb_host */ - /** @var string $clientdb_user */ - /** @var string $clientdb_password */ - $app->system->exec_safe($command, + if($app->system->sqldumpcommand() !== false) { + + //* Check mysqldump capabilities + exec($app->system->sqldumpcommand() . ' --help', $tmp); + $mysqldump_routines = (strpos(implode($tmp), '--routines') !== false) ? '--routines' : ''; + unset($tmp); + + foreach ($records as $rec) { + if (self::backupModeIsRepos($backup_mode)) { + //@todo get $password from server config + $repos_password = ''; + //@todo get compression from server config + $compression = 'zlib'; + $db_name = $rec['database_name']; + $db_repos_folder = self::getBackupReposFolder($backup_mode, 'mysql') . '_' . $db_name; + $backup_repos_path = $db_backup_dir . '/' . $db_repos_folder; + $backup_format_db = ''; + if (self::prepareRepos($backup_mode, $backup_repos_path, $repos_password)) { + $db_backup_archive = ($backup_job == 'manual' ? 'manual-' : '') . 'db_' . $db_name . '_' . date('Y-m-d_H-i'); + $full_archive_path = $backup_repos_path . '::' . $db_backup_archive; + $dump_command = $app->system->sqldumpcommand() . " -h ? -u ? -p? -c --add-drop-table --create-options --quick --max_allowed_packet=512M " . $mysqldump_routines . " ?"; + switch ($backup_mode) { + case 'borg': + $borg_cmd = self::getBorgCommand('borg create', $repos_password); + $borg_options = self::getBorgCreateOptions($compression); + $command = $dump_command . ' | ' . $borg_cmd . ' ' . $borg_options . ' ? -'; + /** @var string $clientdb_host */ + /** @var string $clientdb_user */ + /** @var string $clientdb_password */ + $app->system->exec_safe($command, $clientdb_host, $clientdb_user, $clientdb_password, $db_name, #mysqldump command part $full_archive_path #borg command part - ); + ); $exit_code = $app->system->last_exec_retcode(); break; - } - if ($exit_code == 0) { - $archive_size = self::getReposArchiveSize($backup_mode, $backup_repos_path, $db_backup_archive, $repos_password); - if ($archive_size !== false) { - //* Insert web backup record in database - $sql = "INSERT INTO web_backup (server_id, parent_domain_id, backup_type, backup_mode, backup_format, tstamp, filename, filesize, backup_password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; - //* password is for `Encrypted` column informative purposes, on download password is obtained from web_domain settings - $password = $repos_password ? '*secret*' : ''; - $app->db->query($sql, $server_id, $domain_id, 'mysql', $backup_mode, $backup_format_db, time(), $db_backup_archive, $archive_size, $password); - if ($app->running_on_slaveserver()) - $app->dbmaster->query($sql, $server_id, $domain_id, 'mysql', $backup_mode, $backup_format_db, time(), $db_backup_archive, $archive_size, $password); - $success = true; - } else { - $app->log('Failed to obtain backup size of ' . $full_archive_path . ' for database ' . $rec['database_name'], LOGLEVEL_ERROR); - return false; - } - } else { - rename($backup_repos_path, $new_path = $backup_repos_path . '_failed_' . uniqid()); - $app->log('Failed to process mysql backup format ' . $backup_format_db . ' for database ' . $rec['database_name'] . ' repos renamed to ' . $new_path, LOGLEVEL_ERROR); - } - } else { - $app->log('Failed to initialize repository for database ' . $rec['database_name'] . ', folder ' . $backup_repos_path . ', backup mode ' . $backup_mode . '.', LOGLEVEL_ERROR); - } - } else { - $password = ($web_domain['backup_encrypt'] == 'y') ? trim($web_domain['backup_password']) : ''; - $backup_format_db = $web_domain['backup_format_db']; - if (empty($backup_format_db)) { - $backup_format_db = 'gzip'; - } - $backup_extension_db = self::getBackupDbExtension($backup_format_db); - - if (!empty($backup_extension_db)) { - //* Do the mysql database backup with mysqldump - $db_name = $rec['database_name']; - $db_file_prefix = 'db_' . $db_name . '_' . date('Y-m-d_H-i'); - $db_backup_file = $db_file_prefix . '.sql'; - $db_compressed_file = ($backup_job == 'manual' ? 'manual-' : '') . $db_file_prefix . $backup_extension_db; - $command = "mysqldump -h ? -u ? -p? -c --add-drop-table --create-options --quick --max_allowed_packet=512M " . $mysqldump_routines . " --result-file=? ?"; - /** @var string $clientdb_host */ - /** @var string $clientdb_user */ - /** @var string $clientdb_password */ - $app->system->exec_safe($command, $clientdb_host, $clientdb_user, $clientdb_password, $db_backup_dir . '/' . $db_backup_file, $db_name); - $exit_code = $app->system->last_exec_retcode(); - - //* Compress the backup - if ($exit_code == 0) { - $exit_code = self::runDatabaseCompression($backup_format_db, $db_backup_dir, $db_backup_file, $db_compressed_file, $backup_tmp, $password) ? 0 : 1; - if ($exit_code !== 0) - $app->log('Failed to make backup of database ' . $rec['database_name'], LOGLEVEL_ERROR); - } else { - $app->log('Failed to make backup of database ' . $rec['database_name'] . ', because mysqldump failed', LOGLEVEL_ERROR); - } - - if ($exit_code == 0) { - if (is_file($db_backup_dir . '/' . $db_compressed_file)) { - chmod($db_backup_dir . '/' . $db_compressed_file, 0750); - chown($db_backup_dir . '/' . $db_compressed_file, fileowner($db_backup_dir)); - chgrp($db_backup_dir . '/' . $db_compressed_file, filegroup($db_backup_dir)); - - //* Insert web backup record in database - $file_size = filesize($db_backup_dir . '/' . $db_compressed_file); - $sql = "INSERT INTO web_backup (server_id, parent_domain_id, backup_type, backup_mode, backup_format, tstamp, filename, filesize, backup_password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; - //Making compatible with previous versions of ISPConfig: - $sql_mode = ($backup_format_db == 'gzip') ? 'sqlgz' : ('sql' . $backup_format_db); - $app->db->query($sql, $server_id, $domain_id, 'mysql', $sql_mode, $backup_format_db, time(), $db_compressed_file, $file_size, $password); - if ($app->running_on_slaveserver()) - $app->dbmaster->query($sql, $server_id, $domain_id, 'mysql', $sql_mode, $backup_format_db, time(), $db_compressed_file, $file_size, $password); - $success = true; - } - } else { - if (is_file($db_backup_dir . '/' . $db_compressed_file)) unlink($db_backup_dir . '/' . $db_compressed_file); - } - //* Remove the uncompressed file - if (is_file($db_backup_dir . '/' . $db_backup_file)) unlink($db_backup_dir . '/' . $db_backup_file); - } else { - $app->log('Failed to process mysql backup format ' . $backup_format_db . ' for database ' . $rec['database_name'], LOGLEVEL_ERROR); - } - } - //* Remove old backups - self::backups_garbage_collection($server_id, 'mysql', $domain_id); - $prefix_list = array( - "db_${db_name}_", - "manual-db_${db_name}_", - ); - self::clearBackups($server_id, $domain_id, intval($rec['backup_copies']), $db_backup_dir, $prefix_list); - } - - unset($clientdb_host); - unset($clientdb_user); - unset($clientdb_password); - - return $success; + } + if ($exit_code == 0) { + $archive_size = self::getReposArchiveSize($backup_mode, $backup_repos_path, $db_backup_archive, $repos_password); + if ($archive_size !== false) { + //* Insert web backup record in database + $sql = "INSERT INTO web_backup (server_id, parent_domain_id, backup_type, backup_mode, backup_format, tstamp, filename, filesize, backup_password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + //* password is for `Encrypted` column informative purposes, on download password is obtained from web_domain settings + $password = $repos_password ? '*secret*' : ''; + $app->db->query($sql, $server_id, $domain_id, 'mysql', $backup_mode, $backup_format_db, time(), $db_backup_archive, $archive_size, $password); + if ($app->running_on_slaveserver()) + $app->dbmaster->query($sql, $server_id, $domain_id, 'mysql', $backup_mode, $backup_format_db, time(), $db_backup_archive, $archive_size, $password); + $success = true; + } else { + $app->log('Failed to obtain backup size of ' . $full_archive_path . ' for database ' . $rec['database_name'], LOGLEVEL_ERROR); + return false; + } + } else { + rename($backup_repos_path, $new_path = $backup_repos_path . '_failed_' . uniqid()); + $app->log('Failed to process mysql backup format ' . $backup_format_db . ' for database ' . $rec['database_name'] . ' repos renamed to ' . $new_path, LOGLEVEL_ERROR); + } + } else { + $app->log('Failed to initialize repository for database ' . $rec['database_name'] . ', folder ' . $backup_repos_path . ', backup mode ' . $backup_mode . '.', LOGLEVEL_ERROR); + } + } else { + $password = ($web_domain['backup_encrypt'] == 'y') ? trim($web_domain['backup_password']) : ''; + $backup_format_db = $web_domain['backup_format_db']; + if (empty($backup_format_db)) { + $backup_format_db = 'gzip'; + } + $backup_extension_db = self::getBackupDbExtension($backup_format_db); + + if (!empty($backup_extension_db)) { + //* Do the mysql database backup with mysqldump + $db_name = $rec['database_name']; + $db_file_prefix = 'db_' . $db_name . '_' . date('Y-m-d_H-i'); + $db_backup_file = $db_file_prefix . '.sql'; + $db_compressed_file = ($backup_job == 'manual' ? 'manual-' : '') . $db_file_prefix . $backup_extension_db; + $command = $app->system->sqldumpcommand() . " -h ? -u ? -p? -c --add-drop-table --create-options --quick --max_allowed_packet=512M " . $mysqldump_routines . " --result-file=? ?"; + /** @var string $clientdb_host */ + /** @var string $clientdb_user */ + /** @var string $clientdb_password */ + $app->system->exec_safe($command, $clientdb_host, $clientdb_user, $clientdb_password, $db_backup_dir . '/' . $db_backup_file, $db_name); + $exit_code = $app->system->last_exec_retcode(); + + //* Compress the backup + if ($exit_code == 0) { + $exit_code = self::runDatabaseCompression($backup_format_db, $db_backup_dir, $db_backup_file, $db_compressed_file, $backup_tmp, $password) ? 0 : 1; + if ($exit_code !== 0) + $app->log('Failed to make backup of database ' . $rec['database_name'], LOGLEVEL_ERROR); + } else { + $app->log('Failed to make backup of database ' . $rec['database_name'] . ', because "' . $app->system->sqldumpcommand() . '" failed', LOGLEVEL_ERROR); + } + + if ($exit_code == 0) { + if (is_file($db_backup_dir . '/' . $db_compressed_file)) { + chmod($db_backup_dir . '/' . $db_compressed_file, 0750); + chown($db_backup_dir . '/' . $db_compressed_file, fileowner($db_backup_dir)); + chgrp($db_backup_dir . '/' . $db_compressed_file, filegroup($db_backup_dir)); + + //* Insert web backup record in database + $file_size = filesize($db_backup_dir . '/' . $db_compressed_file); + $sql = "INSERT INTO web_backup (server_id, parent_domain_id, backup_type, backup_mode, backup_format, tstamp, filename, filesize, backup_password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + //Making compatible with previous versions of ISPConfig: + $sql_mode = ($backup_format_db == 'gzip') ? 'sqlgz' : ('sql' . $backup_format_db); + $app->db->query($sql, $server_id, $domain_id, 'mysql', $sql_mode, $backup_format_db, time(), $db_compressed_file, $file_size, $password); + if ($app->running_on_slaveserver()) + $app->dbmaster->query($sql, $server_id, $domain_id, 'mysql', $sql_mode, $backup_format_db, time(), $db_compressed_file, $file_size, $password); + $success = true; + } + } else { + if (is_file($db_backup_dir . '/' . $db_compressed_file)) unlink($db_backup_dir . '/' . $db_compressed_file); + } + //* Remove the uncompressed file + if (is_file($db_backup_dir . '/' . $db_backup_file)) unlink($db_backup_dir . '/' . $db_backup_file); + } else { + $app->log('Failed to process mysql backup format ' . $backup_format_db . ' for database ' . $rec['database_name'], LOGLEVEL_ERROR); + } + } + } + //* Remove old backups + self::backups_garbage_collection($server_id, 'mysql', $domain_id); + $prefix_list = array( + "db_${db_name}_", + "manual-db_${db_name}_", + ); + self::clearBackups($server_id, $domain_id, intval($rec['backup_copies']), $db_backup_dir, $prefix_list); + + + return $success; + } else { + $app->log('Couldn\'t perform SQL backup due to missing SQL dump command (mysqldump / mariadb-dump).', LOGLEVEL_ERROR); + return false; + } + + unset($clientdb_host); + unset($clientdb_user); + unset($clientdb_password); } /** @@ -1990,7 +1997,7 @@ class backup if ($success) { $backup_username = ($global_config['backups_include_into_web_quota'] == 'y') ? $web_user : 'root'; $backup_group = ($global_config['backups_include_into_web_quota'] == 'y') ? $web_group : 'root'; - + //Insert web backup record in database $archive_size = self::getReposArchiveSize($backup_mode, $backup_repos_path, $web_backup_archive, $repos_password); $password = $repos_password ? '*secret*' : ''; diff --git a/server/lib/classes/system.inc.php b/server/lib/classes/system.inc.php index b2c6357d8f1d31a4755d2e1a9c7f90acce615ad6..092a5aa7e09af72c0941f429b4bc6f6d06df9c0e 100644 --- a/server/lib/classes/system.inc.php +++ b/server/lib/classes/system.inc.php @@ -2064,7 +2064,7 @@ class system{ function _getinitcommand($servicename, $action, $init_script_directory = '', $check_service) { global $conf, $app; - + // upstart /* removed upstart support - deprecated if(is_executable('/sbin/initctl')){ @@ -2072,7 +2072,7 @@ class system{ if(intval($retval['retval']) == 0) return 'service '.$servicename.' '.$action; } */ - + if(!in_array($action,array('start','stop','restart','reload','force-reload'))) { $app->log('Invalid init command action '.$action,LOGLEVEL_WARN); return false; @@ -2081,10 +2081,10 @@ class system{ //* systemd (now default in all supported OS) if(is_executable('/bin/systemd') || is_executable('/usr/bin/systemctl')){ $app->log('Trying to use Systemd to restart service',LOGLEVEL_DEBUG); - + //* Test service name via regex if(preg_match('/[a-zA-Z0-9\.\-\_]/',$servicename)) { - + //* Test if systemd service is enabled if ($check_service) { $this->exec_safe("systemctl is-enabled ? 2>&1", $servicename); @@ -2092,7 +2092,7 @@ class system{ } else { $app->log('Systemd service '.$servicename.' not found or not enabled.',LOGLEVEL_DEBUG); } - + //* Return service command if ($ret_val == 0 || !$check_service) { return 'systemctl '.$action.' '.$servicename.'.service'; @@ -2108,69 +2108,69 @@ class system{ //* sysvinit fallback $app->log('Using init script to restart service',LOGLEVEL_DEBUG); - + //* Get init script directory if($init_script_directory == '') $init_script_directory = $conf['init_scripts']; if(substr($init_script_directory, -1) === '/') $init_script_directory = substr($init_script_directory, 0, -1); $init_script_directory = realpath($init_script_directory); - + //* Check init script dir if(!is_dir($init_script_directory)) { $app->log('Init script directory '.$init_script_directory.' not found',LOGLEVEL_WARN); return false; } - + //* Forbidden init script paths if(substr($init_script_directory,0,4) == '/var' || substr($init_script_directory,0,4) == '/tmp') { $app->log('Do not put init scripts in /var or /tmp folder.',LOGLEVEL_WARN); return false; } - + //* Check init script dir owner if(fileowner($init_script_directory) !== 0) { $app->log('Init script directory '.$init_script_directory.' not owned by root user',LOGLEVEL_WARN); return false; } - + $full_init_script_path = realpath($init_script_directory.'/'.$servicename); - + //** Gentoo, keep symlink as init script, but do some checks - if(file_exists('/etc/gentoo-release')) { + if(file_exists('/etc/gentoo-release')) { //* check if init script is symlink - if(is_link($init_script_directory.'/'.$servicename)) { + if(is_link($init_script_directory.'/'.$servicename)) { //* Check init script owner (realpath, symlink is checked later) if(fileowner($full_init_script_path) !== 0) { $app->log('Init script '.$full_init_script_path.' not owned by root user',LOGLEVEL_WARN); return false; } - + //* full path is symlink $full_init_script_path_symlink = $init_script_directory.'/'.$servicename; - + //* check if realpath matches symlink if(strpos($full_init_script_path_symlink,$full_init_script_path) == 0) { $full_init_script_path = $full_init_script_path_symlink; } } } - + if($full_init_script_path == '') { $app->log('No init script, we quit here.',LOGLEVEL_WARN); return false; } - + //* Check init script if(!is_file($full_init_script_path)) { $app->log('Init script '.$full_init_script_path.' not found',LOGLEVEL_WARN); return false; } - + //* Check init script owner if(fileowner($full_init_script_path) !== 0) { $app->log('Init script '.$full_init_script_path.' not owned by root user',LOGLEVEL_WARN); return false; } - + if($check_service && is_executable($full_init_script_path)) { return $full_init_script_path.' '.$action; } @@ -2190,26 +2190,38 @@ class system{ } } - function getopensslversion($get_minor = false) { - global $app; - if($this->is_installed('openssl')) $cmd = 'openssl version'; - else { - $app->log("Could not check OpenSSL version, openssl not found.", LOGLEVEL_DEBUG); - return '1.0.1'; - } + function sqldumpcommand() { + global $app; + + if($this->is_installed('mariadb-dump')) { + return 'mariadb-dump'; + } elseif($this->is_installed('mysqldump')) { + return 'mysqldump'; + } else { + $app->log('Neither mysqldump nor mariadb-dump could be found.', LOGLEVEL_DEBUG); + return false; + } + } + + function getopensslversion($get_minor = false) { + global $app; + if($this->is_installed('openssl')) $cmd = 'openssl version'; + else { + $app->log("Could not check OpenSSL version, openssl not found.", LOGLEVEL_DEBUG); + return '1.0.1'; + } exec($cmd, $output, $return_var); - if($return_var != 0 || !$output[0]) { + if($return_var != 0 || !$output[0]) { $app->log("Could not check OpenSSL version, openssl did not return any data.", LOGLEVEL_WARN); - return '1.0.1'; - } - if(preg_match('/OpenSSL\s*(\d+)(\.(\d+)(\.(\d+))*)?(\D|$)/i', $output[0], $matches)) { + return '1.0.1'; + } + if(preg_match('/OpenSSL\s*(\d+)(\.(\d+)(\.(\d+))*)?(\D|$)/i', $output[0], $matches)) { return $matches[1] . (isset($matches[3]) ? '.' . $matches[3] : '') . (isset($matches[5]) && $get_minor == true ? '.' . $matches[5] : ''); - } else { + } else { $app->log("Could not check OpenSSL version, did not find version string in openssl output.", LOGLEVEL_WARN); return '1.0.1'; - } - + } } function getnginxversion($get_minor = false) {