Marius Cramer
2014-02-17 ebbe6374fc9c308daf729d2ad1b2f8007ed771ce
commit | author | age
5de2af 1 <?php
M 2
3 /*
4 Copyright (c) 2013, Marius Cramer, pixcept KG
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9
10     * Redistributions of source code must retain the above copyright notice,
11       this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright notice,
13       this list of conditions and the following disclaimer in the documentation
14       and/or other materials provided with the distribution.
15     * Neither the name of ISPConfig nor the names of its contributors
16       may be used to endorse or promote products derived from this software without
17       specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 class cronjob_backup extends cronjob {
32
b1a6a5 33     // job schedule
MC 34     protected $_schedule = '0 0 * * *';
5de2af 35
b1a6a5 36     /* this function is optional if it contains no custom code */
MC 37     public function onPrepare() {
38         global $app;
5de2af 39
b1a6a5 40         parent::onPrepare();
MC 41     }
5de2af 42
b1a6a5 43     /* this function is optional if it contains no custom code */
MC 44     public function onBeforeRun() {
45         global $app;
5de2af 46
b1a6a5 47         return parent::onBeforeRun();
MC 48     }
5de2af 49
b1a6a5 50     public function onRunJob() {
MC 51         global $app, $conf;
5de2af 52
b1a6a5 53         $server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
MC 54         $backup_dir = $server_config['backup_dir'];
55         $backup_mode = $server_config['backup_mode'];
56         if($backup_mode == '') $backup_mode = 'userzip';
57
58         $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
59         $http_server_user = $web_config['user'];
60
61         if($backup_dir != '') {
62
63             if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
64                 $backup_dir_permissions = 0755;
65             } else {
66                 $backup_dir_permissions = 0750;
67             }
68
69             if(!is_dir($backup_dir)) {
70                 mkdir(escapeshellcmd($backup_dir), $backup_dir_permissions, true);
71             } else {
72                 chmod(escapeshellcmd($backup_dir), $backup_dir_permissions);
73             }
746e52 74             
MC 75             //* mount backup directory, if necessary
76             $run_backups = true;
77             $server_config['backup_dir_mount_cmd'] = trim($server_config['backup_dir_mount_cmd']);
78             if($server_config['backup_dir_is_mount'] == 'y' && $server_config['backup_dir_mount_cmd'] != ''){
79                 if(!$app->system->is_mounted($backup_dir)){
80                     exec(escapeshellcmd($server_config['backup_dir_mount_cmd']));
81                     sleep(1);
82                     if(!$app->system->is_mounted($backup_dir)) $run_backups = false;
b1a6a5 83                 }
MC 84             }
5de2af 85
746e52 86             if($run_backups){
9f6008 87                 //* backup only active domains
R 88                 $sql = "SELECT * FROM web_domain WHERE server_id = '".$conf['server_id']."' AND (type = 'vhost' OR type = 'vhostsubdomain') AND active = 'y'";
746e52 89                 $records = $app->db->queryAllRecords($sql);
MC 90                 if(is_array($records)) {
91                     foreach($records as $rec) {
5de2af 92
746e52 93                         //* Do the website backup
MC 94                         if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
5de2af 95
746e52 96                             $web_path = $rec['document_root'];
MC 97                             $web_user = $rec['system_user'];
98                             $web_group = $rec['system_group'];
99                             $web_id = $rec['domain_id'];
100                             $web_backup_dir = $backup_dir.'/web'.$web_id;
101                             if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750);
102                             chmod($web_backup_dir, 0750);
103                             //if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
104                             chown($web_backup_dir, $rec['system_user']);
105                             chgrp($web_backup_dir, $rec['system_group']);
106                             /*} else {
107                                 chown($web_backup_dir, 'root');
108                                 chgrp($web_backup_dir, 'root');
109                             }*/
110                         
111                             $backup_excludes = '';
112                             $b_excludes = explode(',', trim($rec['backup_excludes']));
113                             if(is_array($b_excludes) && !empty($b_excludes)){
114                                 foreach($b_excludes as $b_exclude){
115                                     $b_exclude = trim($b_exclude);
116                                     if($b_exclude != ''){
117                                         $backup_excludes .= ' --exclude='.escapeshellarg($b_exclude);
118                                     }
119                                 }
120                             }
121                         
122                             if($backup_mode == 'userzip') {
123                                 //* Create a .zip backup as web user and include also files owned by apache / nginx user
124                                 $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.zip';
125                                 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);
126                                 if($retval == 0 || $retval == 12) 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);
127                             } else {
128                                 //* Create a tar.gz backup as root user
129                                 $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.tar.gz';
130                                 exec('tar pczf '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' --exclude=backup\*'.$backup_excludes.' --directory '.escapeshellarg($web_path).' .', $tmp_output, $retval);
131                             }
132                             if($retval == 0 || ($backup_mode != 'userzip' && $retval == 1) || ($backup_mode == 'userzip' && $retval == 12)) { // tar can return 1, zip can return 12(due to harmless warings) and still create valid backups  
133                                 if(is_file($web_backup_dir.'/'.$web_backup_file)){
134                                     chown($web_backup_dir.'/'.$web_backup_file, 'root');
135                                     chgrp($web_backup_dir.'/'.$web_backup_file, 'root');
136                                     chmod($web_backup_dir.'/'.$web_backup_file, 0750);
5de2af 137
746e52 138                                     //* Insert web backup record in database
MC 139                                     //$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)."')";
140                                     //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
141                                     $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)."')";
142                                     $app->db->query($sql);
143                                     if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
144                                 }
145                             } else {
146                                 if(is_file($web_backup_dir.'/'.$web_backup_file)) unlink($web_backup_dir.'/'.$web_backup_file);
147                             }
5de2af 148
746e52 149                             //* Remove old backups
MC 150                             $backup_copies = intval($rec['backup_copies']);
5de2af 151
746e52 152                             $dir_handle = dir($web_backup_dir);
MC 153                             $files = array();
154                             while (false !== ($entry = $dir_handle->read())) {
155                                 if($entry != '.' && $entry != '..' && substr($entry, 0, 3) == 'web' && is_file($web_backup_dir.'/'.$entry)) {
156                                     $files[] = $entry;
157                                 }
158                             }
159                             $dir_handle->close();
5de2af 160
746e52 161                             rsort($files);
MC 162
163                             for ($n = $backup_copies; $n <= 10; $n++) {
164                                 if(isset($files[$n]) && is_file($web_backup_dir.'/'.$files[$n])) {
165                                     unlink($web_backup_dir.'/'.$files[$n]);
166                                     //$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])."'";
167                                     //$tmp = $app->dbmaster->queryOneRecord($sql);
168                                     //$app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']);
169                                     //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
170                                     $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
171                                     $app->db->query($sql);
172                                     if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
173                                 }
174                             }
175
176                             unset($files);
177                             unset($dir_handle);
178
179                             //* Remove backupdir symlink and create as directory instead
180                             $app->system->web_folder_protection($web_path, false);
181
182                             if(is_link($web_path.'/backup')) {
183                                 unlink($web_path.'/backup');
184                             }
185                             if(!is_dir($web_path.'/backup')) {
186                                 mkdir($web_path.'/backup');
187                                 chown($web_path.'/backup', $rec['system_user']);
188                                 chgrp($web_path.'/backup', $rec['system_group']);
189                             }
190
191                             $app->system->web_folder_protection($web_path, true);
192                         }
193
194                         /* If backup_interval is set to none and we have a
195                         backup directory for the website, then remove the backups */
196                         if($rec['backup_interval'] == 'none' || $rec['backup_interval'] == '') {
197                             $web_id = $rec['domain_id'];
198                             $web_user = $rec['system_user'];
199                             $web_backup_dir = realpath($backup_dir.'/web'.$web_id);
200                             if(is_dir($web_backup_dir)) {
201                                 exec('sudo -u '.escapeshellarg($web_user).' rm -f '.escapeshellarg($web_backup_dir.'/*'));
ebbe63 202                                 $sql = "DELETE FROM web_backup WHERE server_id = ".intval($conf['server_id'])." AND parent_domain_id = ".intval($web_id);
MC 203                                 $app->db->query($sql);
204                                 if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
746e52 205                             }
MC 206                         }
207                     }
208                 }
209
210                 $sql = "SELECT * FROM web_database WHERE server_id = ".$conf['server_id']." AND backup_interval != 'none' AND backup_interval != ''";
211                 $records = $app->db->queryAllRecords($sql);
212                 if(is_array($records)) {
213
214                     include 'lib/mysql_clientdb.conf';
215
216                     foreach($records as $rec) {
217
218                         //* Do the database backup
219                         if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
220
221                             $web_id = $rec['parent_domain_id'];
222                             $db_backup_dir = $backup_dir.'/web'.$web_id;
223                             if(!is_dir($db_backup_dir)) mkdir($db_backup_dir, 0750);
224                             chmod($db_backup_dir, 0750);
225                             chown($db_backup_dir, 'root');
226                             chgrp($db_backup_dir, 'root');
227
228                             //* Do the mysql database backup with mysqldump
229                             $db_id = $rec['database_id'];
230                             $db_name = $rec['database_name'];
b1a6a5 231                             $db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i').'.sql';
MC 232                             //$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."'";
746e52 233                             $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."'";
b1a6a5 234                             exec($command, $tmp_output, $retval);
5de2af 235
b1a6a5 236                             //* Compress the backup with gzip
MC 237                             if($retval == 0) exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'", $tmp_output, $retval);
5de2af 238
b1a6a5 239                             if($retval == 0){
746e52 240                                 if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')){
MC 241                                     chmod($db_backup_dir.'/'.$db_backup_file.'.gz', 0750);
242                                     chown($db_backup_dir.'/'.$db_backup_file.'.gz', fileowner($db_backup_dir));
243                                     chgrp($db_backup_dir.'/'.$db_backup_file.'.gz', filegroup($db_backup_dir));
5de2af 244
746e52 245                                     //* Insert web backup record in database
MC 246                                     //$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')";
247                                     //$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
248                                     $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')";
249                                     $app->db->query($sql);
250                                     if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
251                                 }
b1a6a5 252                             } else {
MC 253                                 if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')) unlink($db_backup_dir.'/'.$db_backup_file.'.gz');
254                             }
255                             //* Remove the uncompressed file
256                             if(is_file($db_backup_dir.'/'.$db_backup_file)) unlink($db_backup_dir.'/'.$db_backup_file);
5de2af 257
746e52 258                             //* Remove old backups
MC 259                             $backup_copies = intval($rec['backup_copies']);
5de2af 260
746e52 261                             $dir_handle = dir($db_backup_dir);
MC 262                             $files = array();
263                             while (false !== ($entry = $dir_handle->read())) {
264                                 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)) {
265                                     if(array_key_exists($matches[1], $files) == false) $files[$matches[1]] = array();
266                                     $files[$matches[1]][] = $entry;
267                                 }
268                             }
269                             $dir_handle->close();
5de2af 270
746e52 271                             reset($files);
MC 272                             foreach($files as $db_name => $filelist) {
273                                 rsort($filelist);
274                                 for ($n = $backup_copies; $n <= 10; $n++) {
275                                     if(isset($filelist[$n]) && is_file($db_backup_dir.'/'.$filelist[$n])) {
276                                         unlink($db_backup_dir.'/'.$filelist[$n]);
277                                         //$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])."'";
278                                         //$tmp = $app->dbmaster->queryOneRecord($sql);
279                                         //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
280                                         $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
b1a6a5 281                                         $app->db->query($sql);
746e52 282                                         if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
b1a6a5 283                                     }
MC 284                                 }
285                             }
5de2af 286
746e52 287                             unset($files);
MC 288                             unset($dir_handle);
b1a6a5 289                         }
MC 290                     }
746e52 291
MC 292                     unset($clientdb_host);
293                     unset($clientdb_user);
294                     unset($clientdb_password);
295
b1a6a5 296                 }
5de2af 297
746e52 298                 // remove non-existing backups from database
MC 299                 $backups = $app->db->queryAllRecords("SELECT * FROM web_backup WHERE server_id = ".$conf['server_id']);
300                 if(is_array($backups) && !empty($backups)){
301                     foreach($backups as $backup){
302                         $backup_file = $backup_dir.'/web'.$backup['parent_domain_id'].'/'.$backup['filename'];
303                         if(!is_file($backup_file)){
304                             $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = ".$backup['parent_domain_id']." AND filename = '".$backup['filename']."'";
305                             $app->db->query($sql);
306                             if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
307                         }
308                     }
309                 }
310             } else {
311                 //* send email to admin that backup directory could not be mounted
312                 $global_config = $app->getconf->get_global_config('mail');
313                 if($global_config['admin_mail'] != ''){
314                     $subject = 'Backup directory '.$backup_dir.' could not be mounted';
315                     $message = "Backup directory ".$backup_dir." could not be mounted.\n\nThe command\n\n".$server_config['backup_dir_mount_cmd']."\n\nfailed.";
316                     mail($global_config['admin_mail'], $subject, $message);
317                 }
b1a6a5 318             }
MC 319         }
320
321         parent::onRunJob();
322     }
323
324     /* this function is optional if it contains no custom code */
325     public function onAfterRun() {
326         global $app;
327
328         parent::onAfterRun();
329     }
5de2af 330
M 331 }
332
333 ?>