def hangup(unused_sig: int, unused_frame: object):
	"""
	Handles the SIGHUP signal by re-reading conf files (if available) and resuming execution
	"""
	global confFile, collectors

	# Signal to the threads to stop
	for collector in collectors:
		collector.pipe[0].send(True)

	# Wait for the threads to exit
	for collector in collectors:
		collector.join()

	# Re-read the input file if exists
	# If it doesn't, print an error and go about your business
	if not confFile:
		utils.error(IOError("No input file to read! (input given on stdin)"))
	else:
		readConf()

	for collector in collectors:
		collector.start()

	raise ContinueException()
	def pingloop(self):
		"""
		Runs a loop for collecting ping statistics as specified in the
		configuration.
		"""
		printFunc = self.printJSONPing if self.conf.JSON else self.printPing
		try:
			# with multiprocessing.pool.ThreadPool(self.conf.NUMPINGS) as pool, ping.Pinger(self.host, bytes(self.conf.PAYLOAD)) as pinger:
				# while True:
				# 	try:
				# 		self.ping(pool, pinger)
				# 	except multiprocessing.TimeoutError:
				# 		self.result[0] = utils.PingResult(-1, -1, -1, -1, 100.)
				# 	printFunc()
				# 	time.sleep(self.conf.PING / 1000)
			with ping.Pinger(self.host, bytes(self.conf.PAYLOAD)) as pinger:
				while True:
					try:
						printFunc(pinger.sendAll(self.conf.NUMPINGS))
					except (socket.gaierror, OSError, TimeoutError) as e:
						utils.error(e)
						printFunc(utils.PingResult(-1, -1, -1, -1, 100.))
					time.sleep(self.conf.PING / 1000)
		except KeyboardInterrupt:
			pass
def readConf():
	"""
	Reads a configuration file. Expects a file object, which can be a true
	file or a pipe such as stdin
	"""
	global collectors, confFile, config

	# Try to open config file if exists, fatal error if file pointed to
	# Does not/no longer exist(s)
	if confFile:
		try:
			file = open(confFile)
		except OSError as e:
			utils.error(FileNotFoundError("Couldn't read input file '%s'"%e), fatal=True)
		hosts = file.readlines()
		file.close()

	# Closing stdin can cause huge problems, esp. for e.g. debuggers
	else:
		hosts = sys.stdin.readlines()

	# You need to clear this, or the monitor will keep querying old hosts.
	collectors = []

	#parse args
	for i,host in enumerate(hosts):

		# ignore empty lines
		if not host.strip():
			continue

		args = host.split()
		host = args.pop(0)
		addrinfo = utils.getaddr(host)
		if not addrinfo:
			utils.error(Exception("Unable to resolve host ( %s )" % host))
			sys.stderr.flush()
			continue

		conf = {"HOSTS": {host: addrinfo}}
		try:
			for arg, value in [a.upper().split('=') for a in args]:
				conf[arg] = config[arg](value)
		except ValueError as e:
			utils.error(IOError("Error parsing value for %s: %s" % (arg,e)), True)
		except KeyError as e:
			utils.error(IOError("Error in config file - unknown option '%s'" % arg), True)

		collectors.append(Collector(host, i+1, Config(**conf)))

	if not collectors:
		utils.error(Exception("No hosts could be parsed!"), fatal=True)
def main() -> int:
	"""
	Runs the main routine, returning an exit status indicating successful termination
	"""
	global confFile, collectors

	signal.signal(signal.SIGHUP, hangup)
	signal.signal(signal.SIGTERM, terminate)

	# Construct a persistent monitor based on argv
	if len(sys.argv) > 1:
		confFile = sys.argv[1]

	readConf()

	# Start the collectors
	for c in collectors:
		c.start()

	# The main thread just checks to see that all of the sub-threads are still going, and handles
	# exceptions.
	try:
		while True:
			try:
				time.sleep(5)
				if not collectors or not any(c.is_alive() for c in collectors):
					return 1
			except ContinueException:
				pass

	except KeyboardInterrupt:
		for c in collectors:
			c.pipe[0].send(True)
		for c in collectors:
			c.join()
	except Exception as e:
		utils.error(e)
		return 1
	print() # Flush the buffer
	return 0
	def run(self):
		"""
		Called when the thread is run
		"""

		# Determine output headers now to save time later
		self.plaintextHdr = self.hostname
		if self.host[0] != self.hostname:
			self.plaintextHdr += " " + self.host[0]

		if self.conf.TIMESTAMP:
			self.jsonHdr = '{"addr":"%s","name":"%s","timestamp":%%f,%%s}'
		else:
			self.jsonHdr = '{"addr":"%s", "name":"%s", %%s}'
		self.jsonHdr %= (self.host[0], self.hostname)

		numThreads = sum(int(bool(x)) for x in (self.conf.PING, self.conf.TRACE, self.conf.SCAN))

		with multiprocessing.pool.ThreadPool(numThreads) as pool:
			try:
				waitables = []
				if self.conf.SCAN:
					waitables.append(pool.apply_async(self.portscanloop, (), error_callback=utils.error))
				if self.conf.TRACE:
					waitables.append(pool.apply_async(self.traceloop, (), error_callback=utils.error))
				if self.conf.PING:
					waitables.append(pool.apply_async(self.pingloop, (), error_callback=utils.error))

				for waitable in waitables:
					waitable.wait()

				pool.close()
				pool.join()

			except KeyboardInterrupt:
				pass
			except Exception as e:
				utils.error("Unknown Error Occurred while polling.")
				utils.error(e)