background.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. # lint: pylint
  3. # pylint: disable=missing-module-docstring, missing-function-docstring
  4. import json
  5. import random
  6. import time
  7. import threading
  8. import os
  9. import signal
  10. from searx import logger, settings, searx_debug
  11. from searx.exceptions import SearxSettingsException
  12. from searx.search.processors import PROCESSORS
  13. from searx.search.checker import Checker
  14. from searx.shared import schedule, storage
  15. CHECKER_RESULT = 'CHECKER_RESULT'
  16. running = threading.Lock()
  17. def _get_interval(every, error_msg):
  18. if isinstance(every, int):
  19. every = (every, every)
  20. if not isinstance(every, (tuple, list))\
  21. or len(every) != 2\
  22. or not isinstance(every[0], int)\
  23. or not isinstance(every[1], int):
  24. raise SearxSettingsException(error_msg, None)
  25. return every
  26. def _get_every():
  27. every = settings.get('checker', {}).get('scheduling', {}).get('every', (300, 1800))
  28. return _get_interval(every, 'checker.scheduling.every is not a int or list')
  29. def get_result(): # pylint: disable=inconsistent-return-statements
  30. serialized_result = storage.get_str(CHECKER_RESULT)
  31. if serialized_result is not None:
  32. return json.loads(serialized_result)
  33. def _set_result(result, include_timestamp=True):
  34. if include_timestamp:
  35. result['timestamp'] = int(time.time() / 3600) * 3600
  36. storage.set_str(CHECKER_RESULT, json.dumps(result))
  37. def run():
  38. if not running.acquire(blocking=False): # pylint: disable=consider-using-with
  39. return
  40. try:
  41. logger.info('Starting checker')
  42. result = {
  43. 'status': 'ok',
  44. 'engines': {}
  45. }
  46. for name, processor in PROCESSORS.items():
  47. logger.debug('Checking %s engine', name)
  48. checker = Checker(processor)
  49. checker.run()
  50. if checker.test_results.succesfull:
  51. result['engines'][name] = {'success': True}
  52. else:
  53. result['engines'][name] = {'success': False, 'errors': checker.test_results.errors}
  54. _set_result(result)
  55. logger.info('Check done')
  56. except Exception: # pylint: disable=broad-except
  57. _set_result({'status': 'error'})
  58. logger.exception('Error while running the checker')
  59. finally:
  60. running.release()
  61. def _run_with_delay():
  62. every = _get_every()
  63. delay = random.randint(0, every[1] - every[0])
  64. logger.debug('Start checker in %i seconds', delay)
  65. time.sleep(delay)
  66. run()
  67. def _start_scheduling():
  68. every = _get_every()
  69. if schedule(every[0], _run_with_delay):
  70. run()
  71. def _signal_handler(_signum, _frame):
  72. t = threading.Thread(target=run)
  73. t.daemon = True
  74. t.start()
  75. def initialize():
  76. if hasattr(signal, 'SIGUSR1'):
  77. # Windows doesn't support SIGUSR1
  78. logger.info('Send SIGUSR1 signal to pid %i to start the checker', os.getpid())
  79. signal.signal(signal.SIGUSR1, _signal_handler)
  80. # disabled by default
  81. _set_result({'status': 'disabled'}, include_timestamp=False)
  82. # special case when debug is activate
  83. if searx_debug and settings.get('checker', {}).get('off_when_debug', True):
  84. logger.info('debug mode: checker is disabled')
  85. return
  86. # check value of checker.scheduling.every now
  87. scheduling = settings.get('checker', {}).get('scheduling', None)
  88. if scheduling is None or not scheduling:
  89. logger.info('Checker scheduler is disabled')
  90. return
  91. #
  92. _set_result({'status': 'unknown'}, include_timestamp=False)
  93. start_after = scheduling.get('start_after', (300, 1800))
  94. start_after = _get_interval(start_after, 'checker.scheduling.start_after is not a int or list')
  95. delay = random.randint(start_after[0], start_after[1])
  96. logger.info('Start checker in %i seconds', delay)
  97. t = threading.Timer(delay, _start_scheduling)
  98. t.daemon = True
  99. t.start()