| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | # SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							| 
									
										
										
										
											2021-05-05 16:47:02 +02:00
										 |  |  | # lint: pylint | 
					
						
							| 
									
										
										
										
											2021-09-07 13:34:35 +02:00
										 |  |  | # pylint: disable=missing-module-docstring | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import json | 
					
						
							|  |  |  | import random | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | import threading | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import signal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from searx import logger, settings, searx_debug | 
					
						
							|  |  |  | from searx.exceptions import SearxSettingsException | 
					
						
							| 
									
										
										
										
											2021-05-05 13:08:54 +02:00
										 |  |  | from searx.search.processors import PROCESSORS | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | from searx.search.checker import Checker | 
					
						
							|  |  |  | from searx.shared import schedule, storage | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CHECKER_RESULT = 'CHECKER_RESULT' | 
					
						
							|  |  |  | running = threading.Lock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _get_interval(every, error_msg): | 
					
						
							|  |  |  |     if isinstance(every, int): | 
					
						
							|  |  |  |         every = (every, every) | 
					
						
							| 
									
										
										
										
											2021-12-27 09:26:22 +01:00
										 |  |  |     if ( | 
					
						
							|  |  |  |         not isinstance(every, (tuple, list)) | 
					
						
							|  |  |  |         or len(every) != 2 | 
					
						
							|  |  |  |         or not isinstance(every[0], int) | 
					
						
							|  |  |  |         or not isinstance(every[1], int) | 
					
						
							|  |  |  |     ): | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |         raise SearxSettingsException(error_msg, None) | 
					
						
							|  |  |  |     return every | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _get_every(): | 
					
						
							|  |  |  |     every = settings.get('checker', {}).get('scheduling', {}).get('every', (300, 1800)) | 
					
						
							|  |  |  |     return _get_interval(every, 'checker.scheduling.every is not a int or list') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-14 18:22:42 +02:00
										 |  |  | def get_result(): | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |     serialized_result = storage.get_str(CHECKER_RESULT) | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |     if serialized_result is not None: | 
					
						
							|  |  |  |         return json.loads(serialized_result) | 
					
						
							| 
									
										
										
										
											2021-08-14 18:22:42 +02:00
										 |  |  |     return {'status': 'unknown'} | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-13 14:07:39 +01:00
										 |  |  | def _set_result(result, include_timestamp=True): | 
					
						
							|  |  |  |     if include_timestamp: | 
					
						
							|  |  |  |         result['timestamp'] = int(time.time() / 3600) * 3600 | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |     storage.set_str(CHECKER_RESULT, json.dumps(result)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | def run(): | 
					
						
							| 
									
										
										
										
											2021-12-27 09:26:22 +01:00
										 |  |  |     if not running.acquire(blocking=False):  # pylint: disable=consider-using-with | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |         return | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         logger.info('Starting checker') | 
					
						
							| 
									
										
										
										
											2021-12-27 09:26:22 +01:00
										 |  |  |         result = {'status': 'ok', 'engines': {}} | 
					
						
							| 
									
										
										
										
											2021-05-05 13:08:54 +02:00
										 |  |  |         for name, processor in PROCESSORS.items(): | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |             logger.debug('Checking %s engine', name) | 
					
						
							|  |  |  |             checker = Checker(processor) | 
					
						
							|  |  |  |             checker.run() | 
					
						
							|  |  |  |             if checker.test_results.succesfull: | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |                 result['engines'][name] = {'success': True} | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |                 result['engines'][name] = {'success': False, 'errors': checker.test_results.errors} | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |         _set_result(result) | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |         logger.info('Check done') | 
					
						
							| 
									
										
										
										
											2021-05-05 16:47:02 +02:00
										 |  |  |     except Exception:  # pylint: disable=broad-except | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |         _set_result({'status': 'error'}) | 
					
						
							|  |  |  |         logger.exception('Error while running the checker') | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         running.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _run_with_delay(): | 
					
						
							|  |  |  |     every = _get_every() | 
					
						
							|  |  |  |     delay = random.randint(0, every[1] - every[0]) | 
					
						
							|  |  |  |     logger.debug('Start checker in %i seconds', delay) | 
					
						
							|  |  |  |     time.sleep(delay) | 
					
						
							|  |  |  |     run() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _start_scheduling(): | 
					
						
							|  |  |  |     every = _get_every() | 
					
						
							| 
									
										
										
										
											2021-01-13 14:07:39 +01:00
										 |  |  |     if schedule(every[0], _run_with_delay): | 
					
						
							|  |  |  |         run() | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 16:47:02 +02:00
										 |  |  | def _signal_handler(_signum, _frame): | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |     t = threading.Thread(target=run) | 
					
						
							|  |  |  |     t.daemon = True | 
					
						
							|  |  |  |     t.start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def initialize(): | 
					
						
							| 
									
										
										
										
											2021-03-14 22:58:59 +01:00
										 |  |  |     if hasattr(signal, 'SIGUSR1'): | 
					
						
							|  |  |  |         # Windows doesn't support SIGUSR1 | 
					
						
							|  |  |  |         logger.info('Send SIGUSR1 signal to pid %i to start the checker', os.getpid()) | 
					
						
							|  |  |  |         signal.signal(signal.SIGUSR1, _signal_handler) | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  |     # disabled by default | 
					
						
							| 
									
										
										
										
											2021-01-17 16:56:36 +01:00
										 |  |  |     _set_result({'status': 'disabled'}, include_timestamp=False) | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |     # special case when debug is activate | 
					
						
							|  |  |  |     if searx_debug and settings.get('checker', {}).get('off_when_debug', True): | 
					
						
							|  |  |  |         logger.info('debug mode: checker is disabled') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # check value of checker.scheduling.every now | 
					
						
							|  |  |  |     scheduling = settings.get('checker', {}).get('scheduling', None) | 
					
						
							|  |  |  |     if scheduling is None or not scheduling: | 
					
						
							|  |  |  |         logger.info('Checker scheduler is disabled') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2021-01-13 14:07:39 +01:00
										 |  |  |     _set_result({'status': 'unknown'}, include_timestamp=False) | 
					
						
							| 
									
										
										
										
											2021-01-11 18:43:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-05 11:24:39 +01:00
										 |  |  |     start_after = scheduling.get('start_after', (300, 1800)) | 
					
						
							|  |  |  |     start_after = _get_interval(start_after, 'checker.scheduling.start_after is not a int or list') | 
					
						
							|  |  |  |     delay = random.randint(start_after[0], start_after[1]) | 
					
						
							|  |  |  |     logger.info('Start checker in %i seconds', delay) | 
					
						
							|  |  |  |     t = threading.Timer(delay, _start_scheduling) | 
					
						
							|  |  |  |     t.daemon = True | 
					
						
							|  |  |  |     t.start() |