tbrehm
2013-08-02 2332b2279d8a8599b4f041370315edc9544b1560
commit | author | age
dead5c 1 <?php
V 2
3 /*
4   Copyright (c) 2007-2011, Till Brehm, projektfarm Gmbh and Oliver Vogel www.muv.com
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 rescue_core_module {
32
33     var $module_name = 'rescue_core_module';
34     var $class_name = 'rescue_core_module';
35     /* No actions at this time. maybe later... */
36     var $actions_available = array();
4ebc7d 37     /**
2332b2 38      * The monitoring-Data of this module.
4ebc7d 39      * [0] are the actual data, [1] are the data 1 minnute ago [2] are teh data 2 minuntes...
V 40      */
41     private $_monitoringData = array();
42     /** The rescue-Data of this module. */
43     private $_rescueData = array();
dead5c 44
V 45     /**
46      *  This function is called during ispconfig installation to determine
47      *  if a symlink shall be created for this plugin.
48      */
49     function onInstall() {
50         return true;
51     }
52
53     /**
54      * This function is called when the module is loaded
55      */
56     function onLoad() {
4ebc7d 57         $this->_doRescue();
dead5c 58     }
V 59
4ebc7d 60     /**
V 61      * This function is called when a change in one of the registered tables is detected.
62      * The function then raises the events for the plugins.
dead5c 63      */
V 64     function process($tablename, $action, $data) {
65         // not needed
66     }
67
4ebc7d 68     /**
V 69      * This Method tries to rescue the server if a service is down.
dead5c 70      */
4ebc7d 71     private function _doRescue() {
V 72         /*
73          * do nothing, if the rescue-system is not enabled
74          */
dead5c 75         global $conf;
2332b2 76         if ((!isset($conf['serverconfig']['rescue']['try_rescue'])) ||
4ebc7d 77                 ((isset($conf['serverconfig']['rescue']['try_rescue'])) && ($conf['serverconfig']['rescue']['try_rescue'] !='y'))){
dead5c 78             return;
4ebc7d 79         }
dead5c 80
V 81         /*
4ebc7d 82          * First we get the monitoring data needed for rescuing the system
dead5c 83          */
4ebc7d 84         $this->_monitoringData = $this->_getMonitoringData();
V 85
86         /*
87          * Next we get the rescue data needed for rescuing the system
88          */
89         $this->_rescueData = $this->_getRescueData();
2332b2 90
T 91         /*
92          * rescue MongoDB if needed
93          */
94         $this->_rescueMongoDB();
95
4ebc7d 96         /*
8ab3cd 97          * rescue mysql if needed (maybe httpd depends on mysql, so try this first!)
039b46 98          */
V 99         $this->_rescueMySql();
2332b2 100
039b46 101         /*
8ab3cd 102          * rescue httpd if needed
4ebc7d 103          */
8ab3cd 104         $this->_rescueHttpd();
2332b2 105
eed6b3 106         /*
4ebc7d 107          * The last step is to save the rescue-data
V 108          */
109         $this->_saveRescueData();
110     }
111
112     /**
113      * This gets the Monitoring-Data, needed for rescuing the system.
114      * Because we can not be 100% sure, that the mysql-DB is up and running, so we use the
115      * file-system (this is no problem, because this module is the only one using this data,
116      * so we do not have parallel access.
117      */
118     private function _getMonitoringData() {
dead5c 119         global $app;
2332b2 120
686742 121         $dataFilename = dirname(__FILE__) . "/../temp/rescue_module_monitoringdata.ser.txt";
2332b2 122
4ebc7d 123         /*
V 124          * If the file containing the data is too old (older than 5 minutes) it is better to
2332b2 125          * delete it, because it could be, that the server was down for some times and so the data
4ebc7d 126          * are outdated
V 127          */
128         if (file_exists($dataFilename) && (filemtime($dataFilename) < (time() - 5 * 60))) {
129             unlink($dataFilename);
130         }
dead5c 131
4ebc7d 132         /*
V 133          * Get the monitoring-data
134          */
135         if (file_exists($dataFilename)) {
136             $data = unserialize(file_get_contents($dataFilename));
dead5c 137         } else {
4ebc7d 138             $data = array();
V 139         }
2332b2 140
4ebc7d 141         /*
V 142          * $temp[0] was the data of the last monitoring (means 1 minute ago), $temp[1] is the data
143          * 2 minutes ago and so on. Now we have make place for the newest data...
144          */
145         $max = sizeof($data);
146         if ($max > 10){
147             $max = 10; // not more than 10 histories
148         }
149         for ($i = $max; $i > 0; $i--){
150             $data[$i] = $data[$i -1];
151         }
2332b2 152
4ebc7d 153         /*
V 154          * we need the monitoring tools
155          */
156         $app->load('monitor_tools');
157         $tools = new monitor_tools();
2332b2 158
4ebc7d 159         /*
V 160          * Call the needed Monitoring-step and get the data
161          */
162         $tmp[0] = $tools->monitorServices();
2332b2 163
4ebc7d 164         /* Add the data at the FIRST position of the history */
V 165         $data[0] = $tmp;
2332b2 166
4ebc7d 167         /*
V 168          * We have the newest monitoring data. Save it!
849624 169          * (and protect it, because there may be sensible data in it)
4ebc7d 170          */
V 171         file_put_contents($dataFilename, serialize($data));
849624 172         chmod($dataFilename, 0600);
2332b2 173
4ebc7d 174         /* Thats it */
V 175         return $data;
176     }
2332b2 177
4ebc7d 178     /**
V 179      * This gets the rescue-Data, needed for rescuing the system.
180      * Because we can not be 100% sure, that the mysql-DB is up and running, so we use the
181      * file-system (this is no problem, because this module is the only one using this data,
182      * so we do not have parallel access.
183      */
184     private function _getRescueData() {
686742 185         $dataFilename = dirname(__FILE__) . "/../temp/rescue_module_rescuedata.ser.txt";
4ebc7d 186
V 187         /*
188          * If the file containing the data is too old (older than 5 minutes) it is better to
2332b2 189          * delete it, because it could be, that the server was down for some times and so the data
4ebc7d 190          * are outdated
V 191          */
192         if (file_exists($dataFilename) && (filemtime($dataFilename) < (time() - 5 * 60))) {
193             unlink($dataFilename);
dead5c 194         }
V 195
196         /*
4ebc7d 197          * Get the rescue-data
dead5c 198          */
4ebc7d 199         if (file_exists($dataFilename)) {
V 200             $data = unserialize(file_get_contents($dataFilename));
201         } else {
202             $data = array();
dead5c 203         }
2332b2 204
4ebc7d 205         /* Thats it */
V 206         return $data;
dead5c 207     }
V 208
4ebc7d 209     /**
V 210      * Writes the rescue data to disk.
211      * Because we can not be 100% sure, that the mysql-DB is up and running, so we use the
212      * file-system (this is no problem, because this module is the only one using this data,
213      * so we do not have parallel access.
214      */
215     private function _saveRescueData() {
686742 216         $dataFilename = dirname(__FILE__) . "/../temp/rescue_module_rescuedata.ser.txt";
849624 217         /*
V 218          * We have the newest data. Save it!
219          * (and protect it, because there may be sensible data in it)
220          */
4ebc7d 221         file_put_contents($dataFilename, serialize($this->_rescueData));
849624 222         chmod($dataFilename, 0600);
4ebc7d 223     }
V 224
225     /**
8ab3cd 226      * restarts httpd, if needed
4ebc7d 227      */
8ab3cd 228     private function _rescueHttpd(){
4ebc7d 229         global $app, $conf;
2332b2 230
dead5c 231         /*
8ab3cd 232          * do nothing, if it is not allowed to rescue httpd
dead5c 233          */
8ab3cd 234         if ((isset($conf['serverconfig']['rescue']['do_not_try_rescue_httpd']) && ($conf['serverconfig']['rescue']['do_not_try_rescue_httpd']) == 'y')){
dead5c 235             return;
4ebc7d 236         }
2332b2 237
4ebc7d 238         /*
V 239          * if the service is up and running, or the service is not installed there is nothing to do...
240          */
241         if ($this->_monitoringData[0][0]['data']['webserver'] != 0){
242             /* Clear the try counter, because we do not have to try to rescue the service */
243             $this->_rescueData['webserver']['try_counter'] = 0;
244             return;
245         }
2332b2 246
4ebc7d 247         /*
V 248          * OK, the service is installed and down.
249          * Maybe this is because of a restart of the service by the admin.
250          * This means, we check the data 1 minute ago
251          */
2332b2 252         if ((!isset($this->_monitoringData[1][0]['data']['webserver'])) ||
4ebc7d 253                 ((isset($this->_monitoringData[1][0]['data']['webserver'])) && ($this->_monitoringData[1][0]['data']['webserver'] != 0))){
2332b2 254             /*
T 255              * We do NOT have this data or we have this data, but the webserver was not down 1 minute ago.
256              * This means, it could be, that the admin is restarting the server.
4ebc7d 257              * We wait one more minute...
dead5c 258              */
4ebc7d 259             return;
V 260         }
2332b2 261
4ebc7d 262         /*#####
V 263          * The service is down and it was down 1 minute ago.
264          * We try to rescue it
265          *#####*/
2332b2 266
4ebc7d 267         /* Get the try counter */
V 268         $tryCount = (!isset($this->_rescueData['webserver']['try_counter']))? 1 : $this->_rescueData['webserver']['try_counter'] + 1;
2332b2 269
4ebc7d 270         /* Set the new try counter */
V 271         $this->_rescueData['webserver']['try_counter'] = $tryCount;
2332b2 272
4ebc7d 273         /* if 5 times will not work, we have to give up... */
V 274         if ($tryCount > 5){
8ab3cd 275             $app->log('httpd is down! Rescue will not help!', LOGLEVEL_ERROR);
4ebc7d 276             return;
V 277         }
2332b2 278
T 279
8ab3cd 280         $app->log('httpd is down! Try rescue httpd (try:' . $tryCount . ')...', LOGLEVEL_WARN);
2332b2 281
8ab3cd 282         if($conf['serverconfig']['web']['server_type'] == 'nginx'){
T 283             $daemon = 'nginx';
dead5c 284         } else {
8ab3cd 285             if(is_file($conf['init_scripts'] . '/' . 'httpd')) {
T 286                 $daemon = 'httpd';
287             } elseif(is_file($conf['init_scripts'] . '/' . 'httpd2')){
288                 $daemon = 'httpd2';
289             } else {
290                 $daemon = 'apache2';
291             }
dead5c 292         }
2332b2 293
eed6b3 294         $this->_rescueDaemon($daemon);
V 295     }
2332b2 296
T 297     /**
298      * restarts MongoDB, if needed
299      */
300     private function _rescueMongoDB(){
301         global $app, $conf;
302
303         /*
304          * do nothing, if it is not allowed to rescue mysql
305          */
306         if ((isset($conf['serverconfig']['rescue']['do_not_try_rescue_mongodb']) && ($conf['serverconfig']['rescue']['do_not_try_rescue_mongodb']) == 'y')){
307             return;
308         }
309
310         /*
311          * if the service is up and running, or the service is not installed there is nothing to do...
312          */
313         if ($this->_monitoringData[0][0]['data']['mongodbserver'] != 0){
314             /* Clear the try counter, because we do not have to try to rescue the service */
315             $this->_rescueData['mongodbserver']['try_counter'] = 0;
316             return;
317         }
318
319         /*
320          * OK, the service is installed and down.
321          * Maybe this is because of a restart of the service by the admin.
322          * This means, we check the data 1 minute ago
323          */
324         if ((!isset($this->_monitoringData[1][0]['data']['mongodbserver'])) ||
325                 ((isset($this->_monitoringData[1][0]['data']['mongodbserver'])) && ($this->_monitoringData[1][0]['data']['mongodbserver'] != 0))){
326             /*
327              * We do NOT have this data or we have this data, but the webserver was not down 1 minute ago.
328              * This means, it could be, that the admin is restarting the server.
329              * We wait one more minute...
330              */
331             return;
332         }
333
334         /*#####
335          * The service is down and it was down 1 minute ago.
336          * We try to rescue it
337          *#####*/
338
339         /* Get the try counter */
340         $tryCount = (!isset($this->_rescueData['mongodbserver']['try_counter']))? 1 : $this->_rescueData['mongodbserver']['try_counter'] + 1;
341
342         /* Set the new try counter */
343         $this->_rescueData['mongodbserver']['try_counter'] = $tryCount;
344
345         /* if 5 times will not work, we have to give up... */
346         if ($tryCount > 5){
347             $app->log('MongoDB is down! Rescue will not help!', LOGLEVEL_ERROR);
348             return;
349         }
350
351
352         $app->log('MongoDB is down! Try rescue MongoDB (try:' . $tryCount . ')...', LOGLEVEL_WARN);
353
354         if(is_file($conf['init_scripts'] . '/' . 'mongodb')) {
355             $daemon = 'mongodb';
356         } else {
357             $daemon = 'mongodb';
358         }
359
360         $this->_rescueDaemon($daemon);
361     }
362
eed6b3 363     /**
V 364      * restarts mysql, if needed
365      */
366     private function _rescueMySql(){
367         global $app, $conf;
2332b2 368
eed6b3 369         /*
V 370          * do nothing, if it is not allowed to rescue mysql
371          */
372         if ((isset($conf['serverconfig']['rescue']['do_not_try_rescue_mysql']) && ($conf['serverconfig']['rescue']['do_not_try_rescue_mysql']) == 'y')){
373             return;
374         }
2332b2 375
eed6b3 376         /*
V 377          * if the service is up and running, or the service is not installed there is nothing to do...
378          */
379         if ($this->_monitoringData[0][0]['data']['mysqlserver'] != 0){
380             /* Clear the try counter, because we do not have to try to rescue the service */
381             $this->_rescueData['mysqlserver']['try_counter'] = 0;
382             return;
383         }
2332b2 384
eed6b3 385         /*
V 386          * OK, the service is installed and down.
387          * Maybe this is because of a restart of the service by the admin.
388          * This means, we check the data 1 minute ago
389          */
2332b2 390         if ((!isset($this->_monitoringData[1][0]['data']['mysqlserver'])) ||
eed6b3 391                 ((isset($this->_monitoringData[1][0]['data']['mysqlserver'])) && ($this->_monitoringData[1][0]['data']['mysqlserver'] != 0))){
2332b2 392             /*
T 393              * We do NOT have this data or we have this data, but the webserver was not down 1 minute ago.
394              * This means, it could be, that the admin is restarting the server.
eed6b3 395              * We wait one more minute...
V 396              */
397             return;
398         }
2332b2 399
eed6b3 400         /*#####
V 401          * The service is down and it was down 1 minute ago.
402          * We try to rescue it
403          *#####*/
2332b2 404
eed6b3 405         /* Get the try counter */
V 406         $tryCount = (!isset($this->_rescueData['mysqlserver']['try_counter']))? 1 : $this->_rescueData['mysqlserver']['try_counter'] + 1;
2332b2 407
eed6b3 408         /* Set the new try counter */
V 409         $this->_rescueData['mysqlserver']['try_counter'] = $tryCount;
2332b2 410
eed6b3 411         /* if 5 times will not work, we have to give up... */
V 412         if ($tryCount > 5){
413             $app->log('MySQL is down! Rescue will not help!', LOGLEVEL_ERROR);
414             return;
415         }
2332b2 416
T 417
eed6b3 418         $app->log('MySQL is down! Try rescue mysql (try:' . $tryCount . ')...', LOGLEVEL_WARN);
V 419
420         if(is_file($conf['init_scripts'] . '/' . 'mysqld')) {
421             $daemon = 'mysqld';
422         } else {
423             $daemon = 'mysql';
424         }
2332b2 425
eed6b3 426         $this->_rescueDaemon($daemon);
V 427     }
428
429     /**
430      * Tries to stop and then restart the daemon
2332b2 431      *
eed6b3 432      * @param type $daemon the name of the daemon
V 433      */
434     private function _rescueDaemon($daemon){
435         global $conf;
2332b2 436
eed6b3 437         // if you need to find all restarts search for "['init_scripts']"
V 438         /*
439          * First we stop the running service "normally"
440          */
2332b2 441
dead5c 442         /*
4ebc7d 443          * ATTENTION!
V 444          * The service hangs. this means it could be, that "stop" will hang also.
445          * So we have to try to stop but if this will not work, we have to kill the stopping
446          * of the service
dead5c 447          */
4ebc7d 448         exec($conf['init_scripts'] . '/' . $daemon . ' stop && (sleep 3; kill $!; sleep 2; kill -9 $!) &> /dev/null');
2332b2 449
4ebc7d 450         /*
V 451          * OK, we tryed to stop it normally, maybe this worked maybe not. So we have to look
452          * if the service is already running or not. If so, we have to kill them hard
453          */
454         exec("kill -9 `ps -A | grep " . $daemon . "| grep -v grep | awk '{print $1}'` &> /dev/null");
2332b2 455
4ebc7d 456         /*
V 457          * There are no more zombies left. Lets start the service..
458          */
459         exec($conf['init_scripts'] . '/' . $daemon . ' start');
dead5c 460     }
V 461 }
462 ?>