From d70390197ee3e06ee1fecbc14d1ed48ba4040c94 Mon Sep 17 00:00:00 2001 From: neodarz Date: Sat, 28 Sep 2019 19:30:41 +0200 Subject: Initial commit --- .gitignore | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 7 +++ commands.py | 91 +++++++++++++++++++++++++++++++++++++++ config.py | 24 +++++++++++ file.py | 8 ++++ log.py | 20 +++++++++ pykeepalive.py | 42 ++++++++++++++++++ tools.py | 37 ++++++++++++++++ 8 files changed, 360 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 commands.py create mode 100644 config.py create mode 100644 file.py create mode 100644 log.py create mode 100644 pykeepalive.py create mode 100644 tools.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01495c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Vim tmp file +.pykeepalive.py.swp + +# Log file +log.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..64f40cd --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# PyKeepAlive + +Rewrite of +["openwrt-lte-keep-alive"](https://github.com/mchsk/openwrt-lte-keep-alive) +for fun, do the same. + +It ping my server and a FDN DNS server, it can be changes in `config.py`. diff --git a/commands.py b/commands.py new file mode 100644 index 0000000..6ddb940 --- /dev/null +++ b/commands.py @@ -0,0 +1,91 @@ +#/bin/python3 + +import sys + +import tailhead + +import platform +import subprocess + +import datetime, time + +import json + +def _make_gen(reader): + b = reader(1024 * 1024) + while b: + yield b + b = reader(1024*1024) + +def file_len(filename): + """ More opti than wc on turis """ + f = open(filename, 'rb') + f_gen = _make_gen(f.raw.read) + return sum( buf.count(b'\n') for buf in f_gen ) + +def multi_ping(hosts, interface): + """ + Returns True if host (list of 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. + """ + + # Option for the number of packets as a function of + count = '-n' if platform.system().lower()=='windows' else '-c' + source = '-S' if platform.system().lower()=='windows' else '-I' + + if type(hosts) is list: + procs = [] + for host in hosts: + command = ['ping', count, '1', source, interface, host] + proc = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + procs.append(proc) + + procs_done = [] + while True: + for proc in procs: + if proc.poll() is not None: + procs_done.append(proc.poll()) + procs.pop(procs.index(proc)) + if len(procs) == 0: + #print("o") + break + time.sleep(1) + + if 0 in procs_done: + return 0 + else: + return 1 + + else: + print("fuck you host must be a list") + sys.exit() + + +def log_write(status): + with open(LOG_PATH_FILE, 'a') as f: + f.write("{} {}\n".format(tools.date(), status)) + +def uqmi_current_settings(): + command = ['uqmi', '-d', '/dev/cdc-wdm0', '--get-current-settings'] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + datas = json.loads(proc.stdout) + return datas["ipv4"]["ip"], datas["ipv4"]["gateway"], datas["ipv4"]["subnet"] + +def ifconfig_update(interface, ip, subnet): + command = ['ifconfig', interface, ip, 'netmask', subnet, 'up'] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +def route_update(gateway): + command = ['route', 'add', 'default', 'gw', gateway] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +def tail(file, lines): + return tailhead.tail(open(file, 'rb'), lines) + + +def date(): + # Calculate the offset taking into account daylight saving time + utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone + utc_offset = datetime.timedelta(seconds=-utc_offset_sec) + return datetime.datetime.now().replace(tzinfo=datetime.timezone(offset=utc_offset)).isoformat() + diff --git a/config.py b/config.py new file mode 100644 index 0000000..c9cc4dc --- /dev/null +++ b/config.py @@ -0,0 +1,24 @@ +#/bin/python3 + +import os +import sys +from pathlib import Path, PurePath + +import tools +import commands + +DIR=os.path.dirname(os.path.abspath( __file__ )) +LOG_FILE=PurePath("log.txt") +LOG_PATH_FILE=Path(DIR / LOG_FILE) +if not os.path.exists(LOG_PATH_FILE): + os.mknod(LOG_PATH_FILE) + +OFFLINE_COUNT=tools.offline_counts(LOG_PATH_FILE) +OFFLINE_COUNT_TRESHOLD=4 + +LINES_MAX=11000 +LINES_MIN=6000 +LINES_COUNT=commands.file_len(LOG_PATH_FILE) + +HOSTS = ["80.67.169.12", "neodarz.net"] +INTERFACE = "wwan0" diff --git a/file.py b/file.py new file mode 100644 index 0000000..efad937 --- /dev/null +++ b/file.py @@ -0,0 +1,8 @@ +#/bin/python3 + +from config import * +import commands + +def log_write(status): + with open(LOG_PATH_FILE, 'a') as f: + f.write("{} {}\n".format(commands.date(), status)) diff --git a/log.py b/log.py new file mode 100644 index 0000000..a4ff80c --- /dev/null +++ b/log.py @@ -0,0 +1,20 @@ +#/bin/python3 + +import platform +import subprocess + +from config import * +import commands + +def tail_log(): + if commands.file_len(LOG_PATH_FILE) > LINES_MAX: + if platform.system().lower()=='windows': + datas = commands.tail(LOG_PATH_FILE, LINES_MIN) + with open(LOG_PATH_FILE, "wb") as f: + f.write(b' \n'.join(datas)) + else: + command = ["tail", "-"+str(LINES_MIN), str(LOG_PATH_FILE)] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + datas = proc.stdout.decode('utf-8') + with open(LOG_PATH_FILE, "w") as f: + f.write("".join(datas)) diff --git a/pykeepalive.py b/pykeepalive.py new file mode 100644 index 0000000..e4d54af --- /dev/null +++ b/pykeepalive.py @@ -0,0 +1,42 @@ +#/bin/python3 + +import sys, time +import tailhead + +from config import * + +import tools +import commands +import file +import log + +if len(sys.argv) == 1: + log.tail_log() + if commands.multi_ping(HOSTS, INTERFACE) == 0: + file.log_write("ONLINE") + else: + print("Ooops, we're offline!") + file.log_write("OFFLINE >> RESETING INTERFACE [{}]".format(INTERFACE)) + print(">> Reseting interface [{}]...".format(INTERFACE)) + ip, gateway, subnet = commands.uqmi_current_settings() + commands.ifconfig_update(INTERFACE, ip, subnet) + commands.route_update(gateway) +elif len(sys.argv) == 2: + if sys.argv[1] == "tail": + log.tail_log() + for line in tailhead.follow_path(LOG_PATH_FILE): + if line is not None: + print(tools.status_color(line)) + else: + time.sleep(1) + elif sys.argv[1] == "cat": + log.tail_log() + with open(LOG_PATH_FILE) as f: + for line in f: + print(tools.status_color(line), end="") + else: + tools.show_help() +else: + tools.show_help() + + diff --git a/tools.py b/tools.py new file mode 100644 index 0000000..862f15d --- /dev/null +++ b/tools.py @@ -0,0 +1,37 @@ +#/bin/python3 + +import sys +import re +import colorama +from termcolor import colored +colorama.init() + +from collections import Counter + +import commands + +def offline_counts(file): + counts = Counter() + for sentence in commands.tail(file, 4): + counts.update(word.strip('.,?!"\'').upper() for word in sentence.decode('utf-8').split()) + return counts["OFFLINE"] + +def show_help(): + print("{} [command]".format(sys.argv[0])) + print("{} Check internet status, log status in file and, if\n {}needed, restart interface.".format(sys.argv[0], " " * len(sys. argv[0]))) + print("{} tail Follow log file like tail command".format(sys.argv[0])) + print("{} cat Like cat command".format(sys.argv[0])) + +offline = re.compile(r'.*OFFLINE.*', re.IGNORECASE) +online = re.compile(r'.*ONLINE.*', re.IGNORECASE) + +def status_color(str_line): + if type(str_line) is bytes: + str_line = str_line.decode('utf-8') + if offline.search(str_line): + return colored(str_line, 'red') + elif online.search(str_line): + return colored(str_line, 'green') + else: + return str_line + -- cgit v1.2.1