def ping(self, p_host): """ Returns True if host (str) responds to a ping request. Remember that a host may not respond to a ping (ICMP) request even if the host name is valid. """ fmt = "{ping_command} -w 1 {c_option} {host}" raw_command = fmt.format(ping_command=self._config.ping_command, # Ping command count option as function of OS c_option='-n 1' if tools.is_windows() else '-c 1', host=shlex.quote(p_host)) command = shlex.split(raw_command) delay = None fmt = "Executing command {cmd} in Popen" self._logger.debug(fmt.format(cmd=command)) proc = subprocess.Popen(command, stdout=subprocess.PIPE) for line in proc.stdout: result = self.ping_result_regex.match(line.decode("UTF-8")) if result: delay = float(result.group(1)) break fmt = "Host {host} is {status}" self._logger.debug( fmt.format(host=p_host, status="responding (%.1f ms)" % delay if delay is not None else "down")) return delay
def install_handlers(self): for signal_id in self._active_signals: signal.signal(signal_id, self.handle_signal) if not tools.is_windows(): signal.pthread_sigmask(signal.SIG_UNBLOCK, self._active_signals)
def __init__(self, p_app_name, p_pid_file, p_arguments, p_dir_name, p_languages=None): super().__init__(pidfile=p_pid_file) self._app_name = p_app_name self._dir_name = p_dir_name self._languages = p_languages self._arguments = p_arguments self._logger = log_handling.get_logger(self.__class__.__name__) self._config = None self._recurring_tasks = [] self._downtime = 0 self._locale_helper = None self._latest_request = None self._partial_basic_init_executed = False self._full_basic_init_executed = False self._active_signals = [signal.SIGTERM, signal.SIGINT] self._done = False if not tools.is_windows(): self._active_signals.append(signal.SIGHUP) if self._languages is None: self._languages = DEFAULT_LANGUAGES self._langs = {} # Only temporary until the app has been initialized completely! self._app_config = BaseAppConfigModel()
def ping(self, p_host): """ Returns True if host (str) responds to a ping request. Remember that a host may not respond to a ping (ICMP) request even if the host name is valid. """ # Ping command count option as function of OS param = '-n 1' if tools.is_windows() else '-c 1' # Building the command. Ex: "ping -c 1 google.com" command = [self._config.ping_command, param, p_host] delay = None proc = subprocess.Popen(command, stdout=subprocess.PIPE) for line in proc.stdout: result = self.ping_result_regex.match(line.decode("UTF-8")) if result: delay = float(result.group(1)) break fmt = "Host {host} is {status}" self._logger.debug( fmt.format(host=p_host, status="responding (%.1f ms)" % delay if delay is not None else "down")) return delay
def local_ping(self, p_host): """ Checks if a host reacts to a ICMP ping request. Remember that a host may not respond to a ping (ICMP) request even if the host name is valid. :param p_host: DNS or ip address of the host to be pinged :return: the delay in milliseconds (as float) if the host responds, None otherwise """ fmt = "{ping_command} {w_option} 1 {c_option} {host}" raw_command = fmt.format( ping_command=self._config.ping_command, # Ping command count option as function of OS c_option='-n 1' if tools.is_windows() else '-c 1', w_option=self._config.ping_wait_option, host=shlex.quote(p_host)) command = shlex.split(raw_command) delay = None fmt = "Executing command {cmd} in Popen" self._logger.debug(fmt.format(cmd=command)) # make sure we get floating points and not commas! run_env = os.environ.copy() run_env["LANG"] = "en_US" proc = subprocess.run(command, stdout=subprocess.PIPE, env=run_env) if proc.returncode >= 2: fmt = "{cmd} returns exit code {exitcode}" raise ConfigurationException( fmt.format(cmd=command, exitcode=proc.returncode)) stdout_string = proc.stdout.decode("UTF-8") for line in stdout_string.split("\n"): fmt = "ping output: {line}" self._logger.debug(fmt.format(line=line)) result = self.ping_result_regex.match(line) if result: delay = float(result.group(3)) break fmt = "Host {host} is {status}" self._logger.debug( fmt.format(host=p_host, status="responding (%.1f ms)" % delay if delay is not None else "down")) return delay
def play_audio_file(self, p_audio_filename): # pragma: no cover if tools.is_windows(): binary = '"' + self._mpg123_binary + '"' filename = '"' + p_audio_filename + '"' else: binary = self._mpg123_binary filename = p_audio_filename play_command = self._play_command_pattern.format(binary=binary, filename=filename) msg = f"Executing command '{play_command}'..." self._logger.debug(msg) cmd_array = shlex.split(play_command) subprocess.run(cmd_array)
def stop(self): """ Stop the daemon """ if self.verbose >= 1: self.log("Stopping daemon...") # Get the pid from the pidfile pid = self.get_pid() if not pid: message = "pidfile %s does not exist. Not running?\n" sys.stderr.write(message % self.pidfile) # Just to be sure. A ValueError might occur if the PID file is # empty but does actually exist if os.path.exists(self.pidfile): os.remove(self.pidfile) return # Not an error in a restart # Try killing the daemon process try: i = 0 while 1: os.kill(pid, signal.SIGTERM) time.sleep(0.1) i = i + 1 if not tools.is_windows(): if i % 10 == 0: os.kill(pid, signal.SIGHUP) except OSError as err: if err.errno == errno.ESRCH: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print(str(err)) sys.exit(1) self.log("Daemon stopped")
def get_site_packages_dir(): major_version = sys.version_info[0] minor_version = sys.version_info[1] if tools.is_windows(): pattern = f".+Python\.{major_version}\.{minor_version}.+\\\\lib$" else: pattern = f".+python{major_version}\.{minor_version}/(site|dist)-packages$" regex = re.compile(pattern) site_packages_dirs = [p for p in sys.path if regex.match(p)] if len(site_packages_dirs) != 1: msg = f"Cannot determine unique site-package dir with pattern '{pattern}'! Candidates {site_packages_dirs} remaining from {sys.path}" raise Exception(msg) return site_packages_dirs[0]
def event_queue(self): self._done = False self._logger.info("Entering event queue...") while not self._done: try: now = datetime.datetime.utcnow() if len(self._recurring_tasks) > 0: task = self._recurring_tasks[0] wait_in_seconds = (task - now).total_seconds() else: task = None wait_in_seconds = ETERNITY if wait_in_seconds > 0: try: fmt = "Sleeping for {seconds} seconds (or until next signal)" self._logger.debug(fmt.format(seconds=wait_in_seconds)) if not tools.is_windows(): signal.pthread_sigmask(signal.SIG_UNBLOCK, self._active_signals) time.sleep(wait_in_seconds) except exceptions.SignalHangUp as e: raise e except Exception as e: if self._app_config.debug_mode: fmt = "Propagating exception due to debug_mode=True" self._logger.warn(fmt) raise e fmt = "Exception %s while waiting for signal" % str(e) self._logger.error(fmt) fmt = "Woken by signal" self._logger.debug(fmt) if task is not None: now = datetime.datetime.utcnow() overslept_in_seconds = (now - task).total_seconds() if overslept_in_seconds > self._app_config.maximum_timer_slack: self.track_downtime( p_downtime=overslept_in_seconds) if len(self._recurring_tasks) > 0: task_executed = True while task_executed: task: RecurringTask = self._recurring_tasks[0] now = datetime.datetime.utcnow() if now > task: delay = (now - task).total_seconds() task = heapq.heappop(self._recurring_tasks) self.add_recurring_task(p_recurring_task=task) fmt = "Executing task {task} {secs:.3f} [s] behind schedule... *** START ***" self._logger.debug( fmt.format(task=task.name, secs=delay)) try: task.handler_method() except Exception as e: self._logger.error( f"Exception {str(e)} while executing task {task.name}" ) if not task.ignore_exceptions: raise e fmt = "Executing task {task} {secs:.3f} [s] behind schedule... *** END ***" self._logger.debug( fmt.format(task=task.name, secs=delay)) if delay > self._app_config.minimum_downtime_duration: self.track_downtime(p_downtime=delay) else: task_executed = False if self._downtime > 0: self.handle_downtime(p_downtime=int(self._downtime)) self.reset_down_time() except exceptions.SignalHangUp: fmt = "Event queue interrupted by signal" self._logger.info(fmt) self._done = True except Exception as e: if self._app_config.debug_mode: fmt = "Propagating exception due to debug_mode=True" self._logger.warn(fmt) raise e fmt = "Exception %s in event queue" % str(e) self._logger.error(fmt) tools.log_stack_trace(p_logger=self._logger) if self._arguments.single_run: self._done = True self._logger.info("Leaving event queue...")