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